# -*- coding: utf-8 -*-
# gui/mainwindow.py
from __future__ import absolute_import # PEP328
from builtins import str
from QtGui import QLineEdit, QDoubleValidator, QValidator
from QtCore import Qt
from utils import clip
[docs]class SciEntryValidator(QDoubleValidator):
"""Assumes the associated QLineEdit is provided as parent object
in the constructor."""
def __init__(self, parent = None):
# FIXME: On Windows, python crashes here, sometimes ...
# everything looks ok, except that it 'stops working'
super(SciEntryValidator, self).__init__(parent)
self.setNotation(QDoubleValidator.ScientificNotation)
# some defaults if nothing is set
self.setRange(-1e200, 1e200)
self.setDecimals(9)
[docs] def validate(self, input, pos):
state, value, pos = QDoubleValidator.validate(self, input, pos)
if state is QValidator.State.Invalid:
# do not accept any invalid text, usually not float compatible
return state, value, pos
# highlight the input on errorneous values, but accept them
self.parent().indicateCorrectness(state is QValidator.State.Acceptable)
if self.parent().hasFocus(): # change nothing, by default
return QValidator.State.Acceptable, input, pos
# verfiy the value range in any case,
# QDoubleValidator.validate() fails sometimes on the range
input = self.fixup(input)
# else: on focus left, fix it if required and validate again
if state is not QValidator.State.Acceptable:
state, input, pos = QDoubleValidator.validate(self, input, pos)
if state is not QValidator.State.Acceptable: # keep focus
self.parent().setFocus(Qt.OtherFocusReason)
self.parent().indicateCorrectness(state is QValidator.State.Acceptable)
return state, input, pos
[docs] def fixup(self, input):
"""Restricts the value to the valid range defined by setTop() and
setBottom(). Limits the precision as well."""
# restrict the value to the valid range
try:
input = self.parent().fmt.format(
clip(float(input), self.bottom(), self.top()))
except ValueError:
pass # do nothing if float conversion fails
return str(input)
[docs]class SciEntryBox(QLineEdit):
toolTipFmt = "A value between {lo} and {hi} (including)."
def __init__(self, parent = None):
super(SciEntryBox, self).__init__(parent)
val = SciEntryValidator(self)
self.setValidator(val)
self.setAlignment(Qt.AlignRight)
self.setMaxLength(14)
setattr(self, "minimum", val.bottom)
setattr(self, "maximum", val.top)
setattr(self, "setDecimals", val.setDecimals)
setattr(self, "decimals", val.decimals)
@property
def fmt(self):
return self.numberFormat(self.decimals())
@staticmethod
[docs] def numberFormat(decimals = None):
if decimals is None:
return "{0:g}"
# according to https://docs.python.org/2/library/string.html#formatspec
return "{0:." + str(max(0, decimals - 3)) + "g}"
@classmethod
[docs] def updateToolTip(cls, widget, decimals = None):
text = []
try:
parentText = widget.parent().toolTip()
if len(parentText):
text.append(parentText)
except:
pass
fmt = cls.numberFormat(decimals)
text.append(("<span>" + cls.toolTipFmt + "</span>").format(
lo = fmt.format(widget.minimum()),
hi = fmt.format(widget.maximum())))
widget.setToolTip("<br />\n".join(text))
[docs] def setMinimum(self, value):
"""Work around issues regarding round-off errors by the 'g' format type
when comparing numbers from user input with calculation results."""
self.validator().setBottom(float(self.fmt.format(value)))
self.updateToolTip(self, self.decimals())
[docs] def setMaximum(self, value):
"""Work around issues regarding round-off errors by the 'g' format type
when comparing numbers from user input with calculation results."""
self.validator().setTop(float(self.fmt.format(value)))
self.updateToolTip(self, self.decimals())
[docs] def indicateCorrectness(self, isValid):
if not isValid:
self.setStyleSheet("background: rgb(255,160,160);")
else:
self.setStyleSheet("")
[docs] def setRange(self, lo, hi):
self.setMinimum(lo)
self.setMaximum(hi)
[docs] def setValue(self, value):
self.setText(str(self.fmt.format(value)))
[docs] def value(self):
return float(self.text())
[docs] def setPrefix(self, value):
self.setPlaceholderText(value)
# vim: set ts=4 sts=4 sw=4 tw=0: