2015-12-28 16:48:31 +08:00
|
|
|
#!/usr/bin/env python3.5
|
|
|
|
|
|
|
|
import numpy as np
|
2016-02-09 05:25:20 +08:00
|
|
|
from PyQt5 import QtWidgets
|
2016-01-13 20:52:33 +08:00
|
|
|
import pyqtgraph
|
|
|
|
|
|
|
|
from artiq.applets.simple import SimpleApplet
|
|
|
|
|
2015-12-28 16:48:31 +08:00
|
|
|
|
2016-01-13 21:53:23 +08:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2016-01-13 22:22:19 +08:00
|
|
|
# pyqtgraph.GraphicsWindow fails to behave like a regular Qt widget
|
|
|
|
# and breaks embedding. Do not use as top widget.
|
|
|
|
class XYHistPlot(QtWidgets.QSplitter):
|
2016-01-13 20:52:33 +08:00
|
|
|
def __init__(self, args):
|
2016-01-13 22:22:19 +08:00
|
|
|
QtWidgets.QSplitter.__init__(self)
|
2016-04-08 01:22:53 +08:00
|
|
|
self.resize(1000, 600)
|
2016-01-13 20:52:33 +08:00
|
|
|
self.setWindowTitle("XY/Histogram")
|
2015-12-28 16:48:31 +08:00
|
|
|
|
2016-01-13 22:22:19 +08:00
|
|
|
self.xy_plot = pyqtgraph.PlotWidget()
|
|
|
|
self.insertWidget(0, self.xy_plot)
|
2015-12-28 16:48:31 +08:00
|
|
|
self.xy_plot_data = None
|
|
|
|
self.arrow = None
|
2016-01-13 21:53:23 +08:00
|
|
|
self.selected_index = None
|
2015-12-28 16:48:31 +08:00
|
|
|
|
2016-01-13 22:22:19 +08:00
|
|
|
self.hist_plot = pyqtgraph.PlotWidget()
|
|
|
|
self.insertWidget(1, self.hist_plot)
|
2015-12-28 16:48:31 +08:00
|
|
|
self.hist_plot_data = None
|
|
|
|
|
2016-01-13 20:52:33 +08:00
|
|
|
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
|
2016-01-13 21:53:23 +08:00
|
|
|
self.selected_index = None
|
2016-01-13 20:52:33 +08:00
|
|
|
|
|
|
|
self.histogram_bins = histogram_bins
|
2015-12-28 16:48:31 +08:00
|
|
|
|
2016-01-13 21:53:23 +08:00
|
|
|
ys = _compute_ys(self.histogram_bins, histograms_counts)
|
2015-12-28 16:48:31 +08:00
|
|
|
self.xy_plot_data = self.xy_plot.plot(x=xs, y=ys,
|
|
|
|
pen=None,
|
|
|
|
symbol="x", symbolSize=20)
|
2016-01-13 20:52:33 +08:00
|
|
|
self.xy_plot_data.sigPointsClicked.connect(self._point_clicked)
|
2016-01-13 21:53:23 +08:00
|
|
|
for index, (point, counts) in (
|
|
|
|
enumerate(zip(self.xy_plot_data.scatter.points(),
|
|
|
|
histograms_counts))):
|
|
|
|
point.histogram_index = index
|
2015-12-28 16:48:31 +08:00
|
|
|
point.histogram_counts = counts
|
|
|
|
|
|
|
|
self.hist_plot_data = self.hist_plot.plot(
|
2016-01-13 20:52:33 +08:00
|
|
|
stepMode=True, fillLevel=0,
|
|
|
|
brush=(0, 0, 255, 150))
|
2015-12-28 16:48:31 +08:00
|
|
|
|
2016-01-13 21:53:23 +08:00
|
|
|
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
|
|
|
|
|
2016-01-13 20:52:33 +08:00
|
|
|
def _point_clicked(self, data_item, spot_items):
|
2015-12-28 16:48:31 +08:00
|
|
|
spot_item = spot_items[0]
|
|
|
|
position = spot_item.pos()
|
|
|
|
if self.arrow is None:
|
2016-01-13 20:52:33 +08:00
|
|
|
self.arrow = pyqtgraph.ArrowItem(
|
|
|
|
angle=-120, tipAngle=30, baseAngle=20, headLen=40,
|
|
|
|
tailLen=40, tailWidth=8, pen=None, brush="y")
|
2015-12-28 16:48:31 +08:00
|
|
|
self.arrow.setPos(position)
|
|
|
|
# NB: temporary glitch if addItem is done before setPos
|
|
|
|
self.xy_plot.addItem(self.arrow)
|
|
|
|
else:
|
|
|
|
self.arrow.setPos(position)
|
2016-01-13 21:53:23 +08:00
|
|
|
self.selected_index = spot_item.histogram_index
|
2016-01-13 20:52:33 +08:00
|
|
|
self.hist_plot_data.setData(x=self.histogram_bins,
|
2015-12-28 16:48:31 +08:00
|
|
|
y=spot_item.histogram_counts)
|
2016-01-13 20:52:33 +08:00
|
|
|
|
2016-01-13 21:53:23 +08:00
|
|
|
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
|
|
|
|
|
2016-01-13 20:52:33 +08:00
|
|
|
def data_changed(self, data, mods):
|
2016-01-13 22:05:30 +08:00
|
|
|
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
|
2016-01-13 21:53:23 +08:00
|
|
|
if self._can_use_partial(mods):
|
|
|
|
self._set_partial_data(xs, histograms_counts)
|
|
|
|
else:
|
|
|
|
self._set_full_data(xs, histogram_bins, histograms_counts)
|
2016-04-08 01:22:53 +08:00
|
|
|
|
2015-12-28 16:48:31 +08:00
|
|
|
|
|
|
|
def main():
|
2016-01-13 20:52:33 +08:00
|
|
|
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__":
|
2015-12-28 16:48:31 +08:00
|
|
|
main()
|