Skip to content

Commit ad0f944

Browse files
committed
HeatMap: Allow setting the center when using diverging palettes
1 parent a0381fb commit ad0f944

File tree

2 files changed

+48
-5
lines changed

2 files changed

+48
-5
lines changed

Orange/widgets/utils/colorgradientselection.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,29 @@
22

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

9-
from Orange.widgets.utils import itemmodels
10+
from Orange.widgets.utils import itemmodels, colorpalettes
1011

1112

1213
class ColorGradientSelection(QWidget):
1314
activated = Signal(int)
1415

1516
currentIndexChanged = Signal(int)
1617
thresholdsChanged = Signal(float, float)
18+
centerChanged = Signal(float)
1719

18-
def __init__(self, *args, thresholds=(0.0, 1.0), **kwargs):
20+
def __init__(self, *args, thresholds=(0.0, 1.0), center=0, **kwargs):
1921
super().__init__(*args, **kwargs)
2022

2123
low = round(clip(thresholds[0], 0., 1.), 2)
2224
high = round(clip(thresholds[1], 0., 1.), 2)
2325
high = max(low, high)
2426
self.__threshold_low, self.__threshold_high = low, high
27+
self.__center = center
2528
form = QFormLayout(
2629
formAlignment=Qt.AlignLeft,
2730
labelAlignment=Qt.AlignLeft,
@@ -42,6 +45,21 @@ def __init__(self, *args, thresholds=(0.0, 1.0), **kwargs):
4245
self.gradient_cb.setModel(model)
4346
self.gradient_cb.activated[int].connect(self.activated)
4447
self.gradient_cb.currentIndexChanged.connect(self.currentIndexChanged)
48+
self.gradient_cb.currentIndexChanged.connect(self.__on_palette_changed)
49+
50+
self.center_box = QWidget()
51+
center_layout = QHBoxLayout()
52+
self.center_box.setLayout(center_layout)
53+
width = QFontMetrics(self.font()).boundingRect("9999999").width()
54+
self.center_edit = QLineEdit(
55+
text=f"{self.__center}",
56+
maximumWidth=width, placeholderText="0", alignment=Qt.AlignRight)
57+
self.center_edit.setValidator(QDoubleValidator())
58+
self.center_edit.editingFinished.connect(self.__on_center_changed)
59+
center_layout.setContentsMargins(0, 0, 0, 0)
60+
center_layout.addStretch(1)
61+
center_layout.addWidget(QLabel("Centered at"))
62+
center_layout.addWidget(self.center_edit)
4563

4664
slider_low = QSlider(
4765
objectName="threshold-low-slider", minimum=0, maximum=100,
@@ -60,6 +78,7 @@ def __init__(self, *args, thresholds=(0.0, 1.0), **kwargs):
6078
"gradient from the higher end")
6179
)
6280
form.setWidget(0, QFormLayout.SpanningRole, self.gradient_cb)
81+
form.setWidget(1, QFormLayout.FieldRole, self.center_box)
6382
form.addRow(self.tr("Low:"), slider_low)
6483
form.addRow(self.tr("High:"), slider_high)
6584
self.slider_low = slider_low
@@ -109,6 +128,14 @@ def thresholdHigh(self) -> float:
109128
def setThresholdHigh(self, high: float) -> None:
110129
self.setThresholds(min(self.__threshold_low, high), high)
111130

131+
def center(self) -> float:
132+
return self.__center
133+
134+
def setCenter(self, center: float) -> None:
135+
self.__center = center
136+
self.center_edit.setText(f"{center}")
137+
self.centerChanged.emit(center)
138+
112139
thresholdHigh_ = Property(
113140
float, thresholdLow, setThresholdLow, notify=thresholdsChanged)
114141

@@ -146,6 +173,14 @@ def setThresholds(self, low: float, high: float) -> None:
146173
self.slider_high.setSliderPosition(high * 100)
147174
self.thresholdsChanged.emit(high, low)
148175

176+
def __on_palette_changed(self):
177+
diverging = \
178+
self.currentData().flags & colorpalettes.Palette.Flags.Diverging
179+
self.center_box.setVisible(diverging)
180+
181+
def __on_center_changed(self):
182+
self.__center = float(self.center_edit.text() or "0")
183+
self.centerChanged.emit(self.__center)
149184

150185
def clip(a, amin, amax):
151186
return min(max(a, amin), amax)

Orange/widgets/visualize/owheatmap.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ class Outputs:
155155

156156
threshold_low = settings.Setting(0.0)
157157
threshold_high = settings.Setting(1.0)
158+
color_center = settings.Setting(0)
158159

159160
merge_kmeans = settings.Setting(False)
160161
merge_kmeans_k = settings.Setting(50)
@@ -244,6 +245,7 @@ def _():
244245

245246
self.color_map_widget = cmw = ColorGradientSelection(
246247
thresholds=(self.threshold_low, self.threshold_high),
248+
center=self.color_center
247249
)
248250
model = itemmodels.ContinuousPalettesModel(parent=self)
249251
cmw.setModel(model)
@@ -257,6 +259,12 @@ def _set_thresholds(low, high):
257259
self.threshold_low, self.threshold_high = low, high
258260
self.update_color_schema()
259261
cmw.thresholdsChanged.connect(_set_thresholds)
262+
263+
def _set_centering(center):
264+
self.color_center = center
265+
self.update_color_schema()
266+
cmw.centerChanged.connect(_set_centering)
267+
260268
colorbox.layout().addWidget(self.color_map_widget)
261269

262270
mergebox = gui.vBox(self.controlArea, "Merge",)
@@ -449,7 +457,7 @@ def color_palette(self):
449457
def color_map(self) -> GradientColorMap:
450458
return GradientColorMap(
451459
self.color_palette(), (self.threshold_low, self.threshold_high),
452-
0 if self.center_palette else None
460+
self.color_map_widget.center() if self.center_palette else None
453461
)
454462

455463
def clear(self):

0 commit comments

Comments
 (0)