2016-01-03 00:46:15 +08:00
|
|
|
import argparse
|
|
|
|
import asyncio
|
|
|
|
|
2016-01-07 20:22:44 +08:00
|
|
|
from quamash import QEventLoop, QtWidgets, QtGui, QtCore
|
2016-01-03 00:46:15 +08:00
|
|
|
|
|
|
|
from artiq.protocols.sync_struct import Subscriber
|
2016-01-07 20:22:44 +08:00
|
|
|
from artiq.protocols.pc_rpc import Client
|
2016-01-03 00:46:15 +08:00
|
|
|
|
|
|
|
|
|
|
|
class SimpleApplet:
|
2016-01-10 22:12:00 +08:00
|
|
|
def __init__(self, main_widget_class, cmd_description=None,
|
|
|
|
default_update_delay=0.0):
|
2016-01-03 00:46:15 +08:00
|
|
|
self.main_widget_class = main_widget_class
|
|
|
|
|
|
|
|
self.argparser = argparse.ArgumentParser(description=cmd_description)
|
2016-01-10 22:12:00 +08:00
|
|
|
self.argparser.add_argument("--update-delay", type=float,
|
|
|
|
default=default_update_delay,
|
|
|
|
help="time to wait after a mod (buffering other mods) "
|
|
|
|
"before updating (default: %(default).2f)")
|
2016-01-03 00:46:15 +08:00
|
|
|
group = self.argparser.add_argument_group("data server")
|
|
|
|
group.add_argument(
|
2016-01-07 20:22:44 +08:00
|
|
|
"--server-notify", default="::1",
|
|
|
|
help="hostname or IP to connect to for dataset notifications")
|
2016-01-03 00:46:15 +08:00
|
|
|
group.add_argument(
|
2016-01-07 20:22:44 +08:00
|
|
|
"--port-notify", default=3250, type=int,
|
|
|
|
help="TCP port to connect to for dataset notifications")
|
|
|
|
group = self.argparser.add_argument_group("GUI server")
|
|
|
|
group.add_argument(
|
|
|
|
"--server-gui", default="::1",
|
|
|
|
help="hostname or IP to connect to for GUI control")
|
|
|
|
group.add_argument(
|
|
|
|
"--port-gui", default=6501, type=int,
|
|
|
|
help="TCP port to connect to for GUI control")
|
|
|
|
group.add_argument("--embed", default=None, type=int,
|
|
|
|
help="embed main widget into existing window")
|
2016-01-03 00:46:15 +08:00
|
|
|
self._arggroup_datasets = self.argparser.add_argument_group("datasets")
|
|
|
|
|
2016-01-10 18:18:47 +08:00
|
|
|
def add_dataset(self, name, help=None, required=True):
|
|
|
|
kwargs = dict()
|
|
|
|
if help is not None:
|
|
|
|
kwargs["help"] = help
|
|
|
|
if required:
|
|
|
|
self._arggroup_datasets.add_argument(name, **kwargs)
|
2016-01-03 00:46:15 +08:00
|
|
|
else:
|
2016-01-10 18:18:47 +08:00
|
|
|
self._arggroup_datasets.add_argument("--" + name, **kwargs)
|
2016-01-03 00:46:15 +08:00
|
|
|
|
|
|
|
def args_init(self):
|
|
|
|
self.args = self.argparser.parse_args()
|
|
|
|
|
|
|
|
def quamash_init(self):
|
|
|
|
app = QtWidgets.QApplication([])
|
|
|
|
self.loop = QEventLoop(app)
|
|
|
|
asyncio.set_event_loop(self.loop)
|
|
|
|
|
|
|
|
def create_main_widget(self):
|
|
|
|
self.main_widget = self.main_widget_class(self.args)
|
2016-01-07 20:22:44 +08:00
|
|
|
# Qt window embedding is ridiculously buggy, and empirical testing
|
|
|
|
# has shown that the following procedure must be followed exactly:
|
|
|
|
# 1. applet creates widget
|
|
|
|
# 2. applet creates native window without showing it, and get its ID
|
|
|
|
# 3. applet sends the ID to host, host embeds the widget
|
|
|
|
# 4. applet shows the widget
|
|
|
|
# Doing embedding the other way around (using QWindow.setParent in the
|
|
|
|
# applet) breaks resizing; furthermore the host needs to know our
|
|
|
|
# window ID to request graceful termination by closing the window.
|
|
|
|
if self.args.embed is not None:
|
|
|
|
win_id = int(self.main_widget.winId())
|
|
|
|
remote = Client(self.args.server_gui, self.args.port_gui, "applets")
|
|
|
|
try:
|
|
|
|
remote.embed(self.args.embed, win_id)
|
|
|
|
finally:
|
|
|
|
remote.close_rpc()
|
2016-01-03 00:46:15 +08:00
|
|
|
self.main_widget.show()
|
|
|
|
|
|
|
|
def sub_init(self, data):
|
|
|
|
self.data = data
|
|
|
|
return data
|
|
|
|
|
2016-01-10 22:12:00 +08:00
|
|
|
def flush_mod_buffer(self):
|
|
|
|
self.main_widget.data_changed(self.data, self.mod_buffer)
|
|
|
|
del self.mod_buffer
|
|
|
|
|
2016-01-03 00:46:15 +08:00
|
|
|
def sub_mod(self, mod):
|
2016-01-10 22:12:00 +08:00
|
|
|
if self.args.update_delay:
|
|
|
|
if hasattr(self, "mod_buffer"):
|
|
|
|
self.mod_buffer.append(mod)
|
|
|
|
else:
|
|
|
|
self.mod_buffer = [mod]
|
|
|
|
asyncio.get_event_loop().call_later(self.args.update_delay,
|
|
|
|
self.flush_mod_buffer)
|
|
|
|
else:
|
|
|
|
self.main_widget.data_changed(self.data, [mod])
|
2016-01-03 00:46:15 +08:00
|
|
|
|
|
|
|
def create_subscriber(self):
|
|
|
|
self.subscriber = Subscriber("datasets",
|
|
|
|
self.sub_init, self.sub_mod)
|
|
|
|
self.loop.run_until_complete(self.subscriber.connect(
|
2016-01-07 20:22:44 +08:00
|
|
|
self.args.server_notify, self.args.port_notify))
|
2016-01-03 00:46:15 +08:00
|
|
|
|
|
|
|
def run(self):
|
|
|
|
self.args_init()
|
|
|
|
self.quamash_init()
|
|
|
|
try:
|
|
|
|
self.create_main_widget()
|
|
|
|
self.create_subscriber()
|
|
|
|
try:
|
|
|
|
self.loop.run_forever()
|
|
|
|
finally:
|
|
|
|
self.loop.run_until_complete(self.subscriber.close())
|
|
|
|
finally:
|
|
|
|
self.loop.close()
|