1"""Common gui.OWComponent components."""
2
3from AnyQt.QtCore import Qt, QRectF
4from AnyQt.QtGui import QColor, QFont
5from AnyQt.QtWidgets import QGraphicsEllipseItem
6
7import pyqtgraph as pg
8from Orange.widgets.visualize.owscatterplotgraph import (
9    OWScatterPlotBase, ScatterBaseParameterSetter
10)
11from Orange.widgets.visualize.utils.customizableplot import Updater
12from Orange.widgets.visualize.utils.plotutils import (
13    MouseEventDelegate, DraggableItemsViewBox
14)
15
16class AnchorParameterSetter(ScatterBaseParameterSetter):
17    ANCHOR_LABEL = "Anchor"
18
19    def __init__(self, master):
20        super().__init__(master)
21        self.anchor_font = QFont()
22
23    def update_setters(self):
24        super().update_setters()
25        self.initial_settings[self.LABELS_BOX].update({
26            self.ANCHOR_LABEL: self.FONT_SETTING
27        })
28
29        def update_anchors(**settings):
30            self.anchor_font = Updater.change_font(self.anchor_font, settings)
31            self.master.update_anchors()
32
33        self._setters[self.LABELS_BOX][self.ANCHOR_LABEL] = update_anchors
34
35
36class OWGraphWithAnchors(OWScatterPlotBase):
37    """
38    Graph for projections in which dimensions can be manually moved
39
40    Class is used as a graph base class for OWFreeViz and OWRadviz."""
41    DISTANCE_DIFF = 0.08
42
43    def __init__(self, scatter_widget, parent, view_box=DraggableItemsViewBox):
44        super().__init__(scatter_widget, parent, view_box)
45        self.anchor_items = None
46        self.circle_item = None
47        self.indicator_item = None
48        self._tooltip_delegate = MouseEventDelegate(self.help_event,
49                                                    self.show_indicator_event)
50        self.plot_widget.scene().installEventFilter(self._tooltip_delegate)
51        self.parameter_setter = AnchorParameterSetter(self)
52
53    def clear(self):
54        super().clear()
55        self.anchor_items = None
56        self.circle_item = None
57        self.indicator_item = None
58
59    def update_coordinates(self):
60        super().update_coordinates()
61        if self.scatterplot_item is not None:
62            self.update_anchors()
63            self.update_circle()
64            self.set_view_box_range()
65            self.view_box.setAspectLocked(True, 1)
66
67    def update_anchors(self):
68        raise NotImplementedError
69
70    def update_circle(self):
71        if self.scatterplot_item is not None and not self.circle_item:
72            self.circle_item = QGraphicsEllipseItem()
73            self.circle_item.setRect(QRectF(-1, -1, 2, 2))
74            self.circle_item.setPen(pg.mkPen(QColor(0, 0, 0), width=2))
75            self.plot_widget.addItem(self.circle_item)
76
77    def reset_button_clicked(self):
78        self.set_view_box_range()
79
80    def set_view_box_range(self):
81        raise NotImplementedError
82
83    def closest_draggable_item(self, pos):
84        return None
85
86    def show_indicator(self, anchor_idx):
87        self._update_indicator_item(anchor_idx)
88
89    def show_indicator_event(self, ev):
90        scene = self.plot_widget.scene()
91        if self.scatterplot_item is None or scene.drag_tooltip.isVisible():
92            return False
93        if self.view_box.mouse_state == 1:
94            return True
95
96        pos = self.scatterplot_item.mapFromScene(ev.scenePos())
97        anchor_idx = self.closest_draggable_item(pos)
98        if anchor_idx is not None:
99            self._update_indicator_item(anchor_idx)
100            if self.view_box.mouse_state == 0:
101                self.view_box.setCursor(Qt.OpenHandCursor)
102        else:
103            if self.indicator_item is not None:
104                self.plot_widget.removeItem(self.indicator_item)
105                self.indicator_item = None
106            self.view_box.setCursor(Qt.ArrowCursor)
107        return True
108
109    def _update_indicator_item(self, anchor_idx):
110        if self.indicator_item is not None:
111            self.plot_widget.removeItem(self.indicator_item)
112        self._add_indicator_item(anchor_idx)
113
114    def _add_indicator_item(self, anchor_idx):
115        pass
116