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: 372bc906c1f2
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: 138ba7e2fab3
Choose a head ref
  • 3 commits
  • 4 files changed
  • 1 contributor

Commits on Oct 26, 2015

  1. Copy the full SHA
    9f2ff32 View commit details

Commits on Oct 27, 2015

  1. gui: shortcut support

    sbourdeauducq committed Oct 27, 2015
    Copy the full SHA
    967d4ed View commit details
  2. Copy the full SHA
    138ba7e View commit details
Showing with 169 additions and 33 deletions.
  1. +65 −28 artiq/gui/explorer.py
  2. +1 −2 artiq/gui/log.py
  3. +98 −0 artiq/gui/shortcuts.py
  4. +5 −3 artiq/test/sync_struct.py
93 changes: 65 additions & 28 deletions artiq/gui/explorer.py
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
from artiq.protocols import pyon
from artiq.gui.tools import DictSyncModel
from artiq.gui.scan import ScanController
from artiq.gui.shortcuts import ShortcutManager


class _ExplistModel(DictSyncModel):
@@ -122,14 +123,14 @@ def set_argument_value(self, value):


class _ArgumentEditor(QtGui.QTreeWidget):
def __init__(self, dialog_parent):
def __init__(self, main_window):
QtGui.QTreeWidget.__init__(self)
self.setColumnCount(2)
self.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
self.header().setVisible(False)
self.setSelectionMode(QtGui.QAbstractItemView.NoSelection)

self.dialog_parent = dialog_parent
self.main_window = main_window
self._groups = dict()
self.set_arguments([])

@@ -176,7 +177,7 @@ def get_argument_values(self, show_error_message):
r[arg] = entry.get_argument_value()
except Exception as e:
if show_error_message:
msgbox = QtGui.QMessageBox(self.dialog_parent)
msgbox = QtGui.QMessageBox(self.main_window)
msgbox.setWindowTitle("Error")
msgbox.setText("Failed to obtain value for argument '{}':\n{}"
.format(arg, str(e)))
@@ -215,10 +216,10 @@ def restore_state(self, state):


class ExplorerDock(dockarea.Dock):
def __init__(self, dialog_parent, status_bar, schedule_ctl):
def __init__(self, main_window, status_bar, schedule_ctl):
dockarea.Dock.__init__(self, "Explorer", size=(1500, 500))

self.dialog_parent = dialog_parent
self.main_window = main_window
self.status_bar = status_bar
self.schedule_ctl = schedule_ctl

@@ -256,8 +257,7 @@ def __init__(self, dialog_parent, status_bar, schedule_ctl):
grid.addWidget(self.flush, 3, 2)

