diff --git a/artiq/applets/simple.py b/artiq/applets/simple.py index 24a76e464..7da0c0a31 100644 --- a/artiq/applets/simple.py +++ b/artiq/applets/simple.py @@ -4,7 +4,7 @@ import asyncio from quamash import QEventLoop, QtWidgets, QtGui, QtCore -from artiq.protocols.sync_struct import Subscriber +from artiq.protocols.sync_struct import Subscriber, process_mod from artiq.protocols import pyon from artiq.protocols.pipe_ipc import AsyncioChildComm @@ -37,6 +37,7 @@ class AppletIPCClient(AsyncioChildComm): self.close_cb() async def listen(self): + data = None while True: obj = await self.read_pyon() try: @@ -44,6 +45,13 @@ class AppletIPCClient(AsyncioChildComm): if action == "terminate": self.close_cb() return + elif action == "mod": + mod = obj["mod"] + if mod["action"] == "init": + data = self.init_cb(mod["struct"]) + else: + process_mod(data, mod) + self.mod_cb(mod) else: raise ValueError("unknown action in applet request") except: @@ -51,9 +59,11 @@ class AppletIPCClient(AsyncioChildComm): exc_info=True) self.close_cb() - def subscribe(self, datasets): + def subscribe(self, datasets, init_cb, mod_cb): self.write_pyon({"action": "subscribe", "datasets": datasets}) + self.init_cb = init_cb + self.mod_cb = mod_cb asyncio.ensure_future(self.listen()) @@ -188,7 +198,7 @@ class SimpleApplet: self.loop.run_until_complete(self.subscriber.connect( self.args.server_notify, self.args.port_notify)) elif self.args.mode == "embedded": - self.ipc.subscribe(self.datasets) + self.ipc.subscribe(self.datasets, self.sub_init, self.sub_mod) else: raise NotImplementedError diff --git a/artiq/frontend/artiq_gui.py b/artiq/frontend/artiq_gui.py index d4e36445b..ddbcc0c48 100755 --- a/artiq/frontend/artiq_gui.py +++ b/artiq/frontend/artiq_gui.py @@ -111,7 +111,7 @@ def main(): d_datasets = datasets.DatasetsDock(win, dock_area, sub_clients["datasets"]) - d_applets = applets.AppletsDock(dock_area) + d_applets = applets.AppletsDock(dock_area, sub_clients["datasets"]) atexit_register_coroutine(d_applets.stop) smgr.register(d_applets) diff --git a/artiq/gui/applets.py b/artiq/gui/applets.py index 626195d87..6df993804 100644 --- a/artiq/gui/applets.py +++ b/artiq/gui/applets.py @@ -15,6 +15,11 @@ logger = logging.getLogger(__name__) class AppletIPCServer(AsyncioParentComm): + def __init__(self, datasets_sub): + AsyncioParentComm.__init__(self) + self.datasets_sub = datasets_sub + self.datasets = set() + def write_pyon(self, obj): self.write(pyon.encode(obj).encode() + b"\n") @@ -22,7 +27,25 @@ class AppletIPCServer(AsyncioParentComm): line = await self.readline() return pyon.decode(line.decode()) + def _synthesize_init(self, data): + struct = {k: v for k, v in data.items() if k in self.datasets} + return {"action": "init", + "struct": struct} + + def _on_mod(self, mod): + if mod["action"] == "init": + mod = self._synthesize_init(mod["struct"]) + else: + if mod["path"]: + if mod["path"][0] not in self.datasets: + return + elif mod["action"] in {"setitem", "delitem"}: + if mod["key"] not in self.datasets: + return + self.write_pyon({"action": "mod", "mod": mod}) + async def serve(self, embed_cb): + self.datasets_sub.notify_cbs.append(self._on_mod) try: while True: obj = await self.read_pyon() @@ -32,7 +55,11 @@ class AppletIPCServer(AsyncioParentComm): embed_cb(obj["win_id"]) self.write_pyon({"action": "embed_done"}) elif action == "subscribe": - print("applet subscribed: ", obj["datasets"]) + self.datasets = obj["datasets"] + if self.datasets_sub.model is not None: + mod = self._synthesize_init( + self.datasets_sub.model.backing_store) + self.write_pyon({"action": "mod", "mod": mod}) else: raise ValueError("unknown action in applet request") except: @@ -44,6 +71,8 @@ class AppletIPCServer(AsyncioParentComm): except: logger.error("error processing data from applet, " "server stopped", exc_info=True) + finally: + self.datasets_sub.notify_cbs.remove(self._on_mod) def start(self, embed_cb): self.server_task = asyncio.ensure_future(self.serve(embed_cb)) @@ -54,11 +83,12 @@ class AppletIPCServer(AsyncioParentComm): class AppletDock(dockarea.Dock): - def __init__(self, uid, name, command): + def __init__(self, datasets_sub, uid, name, command): dockarea.Dock.__init__(self, "applet" + str(uid), label="Applet: " + name, closable=True) self.setMinimumSize(QtCore.QSize(500, 400)) + self.datasets_sub = datasets_sub self.applet_name = name self.command = command @@ -67,7 +97,7 @@ class AppletDock(dockarea.Dock): self.label.setText("Applet: " + name) async def start(self): - self.ipc = AppletIPCServer() + self.ipc = AppletIPCServer(self.datasets_sub) command = self.command.format(python=sys.executable, ipc_address=self.ipc.get_address()) logger.debug("starting command %s for %s", command, self.applet_name) @@ -122,8 +152,9 @@ _templates = [ class AppletsDock(dockarea.Dock): - def __init__(self, dock_area): + def __init__(self, dock_area, datasets_sub): self.dock_area = dock_area + self.datasets_sub = datasets_sub self.dock_to_checkbox = dict() self.applet_uids = set() self.workaround_pyqtgraph_bug = False @@ -170,7 +201,7 @@ class AppletsDock(dockarea.Dock): self.table.cellChanged.connect(self.cell_changed) def create(self, uid, name, command): - dock = AppletDock(uid, name, command) + dock = AppletDock(self.datasets_sub, uid, name, command) # If a dock is floated and then dock state is restored, pyqtgraph # leaves a "phantom" window open. if self.workaround_pyqtgraph_bug: