From 715bef26b16569dc4c4400bbdb0eb5174af629fc Mon Sep 17 00:00:00 2001 From: robert Date: Mon, 20 May 2019 12:03:22 +0200 Subject: [PATCH 1/3] owradviz: fix label overlap * rotate variable labels * shorten labels to 20 characters * add tooltip for labels * limit number of selected variables --- Orange/widgets/utils/plot/owplotgui.py | 33 ++++++++++------ Orange/widgets/visualize/owradviz.py | 53 +++++++++++++++++++------- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/Orange/widgets/utils/plot/owplotgui.py b/Orange/widgets/utils/plot/owplotgui.py index dd5fb9d5cb0..5a156742d98 100644 --- a/Orange/widgets/utils/plot/owplotgui.py +++ b/Orange/widgets/utils/plot/owplotgui.py @@ -58,13 +58,20 @@ class VariableSelectionModel(VariableListModel): SortRole = next(OrangeUserRole) selection_changed = pyqtSignal() - def __init__(self, selected_vars): + def __init__(self, selected_vars, max_vars=None): super().__init__(enable_dnd=True) self.selected_vars = selected_vars + self.max_vars = max_vars def is_selected(self, index): return self[index.row()] in self.selected_vars + def is_full(self): + if self.max_vars is None: + return False + else: + return len(self.selected_vars) >= self.max_vars + def data(self, index, role): if role == self.IsSelected: return self.is_selected(index) @@ -84,7 +91,7 @@ def toggle_item(self, index): var = self[index.row()] if var in self.selected_vars: self.selected_vars.remove(var) - else: + elif not self.is_full(): self.selected_vars.append(var) self.selection_changed.emit() @@ -131,17 +138,19 @@ def paint(self, painter, option, index): rect = QRect(option.rect) is_selected = index.data(VariableSelectionModel.IsSelected) + full_selection = index.model().sourceModel().is_full() if option.state & QStyle.State_MouseOver: - txt = [" Add ", " Remove "][is_selected] - txtw = painter.fontMetrics().width(txt) - painter.save() - painter.setPen(Qt.NoPen) - painter.setBrush(QColor("#ccc")) - brect = QRect(rect.x() + rect.width() - 8 - txtw, rect.y(), - txtw, rect.height()) - painter.drawRoundedRect(brect, 4, 4) - painter.restore() - painter.drawText(brect, Qt.AlignCenter, txt) + if not full_selection or (full_selection and is_selected): + txt = [" Add ", " Remove "][is_selected] + txtw = painter.fontMetrics().width(txt) + painter.save() + painter.setPen(Qt.NoPen) + painter.setBrush(QColor("#ccc")) + brect = QRect(rect.x() + rect.width() - 8 - txtw, rect.y(), + txtw, rect.height()) + painter.drawRoundedRect(brect, 4, 4) + painter.restore() + painter.drawText(brect, Qt.AlignCenter, txt) painter.save() double_pen = painter.pen() diff --git a/Orange/widgets/visualize/owradviz.py b/Orange/widgets/visualize/owradviz.py index a4036d76c41..74bd5d1f10c 100644 --- a/Orange/widgets/visualize/owradviz.py +++ b/Orange/widgets/visualize/owradviz.py @@ -209,13 +209,14 @@ class OWRadvizGraph(OWGraphWithAnchors): def __init__(self, scatter_widget, parent): super().__init__(scatter_widget, parent) self.anchors_scatter_item = None + self.padding = 0.025 def clear(self): super().clear() self.anchors_scatter_item = None def set_view_box_range(self): - self.view_box.setRange(QRectF(-1.2, -1.05, 2.4, 2.1), padding=0.025) + self.view_box.setRange(QRectF(-1, -1, 2, 2), padding=self.padding) def closest_draggable_item(self, pos): points, _ = self.master.get_anchors() @@ -231,18 +232,36 @@ def update_anchors(self): points, labels = self.master.get_anchors() if points is None: return - if self.anchor_items is None: - self.anchor_items = [] - for point, label in zip(points, labels): - anchor = TextItem() - anchor.setText(label) - anchor.setColor(QColor(0, 0, 0)) - anchor.setPos(*point) - self.plot_widget.addItem(anchor) - self.anchor_items.append(anchor) - else: - for anchor, point in zip(self.anchor_items, points): - anchor.setPos(*point) + if self.anchor_items is not None: + for anchor in self.anchor_items: + self.plot_widget.removeItem(anchor) + + self.anchor_items = [] + label_len = [] + for point, label in zip(points, labels): + anchor = TextItem() + anchor.textItem.setToolTip(f"{label}") + if len(label) > 20: + label = label[:17] + "..." + anchor.setText(label) + label_len.append(len(label)) + anchor.setColor(QColor(0, 0, 0)) + + x, y = point + angle = np.rad2deg(np.arctan2(y, x)) + anchor.setPos(x * 1.025, y * 1.025) + + if abs(angle) < 90: + anchor.setAngle(angle) + anchor.setAnchor((0, 0.5)) + else: + anchor.setAngle(angle + 180) + anchor.setAnchor((1, 0.5)) + + self.plot_widget.addItem(anchor) + self.anchor_items.append(anchor) + + self.padding = max(label_len) * 0.02 self._update_anchors_scatter_item(points) def _update_anchors_scatter_item(self, points): @@ -284,9 +303,11 @@ class Warning(OWAnchorProjectionWidget.Warning): invalid_embedding = widget.Msg("No projection for selected features") removed_vars = widget.Msg("Categorical variables with more than" " two values are not shown.") + max_vars_selected = widget.Msg("Maximum number of selected variables reached.") def _add_controls(self): - self.model_selected = VariableSelectionModel(self.selected_vars) + self.model_selected = VariableSelectionModel(self.selected_vars, + max_vars=20) variables_selection(self.controlArea, self, self.model_selected) self.model_selected.selection_changed.connect( self.__model_selected_changed) @@ -318,6 +339,10 @@ def vizrank_set_attrs(self, *attrs): self.model_selected.selection_changed.emit() def __model_selected_changed(self): + if self.model_selected.is_full(): + self.Warning.max_vars_selected() + else: + self.Warning.max_vars_selected.clear() self.init_projection() self.setup_plot() self.commit() From 52c34b2caea97514e71c52c6c55a1915a2e8bec8 Mon Sep 17 00:00:00 2001 From: robert Date: Fri, 31 May 2019 13:07:59 +0200 Subject: [PATCH 2/3] owradviz: split labels --- Orange/widgets/visualize/owradviz.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/Orange/widgets/visualize/owradviz.py b/Orange/widgets/visualize/owradviz.py index 74bd5d1f10c..699da01af75 100644 --- a/Orange/widgets/visualize/owradviz.py +++ b/Orange/widgets/visualize/owradviz.py @@ -205,6 +205,9 @@ def stopped(self): self.n_attrs_spin.setDisabled(False) +MAX_LABEL_LEN = 16 + + class OWRadvizGraph(OWGraphWithAnchors): def __init__(self, scatter_widget, parent): super().__init__(scatter_widget, parent) @@ -237,14 +240,28 @@ def update_anchors(self): self.plot_widget.removeItem(anchor) self.anchor_items = [] - label_len = [] + label_len = 1 for point, label in zip(points, labels): anchor = TextItem() anchor.textItem.setToolTip(f"{label}") - if len(label) > 20: - label = label[:17] + "..." + + if len(label) > MAX_LABEL_LEN: + i = label.rfind(" ", 0, MAX_LABEL_LEN) + if i != -1: + first_row = label[:i] + "\n" + second_row = label[i + 1:] + if len(second_row) > MAX_LABEL_LEN: + j = second_row.rfind(" ", 0, MAX_LABEL_LEN) + if j != -1: + second_row = second_row[:j + 1] + "..." + else: + second_row = second_row[:MAX_LABEL_LEN - 3] + "..." + label = first_row + second_row + else: + label = label[:MAX_LABEL_LEN - 3] + "..." + anchor.setText(label) - label_len.append(len(label)) + label_len = min(MAX_LABEL_LEN, len(label)) anchor.setColor(QColor(0, 0, 0)) x, y = point @@ -261,7 +278,7 @@ def update_anchors(self): self.plot_widget.addItem(anchor) self.anchor_items.append(anchor) - self.padding = max(label_len) * 0.02 + self.padding = label_len * 0.0175 self._update_anchors_scatter_item(points) def _update_anchors_scatter_item(self, points): From cd1d87cff5649289e11abdb7ca4c1e6796eff39c Mon Sep 17 00:00:00 2001 From: robert Date: Fri, 7 Jun 2019 13:57:18 +0200 Subject: [PATCH 3/3] owradviz: align left lables to right --- Orange/widgets/visualize/owradviz.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Orange/widgets/visualize/owradviz.py b/Orange/widgets/visualize/owradviz.py index 699da01af75..d3a936dd189 100644 --- a/Orange/widgets/visualize/owradviz.py +++ b/Orange/widgets/visualize/owradviz.py @@ -275,6 +275,11 @@ def update_anchors(self): anchor.setAngle(angle + 180) anchor.setAnchor((1, 0.5)) + anchor.textItem.setTextWidth(anchor.textItem.boundingRect().width()) + option = anchor.textItem.document().defaultTextOption() + option.setAlignment(Qt.AlignRight) + anchor.textItem.document().setDefaultTextOption(option) + self.plot_widget.addItem(anchor) self.anchor_items.append(anchor)