-
Notifications
You must be signed in to change notification settings - Fork 201
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into subprocess-termination
* master: (44 commits) Revert "conda: restrict binutils-or1k-linux dependency to linux." manual/installing: refresh use https for m-labs.hk gui/log: top cell alignment master/log: do not break lines conda: fix pyqt package name gui/applets: log warning if IPC address not in command applets: make sure pyqtgraph imports qt5 applets: avoid argparse subparser mess examples/histogram: artiq -> artiq.experiment gui/applets: save dock UID in state setup.py: give up trying to check for PyQt setup.py: fix PyQt5 package name Use Qt5 applets: fix error message text applets: handle dataset mutations applets: properly name docks to support state save/restore applets: clean shutdown protocols/pyon: set support protocols/pyon: remove FlatFileDB ...
Showing
25 changed files
with
898 additions
and
550 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#!/usr/bin/env python3.5 | ||
|
||
import numpy as np | ||
import PyQt5 # make sure pyqtgraph imports Qt5 | ||
import pyqtgraph | ||
|
||
from artiq.applets.simple import SimpleApplet | ||
|
||
|
||
class HistogramPlot(pyqtgraph.PlotWidget): | ||
def __init__(self, args): | ||
pyqtgraph.PlotWidget.__init__(self) | ||
self.args = args | ||
|
||
def data_changed(self, data, mods): | ||
try: | ||
y = data[self.args.y][1] | ||
if self.args.x is None: | ||
x = None | ||
else: | ||
x = data[self.args.x][1] | ||
except KeyError: | ||
return | ||
if x is None: | ||
x = list(range(len(y)+1)) | ||
|
||
if len(y) and len(x) == len(y) + 1: | ||
self.clear() | ||
self.plot(x, y, stepMode=True, fillLevel=0, | ||
brush=(0, 0, 255, 150)) | ||
|
||
|
||
def main(): | ||
applet = SimpleApplet(HistogramPlot) | ||
applet.add_dataset("y", "Y values") | ||
applet.add_dataset("x", "Bin boundaries", required=False) | ||
applet.run() | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
#!/usr/bin/env python3.5 | ||
|
||
import numpy as np | ||
import PyQt5 # make sure pyqtgraph imports Qt5 | ||
import pyqtgraph | ||
|
||
from artiq.applets.simple import SimpleApplet | ||
|
||
|
||
class XYPlot(pyqtgraph.PlotWidget): | ||
def __init__(self, args): | ||
pyqtgraph.PlotWidget.__init__(self) | ||
self.args = args | ||
|
||
def data_changed(self, data, mods): | ||
try: | ||
y = data[self.args.y][1] | ||
except KeyError: | ||
return | ||
x = data.get(self.args.x, (False, None))[1] | ||
if x is None: | ||
x = list(range(len(y))) | ||
error = data.get(self.args.error, (False, None))[1] | ||
fit = data.get(self.args.fit, (False, None))[1] | ||
|
||
if not len(y) or len(y) != len(x): | ||
return | ||
if error is not None and hasattr(error, "__len__"): | ||
if not len(error): | ||
error = None | ||
elif len(error) != len(y): | ||
return | ||
if fit is not None: | ||
if not len(fit): | ||
fit = None | ||
elif len(fit) != len(y): | ||
return | ||
|
||
self.clear() | ||
self.plot(x, y, pen=None, symbol="x") | ||
if error is not None: | ||
# See https://github.com/pyqtgraph/pyqtgraph/issues/211 | ||
if hasattr(error, "__len__") and not isinstance(error, np.ndarray): | ||
error = np.array(error) | ||
errbars = pg.ErrorBarItem(x=np.array(x), y=np.array(y), height=error) | ||
self.addItem(errbars) | ||
if fit is not None: | ||
self.plot(x, fit) | ||
|
||
|
||
def main(): | ||
applet = SimpleApplet(XYPlot) | ||
applet.add_dataset("y", "Y values") | ||
applet.add_dataset("x", "X values", required=False) | ||
applet.add_dataset("error", "Error bars for each X value", required=False) | ||
applet.add_dataset("fit", "Fit values for each X value", required=False) | ||
applet.run() | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,136 @@ | ||
#!/usr/bin/env python3.5 | ||
|
||
from pyqtgraph.Qt import QtGui, QtCore | ||
import pyqtgraph as pg | ||
|
||
import numpy as np | ||
from PyQt5 import QtWidgets | ||
import pyqtgraph | ||
|
||
from artiq.applets.simple import SimpleApplet | ||
|
||
|
||
def _compute_ys(histogram_bins, histograms_counts): | ||
bin_centers = np.empty(len(histogram_bins)-1) | ||
for i in range(len(bin_centers)): | ||
bin_centers[i] = (histogram_bins[i] + histogram_bins[i+1])/2 | ||
|
||
ys = np.empty(histograms_counts.shape[0]) | ||
for n, counts in enumerate(histograms_counts): | ||
ys[n] = sum(bin_centers*counts)/sum(counts) | ||
return ys | ||
|
||
class XYHistPlot: | ||
def __init__(self): | ||
self.graphics_window = pg.GraphicsWindow(title="XY/Histogram") | ||
self.graphics_window.resize(1000,600) | ||
self.graphics_window.setWindowTitle("XY/Histogram") | ||
|
||
self.xy_plot = self.graphics_window.addPlot() | ||
# 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): | ||
QtWidgets.QSplitter.__init__(self) | ||
self.resize(1000,600) | ||
self.setWindowTitle("XY/Histogram") | ||
|
||
self.xy_plot = pyqtgraph.PlotWidget() | ||
self.insertWidget(0, self.xy_plot) | ||
self.xy_plot_data = None | ||
self.arrow = None | ||
self.selected_index = None | ||
|
||
self.hist_plot = self.graphics_window.addPlot() | ||
self.hist_plot = pyqtgraph.PlotWidget() | ||
self.insertWidget(1, self.hist_plot) | ||
self.hist_plot_data = None | ||
|
||
def set_data(self, xs, histograms_bins, histograms_counts): | ||
ys = np.empty_like(xs) | ||
ys.fill(np.nan) | ||
for n, (bins, counts) in enumerate(zip(histograms_bins, | ||
histograms_counts)): | ||
bin_centers = np.empty(len(bins)-1) | ||
for i in range(len(bin_centers)): | ||
bin_centers[i] = (bins[i] + bins[i+1])/2 | ||
ys[n] = sum(bin_centers*counts)/sum(bin_centers) | ||
self.args = args | ||
|
||
def _set_full_data(self, xs, histogram_bins, histograms_counts): | ||
self.xy_plot.clear() | ||
self.hist_plot.clear() | ||
self.xy_plot_data = None | ||
self.hist_plot_data = None | ||
self.arrow = None | ||
self.selected_index = None | ||
|
||
self.histogram_bins = histogram_bins | ||
|
||
ys = _compute_ys(self.histogram_bins, histograms_counts) | ||
self.xy_plot_data = self.xy_plot.plot(x=xs, y=ys, | ||
pen=None, | ||
symbol="x", symbolSize=20) | ||
self.xy_plot_data.sigPointsClicked.connect(self.point_clicked) | ||
for point, bins, counts in zip(self.xy_plot_data.scatter.points(), | ||
histograms_bins, histograms_counts): | ||
point.histogram_bins = bins | ||
self.xy_plot_data.sigPointsClicked.connect(self._point_clicked) | ||
for index, (point, counts) in ( | ||
enumerate(zip(self.xy_plot_data.scatter.points(), | ||
histograms_counts))): | ||
point.histogram_index = index | ||
point.histogram_counts = counts | ||
|
||
self.hist_plot_data = self.hist_plot.plot( | ||
stepMode=True, fillLevel=0, | ||
brush=(0, 0, 255, 150)) | ||
stepMode=True, fillLevel=0, | ||
brush=(0, 0, 255, 150)) | ||
|
||
def _set_partial_data(self, xs, histograms_counts): | ||
ys = _compute_ys(self.histogram_bins, histograms_counts) | ||
self.xy_plot_data.setData(x=xs, y=ys, | ||
pen=None, | ||
symbol="x", symbolSize=20) | ||
for index, (point, counts) in ( | ||
enumerate(zip(self.xy_plot_data.scatter.points(), | ||
histograms_counts))): | ||
point.histogram_index = index | ||
point.histogram_counts = counts | ||
|
||
def point_clicked(self, data_item, spot_items): | ||
def _point_clicked(self, data_item, spot_items): | ||
spot_item = spot_items[0] | ||
position = spot_item.pos() | ||
if self.arrow is None: | ||
self.arrow = pg.ArrowItem(angle=-120, tipAngle=30, baseAngle=20, | ||
headLen=40, tailLen=40, tailWidth=8, | ||
pen=None, brush="y") | ||
self.arrow = pyqtgraph.ArrowItem( | ||
angle=-120, tipAngle=30, baseAngle=20, headLen=40, | ||
tailLen=40, tailWidth=8, pen=None, brush="y") | ||
self.arrow.setPos(position) | ||
# NB: temporary glitch if addItem is done before setPos | ||
self.xy_plot.addItem(self.arrow) | ||
else: | ||
self.arrow.setPos(position) | ||
self.hist_plot_data.setData(x=spot_item.histogram_bins, | ||
self.selected_index = spot_item.histogram_index | ||
self.hist_plot_data.setData(x=self.histogram_bins, | ||
y=spot_item.histogram_counts) | ||
|
||
def _can_use_partial(self, mods): | ||
if self.hist_plot_data is None: | ||
return False | ||
for mod in mods: | ||
if mod["action"] != "setitem": | ||
return False | ||
if mod["path"] == [self.args.xs, 1]: | ||
if mod["key"] == self.selected_index: | ||
return False | ||
elif mod["path"][:2] == [self.args.histograms_counts, 1]: | ||
if len(mod["path"]) > 2: | ||
index = mod["path"][2] | ||
else: | ||
index = mod["key"] | ||
if index == self.selected_index: | ||
return False | ||
else: | ||
return False | ||
return True | ||
|
||
def data_changed(self, data, mods): | ||
try: | ||
xs = data[self.args.xs][1] | ||
histogram_bins = data[self.args.histogram_bins][1] | ||
histograms_counts = data[self.args.histograms_counts][1] | ||
except KeyError: | ||
return | ||
if self._can_use_partial(mods): | ||
self._set_partial_data(xs, histograms_counts) | ||
else: | ||
self._set_full_data(xs, histogram_bins, histograms_counts) | ||
|
||
|
||
def main(): | ||
app = QtGui.QApplication([]) | ||
plot = XYHistPlot() | ||
plot.set_data(np.array([1, 2, 3, 4, 1]), | ||
np.array([[1, 2, 3], [1, 2, 3], [1, 2, 3], [40, 70, 100], [4, 7, 10, 20]]), | ||
np.array([[1, 1], [2, 3], [10, 20], [3, 1], [100, 67, 102]])) | ||
app.exec_() | ||
|
||
if __name__ == '__main__': | ||
applet = SimpleApplet(XYHistPlot) | ||
applet.add_dataset("xs", "1D array of point abscissas") | ||
applet.add_dataset("histogram_bins", | ||
"1D array of histogram bin boundaries") | ||
applet.add_dataset("histograms_counts", | ||
"2D array of histogram counts, for each point") | ||
applet.run() | ||
|
||
if __name__ == "__main__": | ||
main() |
Oops, something went wrong.