diff --git a/artiq/applets/big_number.py b/artiq/applets/big_number.py index 62348c8cf..b637847ff 100755 --- a/artiq/applets/big_number.py +++ b/artiq/applets/big_number.py @@ -6,7 +6,7 @@ from artiq.applets.simple import SimpleApplet class NumberWidget(QtWidgets.QLCDNumber): - def __init__(self, args): + def __init__(self, args, ctl): QtWidgets.QLCDNumber.__init__(self) self.setDigitCount(args.digit_count) self.dataset_name = args.dataset diff --git a/artiq/applets/image.py b/artiq/applets/image.py index b7d36c1a1..7a192bcda 100755 --- a/artiq/applets/image.py +++ b/artiq/applets/image.py @@ -7,7 +7,7 @@ from artiq.applets.simple import SimpleApplet class Image(pyqtgraph.ImageView): - def __init__(self, args): + def __init__(self, args, ctl): pyqtgraph.ImageView.__init__(self) self.args = args diff --git a/artiq/applets/plot_hist.py b/artiq/applets/plot_hist.py index b3493c836..9dbebcf4c 100755 --- a/artiq/applets/plot_hist.py +++ b/artiq/applets/plot_hist.py @@ -8,7 +8,7 @@ from artiq.applets.simple import TitleApplet class HistogramPlot(pyqtgraph.PlotWidget): - def __init__(self, args): + def __init__(self, args, ctl): pyqtgraph.PlotWidget.__init__(self) self.args = args self.timer = QTimer() diff --git a/artiq/applets/plot_xy.py b/artiq/applets/plot_xy.py index 8eaf9786a..bb3eab388 100755 --- a/artiq/applets/plot_xy.py +++ b/artiq/applets/plot_xy.py @@ -9,7 +9,7 @@ from artiq.applets.simple import TitleApplet class XYPlot(pyqtgraph.PlotWidget): - def __init__(self, args): + def __init__(self, args, ctl): pyqtgraph.PlotWidget.__init__(self) self.args = args self.timer = QTimer() diff --git a/artiq/applets/plot_xy_hist.py b/artiq/applets/plot_xy_hist.py index 39bd49098..82c06d778 100755 --- a/artiq/applets/plot_xy_hist.py +++ b/artiq/applets/plot_xy_hist.py @@ -22,7 +22,7 @@ def _compute_ys(histogram_bins, histograms_counts): # pyqtgraph.GraphicsWindow fails to behave like a regular Qt widget # and breaks embedding. Do not use as top widget. class XYHistPlot(QtWidgets.QSplitter): - def __init__(self, args): + def __init__(self, args, ctl): QtWidgets.QSplitter.__init__(self) self.resize(1000, 600) self.setWindowTitle("XY/Histogram") diff --git a/artiq/applets/progress_bar.py b/artiq/applets/progress_bar.py index bbded954c..f7eed5c9e 100644 --- a/artiq/applets/progress_bar.py +++ b/artiq/applets/progress_bar.py @@ -6,7 +6,7 @@ from artiq.applets.simple import SimpleApplet class ProgressWidget(QtWidgets.QProgressBar): - def __init__(self, args): + def __init__(self, args, ctl): QtWidgets.QProgressBar.__init__(self) self.setMinimum(args.min) self.setMaximum(args.max) diff --git a/artiq/applets/simple.py b/artiq/applets/simple.py index 196b8f1e3..d06334936 100644 --- a/artiq/applets/simple.py +++ b/artiq/applets/simple.py @@ -7,6 +7,7 @@ import string from qasync import QEventLoop, QtWidgets, QtCore from sipyco.sync_struct import Subscriber, process_mod +from sipyco.pc_rpc import AsyncioClient as RPCClient from sipyco import pyon from sipyco.pipe_ipc import AsyncioChildComm @@ -14,6 +15,29 @@ from sipyco.pipe_ipc import AsyncioChildComm logger = logging.getLogger(__name__) +class AppletControlIPC: + def __init__(self, ipc): + self.ipc = ipc + + def set_dataset(self, key, value, persist=None): + self.ipc.set_dataset(key, value, persist) + + +class AppletControlRPC: + def __init__(self, loop, dataset_ctl): + self.loop = loop + self.dataset_ctl = dataset_ctl + self.background_tasks = set() + + def _background(self, coro, *args): + task = self.loop.create_task(coro(*args)) + self.background_tasks.add(task) + task.add_done_callback(self.background_tasks.discard) + + def set_dataset(self, key, value, persist=None): + self._background(self.dataset_ctl.set, key, value, persist) + + class AppletIPCClient(AsyncioChildComm): def set_close_cb(self, close_cb): self.close_cb = close_cb @@ -72,6 +96,12 @@ class AppletIPCClient(AsyncioChildComm): self.mod_cb = mod_cb asyncio.ensure_future(self.listen()) + def set_dataset(self, key, value, persist=None): + self.write_pyon({"action": "set_dataset", + "key": key, + "value": value, + "persist": persist}) + class SimpleApplet: def __init__(self, main_widget_class, cmd_description=None, @@ -92,8 +122,11 @@ class SimpleApplet: "for dataset notifications " "(ignored in embedded mode)") group.add_argument( - "--port", default=3250, type=int, - help="TCP port to connect to") + "--port-notify", default=3250, type=int, + help="TCP port to connect to for notifications (ignored in embedded mode)") + group.add_argument( + "--port-control", default=3251, type=int, + help="TCP port to connect to for control (ignored in embedded mode)") self._arggroup_datasets = self.argparser.add_argument_group("datasets") @@ -132,8 +165,21 @@ class SimpleApplet: if self.embed is not None: self.ipc.close() + def ctl_init(self): + if self.embed is None: + dataset_ctl = RPCClient() + self.loop.run_until_complete(dataset_ctl.connect_rpc( + self.args.server, self.args.port_control, "master_dataset_db")) + self.ctl = AppletControlRPC(self.loop, dataset_ctl) + else: + self.ctl = AppletControlIPC(self.ipc) + + def ctl_close(self): + if self.embed is None: + self.ctl.dataset_ctl.close_rpc() + def create_main_widget(self): - self.main_widget = self.main_widget_class(self.args) + self.main_widget = self.main_widget_class(self.args, self.ctl) if self.embed is not None: self.ipc.set_close_cb(self.main_widget.close) if os.name == "nt": @@ -214,7 +260,7 @@ class SimpleApplet: self.subscriber = Subscriber("datasets", self.sub_init, self.sub_mod) self.loop.run_until_complete(self.subscriber.connect( - self.args.server, self.args.port)) + self.args.server, self.args.port_notify)) else: self.ipc.subscribe(self.datasets, self.sub_init, self.sub_mod, dataset_prefixes=self.dataset_prefixes) @@ -229,12 +275,16 @@ class SimpleApplet: try: self.ipc_init() try: - self.create_main_widget() - self.subscribe() + self.ctl_init() try: - self.loop.run_forever() + self.create_main_widget() + self.subscribe() + try: + self.loop.run_forever() + finally: + self.unsubscribe() finally: - self.unsubscribe() + self.ctl_close() finally: self.ipc_close() finally: