Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1c8202e2072c
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 9775faa32201
Choose a head ref
  • 3 commits
  • 2 files changed
  • 1 contributor

Commits on Aug 11, 2016

  1. Copy the full SHA
    6fe23b8 View commit details
  2. Copy the full SHA
    941f6fc View commit details
  3. Copy the full SHA
    9775faa View commit details
Showing with 75 additions and 64 deletions.
  1. +11 −3 artiq/gui/entries.py
  2. +64 −61 artiq/gui/scientific_spinbox.py
14 changes: 11 additions & 3 deletions artiq/gui/entries.py
Original file line number Diff line number Diff line change
@@ -69,14 +69,16 @@ def default_state(procdesc):
return procdesc["choices"][0]


class NumberEntry(QtWidgets.QDoubleSpinBox):
class NumberEntry(ScientificSpinBox):
def __init__(self, argument):
QtWidgets.QDoubleSpinBox.__init__(self)
ScientificSpinBox.__init__(self)
disable_scroll_wheel(self)
procdesc = argument["desc"]
scale = procdesc["scale"]
self.setDecimals(procdesc["ndecimals"])
self.setPrecision()
self.setSingleStep(procdesc["step"]/scale)
self.setRelativeStep()
if procdesc["min"] is not None:
self.setMinimum(procdesc["min"]/scale)
else:
@@ -110,9 +112,10 @@ def __init__(self, procdesc, state):
LayoutWidget.__init__(self)

scale = procdesc["scale"]
self.value = QtWidgets.QDoubleSpinBox()
self.value = ScientificSpinBox()
disable_scroll_wheel(self.value)
self.value.setDecimals(procdesc["ndecimals"])
self.value.setPrecision()
if procdesc["global_min"] is not None:
self.value.setMinimum(procdesc["global_min"]/scale)
else:
@@ -122,6 +125,7 @@ def __init__(self, procdesc, state):
else:
self.value.setMaximum(float("inf"))
self.value.setSingleStep(procdesc["global_step"]/scale)
self.value.setRelativeStep()
if procdesc["unit"]:
self.value.setSuffix(" " + procdesc["unit"])
self.addWidget(QtWidgets.QLabel("Value:"), 0, 0)
@@ -179,7 +183,11 @@ def apply_properties(widget):
self.addWidget(stop, 2, 1)

apply_properties(start)
start.setPrecision()
start.setRelativeStep()
apply_properties(stop)
stop.setPrecision()
stop.setRelativeStep()
apply_properties(scanner)

def update_start(value):
125 changes: 64 additions & 61 deletions artiq/gui/scientific_spinbox.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,74 @@
import re
from PyQt5 import QtGui, QtWidgets
from math import inf, copysign
from PyQt5 import QtCore, QtGui, QtWidgets

# after
# http://jdreaver.com/posts/2014-07-28-scientific-notation-spin-box-pyside.html


_inf = float("inf")
# Regular expression to find floats. Match groups are the whole string, the
# whole coefficient, the decimal part of the coefficient, and the exponent
# part.
_float_re = re.compile(r"(([+-]?\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)")


def valid_float_string(string):
match = _float_re.search(string)
if match:
return match.groups()[0] == string
return False


class FloatValidator(QtGui.QValidator):
def validate(self, string, position):
if valid_float_string(string):
return self.Acceptable, string, position
if string == "" or string[position-1] in "eE.-+":
return self.Intermediate, string, position
return self.Invalid, string, position

def fixup(self, text):
match = _float_re.search(text)
if match:
return match.groups()[0]
return ""
_float_acceptable = re.compile(
r"([-+]?\d*(?:\d|\.\d|\d\.)\d*)(?:[eE]([-+]?\d+))?",
)
_float_intermediate = re.compile(
r"[-+]?\d*\.?\d*(?:(?:(?<=\d)|(?<=\d\.))[eE][-+]?\d*)?",
)
_exp_shorten = re.compile(r"e\+?0*")


class ScientificSpinBox(QtWidgets.QDoubleSpinBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setMinimum(-_inf)
self.setMaximum(_inf)
self.validator = FloatValidator()
self.setDecimals(20)

def validate(self, text, position):
return self.validator.validate(text, position)

def fixup(self, text):
return self.validator.fixup(text)
self.setGroupSeparatorShown(False)
self.setInputMethodHints(QtCore.Qt.ImhNone)
self.setCorrectionMode(self.CorrectToPreviousValue)
# singleStep: resolution for step, buttons, accelerators
# decimals: absolute rounding granularity
# precision: number of significant digits shown, %g precision
self.setPrecision()
self.setRelativeStep()
self.setRange(-inf, inf)
self.setValue(0)
# self.setKeyboardTracking(False)

def setPrecision(self, d=None):
if d is None:
d = self.decimals() + 3
self._precision = max(1, int(d))
self._fmt = "{{:.{}g}}".format(self._precision)

def precision(self):
return self._precision

def setRelativeStep(self, s=None):
if s is None:
s = 1 + self.singleStep()
self._relative_step = max(1 + 10**-self.decimals(), float(s))

def relativeStep(self):
return self._relative_step

def setGroupSeparatorShown(self, s):
if s:
raise NotImplementedError

def textFromValue(self, v):
t = self._fmt.format(v)
t = re.sub(_exp_shorten, "e", t, 1)
return t

def valueFromText(self, text):
return float(text)

def textFromValue(self, value):
return format_float(value)

def stepBy(self, steps):
text = self.cleanText()
groups = _float_re.search(text).groups()
decimal = float(groups[1])
decimal += steps
new_string = "{:g}".format(decimal) + (groups[3] if groups[3] else "")
self.lineEdit().setText(new_string)


def format_float(value):
"""Modified form of the 'g' format specifier."""
string = "{:g}".format(value)
string = string.replace("e+", "e")
string = re.sub("e(-?)0*(\d+)", r"e\1\2", string)
return string
return round(float(text), self.decimals())

def validate(self, text, pos):
try:
float(text) # faster than matching
return QtGui.QValidator.Acceptable, text, pos
except ValueError:
if re.fullmatch(_float_intermediate, text):
return QtGui.QValidator.Intermediate, text, pos
return QtGui.QValidator.Invalid, text, pos

def stepBy(self, s):
if abs(s) < 10: # unaccelerated buttons, keys, wheel/trackpad
super().stepBy(s)
else: # accelerated PageUp/Down or CTRL-wheel
v = self.value()
v *= self._relative_step**(s/copysign(10., v))
self.setValue(v)