Skip to content

Commit 338e5fe

Browse files
committedFeb 8, 2016
Merge branch 'applets_pipeipc'
2 parents 13a8f9c + 44a1efa commit 338e5fe

File tree

10 files changed

+291
-195
lines changed

10 files changed

+291
-195
lines changed
 

Diff for: ‎artiq/applets/big_number.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python3.5
22

3-
from quamash import QtWidgets
3+
from PyQt5 import QtWidgets
44

55
from artiq.applets.simple import SimpleApplet
66

Diff for: ‎artiq/applets/plot_hist.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3.5
22

33
import numpy as np
4+
import PyQt5 # make sure pyqtgraph imports Qt5
45
import pyqtgraph
56

67
from artiq.applets.simple import SimpleApplet

Diff for: ‎artiq/applets/plot_xy.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3.5
22

33
import numpy as np
4+
import PyQt5 # make sure pyqtgraph imports Qt5
45
import pyqtgraph
56

67
from artiq.applets.simple import SimpleApplet

Diff for: ‎artiq/applets/plot_xy_hist.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3.5
22

33
import numpy as np
4-
from quamash import QtWidgets
4+
from PyQt5 import QtWidgets
55
import pyqtgraph
66

77
from artiq.applets.simple import SimpleApplet

Diff for: ‎artiq/applets/simple.py

+112-32
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,70 @@
1+
import logging
12
import argparse
23
import asyncio
34

45
from quamash import QEventLoop, QtWidgets, QtGui, QtCore
56

6-
from artiq.protocols.sync_struct import Subscriber
7-
from artiq.protocols.pc_rpc import Client
7+
from artiq.protocols.sync_struct import Subscriber, process_mod
8+
from artiq.protocols import pyon
9+
from artiq.protocols.pipe_ipc import AsyncioChildComm
10+
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
class AppletIPCClient(AsyncioChildComm):
16+
def set_close_cb(self, close_cb):
17+
self.close_cb = close_cb
18+
19+
def write_pyon(self, obj):
20+
self.write(pyon.encode(obj).encode() + b"\n")
21+
22+
async def read_pyon(self):
23+
line = await self.readline()
24+
return pyon.decode(line.decode())
25+
26+
async def embed(self, win_id):
27+
# This function is only called when not subscribed to anything,
28+
# so the only normal replies are embed_done and terminate.
29+
self.write_pyon({"action": "embed",
30+
"win_id": win_id})
31+
reply = await self.read_pyon()
32+
if reply["action"] == "terminate":
33+
self.close_cb()
34+
elif reply["action"] != "embed_done":
35+
logger.error("unexpected action reply to embed request: %s",
36+
action)
37+
self.close_cb()
38+
39+
async def listen(self):
40+
data = None
41+
while True:
42+
obj = await self.read_pyon()
43+
try:
44+
action = obj["action"]
45+
if action == "terminate":
46+
self.close_cb()
47+
return
48+
elif action == "mod":
49+
mod = obj["mod"]
50+
if mod["action"] == "init":
51+
data = self.init_cb(mod["struct"])
52+
else:
53+
process_mod(data, mod)
54+
self.mod_cb(mod)
55+
else:
56+
raise ValueError("unknown action in parent message")
57+
except:
58+
logger.error("error processing parent message",
59+
exc_info=True)
60+
self.close_cb()
61+
62+
def subscribe(self, datasets, init_cb, mod_cb):
63+
self.write_pyon({"action": "subscribe",
64+
"datasets": datasets})
65+
self.init_cb = init_cb
66+
self.mod_cb = mod_cb
67+
asyncio.ensure_future(self.listen())
868

969

1070
class SimpleApplet:
@@ -13,27 +73,27 @@ def __init__(self, main_widget_class, cmd_description=None,
1373
self.main_widget_class = main_widget_class
1474

1575
self.argparser = argparse.ArgumentParser(description=cmd_description)
76+
1677
self.argparser.add_argument("--update-delay", type=float,
1778
default=default_update_delay,
1879
help="time to wait after a mod (buffering other mods) "
1980
"before updating (default: %(default).2f)")
20-
group = self.argparser.add_argument_group("data server")
21-
group.add_argument(
22-
"--server-notify", default="::1",
23-
help="hostname or IP to connect to for dataset notifications")
24-
group.add_argument(
25-
"--port-notify", default=3250, type=int,
26-
help="TCP port to connect to for dataset notifications")
27-
group = self.argparser.add_argument_group("GUI server")
81+
82+
group = self.argparser.add_argument_group("standalone mode (default)")
2883
group.add_argument(
29-
"--server-gui", default="::1",
30-
help="hostname or IP to connect to for GUI control")
84+
"--server", default="::1",
85+
help="hostname or IP of the master to connect to "
86+
"for dataset notifications "
87+
"(ignored in embedded mode)")
3188
group.add_argument(
32-
"--port-gui", default=6501, type=int,
33-
help="TCP port to connect to for GUI control")
34-
group.add_argument("--embed", default=None, type=int,
35-
help="embed main widget into existing window")
89+
"--port", default=3250, type=int,
90+
help="TCP port to connect to")
91+
92+
self.argparser.add_argument("--embed", default=None,
93+
help="embed into GUI", metavar="IPC_ADDRESS")
94+
3695
self._arggroup_datasets = self.argparser.add_argument_group("datasets")
96+
3797
self.dataset_args = set()
3898

3999
def add_dataset(self, name, help=None, required=True):
@@ -56,6 +116,15 @@ def quamash_init(self):
56116
self.loop = QEventLoop(app)
57117
asyncio.set_event_loop(self.loop)
58118

