Skip to content

Commit 715bef2

Browse files
committed
owradviz: fix label overlap
* rotate variable labels * shorten labels to 20 characters * add tooltip for labels * limit number of selected variables
1 parent ace29e0 commit 715bef2

File tree

2 files changed

+60
-26
lines changed

2 files changed

+60
-26
lines changed

Orange/widgets/utils/plot/owplotgui.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,20 @@ class VariableSelectionModel(VariableListModel):
5858
SortRole = next(OrangeUserRole)
5959
selection_changed = pyqtSignal()
6060

61-
def __init__(self, selected_vars):
61+
def __init__(self, selected_vars, max_vars=None):
6262
super().__init__(enable_dnd=True)
6363
self.selected_vars = selected_vars
64+
self.max_vars = max_vars
6465

6566
def is_selected(self, index):
6667
return self[index.row()] in self.selected_vars
6768

69+
def is_full(self):
70+
if self.max_vars is None:
71+
return False
72+
else:
73+
return len(self.selected_vars) >= self.max_vars
74+
6875
def data(self, index, role):
6976
if role == self.IsSelected:
7077
return self.is_selected(index)
@@ -84,7 +91,7 @@ def toggle_item(self, index):
8491
var = self[index.row()]
8592
if var in self.selected_vars:
8693
self.selected_vars.remove(var)
87-
else:
94+
elif not self.is_full():
8895
self.selected_vars.append(var)
8996
self.selection_changed.emit()
9097

@@ -131,17 +138,19 @@ def paint(self, painter, option, index):
131138
rect = QRect(option.rect)
132139

133140
is_selected = index.data(VariableSelectionModel.IsSelected)
141+
full_selection = index.model().sourceModel().is_full()
134142
if option.state & QStyle.State_MouseOver:
135-
txt = [" Add ", " Remove "][is_selected]
136-
txtw = painter.fontMetrics().width(txt)
137-
painter.save()
138-
painter.setPen(Qt.NoPen)
139-
painter.setBrush(QColor("#ccc"))
140-
brect = QRect(rect.x() + rect.width() - 8 - txtw, rect.y(),
141-
txtw, rect.height())
142-
painter.drawRoundedRect(brect, 4, 4)
143-
painter.restore()
144-
painter.drawText(brect, Qt.AlignCenter, txt)
143+
if not full_selection or (full_selection and is_selected):
144+
txt = [" Add ", " Remove "][is_selected]
145+
txtw = painter.fontMetrics().width(txt)
146+
painter.save()
147+
painter.setPen(Qt.NoPen)
148+
painter.setBrush(QColor("#ccc"))
149+
brect = QRect(rect.x() + rect.width() - 8 - txtw, rect.y(),
150+
txtw, rect.height())
151+
painter.drawRoundedRect(brect, 4, 4)
152+
painter.restore()
153+
painter.drawText(brect, Qt.AlignCenter, txt)
145154

146155
painter.save()
147156
double_pen = painter.pen()

Orange/widgets/visualize/owradviz.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,14 @@ class OWRadvizGraph(OWGraphWithAnchors):
209209
def __init__(self, scatter_widget, parent):
210210
super().__init__(scatter_widget, parent)
211211
self.anchors_scatter_item = None
212+
self.padding = 0.025
212213

213214
def clear(self):
214215
super().clear()
215216
self.anchors_scatter_item = None
216217

217218
def set_view_box_range(self):
218-
self.view_box.setRange(QRectF(-1.2, -1.05, 2.4, 2.1), padding=0.025)
219+
self.view_box.setRange(QRectF(-1, -1, 2, 2), padding=self.padding)
219220

220221
def closest_draggable_item(self, pos):
221222
points, _ = self.master.get_anchors()
@@ -231,18 +232,36 @@ def update_anchors(self):
231232
points, labels = self.master.get_anchors()
232233
if points is None:
233234
return
234-
if self.anchor_items is None:
235-
self.anchor_items = []
236-
for point, label in zip(points, labels):
237-
anchor = TextItem()
238-
anchor.setText(label)
239-
anchor.setColor(QColor(0, 0, 0))
240-
anchor.setPos(*point)
241-
self.plot_widget.addItem(anchor)
242-
self.anchor_items.append(anchor)
243-
else:
244-
for anchor, point in zip(self.anchor_items, points):
245-
anchor.setPos(*point)
235+
if self.anchor_items is not None:
236+
for anchor in self.anchor_items:
237+
self.plot_widget.removeItem(anchor)
238+
239+
self.anchor_items = []
240+
label_len = []
241+
for point, label in zip(points, labels):
242+
anchor = TextItem()
243+
anchor.textItem.setToolTip(f"<b>{label}</b>")
244+
if len(label) > 20:
245+
label = label[:17] + "..."
246+
anchor.setText(label)
247+
label_len.append(len(label))
248+
anchor.setColor(QColor(0, 0, 0))
249+
250+
x, y = point
251+
angle = np.rad2deg(np.arctan2(y, x))
252+
anchor.setPos(x * 1.025, y * 1.025)
253+
254+
if abs(angle) < 90:
255+
anchor.setAngle(angle)
256+
anchor.setAnchor((0, 0.5))
257+
else:
258+
anchor.setAngle(angle + 180)
259+
anchor.setAnchor((1, 0.5))
260+
261+
self.plot_widget.addItem(anchor)
262+
self.anchor_items.append(anchor)
263+
264+
self.padding = max(label_len) * 0.02
246265
self._update_anchors_scatter_item(points)
247266

248267
def _update_anchors_scatter_item(self, points):
@@ -284,9 +303,11 @@ class Warning(OWAnchorProjectionWidget.Warning):
284303
invalid_embedding = widget.Msg("No projection for selected features")
285304
removed_vars = widget.Msg("Categorical variables with more than"
286305
" two values are not shown.")
306+
max_vars_selected = widget.Msg("Maximum number of selected variables reached.")
287307

288308
def _add_controls(self):
289-
self.model_selected = VariableSelectionModel(self.selected_vars)
309+
self.model_selected = VariableSelectionModel(self.selected_vars,
310+
max_vars=20)
290311
variables_selection(self.controlArea, self, self.model_selected)
291312
self.model_selected.selection_changed.connect(
292313
self.__model_selected_changed)
@@ -318,6 +339,10 @@ def vizrank_set_attrs(self, *attrs):
318339
self.model_selected.selection_changed.emit()
319340

320341
def __model_selected_changed(self):
342+
if self.model_selected.is_full():
343+
self.Warning.max_vars_selected()
344+
else:
345+
self.Warning.max_vars_selected.clear()
321346
self.init_projection()
322347
self.setup_plot()
323348
self.commit()

0 commit comments

Comments
 (0)