Skip to content

Commit 3634e37

Browse files
authored
Merge pull request #5647 from ales-erjavec/color-gradient-selection-center-spin
[FIX] HeatMap: Color gradient center value edit
2 parents 67c247f + adc9fbb commit 3634e37

File tree

3 files changed

+79
-28
lines changed

3 files changed

+79
-28
lines changed

Orange/widgets/utils/colorgradientselection.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
from AnyQt.QtCore import Qt, QSize, QAbstractItemModel, Property
44
from AnyQt.QtWidgets import (
5-
QWidget, QSlider, QFormLayout, QComboBox, QStyle,
6-
QHBoxLayout, QLineEdit, QLabel)
5+
QWidget, QSlider, QFormLayout, QComboBox, QStyle, QHBoxLayout, QLabel,
6+
QSizePolicy
7+
)
78
from AnyQt.QtCore import Signal
8-
from AnyQt.QtGui import QFontMetrics, QDoubleValidator
99

1010
from orangewidget.gui import Slider
1111

1212
from Orange.widgets.utils import itemmodels, colorpalettes
13+
from Orange.widgets.utils.spinbox import DoubleSpinBox, DBL_MIN, DBL_MAX
1314

1415

1516
class ColorGradientSelection(QWidget):
@@ -49,19 +50,23 @@ def __init__(self, *args, thresholds=(0.0, 1.0), center=None, **kwargs):
4950
self.gradient_cb.currentIndexChanged.connect(self.currentIndexChanged)
5051

5152
if center is not None:
52-
def __on_center_changed():
53-
self.__center = float(self.center_edit.text() or "0")
54-
self.centerChanged.emit(self.__center)
53+
def on_center_spin_value_changed(value):
54+
if self.__center != value:
55+
self.__center = value
56+
self.centerChanged.emit(self.__center)
5557

5658
self.center_box = QWidget()
5759
center_layout = QHBoxLayout()
5860
self.center_box.setLayout(center_layout)
59-
width = QFontMetrics(self.font()).boundingRect("9999999").width()
60-
self.center_edit = QLineEdit(
61-
text=f"{self.__center}",
62-
maximumWidth=width, placeholderText="0", alignment=Qt.AlignRight)
63-
self.center_edit.setValidator(QDoubleValidator())
64-
self.center_edit.editingFinished.connect(__on_center_changed)
61+
self.center_edit = DoubleSpinBox(
62+
value=self.__center,
63+
minimum=DBL_MIN, maximum=DBL_MAX, minimumStep=0.01,
64+
minimumContentsLenght=8,
65+
stepType=DoubleSpinBox.AdaptiveDecimalStepType,
66+
keyboardTracking=False,
67+
sizePolicy=QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
68+
)
69+
self.center_edit.valueChanged.connect(on_center_spin_value_changed)
6570
center_layout.setContentsMargins(0, 0, 0, 0)
6671
center_layout.addStretch(1)
6772
center_layout.addWidget(QLabel("Centered at"))
@@ -140,14 +145,6 @@ def thresholdHigh(self) -> float:
140145
def setThresholdHigh(self, high: float) -> None:
141146
self.setThresholds(min(self.__threshold_low, high), high)
142147

143-
def center(self) -> float:
144-
return self.__center
145-
146-
def setCenter(self, center: float) -> None:
147-
self.__center = center
148-
self.center_edit.setText(f"{center}")
149-
self.centerChanged.emit(center)
150-
151148
thresholdHigh_ = Property(
152149
float, thresholdLow, setThresholdLow, notify=thresholdsChanged)
153150

@@ -194,6 +191,17 @@ def __update_center_visibility(self):
194191
isinstance(palette, colorpalettes.Palette)
195192
and palette.flags & palette.Flags.Diverging != 0)
196193

194+
def center(self) -> float:
195+
return self.__center
196+
197+
def setCenter(self, center: float) -> None:
198+
if self.__center != center:
199+
self.__center = center
200+
self.center_edit.setValue(center)
201+
self.centerChanged.emit(center)
202+
203+
center_ = Property(float, center, setCenter, notify=centerChanged)
204+
197205

