forked from M-Labs/artiq
gui/explorer: file selector for experiments outside repos
This commit is contained in:
parent
7b2580583a
commit
93317d48c9
|
@ -98,7 +98,7 @@ def get_argparser():
|
|||
|
||||
parser_ls = subparsers.add_parser(
|
||||
"ls", help="list a directory on the master")
|
||||
parser_ls.add_argument("directory")
|
||||
parser_ls.add_argument("directory", default="", nargs="?")
|
||||
|
||||
return parser
|
||||
|
||||
|
@ -160,11 +160,8 @@ def _action_scan_repository(remote, args):
|
|||
|
||||
def _action_ls(remote, args):
|
||||
contents = remote.list_directory(args.directory)
|
||||
for name, is_dir in sorted(contents, key=lambda x: (-x[1], x[0])):
|
||||
if is_dir:
|
||||
print("<DIR> " + name)
|
||||
else:
|
||||
print(" " + name)
|
||||
for name in sorted(contents, key=lambda x: (x[-1] not in "\\/", x)):
|
||||
print(name)
|
||||
|
||||
|
||||
def _show_schedule(schedule):
|
||||
|
|
|
@ -13,29 +13,94 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class _OpenFileDialog(QtGui.QDialog):
|
||||
def __init__(self, parent, exp_manager):
|
||||
QtGui.QDialog.__init__(self, parent=parent)
|
||||
def __init__(self, explorer, exp_manager, experiment_db_ctl):
|
||||
QtGui.QDialog.__init__(self, parent=explorer)
|
||||
self.resize(710, 700)
|
||||
self.setWindowTitle("Open file outside repository")
|
||||
|
||||
self.explorer = explorer
|
||||
self.exp_manager = exp_manager
|
||||
self.experiment_db_ctl = experiment_db_ctl
|
||||
|
||||
grid = QtGui.QGridLayout()
|
||||
self.setLayout(grid)
|
||||
|
||||
grid.addWidget(QtGui.QLabel("Filename:"), 0, 0)
|
||||
self.filename = QtGui.QLineEdit()
|
||||
grid.addWidget(self.filename, 0, 1)
|
||||
grid.addWidget(QtGui.QLabel("Location:"), 0, 0)
|
||||
self.location_label = QtGui.QLabel("")
|
||||
grid.addWidget(self.location_label, 0, 1)
|
||||
grid.setColumnStretch(1, 1)
|
||||
|
||||
self.file_list = QtGui.QListWidget()
|
||||
asyncio.ensure_future(self.refresh_view())
|
||||
grid.addWidget(self.file_list, 1, 0, 1, 2)
|
||||
self.file_list.doubleClicked.connect(self.accept)
|
||||
|
||||
buttons = QtGui.QDialogButtonBox(
|
||||
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
|
||||
grid.addWidget(buttons, 1, 0, 1, 2)
|
||||
grid.addWidget(buttons, 2, 0, 1, 2)
|
||||
buttons.accepted.connect(self.accept)
|
||||
buttons.rejected.connect(self.reject)
|
||||
|
||||
self.accepted.connect(self.open_file)
|
||||
async def refresh_view(self):
|
||||
self.file_list.clear()
|
||||
if not self.explorer.current_directory:
|
||||
self.location_label.setText("<root>")
|
||||
else:
|
||||
self.location_label.setText(self.explorer.current_directory)
|
||||
|
||||
def open_file(self):
|
||||
file = self.filename.text()
|
||||
item = QtGui.QListWidgetItem()
|
||||
item.setText("..")
|
||||
item.setIcon(QtGui.QApplication.style().standardIcon(
|
||||
QtGui.QStyle.SP_FileDialogToParent))
|
||||
self.file_list.addItem(item)
|
||||
|
||||
try:
|
||||
contents = await self.experiment_db_ctl.list_directory(
|
||||
self.explorer.current_directory)
|
||||
except:
|
||||
logger.error("Failed to list directory '%s'",
|
||||
self.explorer.current_directory, exc_info=True)
|
||||
self.explorer.current_directory = ""
|
||||
for name in sorted(contents, key=lambda x: (x[-1] not in "\\/", x)):
|
||||
if name[-1] in "\\/":
|
||||
icon = QtGui.QStyle.SP_DirIcon
|
||||
else:
|
||||
icon = QtGui.QStyle.SP_FileIcon
|
||||
if name[-3:] != ".py":
|
||||
continue
|
||||
item = QtGui.QListWidgetItem()
|
||||
item.setText(name)
|
||||
item.setIcon(QtGui.QApplication.style().standardIcon(icon))
|
||||
self.file_list.addItem(item)
|
||||
|
||||
def accept(self):
|
||||
selected = self.file_list.selectedItems()
|
||||
if selected:
|
||||
selected = selected[0].text()
|
||||
if selected == "..":
|
||||
if (not self.explorer.current_directory
|
||||
or self.explorer.current_directory[-1] not in "\\/"):
|
||||
return
|
||||
idx = None
|
||||
for sep in "\\/":
|
||||
try:
|
||||
idx = self.explorer.current_directory[:-1].rindex(sep)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
if idx is None:
|
||||
return
|
||||
self.explorer.current_directory = \
|
||||
self.explorer.current_directory[:idx+1]
|
||||
if self.explorer.current_directory == "/":
|
||||
self.explorer.current_directory = ""
|
||||
asyncio.ensure_future(self.refresh_view())
|
||||
elif selected[-1] in "\\/":
|
||||
self.explorer.current_directory += selected
|
||||
asyncio.ensure_future(self.refresh_view())
|
||||
else:
|
||||
file = self.explorer.current_directory + selected
|
||||
async def open_task():
|
||||
try:
|
||||
await self.exp_manager.open_file(file)
|
||||
|
@ -43,6 +108,7 @@ class _OpenFileDialog(QtGui.QDialog):
|
|||
logger.error("Failed to open file '%s'",
|
||||
file, exc_info=True)
|
||||
asyncio.ensure_future(open_task())
|
||||
QtGui.QDialog.accept(self)
|
||||
|
||||
|
||||
class Model(DictSyncTreeSepModel):
|
||||
|
@ -127,10 +193,12 @@ class ExplorerDock(dockarea.Dock):
|
|||
scan_repository_action.triggered.connect(scan_repository)
|
||||
self.el.addAction(scan_repository_action)
|
||||
|
||||
self.current_directory = ""
|
||||
open_file_action = QtGui.QAction("Open file outside repository",
|
||||
self.el)
|
||||
open_file_action.triggered.connect(
|
||||
lambda: _OpenFileDialog(self, self.exp_manager).open())
|
||||
lambda: _OpenFileDialog(self, self.exp_manager,
|
||||
experiment_db_ctl).open())
|
||||
self.el.addAction(open_file_action)
|
||||
|
||||
def set_model(self, model):
|
||||
|
|
|
@ -7,7 +7,7 @@ from functools import partial
|
|||
|
||||
from artiq.protocols.sync_struct import Notifier
|
||||
from artiq.master.worker import Worker
|
||||
from artiq.tools import exc_to_warning
|
||||
from artiq.tools import get_windows_drives, exc_to_warning
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -130,7 +130,21 @@ class ExperimentDB:
|
|||
return description
|
||||
|
||||
def list_directory(self, directory):
|
||||
return [(de.name, de.is_dir()) for de in os.scandir(directory)]
|
||||
r = []
|
||||
prefix = ""
|
||||
if not directory:
|
||||
if os.name == "nt":
|
||||
drives = get_windows_drives()
|
||||
return [drive + ":\\" for drive in drives]
|
||||
else:
|
||||
directory = "/"
|
||||
prefix = "/"
|
||||
for de in os.scandir(directory):
|
||||
if de.is_dir():
|
||||
r.append(prefix + de.name + os.path.sep)
|
||||
else:
|
||||
r.append(prefix + de.name)
|
||||
return r
|
||||
|
||||
|
||||
class FilesystemBackend:
|
||||
|
|
|
@ -8,6 +8,7 @@ import time
|
|||
import collections
|
||||
import os
|
||||
import atexit
|
||||
import string
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
@ -18,7 +19,7 @@ from artiq.protocols import pyon
|
|||
__all__ = ["artiq_dir", "parse_arguments", "elide", "short_format", "file_import",
|
||||
"get_experiment", "verbosity_args", "simple_network_args", "init_logger",
|
||||
"atexit_register_coroutine", "exc_to_warning", "asyncio_wait_or_cancel",
|
||||
"TaskObject", "Condition", "workaround_asyncio263"]
|
||||
"TaskObject", "Condition", "workaround_asyncio263", "get_windows_drives"]
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -198,3 +199,15 @@ class Condition:
|
|||
@asyncio.coroutine
|
||||
def workaround_asyncio263():
|
||||
yield
|
||||
|
||||
|
||||
def get_windows_drives():
|
||||
from ctypes import windll
|
||||
|
||||
drives = []
|
||||
bitmask = windll.kernel32.GetLogicalDrives()
|
||||
for letter in string.ascii_uppercase:
|
||||
if bitmask & 1:
|
||||
drives.append(letter)
|
||||
bitmask >>= 1
|
||||
return drives
|
||||
|
|
Loading…
Reference in New Issue