119+
def ipc_init(self):
120+
if self.args.embed is not None:
121+
self.ipc = AppletIPCClient(self.args.embed)
122+
self.loop.run_until_complete(self.ipc.connect())
123+
124+
def ipc_close(self):
125+
if self.args.embed is not None:
126+
self.ipc.close()
127+
59128
def create_main_widget(self):
60129
self.main_widget = self.main_widget_class(self.args)
61130
# Qt window embedding is ridiculously buggy, and empirical testing
@@ -65,22 +134,22 @@ def create_main_widget(self):
65134
# 3. applet sends the ID to host, host embeds the widget
66135
# 4. applet shows the widget
67136
# Doing embedding the other way around (using QWindow.setParent in the
68-
# applet) breaks resizing; furthermore the host needs to know our
69-
# window ID to request graceful termination by closing the window.
137+
# applet) breaks resizing.
70138
if self.args.embed is not None:
139+
self.ipc.set_close_cb(self.main_widget.close)
71140
win_id = int(self.main_widget.winId())
72-
remote = Client(self.args.server_gui, self.args.port_gui, "applets")
73-
try:
74-
remote.embed(self.args.embed, win_id)
75-
finally:
76-
remote.close_rpc()
141+
self.loop.run_until_complete(self.ipc.embed(win_id))
77142
self.main_widget.show()
78143

79144
def sub_init(self, data):
80145
self.data = data
81146
return data
82147

83148
def filter_mod(self, mod):
149+
if self.args.embed is not None:
150+
# the parent already filters for us
151+
return True
152+
84153
if mod["action"] == "init":
85154
return True
86155
if mod["path"]:
@@ -108,21 +177,32 @@ def sub_mod(self, mod):
108177
else:
109178
self.main_widget.data_changed(self.data, [mod])
110179

111-
def create_subscriber(self):
112-
self.subscriber = Subscriber("datasets",
113-
self.sub_init, self.sub_mod)
114-
self.loop.run_until_complete(self.subscriber.connect(
115-
self.args.server_notify, self.args.port_notify))
180+
def subscribe(self):
181+
if self.args.embed is None:
182+
self.subscriber = Subscriber("datasets",
183+
self.sub_init, self.sub_mod)
184+
self.loop.run_until_complete(self.subscriber.connect(
185+
self.args.server, self.args.port))
186+
else:
187+
self.ipc.subscribe(self.datasets, self.sub_init, self.sub_mod)
188+
189+
def unsubscribe(self):
190+
if self.args.embed is None:
191+
self.loop.run_until_complete(self.subscriber.close())
116192

117193
def run(self):
118194
self.args_init()
119195
self.quamash_init()
120196
try:
121-
self.create_main_widget()
122-
self.create_subscriber()
197+
self.ipc_init()
123198
try:
124-
self.loop.run_forever()
199+
self.create_main_widget()
200+
self.subscribe()
201+
try:
202+
self.loop.run_forever()
203+
finally:
204+
self.unsubscribe()
125205
finally:
126-
self.loop.run_until_complete(self.subscriber.close())
206+
self.ipc_close()
127207
finally:
128208
self.loop.close()

Diff for: ‎artiq/frontend/artiq_gui.py

+8-13
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from artiq import __artiq_dir__ as artiq_dir
1616
from artiq.tools import *
17-
from artiq.protocols.pc_rpc import AsyncioClient, Server
17+
from artiq.protocols.pc_rpc import AsyncioClient
1818
from artiq.gui.models import ModelSubscriber
1919
from artiq.gui import (state, experiments, shortcuts, explorer,
2020
moninj, datasets, applets, schedule, log, console)
@@ -113,9 +113,9 @@ def main():
113113

114114
d_datasets = datasets.DatasetsDock(win, dock_area, sub_clients["datasets"])
115115

116-
appletmgr = applets.AppletManager(dock_area)
117-
atexit_register_coroutine(appletmgr.stop)
118-
smgr.register(appletmgr)
116+
d_applets = applets.AppletsDock(dock_area, sub_clients["datasets"])
117+
atexit_register_coroutine(d_applets.stop)
118+
smgr.register(d_applets)
119119

120120
if os.name != "nt":
121121
d_ttl_dds = moninj.MonInj()
@@ -135,11 +135,11 @@ def main():
135135
if os.name != "nt":
136136
dock_area.addDock(d_ttl_dds.dds_dock, "top")
137137
dock_area.addDock(d_ttl_dds.ttl_dock, "above", d_ttl_dds.dds_dock)
138-
dock_area.addDock(appletmgr.main_dock, "above", d_ttl_dds.ttl_dock)
139-
dock_area.addDock(d_datasets, "above", appletmgr.main_dock)
138+
dock_area.addDock(d_applets, "above", d_ttl_dds.ttl_dock)
139+
dock_area.addDock(d_datasets, "above", d_applets)
140140
else:
141-
dock_area.addDock(appletmgr.main_dock, "top")
142-
dock_area.addDock(d_datasets, "above", appletmgr.main_dock)
141+
dock_area.addDock(d_applets, "top")
142+
dock_area.addDock(d_datasets, "above", d_applets)
143143
dock_area.addDock(d_shortcuts, "above", d_datasets)
144144
dock_area.addDock(d_explorer, "above", d_shortcuts)
145145
dock_area.addDock(d_console, "bottom")
@@ -155,11 +155,6 @@ def main():
155155
if d_log0 is not None:
156156
dock_area.addDock(d_log0, "right", d_explorer)
157157

158-
# start RPC server
159-
rpc_server = Server({"applets": appletmgr.rpc})
160-
loop.run_until_complete(rpc_server.start("::1", 6501))
161-
atexit_register_coroutine(rpc_server.stop)
162-
163158
# run
164159
win.show()
165160
loop.run_until_complete(win.exit_request.wait())

0 commit comments

Comments
 (0)
Please sign in to comment.