forked from M-Labs/artiq
1
0
Fork 0

Compare commits

...

3 Commits

3 changed files with 204 additions and 5 deletions

View File

@ -0,0 +1,190 @@
import logging
import asyncio
from PyQt5 import QtCore, QtWidgets, QtGui
from artiq.gui.tools import LayoutWidget
from artiq.gui.models import DictSyncModel
from artiq.gui.applets import EntryArea
logger = logging.getLogger(__name__)
class Model(DictSyncModel):
def __init__(self, init):
DictSyncModel.__init__(self,
["RID"],
init)
def convert(self, k, v, column):
if column == 0:
return k
else:
raise ValueError
def sort_key(self, k, v):
return k
class _InputRequestView(QtWidgets.QScrollArea):
supplied = QtCore.pyqtSignal(int, dict)
cancelled = QtCore.pyqtSignal(int)
def __init__(self, interactive_args_sub):
QtWidgets.QScrollArea.__init__(self)
self.setWidgetResizable(True)
self.interactive_args_sub = interactive_args_sub
self.request_stack = QtWidgets.QStackedWidget()
self.request_stack.addWidget(QtWidgets.QLabel("No input request selected"))
self.setWidget(self.request_stack)
self.model = Model({})
self.interactive_args_sub.add_setmodel_callback(self.setModel)
def setModel(self, model):
self.model = model
self.model.dataChanged.connect(self.update_widget)
self.model.rowsInserted.connect(self.insert)
self.model.rowsMoved.connect(self.move)
self.model.rowsRemoved.connect(self.remove)
def _insert_widget(self, index):
rid = self.model.row_to_key[index]
arglist_desc = self.model.backing_store[rid]
entry_area = EntryArea()
for args in arglist_desc:
entry_area.setattr_argument(*args)
self.request_stack.insertWidget(index, entry_area)
def update_widget(self, top_left, bottom_right, roles):
index = top_left.row()
widget = self.request_stack.widget(index)
self.request_stack.removeWidget(widget)
self._insert_widget(index)
widget.deleteLater()
def insert(self, parent, first, last):
self._insert_widget(first)
def move(self, src_parent, src_start, src_end, dest_parent, dest_end):
widget = self.request_stack.widget(src_start)
self.request_stack.insertWidget(dest_end)
def remove(self, parent, first, last):
widget = self.request_stack.widget(first)
self.request_stack.removeWidget(widget)
widget.deleteLater()
def set_selected_request(self, row):
self.request_stack.setCurrentIndex(row)
def supply_selected(self):
index = self.request_stack.currentIndex()
try:
self.supply(index)
except IndexError:
logger.error("no input request selected", exc_info=True)
def supply(self, index):
rid = self.model.row_to_key[index]
widget = self.request_stack.widget(index)
d = widget.get_values()
self.supplied.emit(rid, d)
def cancel_selected(self):
index = self.request_stack.currentIndex()
try:
self.cancel(index)
except IndexError:
logger.error("no input request selected", exc_info=True)
def cancel(self, index):
rid = self.model.row_to_key[index]
self.cancelled.emit(rid)
def supply_all(self):
rows = self.model.rowCount(QtCore.QModelIndex())
values = [self.request_stack.widget(row).get_values() for row in range(rows)]
rids = [self.model.row_to_key[row] for row in range(rows)]
for rid, value in zip(rids, values):
self.supplied.emit(rid, value)
def cancel_all(self):
rows = self.model.rowCount(QtCore.QModelIndex())
rids = [self.model.row_to_key[row] for row in range(rows)]
for rid in rids:
self.cancelled.emit(rid)
class _RequestList(QtWidgets.QListView):
selected = QtCore.pyqtSignal(int)
def __init__(self):
QtWidgets.QListView.__init__(self)
self.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.setMaximumWidth(200)
def selectionChanged(self, selected, deselected):
indexes = selected.indexes()
row = selected.indexes()[0].row() if len(indexes) > 0 else 0
self.selected.emit(row)
class InteractiveArgsDock(QtWidgets.QDockWidget):
def __init__(self, interactive_args_sub, interactive_args_rpc):
QtWidgets.QDockWidget.__init__(self, "Interactive Args")
self.interactive_args_sub = interactive_args_sub
self.interactive_args_rpc = interactive_args_rpc
layout = LayoutWidget(self)
self.setWidget(layout)
self.request_queue = _RequestList()
model = Model({})
self.request_queue.setModel(model)
layout.addWidget(self.request_queue, 0, 0)
self.args_area = _InputRequestView(self.interactive_args_sub)
self.args_area.supplied.connect(self.supply)
self.args_area.cancelled.connect(self.cancel)
layout.addWidget(self.args_area, 0, 1, rowspan=5)
self.request_queue.selected.connect(self.args_area.set_selected_request)
self.supply_btn = QtWidgets.QPushButton("Supply selected")
self.supply_btn.clicked.connect(self.args_area.supply_selected)
layout.addWidget(self.supply_btn, 1, 0)
self.cancel_btn = QtWidgets.QPushButton("Cancel selected")
self.cancel_btn.clicked.connect(self.args_area.cancel_selected)
layout.addWidget(self.cancel_btn, 2, 0)
self.supply_all_btn = QtWidgets.QPushButton("Supply all")
self.supply_all_btn.clicked.connect(self.args_area.supply_all)
layout.addWidget(self.supply_all_btn, 3, 0)
self.cancel_all_btn = QtWidgets.QPushButton("Cancel all")
self.cancel_all_btn.clicked.connect(self.args_area.cancel_all)
layout.addWidget(self.cancel_all_btn, 4, 0)
self.interactive_args_sub.add_setmodel_callback(self.request_queue.setModel)
def supply(self, rid, values):
asyncio.ensure_future(self._supply_task(rid, values))
async def _supply_task(self, rid, values):
try:
await self.interactive_args_rpc.supply(rid, values)
except:
logger.error("dashboard failed to supply interactive arguments for experiment: %d", rid, exc_info=True)
def cancel(self, rid):
asyncio.ensure_future(self._cancel_task(rid))
async def _cancel_task(self, rid):
try:
await self.interactive_args_rpc.cancel(rid)
except:
logger.error("dashboard failed to cancel input request for experiment: %d", rid, exc_info=True)

View File

@ -23,7 +23,7 @@ from artiq.gui.models import ModelSubscriber
from artiq.gui import state, log
from artiq.dashboard import (experiments, shortcuts, explorer,
moninj, datasets, schedule, applets_ccb,
waveform)
waveform, interactive_args)
def get_argparser():
@ -141,7 +141,7 @@ def main():
# create connections to master
rpc_clients = dict()
for target in "schedule", "experiment_db", "dataset_db", "device_db":
for target in "schedule", "experiment_db", "dataset_db", "device_db", "interactive_arg_db":
client = AsyncioClient()
loop.run_until_complete(client.connect_rpc(
args.server, args.port_control, target))
@ -166,7 +166,8 @@ def main():
for notifier_name, modelf in (("explist", explorer.Model),
("explist_status", explorer.StatusUpdater),
("datasets", datasets.Model),
("schedule", schedule.Model)):
("schedule", schedule.Model),
("interactive_args", interactive_args.Model)):
subscriber = ModelSubscriber(notifier_name, modelf,
report_disconnect)
loop.run_until_complete(subscriber.connect(
@ -244,6 +245,11 @@ def main():
loop.run_until_complete(devices_sub.connect(args.server, args.port_notify))
atexit_register_coroutine(devices_sub.close, loop=loop)
d_interactive_args = interactive_args.InteractiveArgsDock(
sub_clients["interactive_args"],
rpc_clients["interactive_arg_db"]
)
d_schedule = schedule.ScheduleDock(
rpc_clients["schedule"], sub_clients["schedule"])
smgr.register(d_schedule)
@ -257,7 +263,7 @@ def main():
right_docks = [
d_explorer, d_shortcuts,
d_ttl_dds.ttl_dock, d_ttl_dds.dds_dock, d_ttl_dds.dac_dock,
d_datasets, d_applets, d_waveform
d_datasets, d_applets, d_waveform, d_interactive_args
]
main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, right_docks[0])
for d1, d2 in zip(right_docks, right_docks[1:]):

View File

@ -69,7 +69,10 @@ class EntryArea(QtWidgets.QTreeWidget):
def setattr_argument(self, key, processor, group=None, tooltip=None):
argument = dict()
desc = processor.describe()
if isinstance(processor, dict):
desc = processor
else:
desc = processor.describe()
argument["desc"] = desc
argument["group"] = group
argument["tooltip"] = tooltip