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(
|
parser_ls = subparsers.add_parser(
|
||||||
"ls", help="list a directory on the master")
|
"ls", help="list a directory on the master")
|
||||||
parser_ls.add_argument("directory")
|
parser_ls.add_argument("directory", default="", nargs="?")
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
@ -160,11 +160,8 @@ def _action_scan_repository(remote, args):
|
||||||
|
|
||||||
def _action_ls(remote, args):
|
def _action_ls(remote, args):
|
||||||
contents = remote.list_directory(args.directory)
|
contents = remote.list_directory(args.directory)
|
||||||
for name, is_dir in sorted(contents, key=lambda x: (-x[1], x[0])):
|
for name in sorted(contents, key=lambda x: (x[-1] not in "\\/", x)):
|
||||||
if is_dir:
|
print(name)
|
||||||
print("<DIR> " + name)
|
|
||||||
else:
|
|
||||||
print(" " + name)
|
|
||||||
|
|
||||||
|
|
||||||
def _show_schedule(schedule):
|
def _show_schedule(schedule):
|
||||||
|
|
|
@ -13,29 +13,94 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class _OpenFileDialog(QtGui.QDialog):
|
class _OpenFileDialog(QtGui.QDialog):
|
||||||
def __init__(self, parent, exp_manager):
|
def __init__(self, explorer, exp_manager, experiment_db_ctl):
|
||||||
QtGui.QDialog.__init__(self, parent=parent)
|
QtGui.QDialog.__init__(self, parent=explorer)
|
||||||
|
self.resize(710, 700)
|
||||||
self.setWindowTitle("Open file outside repository")
|
self.setWindowTitle("Open file outside repository")
|
||||||
|
|
||||||
|
self.explorer = explorer
|
||||||
self.exp_manager = exp_manager
|
self.exp_manager = exp_manager
|
||||||
|
self.experiment_db_ctl = experiment_db_ctl
|
||||||
|
|
||||||
grid = QtGui.QGridLayout()
|
grid = QtGui.QGridLayout()
|
||||||
self.setLayout(grid)
|
self.setLayout(grid)
|
||||||
|
|
||||||
grid.addWidget(QtGui.QLabel("Filename:"), 0, 0)
|
grid.addWidget(QtGui.QLabel("Location:"), 0, 0)
|
||||||
self.filename = QtGui.QLineEdit()
|
self.location_label = QtGui.QLabel("")
|
||||||
grid.addWidget(self.filename, 0, 1)
|
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(
|
buttons = QtGui.QDialogButtonBox(
|
||||||
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
|
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.accepted.connect(self.accept)
|
||||||
buttons.rejected.connect(self.reject)
|
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):
|
item = QtGui.QListWidgetItem()
|
||||||
file = self.filename.text()
|
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():
|
async def open_task():
|
||||||
try:
|
try:
|
||||||
await self.exp_manager.open_file(file)
|
await self.exp_manager.open_file(file)
|
||||||
|
@ -43,6 +108,7 @@ class _OpenFileDialog(QtGui.QDialog):
|
||||||
logger.error("Failed to open file '%s'",
|
logger.error("Failed to open file '%s'",
|
||||||
file, exc_info=True)
|
file, exc_info=True)
|
||||||
asyncio.ensure_future(open_task())
|
asyncio.ensure_future(open_task())
|
||||||
|
QtGui.QDialog.accept(self)
|
||||||
|
|
||||||
|
|
||||||
class Model(DictSyncTreeSepModel):
|
class Model(DictSyncTreeSepModel):
|
||||||
|
@ -127,10 +193,12 @@ class ExplorerDock(dockarea.Dock):
|
||||||
scan_repository_action.triggered.connect(scan_repository)
|
scan_repository_action.triggered.connect(scan_repository)
|
||||||
self.el.addAction(scan_repository_action)
|
self.el.addAction(scan_repository_action)
|
||||||
|
|
||||||
|
self.current_directory = ""
|
||||||
open_file_action = QtGui.QAction("Open file outside repository",
|
open_file_action = QtGui.QAction("Open file outside repository",
|
||||||
self.el)
|
self.el)
|
||||||
open_file_action.triggered.connect(
|
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)
|
self.el.addAction(open_file_action)
|
||||||
|
|
||||||
def set_model(self, model):
|
def set_model(self, model):
|
||||||
|
|
|
@ -7,7 +7,7 @@ from functools import partial
|
||||||
|
|
||||||
from artiq.protocols.sync_struct import Notifier
|
from artiq.protocols.sync_struct import Notifier
|
||||||
from artiq.master.worker import Worker
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -130,7 +130,21 @@ class ExperimentDB:
|
||||||
return description
|
return description
|
||||||
|
|
||||||
def list_directory(self, directory):
|
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:
|
class FilesystemBackend:
|
||||||
|
|
|
@ -8,6 +8,7 @@ import time
|
||||||
import collections
|
import collections
|
||||||
import os
|
import os
|
||||||
import atexit
|
import atexit
|
||||||
|
import string
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ from artiq.protocols import pyon
|
||||||
__all__ = ["artiq_dir", "parse_arguments", "elide", "short_format", "file_import",
|
__all__ = ["artiq_dir", "parse_arguments", "elide", "short_format", "file_import",
|
||||||
"get_experiment", "verbosity_args", "simple_network_args", "init_logger",
|
"get_experiment", "verbosity_args", "simple_network_args", "init_logger",
|
||||||
"atexit_register_coroutine", "exc_to_warning", "asyncio_wait_or_cancel",
|
"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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -198,3 +199,15 @@ class Condition:
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def workaround_asyncio263():
|
def workaround_asyncio263():
|
||||||
yield
|
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