From 179ca36d09cd4724c59b4dab9cff63cd454c5d17 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 21 Jul 2015 17:23:32 +0200 Subject: [PATCH] gui: basic scan support --- artiq/frontend/artiq_gui.py | 11 +- artiq/gui/explorer.py | 4 +- artiq/gui/scan.py | 137 +++++++++++++++++++ artiq/language/scan.py | 3 +- examples/master/repository/arguments_demo.py | 3 + 5 files changed, 150 insertions(+), 8 deletions(-) create mode 100644 artiq/gui/scan.py diff --git a/artiq/frontend/artiq_gui.py b/artiq/frontend/artiq_gui.py index 05b2e7384..3a2cd17b8 100755 --- a/artiq/frontend/artiq_gui.py +++ b/artiq/frontend/artiq_gui.py @@ -87,17 +87,18 @@ def main(): d_ttl_dds = MonInj() loop.run_until_complete(d_ttl_dds.start(args.server, args.port_notify)) atexit.register(lambda: loop.run_until_complete(d_ttl_dds.stop())) - area.addDock(d_ttl_dds.dds_dock, "top") - area.addDock(d_ttl_dds.ttl_dock, "above", d_ttl_dds.dds_dock) - area.addDock(d_results, "above", d_ttl_dds.ttl_dock) - area.addDock(d_explorer, "above", d_results) d_params = ParametersDock() - area.addDock(d_params, "right", d_explorer) loop.run_until_complete(d_params.sub_connect( args.server, args.port_notify)) atexit.register(lambda: loop.run_until_complete(d_params.sub_close())) + area.addDock(d_ttl_dds.dds_dock, "top") + area.addDock(d_ttl_dds.ttl_dock, "above", d_ttl_dds.dds_dock) + area.addDock(d_results, "above", d_ttl_dds.ttl_dock) + area.addDock(d_params, "above", d_results) + area.addDock(d_explorer, "above", d_params) + d_schedule = ScheduleDock(schedule_ctl) loop.run_until_complete(d_schedule.sub_connect( args.server, args.port_notify)) diff --git a/artiq/gui/explorer.py b/artiq/gui/explorer.py index ccf1bea46..1d82b2a99 100644 --- a/artiq/gui/explorer.py +++ b/artiq/gui/explorer.py @@ -8,6 +8,7 @@ from pyqtgraph import LayoutWidget from artiq.protocols.sync_struct import Subscriber from artiq.protocols import pyon from artiq.gui.tools import DictSyncModel +from artiq.gui.scan import ScanController class _ExplistModel(DictSyncModel): @@ -93,7 +94,8 @@ _procty_to_entry = { "BooleanValue": _BooleanEntry, "EnumerationValue": _EnumerationEntry, "NumberValue": _NumberEntry, - "StringValue": _StringEntry + "StringValue": _StringEntry, + "Scannable": ScanController } diff --git a/artiq/gui/scan.py b/artiq/gui/scan.py new file mode 100644 index 000000000..1ec7c2dcf --- /dev/null +++ b/artiq/gui/scan.py @@ -0,0 +1,137 @@ +from quamash import QtGui +from pyqtgraph import LayoutWidget + + +class _Range(LayoutWidget): + def __init__(self, global_min, global_max, global_step, unit): + LayoutWidget.__init__(self) + + def apply_properties(spinbox): + if global_min is not None: + spinbox.setMinimum(global_min) + if global_max is not None: + spinbox.setMaximum(global_max) + if global_step is not None: + spinbox.setSingleStep(global_step) + if unit: + spinbox.setSuffix(" " + unit) + + self.addWidget(QtGui.QLabel("Min:"), 0, 0) + self.min = QtGui.QDoubleSpinBox() + apply_properties(self.min) + self.addWidget(self.min, 0, 1) + + self.addWidget(QtGui.QLabel("Max:"), 0, 2) + self.max = QtGui.QDoubleSpinBox() + apply_properties(self.max) + self.addWidget(self.max, 0, 3) + + self.addWidget(QtGui.QLabel("#Points:"), 0, 4) + self.npoints = QtGui.QSpinBox() + self.npoints.setMinimum(2) + self.npoints.setValue(10) + self.addWidget(self.npoints, 0, 5) + + def set_values(self, min, max, npoints): + self.min.setValue(min) + self.max.setValue(max) + self.npoints.setValue(npoints) + + def get_values(self): + return { + "min": self.min.value(), + "max": self.max.value(), + "npoints": self.npoints.value() + } + + +class ScanController(LayoutWidget): + def __init__(self, procdesc): + LayoutWidget.__init__(self) + + self.stack = QtGui.QStackedWidget() + self.addWidget(self.stack, 1, 0, colspan=4) + + gmin, gmax = procdesc["global_min"], procdesc["global_max"] + gstep = procdesc["global_step"] + unit = procdesc["unit"] + + self.v_noscan = QtGui.QDoubleSpinBox() + if gmin is not None: + self.v_noscan.setMinimum(gmin) + if gmax is not None: + self.v_noscan.setMaximum(gmax) + if gstep is not None: + self.v_noscan.setSingleStep(gstep) + if unit: + self.v_noscan.setSuffix(" " + unit) + self.v_noscan_gr = LayoutWidget() + self.v_noscan_gr.addWidget(QtGui.QLabel("Value:"), 0, 0) + self.v_noscan_gr.addWidget(self.v_noscan, 0, 1) + self.stack.addWidget(self.v_noscan_gr) + + self.v_linear = _Range(gmin, gmax, gstep, unit) + self.stack.addWidget(self.v_linear) + + self.v_random = _Range(gmin, gmax, gstep, unit) + self.stack.addWidget(self.v_random) + + self.v_explicit = QtGui.QLineEdit() + self.v_explicit_gr = LayoutWidget() + self.v_explicit_gr.addWidget(QtGui.QLabel("Sequence:"), 0, 0) + self.v_explicit_gr.addWidget(self.v_explicit, 0, 1) + self.stack.addWidget(self.v_explicit_gr) + + self.noscan = QtGui.QRadioButton("No scan") + self.linear = QtGui.QRadioButton("Linear") + self.random = QtGui.QRadioButton("Random") + self.explicit = QtGui.QRadioButton("Explicit") + radiobuttons = QtGui.QButtonGroup() + for n, b in enumerate([self.noscan, self.linear, + self.random, self.explicit]): + self.addWidget(b, 0, n) + radiobuttons.addButton(b) + b.toggled.connect(self.select_page) + + if "default" in procdesc: + d = procdesc["default"] + if d["ty"] == "NoScan": + self.noscan.setChecked(True) + self.v_noscan.setValue(d["value"]) + elif d["ty"] == "LinearScan": + self.linear.setChecked(True) + self.v_linear.set_values(d["min"], d["max"], d["step"]) + elif d["ty"] == "RandomScan": + self.random.setChecked(True) + self.v_random.set_values(d["min"], d["max"], d["step"]) + elif d["ty"] == "ExplicitScan": + self.explicit.setChecked(True) + self.v_explicit.insert(" ".join( + [str(x) for x in d["sequence"]])) + else: + self.noscan.setChecked(True) + + def select_page(self): + if self.noscan.isChecked(): + self.stack.setCurrentWidget(self.v_noscan_gr) + elif self.linear.isChecked(): + self.stack.setCurrentWidget(self.v_linear) + elif self.random.isChecked(): + self.stack.setCurrentWidget(self.v_random) + elif self.explicit.isChecked(): + self.stack.setCurrentWidget(self.v_explicit_gr) + + def get_argument_value(self): + if self.noscan.isChecked(): + return {"ty": "NoScan", "value": self.v_noscan.value()} + elif self.linear.isChecked(): + d = {"ty": "LinearScan"} + d.update(self.v_linear.get_values()) + return d + elif self.random.isChecked(): + d = {"ty": "RandomScan"} + d.update(self.v_random.get_values()) + return d + elif self.explicit.isChecked(): + sequence = [float(x) for x in self.v_explicit.text().split()] + return {"ty": "ExplicitScan", "sequence": sequence} diff --git a/artiq/language/scan.py b/artiq/language/scan.py index c1a0182f8..90867737d 100644 --- a/artiq/language/scan.py +++ b/artiq/language/scan.py @@ -60,7 +60,6 @@ class RandomScan: "min": self.min, "max": self.max, "npoints": self.npoints} - class ExplicitScan: def __init__(self, sequence): self.sequence = sequence @@ -109,7 +108,7 @@ class Scannable: d["global_min"] = self.global_min d["global_max"] = self.global_max d["global_step"] = self.global_step - d["unit"]= self.unit + d["unit"] = self.unit if hasattr(self, "default_value"): d["default"] = self.default_value.describe() return d diff --git a/examples/master/repository/arguments_demo.py b/examples/master/repository/arguments_demo.py index 78b8521b3..97393df19 100644 --- a/examples/master/repository/arguments_demo.py +++ b/examples/master/repository/arguments_demo.py @@ -9,6 +9,7 @@ class ArgumentsDemo(EnvExperiment): ["foo", "bar", "quux"], "foo")) self.attr_argument("number", NumberValue(42, unit="s", step=0.1)) self.attr_argument("string", StringValue("Hello World")) + self.attr_argument("scan", Scannable(global_max=400, default=NoScan(325))) def run(self): print(self.free_value) @@ -16,3 +17,5 @@ class ArgumentsDemo(EnvExperiment): print(self.enum) print(self.number) print(self.string) + for i in self.scan: + print(i)