198206
def clip(a, amin, amax):
199207
return min(max(a, amin), amax)

Orange/widgets/utils/spinbox.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
import numpy as np
55

6-
from AnyQt.QtCore import QLocale
7-
from AnyQt.QtWidgets import QDoubleSpinBox
6+
from AnyQt.QtCore import QLocale, QSize
7+
from AnyQt.QtWidgets import QDoubleSpinBox, QStyle, QStyleOptionSpinBox
88

99
DBL_MIN = float(np.finfo(float).min)
1010
DBL_MAX = float(np.finfo(float).max)
@@ -16,9 +16,11 @@ class DoubleSpinBox(QDoubleSpinBox):
1616
"""
1717
A QDoubleSpinSubclass with non-fixed decimal precision/rounding.
1818
"""
19-
def __init__(self, parent=None, decimals=-1, minimumStep=1e-5, **kwargs):
19+
def __init__(self, parent=None, decimals=-1, minimumStep=1e-5,
20+
minimumContentsLenght=-1, **kwargs):
2021
self.__decimals = decimals
2122
self.__minimumStep = minimumStep
23+
self.__minimumContentsLength = minimumContentsLenght
2224
stepType = kwargs.pop("stepType", DoubleSpinBox.DefaultStepType)
2325
super().__init__(parent, **kwargs)
2426
if decimals < 0:
@@ -107,3 +109,42 @@ def setStepType(self, stepType):
107109

108110
def stepType(self):
109111
return self.__stepType
112+
113+
def setMinimumContentsLength(self, characters: int):
114+
self.__minimumContentsLength = characters
115+
self.updateGeometry()
116+
117+
def minimumContentsLength(self):
118+
return self.__minimumContentsLength
119+
120+
def sizeHint(self) -> QSize:
121+
if self.minimumContentsLength() < 0:
122+
return super().sizeHint()
123+
self.ensurePolished()
124+
fm = self.fontMetrics()
125+
template = "X" * self.minimumContentsLength()
126+
template += "."
127+
if self.prefix():
128+
template = self.prefix() + " " + template
129+
if self.suffix():
130+
template = template + self.suffix()
131+
if self.minimum() < 0.0:
132+
template = "-" + template
133+
if self.specialValueText():
134+
templates = [template, self.specialValueText()]
135+
else:
136+
templates = [template]
137+
height = self.lineEdit().sizeHint().height()
138+
width = max(map(fm.horizontalAdvance, templates))
139+
width += 2 # cursor blinking space
140+
hint = QSize(width, height)
141+
opt = QStyleOptionSpinBox()
142+
self.initStyleOption(opt)
143+
sh = self.style().sizeFromContents(QStyle.CT_SpinBox, opt, hint, self)
144+
return sh
145+
146+
def minimumSizeHint(self) -> QSize:
147+
if self.minimumContentsLength() < 0:
148+
return super().minimumSizeHint()
149+
else:
150+
return self.sizeHint()

Orange/widgets/utils/tests/test_colorgradientselection.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import numpy as np
44

5-
from AnyQt.QtTest import QSignalSpy
5+
from AnyQt.QtTest import QSignalSpy, QTest
66
from AnyQt.QtCore import Qt, QStringListModel, QModelIndex
77

88
from Orange.widgets.utils import itemmodels
@@ -105,7 +105,9 @@ def test_center_visibility(self):
105105
def test_center_changed(self):
106106
w = ColorGradientSelection(center=42)
107107
changed = QSignalSpy(w.centerChanged)
108-
w.center_edit.setText("41")
109-
w.center_edit.editingFinished.emit()
110-
self.assertEqual(w.center(), 41)
111-
self.assertEqual(list(changed), [[41]])
108+
ledit = w.center_edit.lineEdit()
109+
ledit.selectAll()
110+
QTest.keyClicks(ledit, "41")
111+
QTest.keyClick(ledit, Qt.Key_Return)
112+
self.assertEqual(w.center(), 41.0)
113+
self.assertEqual(list(changed), [[41.0]])

0 commit comments

Comments
 (0)