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.
This requires reflashing the runtime and the flash storage filesystem image
or erase and rewrite its entries.
* RTIOCollisionError has been renamed to RTIOCollision
* the new API for DDS batches is:
* ``RTIOCollisionError`` has been renamed to ``RTIOCollision``
* the new API for DDS batches is::
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.
* 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"]:
spinbox.setSuffix(" " + procdesc["unit"])
self.scanner = scanner = ScanWidget()
scanner = ScanWidget()
disable_scroll_wheel(scanner)
self.addWidget(scanner, 0, 0, -1, 1)
self.min = ScientificSpinBox()
self.min.setStyleSheet("QDoubleSpinBox {color:blue}")
self.min.setMinimumSize(110, 0)
self.min.setSizePolicy(QtWidgets.QSizePolicy(
start = ScientificSpinBox()
start.setStyleSheet("QDoubleSpinBox {color:blue}")
start.setMinimumSize(110, 0)
start.setSizePolicy(QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed))
disable_scroll_wheel(self.min)
self.addWidget(self.min, 0, 1)
disable_scroll_wheel(start)
self.addWidget(start, 0, 1)
self.npoints = QtWidgets.QSpinBox()
self.npoints.setMinimum(1)
disable_scroll_wheel(self.npoints)
self.addWidget(self.npoints, 1, 1)
npoints = QtWidgets.QSpinBox()
npoints.setMinimum(1)
disable_scroll_wheel(npoints)
self.addWidget(npoints, 1, 1)
self.max = ScientificSpinBox()
self.max.setStyleSheet("QDoubleSpinBox {color:red}")
self.max.setMinimumSize(110, 0)
disable_scroll_wheel(self.max)
self.addWidget(self.max, 2, 1)
stop = ScientificSpinBox()
stop.setStyleSheet("QDoubleSpinBox {color:red}")
stop.setMinimumSize(110, 0)
disable_scroll_wheel(stop)
self.addWidget(stop, 2, 1)
def update_min(value):
state["min"] = value*scale
def update_start(value):
state["start"] = value*scale
scanner.setStart(value)
def update_max(value):
state["max"] = value*scale
def update_stop(value):
state["stop"] = value*scale
scanner.setStop(value)
def update_npoints(value):
state["npoints"] = value
scanner.setNum(value)
scanner.startChanged.connect(self.min.setValue)
scanner.numChanged.connect(self.npoints.setValue)
scanner.stopChanged.connect(self.max.setValue)
self.min.valueChanged.connect(update_min)
self.npoints.valueChanged.connect(update_npoints)
self.max.valueChanged.connect(update_max)
scanner.setStart(state["min"]/scale)
scanner.startChanged.connect(start.setValue)
scanner.numChanged.connect(npoints.setValue)
scanner.stopChanged.connect(stop.setValue)
start.valueChanged.connect(update_start)
npoints.valueChanged.connect(update_npoints)
stop.valueChanged.connect(update_stop)
scanner.setStart(state["start"]/scale)
scanner.setNum(state["npoints"])
scanner.setStop(state["max"]/scale)
apply_properties(self.min)
apply_properties(self.max)
scanner.setStop(state["stop"]/scale)
apply_properties(start)
apply_properties(stop)
class _ExplicitScan(LayoutWidget):
@ -266,8 +266,8 @@ class _ScanEntry(LayoutWidget):
state = {
"selected": "NoScan",
"NoScan": {"value": 0.0},
"LinearScan": {"min": 0.0, "max": 100.0*scale, "npoints": 10},
"RandomScan": {"min": 0.0, "max": 100.0*scale, "npoints": 10},
"LinearScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10},
"RandomScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10},
"ExplicitScan": {"sequence": []}
}
if "default" in procdesc:
@ -278,8 +278,8 @@ class _ScanEntry(LayoutWidget):
state["NoScan"]["value"] = default["value"]
elif ty == "LinearScan" or ty == "RandomScan":
for d in state["LinearScan"], state["RandomScan"]:
d["min"] = default["min"]
d["max"] = default["max"]
d["start"] = default["start"]
d["stop"] = default["stop"]
d["npoints"] = default["npoints"]
elif ty == "ExplicitScan":
state["ExplicitScan"]["sequence"] = default["sequence"]

View File

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