Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 88740f687da3
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4c78bb4950ea
Choose a head ref
  • 6 commits
  • 3 files changed
  • 1 contributor

Commits on May 8, 2016

  1. Copy the full SHA
    c44e786 View commit details
  2. Copy the full SHA
    c5fcb6b View commit details
  3. dashboard/experiments: style

    jordens committed May 8, 2016
    Copy the full SHA
    904db49 View commit details
  4. browser: examine can fail

    jordens committed May 8, 2016
    Copy the full SHA
    02b5493 View commit details
  5. browser: fixes/string style

    jordens committed May 8, 2016
    Copy the full SHA
    4fee205 View commit details
  6. browser: cleanup

    jordens committed May 8, 2016
    Copy the full SHA
    4c78bb4 View commit details
Showing with 81 additions and 68 deletions.
  1. +45 −40 artiq/browser/experiments.py
  2. +35 −27 artiq/dashboard/experiments.py
  3. +1 −1 artiq/gui/tools.py
85 changes: 45 additions & 40 deletions artiq/browser/experiments.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
import h5py

from artiq import __artiq_dir__ as artiq_dir
from artiq.gui.tools import LayoutWidget, log_level_to_name, getOpenFileName
from artiq.gui.tools import LayoutWidget, log_level_to_name, get_open_file_name
from artiq.gui.entries import argty_to_entry
from artiq.protocols import pyon
from artiq.master.worker import Worker
@@ -105,7 +105,7 @@ def _get_group(self, name):
return group

def _recompute_arguments_clicked(self):
asyncio.ensure_future(self._dock.recompute_arguments())
asyncio.ensure_future(self._dock._recompute_arguments())

def _recompute_argument_clicked(self, name):
asyncio.ensure_future(self._recompute_argument(name))
@@ -152,7 +152,7 @@ def restore_state(self, state):
class _ExperimentDock(QtWidgets.QMdiSubWindow):
sigClosed = QtCore.pyqtSignal()

