experiment/gui: support reverse scan (closes #246)

This commit is contained in:
Robert Jördens 2016-03-17 18:41:49 +01:00
parent 2859382e11
commit 33da27a749
3 changed files with 64 additions and 58 deletions

View File

@ -10,9 +10,13 @@ Release notes
* Core device flash storage has moved due to increased runtime size. * Core device flash storage has moved due to increased runtime size.
This requires reflashing the runtime and the flash storage filesystem image This requires reflashing the runtime and the flash storage filesystem image
or erase and rewrite its entries. or erase and rewrite its entries.
* RTIOCollisionError has been renamed to RTIOCollision * ``RTIOCollisionError`` has been renamed to ``RTIOCollision``
* the new API for DDS batches is: * the new API for DDS batches is::
with self.core_dds.batch: with self.core_dds.batch:
... ...
with core_dds a device of type artiq.coredevice.dds.CoreDDS.
with ``core_dds`` a device of type ``artiq.coredevice.dds.CoreDDS``.
The dds_bus device should not be used anymore. The dds_bus device should not be used anymore.
* LinearScan now supports scanning from high to low. Accordingly,
it's arguments ``min/max`` have been renamed to ``start/stop`` respectively.
Same for RandomScan (even though there direction matters little).

View File

@ -154,52 +154,52 @@ class _RangeScan(LayoutWidget):
if procdesc["unit"]: if procdesc["unit"]:
spinbox.setSuffix(" " + procdesc["unit"]) spinbox.setSuffix(" " + procdesc["unit"])
self.scanner = scanner = ScanWidget() scanner = ScanWidget()
disable_scroll_wheel(scanner) disable_scroll_wheel(scanner)
self.addWidget(scanner, 0, 0, -1, 1) self.addWidget(scanner, 0, 0, -1, 1)
self.min = ScientificSpinBox() start = ScientificSpinBox()
self.min.setStyleSheet("QDoubleSpinBox {color:blue}") start.setStyleSheet("QDoubleSpinBox {color:blue}")
self.min.setMinimumSize(110, 0) start.setMinimumSize(110, 0)
self.min.setSizePolicy(QtWidgets.QSizePolicy( start.setSizePolicy(QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed))
disable_scroll_wheel(self.min) disable_scroll_wheel(start)
self.addWidget(self.min, 0, 1) self.addWidget(start, 0, 1)
self.npoints = QtWidgets.QSpinBox() npoints = QtWidgets.QSpinBox()
self.npoints.setMinimum(1) npoints.setMinimum(1)
disable_scroll_wheel(self.npoints) disable_scroll_wheel(npoints)
self.addWidget(self.npoints, 1, 1) self.addWidget(npoints, 1, 1)
self.max = ScientificSpinBox() stop = ScientificSpinBox()
self.max.setStyleSheet("QDoubleSpinBox {color:red}") stop.setStyleSheet("QDoubleSpinBox {color:red}")
self.max.setMinimumSize(110, 0) stop.setMinimumSize(110, 0)
disable_scroll_wheel(self.max) disable_scroll_wheel(stop)
self.addWidget(self.max, 2, 1) self.addWidget(stop, 2, 1)
def update_min(value): def update_start(value):
state["min"] = value*scale state["start"] = value*scale
scanner.setStart(value) scanner.setStart(value)
def update_max(value): def update_stop(value):
state["max"] = value*scale state["stop"] = value*scale
scanner.setStop(value) scanner.setStop(value)
def update_npoints(value): def update_npoints(value):
state["npoints"] = value state["npoints"] = value
scanner.setNum(value) scanner.setNum(value)
scanner.startChanged.connect(self.min.setValue) scanner.startChanged.connect(start.setValue)
scanner.numChanged.connect(self.npoints.setValue) scanner.numChanged.connect(npoints.setValue)
scanner.stopChanged.connect(self.max.setValue) scanner.stopChanged.connect(stop.setValue)
self.min.valueChanged.connect(update_min) start.valueChanged.connect(update_start)
self.npoints.valueChanged.connect(update_npoints) npoints.valueChanged.connect(update_npoints)
self.max.valueChanged.connect(update_max) stop.valueChanged.connect(update_stop)
scanner.setStart(state["min"]/scale) scanner.setStart(state["start"]/scale)
scanner.setNum(state["npoints"]) scanner.setNum(state["npoints"])
scanner.setStop(state["max"]/scale) scanner.setStop(state["stop"]/scale)
apply_properties(self.min) apply_properties(start)
apply_properties(self.max) apply_properties(stop)
class _ExplicitScan(LayoutWidget): class _ExplicitScan(LayoutWidget):
@ -266,8 +266,8 @@ class _ScanEntry(LayoutWidget):
state = { state = {
"selected": "NoScan", "selected": "NoScan",
"NoScan": {"value": 0.0}, "NoScan": {"value": 0.0},
"LinearScan": {"min": 0.0, "max": 100.0*scale, "npoints": 10}, "LinearScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10},
"RandomScan": {"min": 0.0, "max": 100.0*scale, "npoints": 10}, "RandomScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10},
"ExplicitScan": {"sequence": []} "ExplicitScan": {"sequence": []}
} }
if "default" in procdesc: if "default" in procdesc:
@ -278,8 +278,8 @@ class _ScanEntry(LayoutWidget):
state["NoScan"]["value"] = default["value"] state["NoScan"]["value"] = default["value"]
elif ty == "LinearScan" or ty == "RandomScan": elif ty == "LinearScan" or ty == "RandomScan":
for d in state["LinearScan"], state["RandomScan"]: for d in state["LinearScan"], state["RandomScan"]:
d["min"] = default["min"] d["start"] = default["start"]
d["max"] = default["max"] d["stop"] = default["stop"]
d["npoints"] = default["npoints"] d["npoints"] = default["npoints"]
elif ty == "ExplicitScan": elif ty == "ExplicitScan":
state["ExplicitScan"]["sequence"] = default["sequence"] state["ExplicitScan"]["sequence"] = default["sequence"]

View File

@ -11,7 +11,7 @@ Iterate on a scan object to scan it, e.g. ::
do_something(variable) do_something(variable)
Iterating multiple times on the same scan object is possible, with the scan Iterating multiple times on the same scan object is possible, with the scan
restarting at the minimum value each time. Iterating concurrently on the yielding the same values each time. Iterating concurrently on the
same scan object (e.g. via nested loops) is also supported, and the same scan object (e.g. via nested loops) is also supported, and the
iterators are independent from each other. iterators are independent from each other.
@ -55,21 +55,23 @@ class NoScan(ScanObject):
class LinearScan(ScanObject): class LinearScan(ScanObject):
"""A scan object that yields a fixed number of increasing evenly """A scan object that yields a fixed number of evenly
spaced values in a range.""" spaced values in a range."""
def __init__(self, min, max, npoints): def __init__(self, start, stop, npoints):
if min > max: self.start = start
raise ValueError("Scan minimum must be less than maximum") self.stop = stop
self.min = min
self.max = max
self.npoints = npoints self.npoints = npoints
@portable @portable
def _gen(self): def _gen(self):
r = self.max - self.min if self.npoints == 0:
d = self.npoints - 1 return
if self.npoints == 1:
yield self.start
else:
dx = (self.stop - self.start)/(self.npoints - 1)
for i in range(self.npoints): for i in range(self.npoints):
yield r*i/d + self.min yield i*dx + self.start
@portable @portable
def __iter__(self): def __iter__(self):
@ -80,19 +82,18 @@ class LinearScan(ScanObject):
def describe(self): def describe(self):
return {"ty": "LinearScan", return {"ty": "LinearScan",
"min": self.min, "max": self.max, "npoints": self.npoints} "start": self.start, "stop": self.stop,
"npoints": self.npoints}
class RandomScan(ScanObject): class RandomScan(ScanObject):
"""A scan object that yields a fixed number of randomly ordered evenly """A scan object that yields a fixed number of randomly ordered evenly
spaced values in a range.""" spaced values in a range."""
def __init__(self, min, max, npoints, seed=0): def __init__(self, start, stop, npoints, seed=0):
if min > max: self.start = start
raise ValueError("Scan minimum must be less than maximum") self.stop = stop
self.min = min
self.max = max
self.npoints = npoints self.npoints = npoints
self.sequence = list(LinearScan(min, max, npoints)) self.sequence = list(LinearScan(start, stop, npoints))
shuffle(self.sequence, Random(seed).random) shuffle(self.sequence, Random(seed).random)
@portable @portable
@ -104,7 +105,8 @@ class RandomScan(ScanObject):
def describe(self): def describe(self):
return {"ty": "RandomScan", return {"ty": "RandomScan",
"min": self.min, "max": self.max, "npoints": self.npoints} "start": self.start, "stop": self.stop,
"npoints": self.npoints}
class ExplicitScan(ScanObject): class ExplicitScan(ScanObject):
@ -142,8 +144,8 @@ class Scannable:
:param global_step: The step with which the value should be modified by :param global_step: The step with which the value should be modified by
up/down buttons in a user interface. The default is the scale divided up/down buttons in a user interface. The default is the scale divided
by 10. by 10.
:param unit: A string representing the unit of the scanned variable, for user :param unit: A string representing the unit of the scanned variable, for
interface (UI) purposes. user interface (UI) purposes.
:param scale: The scale of value for UI purposes. The displayed value is :param scale: The scale of value for UI purposes. The displayed value is
divided by the scale. divided by the scale.
:param ndecimals: The number of decimals a UI should use. :param ndecimals: The number of decimals a UI should use.