forked from M-Labs/artiq
dashboard: support reloading arguments from HDF5
This commit is contained in:
parent
c50555e11c
commit
597d7c389e
|
@ -38,6 +38,8 @@ unreleased [2.x]
|
|||
current directory.
|
||||
* The ``parent`` keyword argument of ``HasEnvironment`` (and ``EnvExperiment``)
|
||||
has been replaced. Pass the parent as first argument instead.
|
||||
* In the dashboard's experiment windows, partial or full argument recomputation
|
||||
takes into account the repository revision field.
|
||||
|
||||
|
||||
1.0rc4 (unreleased)
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import logging
|
||||
import asyncio
|
||||
import os
|
||||
from functools import partial
|
||||
from collections import OrderedDict
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
import h5py
|
||||
|
||||
from artiq.gui.tools import LayoutWidget, log_level_to_name
|
||||
from artiq.gui.entries import argty_to_entry
|
||||
from artiq.protocols import pyon
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -83,12 +86,21 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
|
|||
recompute_arguments = QtWidgets.QPushButton("Recompute all arguments")
|
||||
recompute_arguments.setIcon(QtWidgets.QApplication.style().standardIcon(
|
||||
QtWidgets.QStyle.SP_BrowserReload))
|
||||
recompute_arguments.setSizePolicy(QtWidgets.QSizePolicy.Maximum,
|
||||
QtWidgets.QSizePolicy.Maximum)
|
||||
recompute_arguments.clicked.connect(dock._recompute_arguments_clicked)
|
||||
fix_layout = LayoutWidget()
|
||||
fix_layout.addWidget(recompute_arguments)
|
||||
self.setItemWidget(widget_item, 1, fix_layout)
|
||||
|
||||
load_hdf5 = QtWidgets.QPushButton("Load HDF5")
|
||||
load_hdf5.setIcon(QtWidgets.QApplication.style().standardIcon(
|
||||
QtWidgets.QStyle.SP_DialogOpenButton))
|
||||
load_hdf5.clicked.connect(dock._load_hdf5_clicked)
|
||||
|
||||
buttons = LayoutWidget()
|
||||
buttons.addWidget(recompute_arguments, 1, 1)
|
||||
buttons.addWidget(load_hdf5, 1, 2)
|
||||
buttons.layout.setColumnStretch(0, 1)
|
||||
buttons.layout.setColumnStretch(1, 0)
|
||||
buttons.layout.setColumnStretch(2, 0)
|
||||
buttons.layout.setColumnStretch(3, 1)
|
||||
self.setItemWidget(widget_item, 1, buttons)
|
||||
|
||||
def _get_group(self, name):
|
||||
if name in self._groups:
|
||||
|
@ -143,6 +155,9 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
|
|||
pass
|
||||
|
||||
|
||||
log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
|
||||
|
||||
class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
||||
sigClosed = QtCore.pyqtSignal()
|
||||
|
||||
|
@ -222,7 +237,6 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
|||
flush.stateChanged.connect(update_flush)
|
||||
|
||||
log_level = QtWidgets.QComboBox()
|
||||
log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
log_level.addItems(log_levels)
|
||||
log_level.setCurrentIndex(1)
|
||||
log_level.setToolTip("Minimum level for log entry production")
|
||||
|
@ -236,6 +250,7 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
|||
def update_log_level(index):
|
||||
options["log_level"] = getattr(logging, log_level.currentText())
|
||||
log_level.currentIndexChanged.connect(update_log_level)
|
||||
self.log_level = log_level
|
||||
|
||||
if "repo_rev" in options:
|
||||
repo_rev = QtWidgets.QLineEdit()
|
||||
|
@ -253,7 +268,8 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
|||
options["repo_rev"] = text
|
||||
else:
|
||||
options["repo_rev"] = None
|
||||
repo_rev.textEdited.connect(update_repo_rev)
|
||||
repo_rev.textChanged.connect(update_repo_rev)
|
||||
self.repo_rev = repo_rev
|
||||
|
||||
submit = QtWidgets.QPushButton("Submit")
|
||||
submit.setIcon(QtWidgets.QApplication.style().standardIcon(
|
||||
|
@ -275,6 +291,8 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
|||
self.layout.addWidget(reqterm, 3, 4)
|
||||
reqterm.clicked.connect(self.reqterm_clicked)
|
||||
|
||||
self.hdf5_load_directory = os.path.expanduser("~")
|
||||
|
||||
def submit_clicked(self):
|
||||
try:
|
||||
self.manager.submit(self.expurl)
|
||||
|
@ -296,18 +314,59 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
|||
def _recompute_arguments_clicked(self):
|
||||
asyncio.ensure_future(self._recompute_arguments_task())
|
||||
|
||||
async def _recompute_arguments_task(self):
|
||||
async def _recompute_arguments_task(self, overrides=dict()):
|
||||
try:
|
||||
arginfo = await self.manager.compute_arginfo(self.expurl)
|
||||
except:
|
||||
logger.error("Could not recompute arguments of '%s'",
|
||||
self.expurl, exc_info=True)
|
||||
return
|
||||
for k, v in overrides.items():
|
||||
arginfo[k][0]["default"] = v
|
||||
self.manager.initialize_submission_arguments(self.expurl, arginfo)
|
||||
|
||||
self.argeditor.deleteLater()
|
||||
self.argeditor = _ArgumentEditor(self.manager, self, self.expurl)
|
||||
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):
|
||||
try:
|
||||
with h5py.File(filename, "r") as f:
|
||||
expid = f["expid"][()]
|
||||
expid = pyon.decode(expid)
|
||||
arguments = expid["arguments"]
|
||||
except:
|
||||
logger.error("Could not retrieve expid from HDF5 file",
|
||||
exc_info=True)
|
||||
return
|
||||
|
||||
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")):
|
||||
self.repo_rev.setText(expid["repo_rev"])
|
||||
except:
|
||||
logger.error("Could not set submission options from HDF5 expid",
|
||||
exc_info=True)
|
||||
return
|
||||
|
||||
await self._recompute_arguments_task(arguments)
|
||||
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.sigClosed.emit()
|
||||
QtWidgets.QMdiSubWindow.closeEvent(self, event)
|
||||
|
@ -315,12 +374,14 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
|||
def save_state(self):
|
||||
return {
|
||||
"args": self.argeditor.save_state(),
|
||||
"geometry": bytes(self.saveGeometry())
|
||||
"geometry": bytes(self.saveGeometry()),
|
||||
"hdf5_load_directory": self.hdf5_load_directory
|
||||
}
|
||||
|
||||
def restore_state(self, state):
|
||||
self.argeditor.restore_state(state["args"])
|
||||
self.restoreGeometry(QtCore.QByteArray(state["geometry"]))
|
||||
self.hdf5_load_directory = state["hdf5_load_directory"]
|
||||
|
||||
|
||||
class ExperimentManager:
|
||||
|
@ -490,8 +551,10 @@ class ExperimentManager:
|
|||
|
||||
async def compute_arginfo(self, expurl):
|
||||
file, class_name, use_repository = self.resolve_expurl(expurl)
|
||||
description = await self.experiment_db_ctl.examine(file,
|
||||
use_repository)
|
||||
if use_repository:
|
||||
revision = self.get_submission_options(expurl)["repo_rev"]
|
||||
description = await self.experiment_db_ctl.examine(
|
||||
file, use_repository, revision)
|
||||
return description[class_name]["arginfo"]
|
||||
|
||||
async def open_file(self, file):
|
||||
|
|
|
@ -120,8 +120,9 @@ class ExperimentDB:
|
|||
asyncio.ensure_future(
|
||||
exc_to_warning(self.scan_repository(new_cur_rev)))
|
||||
|
||||
async def examine(self, filename, use_repository=True):
|
||||
async def examine(self, filename, use_repository=True, revision=None):
|
||||
if use_repository:
|
||||
if revision is None:
|
||||
revision = self.cur_rev
|
||||
wd, _ = self.repo_backend.request_rev(revision)
|
||||
filename = os.path.join(wd, filename)
|
||||
|
|
Loading…
Reference in New Issue