mirror of https://github.com/m-labs/artiq.git
gui: basic plotting
This commit is contained in:
parent
a83473a19a
commit
9649e1837a
|
@ -3,12 +3,12 @@
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import atexit
|
import atexit
|
||||||
|
import os
|
||||||
|
|
||||||
# Quamash must be imported first so that pyqtgraph picks up the Qt binding
|
# Quamash must be imported first so that pyqtgraph picks up the Qt binding
|
||||||
# it has chosen.
|
# it has chosen.
|
||||||
from quamash import QEventLoop, QtGui
|
from quamash import QEventLoop, QtGui
|
||||||
from pyqtgraph import dockarea
|
from pyqtgraph import dockarea
|
||||||
import os
|
|
||||||
|
|
||||||
from artiq.protocols.file_db import FlatFileDB
|
from artiq.protocols.file_db import FlatFileDB
|
||||||
from artiq.protocols.pc_rpc import AsyncioClient
|
from artiq.protocols.pc_rpc import AsyncioClient
|
||||||
|
@ -71,7 +71,7 @@ def main():
|
||||||
args.server, args.port_notify))
|
args.server, args.port_notify))
|
||||||
atexit.register(lambda: loop.run_until_complete(d_explorer.sub_close()))
|
atexit.register(lambda: loop.run_until_complete(d_explorer.sub_close()))
|
||||||
|
|
||||||
d_results = ResultsDock()
|
d_results = ResultsDock(win, area)
|
||||||
loop.run_until_complete(d_results.sub_connect(
|
loop.run_until_complete(d_results.sub_connect(
|
||||||
args.server, args.port_notify))
|
args.server, args.port_notify))
|
||||||
atexit.register(lambda: loop.run_until_complete(d_results.sub_close()))
|
atexit.register(lambda: loop.run_until_complete(d_results.sub_close()))
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from quamash import QtGui
|
||||||
|
import pyqtgraph as pg
|
||||||
|
from pyqtgraph import dockarea
|
||||||
|
|
||||||
|
|
||||||
|
class _SimpleSettings(QtGui.QDialog):
|
||||||
|
def __init__(self, parent, prev_name, prev_settings,
|
||||||
|
result_list, create_cb):
|
||||||
|
QtGui.QDialog.__init__(self, parent=parent)
|
||||||
|
self.setWindowTitle(self._window_title)
|
||||||
|
|
||||||
|
grid = QtGui.QGridLayout()
|
||||||
|
self.setLayout(grid)
|
||||||
|
|
||||||
|
grid.addWidget(QtGui.QLabel("Name:"), 0, 0)
|
||||||
|
self.name = name = QtGui.QLineEdit()
|
||||||
|
grid.addWidget(name, 0, 1)
|
||||||
|
if prev_name is not None:
|
||||||
|
name.insert(prev_name)
|
||||||
|
|
||||||
|
grid.addWidget(QtGui.QLabel("Result:"))
|
||||||
|
self.result = result = QtGui.QComboBox()
|
||||||
|
grid.addWidget(result, 1, 1)
|
||||||
|
result.addItems(result_list)
|
||||||
|
result.setEditable(True)
|
||||||
|
if "result" in prev_settings:
|
||||||
|
result.setEditText(prev_settings["result"])
|
||||||
|
|
||||||
|
buttons = QtGui.QDialogButtonBox(
|
||||||
|
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
|
||||||
|
grid.addWidget(buttons, 2, 0, 1, 2)
|
||||||
|
buttons.accepted.connect(self.accept)
|
||||||
|
buttons.rejected.connect(self.reject)
|
||||||
|
|
||||||
|
def on_accept():
|
||||||
|
create_cb(name.text(), {"result": result.currentText()})
|
||||||
|
self.accepted.connect(on_accept)
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
if self.name.text() and self.result.currentText():
|
||||||
|
QtGui.QDialog.accept(self)
|
||||||
|
|
||||||
|
|
||||||
|
class NumberDisplaySettings(_SimpleSettings):
|
||||||
|
_window_title = "Number display"
|
||||||
|
|
||||||
|
|
||||||
|
class NumberDisplay(dockarea.Dock):
|
||||||
|
def __init__(self, name, settings):
|
||||||
|
dockarea.Dock.__init__(self, "Display: " + name, size=(250, 250),
|
||||||
|
closable=True)
|
||||||
|
self.settings = settings
|
||||||
|
self.number = QtGui.QLCDNumber()
|
||||||
|
self.number.setDigitCount(10)
|
||||||
|
self.addWidget(self.number)
|
||||||
|
|
||||||
|
def data_sources(self):
|
||||||
|
return {self.settings["result"]}
|
||||||
|
|
||||||
|
def update_data(self, data):
|
||||||
|
result = self.settings["result"]
|
||||||
|
try:
|
||||||
|
n = float(data[result])
|
||||||
|
except:
|
||||||
|
n = 0.0
|
||||||
|
self.number.display(n)
|
||||||
|
|
||||||
|
|
||||||
|
class XYDisplaySettings(_SimpleSettings):
|
||||||
|
_window_title = "XY plot"
|
||||||
|
|
||||||
|
|
||||||
|
class XYDisplay(dockarea.Dock):
|
||||||
|
def __init__(self, name, settings):
|
||||||
|
dockarea.Dock.__init__(self, "XY: " + name, size=(640, 480),
|
||||||
|
closable=True)
|
||||||
|
self.settings = settings
|
||||||
|
self.plot = pg.PlotWidget()
|
||||||
|
self.addWidget(self.plot)
|
||||||
|
|
||||||
|
def data_sources(self):
|
||||||
|
return {self.settings["result"]}
|
||||||
|
|
||||||
|
def update_data(self, data):
|
||||||
|
result = self.settings["result"]
|
||||||
|
try:
|
||||||
|
y = data[result]
|
||||||
|
except KeyError:
|
||||||
|
return
|
||||||
|
self.plot.clear()
|
||||||
|
if not y:
|
||||||
|
return
|
||||||
|
self.plot.plot(y)
|
||||||
|
|
||||||
|
|
||||||
|
class HistogramDisplaySettings(_SimpleSettings):
|
||||||
|
_window_title = "Histogram"
|
||||||
|
|
||||||
|
|
||||||
|
class HistogramDisplay(dockarea.Dock):
|
||||||
|
def __init__(self, name, settings):
|
||||||
|
dockarea.Dock.__init__(self, "Histogram: " + name, size=(640, 480),
|
||||||
|
closable=True)
|
||||||
|
self.settings = settings
|
||||||
|
self.plot = pg.PlotWidget()
|
||||||
|
self.addWidget(self.plot)
|
||||||
|
|
||||||
|
def data_sources(self):
|
||||||
|
return {self.settings["result"]}
|
||||||
|
|
||||||
|
def update_data(self, data):
|
||||||
|
result = self.settings["result"]
|
||||||
|
try:
|
||||||
|
y = data[result]
|
||||||
|
except KeyError:
|
||||||
|
return
|
||||||
|
x = list(range(len(y)+1))
|
||||||
|
self.plot.clear()
|
||||||
|
if not y:
|
||||||
|
return
|
||||||
|
self.plot.plot(x, y, stepMode=True, fillLevel=0, brush=(0, 0, 255, 150))
|
||||||
|
|
||||||
|
|
||||||
|
display_types = OrderedDict([
|
||||||
|
("Number", (NumberDisplaySettings, NumberDisplay)),
|
||||||
|
("XY", (XYDisplaySettings, XYDisplay)),
|
||||||
|
("Histogram", (HistogramDisplaySettings, HistogramDisplay))
|
||||||
|
])
|
|
@ -1,4 +1,6 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from collections import OrderedDict
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from quamash import QtGui, QtCore
|
from quamash import QtGui, QtCore
|
||||||
from pyqtgraph import dockarea
|
from pyqtgraph import dockarea
|
||||||
|
@ -6,6 +8,7 @@ from pyqtgraph import LayoutWidget
|
||||||
|
|
||||||
from artiq.protocols.sync_struct import Subscriber
|
from artiq.protocols.sync_struct import Subscriber
|
||||||
from artiq.gui.tools import DictSyncModel
|
from artiq.gui.tools import DictSyncModel
|
||||||
|
from artiq.gui.displays import *
|
||||||
|
|
||||||
|
|
||||||
def _fmt_type(v):
|
def _fmt_type(v):
|
||||||
|
@ -34,8 +37,10 @@ class ResultsModel(DictSyncModel):
|
||||||
|
|
||||||
|
|
||||||
class ResultsDock(dockarea.Dock):
|
class ResultsDock(dockarea.Dock):
|
||||||
def __init__(self):
|
def __init__(self, dialog_parent, dock_area):
|
||||||
dockarea.Dock.__init__(self, "Results", size=(1500, 500))
|
dockarea.Dock.__init__(self, "Results", size=(1500, 500))
|
||||||
|
self.dialog_parent = dialog_parent
|
||||||
|
self.dock_area = dock_area
|
||||||
|
|
||||||
grid = LayoutWidget()
|
grid = LayoutWidget()
|
||||||
self.addWidget(grid)
|
self.addWidget(grid)
|
||||||
|
@ -49,13 +54,17 @@ class ResultsDock(dockarea.Dock):
|
||||||
display_grid = QtGui.QGridLayout()
|
display_grid = QtGui.QGridLayout()
|
||||||
add_display_box.setLayout(display_grid)
|
add_display_box.setLayout(display_grid)
|
||||||
|
|
||||||
for n, name in enumerate(["Number", "XY", "Histogram"]):
|
for n, name in enumerate(display_types.keys()):
|
||||||
btn = QtGui.QPushButton(name)
|
btn = QtGui.QPushButton(name)
|
||||||
display_grid.addWidget(btn, n, 0)
|
display_grid.addWidget(btn, n, 0)
|
||||||
|
btn.clicked.connect(partial(self.create_dialog, name))
|
||||||
|
|
||||||
|
self.displays = dict()
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def sub_connect(self, host, port):
|
def sub_connect(self, host, port):
|
||||||
self.subscriber = Subscriber("rt_results", self.init_results_model)
|
self.subscriber = Subscriber("rt_results", self.init_results_model,
|
||||||
|
self.on_mod)
|
||||||
yield from self.subscriber.connect(host, port)
|
yield from self.subscriber.connect(host, port)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -63,6 +72,44 @@ class ResultsDock(dockarea.Dock):
|
||||||
yield from self.subscriber.close()
|
yield from self.subscriber.close()
|
||||||
|
|
||||||
def init_results_model(self, init):
|
def init_results_model(self, init):
|
||||||
table_model = ResultsModel(self.table, init)
|
self.table_model = ResultsModel(self.table, init)
|
||||||
self.table.setModel(table_model)
|
self.table.setModel(self.table_model)
|
||||||
return table_model
|
return self.table_model
|
||||||
|
|
||||||
|
def on_mod(self, mod):
|
||||||
|
if mod["action"] == "init":
|
||||||
|
for display in self.displays.values():
|
||||||
|
display.update_data(self.table_model.backing_store)
|
||||||
|
return
|
||||||
|
|
||||||
|
if mod["action"] == "setitem":
|
||||||
|
source = mod["key"]
|
||||||
|
elif mod["path"]:
|
||||||
|
source = mod["path"][0]
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
for display in self.displays.values():
|
||||||
|
if source in display.data_sources():
|
||||||
|
display.update_data(self.table_model.backing_store)
|
||||||
|
|
||||||
|
def create_dialog(self, ty):
|
||||||
|
dlg_class = display_types[ty][0]
|
||||||
|
dlg = dlg_class(self.dialog_parent, None, dict(),
|
||||||
|
sorted(self.table_model.backing_store.keys()),
|
||||||
|
partial(self.create_display, ty, None))
|
||||||
|
dlg.open()
|
||||||
|
|
||||||
|
def create_display(self, ty, prev_name, name, settings):
|
||||||
|
if prev_name is not None and prev_name in self.displays:
|
||||||
|
raise NotImplementedError
|
||||||
|
dsp_class = display_types[ty][1]
|
||||||
|
dsp = dsp_class(name, settings)
|
||||||
|
self.displays[name] = dsp
|
||||||
|
dsp.update_data(self.table_model.backing_store)
|
||||||
|
|
||||||
|
def on_close():
|
||||||
|
del self.displays[name]
|
||||||
|
dsp.sigClosed.connect(on_close)
|
||||||
|
self.dock_area.addDock(dsp)
|
||||||
|
self.dock_area.floatDock(dsp)
|
||||||
|
|
Loading…
Reference in New Issue