def __init__(self, area, expurl, arguments, worker_handlers):
def __init__(self, area, expurl, arguments):
QtWidgets.QMdiSubWindow.__init__(self)
self.setWindowTitle(expurl)
self.setWindowIcon(QtWidgets.QApplication.style().standardIcon(
@@ -168,7 +168,6 @@ def __init__(self, area, expurl, arguments, worker_handlers):

self._area = area
self.expurl = expurl
self.worker_handlers = worker_handlers
self.arguments = arguments

self.argeditor = _ArgumentEditor(self)
@@ -195,25 +194,25 @@ def update_log_level(index):
log_level.currentIndexChanged.connect(update_log_level)
self.log_level = log_level

submit = QtWidgets.QPushButton("Submit")
submit.setIcon(QtWidgets.QApplication.style().standardIcon(
run = QtWidgets.QPushButton("Analyze")
run.setIcon(QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_DialogOkButton))
submit.setToolTip("Schedule the experiment (Ctrl+Return)")
submit.setShortcut("CTRL+RETURN")
submit.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
self.layout.addWidget(submit, 1, 4, 2, 1)
submit.clicked.connect(self.submit_clicked)

reqterm = QtWidgets.QPushButton("Terminate instances")
reqterm.setIcon(QtWidgets.QApplication.style().standardIcon(
run.setToolTip("Run analysis stage (Ctrl+Return)")
run.setShortcut("CTRL+RETURN")
run.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
self.layout.addWidget(run, 1, 4, 2, 1)
run.clicked.connect(self.run_clicked)

terminate = QtWidgets.QPushButton("Terminate")
terminate.setIcon(QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_DialogCancelButton))
reqterm.setToolTip("Request termination of instances (Ctrl+Backspace)")
reqterm.setShortcut("CTRL+BACKSPACE")
reqterm.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
self.layout.addWidget(reqterm, 3, 4)
reqterm.clicked.connect(self.reqterm_clicked)
terminate.setToolTip("Terminate analysis (Ctrl+Backspace)")
terminate.setShortcut("CTRL+BACKSPACE")
terminate.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
self.layout.addWidget(terminate, 3, 4)
terminate.clicked.connect(self.terminate_clicked)

def dragEnterEvent(self, ev):
if ev.mimeData().hasFormat("text/uri-list"):
@@ -222,7 +221,7 @@ def dragEnterEvent(self, ev):
def dropEvent(self, ev):
for uri in ev.mimeData().urls():
if uri.scheme() == "file":
logger.info("loading HDF5 arguments from %s", uri.path())
logger.info("Loading HDF5 arguments from %s", uri.path())
asyncio.ensure_future(self._load_hdf5_task(uri.path()))
ev.acceptProposedAction()

@@ -262,16 +261,16 @@ async def _load_hdf5_task(self, filename):

await self._recompute_arguments(arguments)

def submit_clicked(self):
def run_clicked(self):
try:
pass # TODO
except:
# May happen when experiment has been removed
# from repository/explist
logger.error("Failed to submit '%s'",
logger.error("Failed to run '%s'",
self.expurl, exc_info=True)

def reqterm_clicked(self):
def terminate_clicked(self):
try:
pass # TODO
except:
@@ -303,14 +302,7 @@ def __init__(self, root, datasets_sub):
self.pixmap = QtGui.QPixmap(os.path.join(
artiq_dir, "gui", "logo20.svg"))
self.current_dir = root
self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
self.setFocusPolicy(QtCore.Qt.StrongFocus)

action = QtWidgets.QAction("&Open experiment", self)
action.setShortcut(QtGui.QKeySequence("CTRL+o"))
action.setShortcutContext(QtCore.Qt.WidgetShortcut)
action.triggered.connect(self._select_experiment)
self.addAction(action)
self.setToolTip("Click to open experiment")

self.open_experiments = []

@@ -321,6 +313,10 @@ def __init__(self, root, datasets_sub):
"update_dataset": lambda k, v: None,
}

def mousePressEvent(self, ev):
if ev.button() == QtCore.Qt.LeftButton:
asyncio.ensure_future(self._select_experiment_task())

def paintEvent(self, event):
QtWidgets.QMdiArea.paintEvent(self, event)
painter = QtGui.QPainter(self.viewport())
@@ -349,14 +345,19 @@ def _select_experiment(self):

async def _select_experiment_task(self):
try:
file = await getOpenFileName(
file = await get_open_file_name(
self, "Open experiment", self.current_dir,
"Experiments (*.py);;All files (*.*)")
except asyncio.CancelledError:
return
self.current_dir = os.path.dirname(file)
logger.info("opening experiment %s", file)
description = await self.examine(file)
logger.info("Opening experiment %s", file)
try:
description = await self.examine(file)
except:
logger.error("Could not examine experiment '%s'",
file, exc_info=True)
return
for class_name, class_desc in description.items():
expurl = "{}@{}".format(class_name, file)
arguments = self.initialize_submission_arguments(
@@ -383,18 +384,22 @@ async def examine(self, file):

async def compute_arginfo(self, expurl):
class_name, file = expurl.split("@", maxsplit=1)
desc = await self.examine(file)
try:
desc = await self.examine(file)
except:
logger.error("Could not examine experiment '%s'",
file, exc_info=True)
return
return desc[class_name]["arginfo"]

def open_experiment(self, expurl, arguments):
try:
dock = _ExperimentDock(self, expurl, arguments,
self.worker_handlers)
dock = _ExperimentDock(self, expurl, arguments)
except:
logger.warning("Failed to create experiment dock for %s, "
"retrying with arguments reset", expurl,
exc_info=True)
dock = _ExperimentDock(self, expurl, {}, self.worker_handlers)
dock = _ExperimentDock(self, expurl, {})
asyncio.ensure_future(dock._recompute_arguments())
self.addSubWindow(dock)
dock.show()
62 changes: 35 additions & 27 deletions artiq/dashboard/experiments.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
from PyQt5 import QtCore, QtGui, QtWidgets
import h5py

from artiq.gui.tools import LayoutWidget, log_level_to_name
from artiq.gui.tools import LayoutWidget, log_level_to_name, get_open_file_name
from artiq.gui.entries import argty_to_entry
from artiq.protocols import pyon

@@ -46,9 +46,9 @@ def __init__(self, manager, dock, expurl):
set_resize_mode(1, QtWidgets.QHeaderView.Stretch)
set_resize_mode(2, QtWidgets.QHeaderView.ResizeToContents)
self.header().setVisible(False)
self.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
self.setSelectionMode(self.NoSelection)
self.setHorizontalScrollMode(self.ScrollPerPixel)
self.setVerticalScrollMode(self.ScrollPerPixel)

self.viewport().installEventFilter(_WheelFilter(self.viewport()))

@@ -73,8 +73,9 @@ def __init__(self, manager, dock, expurl):
recompute_argument = QtWidgets.QToolButton()
recompute_argument.setToolTip("Re-run the experiment's build "
"method and take the default value")
recompute_argument.setIcon(QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_BrowserReload))
recompute_argument.setIcon(
QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_BrowserReload))
recompute_argument.clicked.connect(
partial(self._recompute_argument_clicked, name))
fix_layout = LayoutWidget()
@@ -84,8 +85,9 @@ def __init__(self, manager, dock, expurl):
widget_item = QtWidgets.QTreeWidgetItem()
self.addTopLevelItem(widget_item)
recompute_arguments = QtWidgets.QPushButton("Recompute all arguments")
recompute_arguments.setIcon(QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_BrowserReload))
recompute_arguments.setIcon(
QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_BrowserReload))
recompute_arguments.clicked.connect(dock._recompute_arguments_clicked)

load_hdf5 = QtWidgets.QPushButton("Load HDF5")
@@ -196,10 +198,12 @@ def __init__(self, manager, expurl):
datetime.setDateTime(QtCore.QDateTime.fromMSecsSinceEpoch(
scheduling["due_date"]*1000))
datetime_en.setChecked(scheduling["due_date"] is not None)

def update_datetime(dt):
scheduling["due_date"] = dt.toMSecsSinceEpoch()/1000
datetime_en.setChecked(True)
datetime.dateTimeChanged.connect(update_datetime)

def update_datetime_en(checked):
if checked:
due_date = datetime.dateTime().toMSecsSinceEpoch()/1000
@@ -213,6 +217,7 @@ def update_datetime_en(checked):
self.layout.addWidget(pipeline_name, 1, 3)

pipeline_name.setText(scheduling["pipeline_name"])

def update_pipeline_name(text):
scheduling["pipeline_name"] = text
pipeline_name.textEdited.connect(update_pipeline_name)
@@ -223,6 +228,7 @@ def update_pipeline_name(text):
self.layout.addWidget(priority, 2, 1)

priority.setValue(scheduling["priority"])

def update_priority(value):
scheduling["priority"] = value
priority.valueChanged.connect(update_priority)
@@ -232,6 +238,7 @@ def update_priority(value):
self.layout.addWidget(flush, 2, 2, 1, 2)

flush.setChecked(scheduling["flush"])

def update_flush(checked):
scheduling["flush"] = bool(checked)
flush.stateChanged.connect(update_flush)
@@ -247,6 +254,7 @@ def update_flush(checked):

log_level.setCurrentIndex(log_levels.index(
log_level_to_name(options["log_level"])))

def update_log_level(index):
options["log_level"] = getattr(logging, log_level.currentText())
log_level.currentIndexChanged.connect(update_log_level)
@@ -263,6 +271,7 @@ def update_log_level(index):

if options["repo_rev"] is not None:
repo_rev.setText(options["repo_rev"])

def update_repo_rev(text):
if text:
options["repo_rev"] = text
@@ -330,18 +339,18 @@ async def _recompute_arguments_task(self, overrides=dict()):
self.layout.addWidget(self.argeditor, 0, 0, 1, 5)

def _load_hdf5_clicked(self):
dialog = QtWidgets.QFileDialog(self.manager.main_window,
"Load HDF5", self.hdf5_load_directory,
"HDF5 files (*.h5 *.hdf5);;All files (*.*)")
dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile)
def on_accept():
filename = dialog.selectedFiles()[0]
self.hdf5_load_directory = os.path.dirname(filename)
asyncio.ensure_future(self._load_hdf5_task(filename))
dialog.accepted.connect(on_accept)
dialog.open()

async def _load_hdf5_task(self, filename):
asyncio.ensure_future(self._load_hdf5_task())

async def _load_hdf5_task(self):
try:
filename = await get_open_file_name(
self.manager.main_window, "Load HDF5",
self.hdf5_load_directory,
"HDF5 files (*.h5 *.hdf5);;All files (*.*)")
except asyncio.CancelledError:
return
self.hdf5_load_directory = os.path.dirname(filename)

try:
with h5py.File(filename, "r") as f:
expid = f["expid"][()]
@@ -355,9 +364,9 @@ async def _load_hdf5_task(self, filename):
try:
self.log_level.setCurrentIndex(log_levels.index(
log_level_to_name(expid["log_level"])))
if ("repo_rev" in expid
and expid["repo_rev"] != "N/A"
and hasattr(self, "repo_rev")):
if ("repo_rev" in expid and
expid["repo_rev"] != "N/A" and
hasattr(self, "repo_rev")):
self.repo_rev.setText(expid["repo_rev"])
except:
logger.error("Could not set submission options from HDF5 expid",
@@ -366,7 +375,6 @@ async def _load_hdf5_task(self, filename):

await self._recompute_arguments_task(arguments)


def closeEvent(self, event):
self.sigClosed.emit()
QtWidgets.QMdiSubWindow.closeEvent(self, event)
@@ -543,9 +551,9 @@ def request_inst_term(self, expurl):
repo_match = "repo_rev" in expid
else:
repo_match = "repo_rev" not in expid
if (repo_match
and expid["file"] == file
and expid["class_name"] == class_name):
if (repo_match and
expid["file"] == file and
expid["class_name"] == class_name):
rids.append(rid)
asyncio.ensure_future(self._request_term_multiple(rids))

2 changes: 1 addition & 1 deletion artiq/gui/tools.py
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ def addWidget(self, item, row=0, col=0, rowspan=1, colspan=1):
self.layout.addWidget(item, row, col, rowspan, colspan)


async def getOpenFileName(parent, caption, dir, filter):
async def get_open_file_name(parent, caption, dir, filter):
"""like QtWidgets.QFileDialog.getOpenFileName(), but a coroutine"""
dialog = QtWidgets.QFileDialog(parent, caption, dir, filter)
dialog.setFileMode(dialog.ExistingFile)