self.log_level = QtGui.QComboBox()
for item in "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL":
self.log_level.addItem(item)
self.log_level.addItems(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
self.log_level.setCurrentIndex(1)
self.log_level.setToolTip("Minimum level for log entry production")
grid.addWidget(self.log_level, 3, 3)
@@ -268,20 +268,27 @@ def __init__(self, dialog_parent, status_bar, schedule_ctl):
grid.addWidget(submit, 4, 0, colspan=4)
submit.clicked.connect(self.submit_clicked)

self.argeditor = _ArgumentEditor(self.dialog_parent)
self.argeditor = _ArgumentEditor(self.main_window)
self.splitter.addWidget(self.argeditor)
self.splitter.setSizes([grid.minimumSizeHint().width(), 1000])
self.state = dict()
self.argeditor_states = dict()

self.shortcuts = ShortcutManager(self.main_window, self)

self.el.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
edit_shortcuts_action = QtGui.QAction("Edit shortcuts", self.el)
edit_shortcuts_action.triggered.connect(self.edit_shortcuts)
self.el.addAction(edit_shortcuts_action)

def update_selection(self, selected, deselected):
if deselected:
self.state[deselected] = self.argeditor.save_state()
self.argeditor_states[deselected] = self.argeditor.save_state()

if selected:
expinfo = self.explist_model.backing_store[selected]
self.argeditor.set_arguments(expinfo["arguments"])
if selected in self.state:
self.argeditor.restore_state(self.state[selected])
if selected in self.argeditor_states:
self.argeditor.restore_state(self.argeditor_states[selected])
self.splitter.insertWidget(1, self.argeditor)
self.selected_key = selected

@@ -302,11 +309,20 @@ def save_state(self):
if idx:
row = idx[0].row()
key = self.explist_model.row_to_key[row]
self.state[key] = self.argeditor.save_state()
return self.state
self.argeditor_states[key] = self.argeditor.save_state()
return {
"argeditor": self.argeditor_states,
"shortcuts": self.shortcuts.save_state()
}

def restore_state(self, state):
self.state = state
try:
argeditor_states = state["argeditor"]
shortcuts_state = state["shortcuts"]
except KeyError:
return
self.argeditor_states = argeditor_states
self.shortcuts.restore_state(shortcuts_state)

def enable_duedate(self):
self.datetime_en.setChecked(True)
@@ -324,8 +340,8 @@ def init_explist_model(self, init):
self.el.setModel(self.explist_model)
return self.explist_model

async def submit(self, pipeline_name, file, class_name, arguments,
priority, due_date, flush):
async def submit_task(self, pipeline_name, file, class_name, arguments,
priority, due_date, flush):
expid = {
"log_level": getattr(logging, self.log_level.currentText()),
"repo_rev": None,
@@ -337,20 +353,41 @@ async def submit(self, pipeline_name, file, class_name, arguments,
priority, due_date, flush)
self.status_bar.showMessage("Submitted RID {}".format(rid))

def submit(self, pipeline, key, priority, due_date, flush):
# TODO: refactor explorer and cleanup.
# Argument editors should immediately modify the global state.
expinfo = self.explist_model.backing_store[key]
if key == self.selected_key:
arguments = self.argeditor.get_argument_values(True)
if arguments is None:
# There has been an error. Displaying the error message box
# was done by argeditor.
return
else:
try:
arguments = self.argeditor_states[key]["argument_values"]
except KeyError:
arguments = dict()
asyncio.ensure_future(self.submit_task(self.pipeline.text(),
expinfo["file"],
expinfo["class_name"],
arguments,
priority,
due_date,
flush))

def submit_clicked(self):
if self.selected_key is not None:
expinfo = self.explist_model.backing_store[self.selected_key]
if self.datetime_en.isChecked():
due_date = self.datetime.dateTime().toMSecsSinceEpoch()/1000
else:
due_date = None
arguments = self.argeditor.get_argument_values(True)
if arguments is None:
return
asyncio.ensure_future(self.submit(self.pipeline.text(),
expinfo["file"],
expinfo["class_name"],
arguments,
self.priority.value(),
due_date,
self.flush.isChecked()))
self.submit(self.pipeline.text(),
self.selected_key,
self.priority.value(),
due_date,
self.flush.isChecked())

def edit_shortcuts(self):
experiments = sorted(self.explist_model.backing_store.keys())
self.shortcuts.edit(experiments)
3 changes: 1 addition & 2 deletions artiq/gui/log.py
Original file line number Diff line number Diff line change
@@ -100,8 +100,7 @@ def __init__(self):
grid.layout.setColumnStretch(1, 0)
grid.layout.setColumnStretch(2, 1)
self.filterbox = QtGui.QComboBox()
for item in "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL":
self.filterbox.addItem(item)
self.filterbox.addItems(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
self.filterbox.setToolTip("Display entries at or above this level")
grid.addWidget(self.filterbox, 0, 1)
self.filterbox.currentIndexChanged.connect(self.filter_changed)
98 changes: 98 additions & 0 deletions artiq/gui/shortcuts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from functools import partial

from quamash import QtGui
try:
from quamash import QtWidgets
QShortcut = QtWidgets.QShortcut
except:
QShortcut = QtGui.QShortcut


class _ShortcutEditor(QtGui.QDialog):
def __init__(self, parent, experiments, shortcuts):
QtGui.QDialog.__init__(self, parent=parent)
self.setWindowTitle("Shortcuts")

self.shortcuts = shortcuts
self.edit_widgets = dict()

grid = QtGui.QGridLayout()
self.setLayout(grid)

for n, title in enumerate(["Key", "Experiment", "Priority", "Pipeline"]):
label = QtGui.QLabel("<b>" + title + "</b")
grid.addWidget(label, 0, n)
label.setMaximumHeight(label.sizeHint().height())
grid.setColumnStretch(1, 1)
grid.setColumnStretch(3, 1)

for i in range(12):
row = i + 1
existing_shortcut = self.shortcuts.get(i, dict())

grid.addWidget(QtGui.QLabel("F" + str(i+1)), row, 0)

experiment = QtGui.QComboBox()
grid.addWidget(experiment, row, 1)
experiment.addItem("<None>")
experiment.addItems(experiments)
experiment.setEditable(True)
experiment.setEditText(
existing_shortcut.get("experiment", "<None>"))

priority = QtGui.QSpinBox()
grid.addWidget(priority, row, 2)
priority.setRange(-99, 99)
priority.setValue(existing_shortcut.get("priority", 0))

pipeline = QtGui.QLineEdit()
grid.addWidget(pipeline, row, 3)
pipeline.setText(existing_shortcut.get("pipeline", "main"))

self.edit_widgets[i] = {
"experiment": experiment,
"priority": priority,
"pipeline": pipeline
}

buttons = QtGui.QDialogButtonBox(
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
grid.addWidget(buttons, 14, 0, 1, 4)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
self.accepted.connect(self.on_accept)

def on_accept(self):
for n, widgets in self.edit_widgets.items():
self.shortcuts[n] = {
"experiment": widgets["experiment"].currentText(),
"priority": widgets["priority"].value(),
"pipeline": widgets["pipeline"].text()
}


class ShortcutManager:
def __init__(self, main_window, explorer):
for i in range(12):
shortcut = QShortcut("F" + str(i+1), main_window)
shortcut.activated.connect(partial(self._activated, i))
self.main_window = main_window
self.explorer = explorer
self.shortcuts = dict()

def edit(self, experiments):
dlg = _ShortcutEditor(self.main_window, experiments, self.shortcuts)
dlg.open()

def _activated(self, nr):
info = self.shortcuts.get(nr, dict())
experiment = info.get("experiment", "")
if experiment and experiment != "<None>":
self.explorer.submit(info["pipeline"], experiment,
info["priority"], None, False)

def save_state(self):
return self.shortcuts

def restore_state(self, state):
self.shortcuts = state
8 changes: 5 additions & 3 deletions artiq/test/sync_struct.py
Original file line number Diff line number Diff line change
@@ -17,15 +17,17 @@ def write_test_data(test_dict):
for key, value in enumerate(test_values):
test_dict[key] = value
test_dict[1.5] = 1.5
test_dict["array"] = []
test_dict["array"].append(42)
test_dict["array"].insert(1, 1)
test_dict["list"] = []
test_dict["list"].append(42)
test_dict["list"].insert(1, 1)
test_dict[100] = 0
test_dict[100] = 1
test_dict[101] = 1
test_dict.pop(101)
test_dict[102] = 1
del test_dict[102]
test_dict["array"] = np.zeros(1)
test_dict["array"][0] = 10
test_dict["finished"] = True