source: orange/orange/OrangeWidgets/plot/owlegend.py @ 9219:c1dac072ad0a

Revision 9219:c1dac072ad0a, 13.4 KB checked in by Noughmad <Noughmad@…>, 2 years ago (diff)

Remove qDebug messages from widgets in Visualize Qt

Line 
1"""
2##########################
3Plot legend (``owlegend``)
4##########################
5
6.. autoclass:: OWLegendItem
7
8.. autoclass:: OWLegendTitle
9
10.. autoclass:: OWLegend
11    :members:
12
13"""
14
15from PyQt4.QtGui import QGraphicsTextItem, QGraphicsRectItem, QGraphicsObject, QColor, QPen, QLinearGradient
16from PyQt4.QtCore import QPointF, QRectF, Qt, QPropertyAnimation, QSizeF
17
18from owpoint import *
19from owcurve import OWCurve
20from owtools import move_item_xy
21from owpalette import OWPalette
22
23PointColor = 1
24PointSize = 2
25PointSymbol = 4
26
27class OWLegendItem(QGraphicsObject):
28    """
29        Represents a legend item with a title and a point symbol.
30       
31        :param name: The text to display
32        :type name: str
33       
34        :param point: The point symbol
35        :type point: :obj:`.OWPoint`
36       
37        :param parent: The parent item, passed to QGraphicsItem
38        :type parent: :obj:`QGraphicsItem`
39       
40        .. seealso:: :meth:`.OWLegend.add_item`, :meth:`.OWLegend.add_curve`.
41    """
42    def __init__(self, name, point, parent):
43        QGraphicsObject.__init__(self, parent)
44        self.text_item = QGraphicsTextItem(name, self)
45        if point:
46            s = point.size()
47            height = max(2*s, self.text_item.boundingRect().height())
48        else:
49            height = self.text_item.boundingRect().height()
50        p = 0.5 * height
51        self.text_item.setPos(height, 0)
52        self.point_item = point
53        if point:
54            self.point_item.setParentItem(self)
55            self.point_item.setPos(p, p)
56        self._rect = QRectF(0, 0, height + self.text_item.boundingRect().width(), height )
57        self.rect_item = QGraphicsRectItem(self._rect, self)
58        self.rect_item.setPen(QPen(Qt.NoPen))
59        self.rect_item.stackBefore(self.text_item)
60        if self.point_item:
61            self.rect_item.stackBefore(self.point_item)
62       
63    def boundingRect(self):
64        return self._rect
65       
66    def paint(self, painter, option, widget):
67        pass
68   
69class OWLegendTitle(QGraphicsObject):
70    """
71        A legend item that shows ``text`` with a bold font and no symbol.
72    """
73    def __init__(self, text, parent):
74        QGraphicsObject.__init__(self, parent)
75        self.text_item = QGraphicsTextItem(text + ':', self)
76        f = self.text_item.font()
77        f.setBold(True)
78        self.text_item.setFont(f)
79        self.rect_item = QGraphicsRectItem(self.text_item.boundingRect(), self)
80        self.rect_item.setPen(QPen(Qt.NoPen))
81        self.rect_item.stackBefore(self.text_item)
82       
83    def boundingRect(self):
84        return self.text_item.boundingRect()
85       
86    def paint(self, painter, option, widget):
87        pass
88   
89class OWLegendGradient(QGraphicsObject):
90   
91    gradient_width = 20
92   
93    def __init__(self, palette, values, parent):
94        QGraphicsObject.__init__(self, parent)
95        self.parent = parent
96        self.palette = palette
97        self.values = values
98        self.legend = parent
99        self.label_items = [QGraphicsTextItem(text, self) for text in values]
100        for i in self.label_items:
101            i.setTextWidth(50)
102           
103        self.rect = QRectF()
104           
105        self.gradient_item = QGraphicsRectItem(self)
106        self.gradient = QLinearGradient()
107        self.gradient.setStops([(v*0.1, self.palette[v*0.1]) for v in range(11) ])
108        self.orientation = Qt.Horizontal
109        self.set_orientation(Qt.Vertical)
110       
111    def set_orientation(self, orientation):
112        if self.orientation == orientation:
113            return
114           
115        self.orientation = orientation
116       
117        if self.orientation == Qt.Vertical:
118            height = max([item.boundingRect().height() for item in self.label_items])
119            total_height = height * max(5, len(self.label_items))
120            interval = (total_height - self.label_items[-1].boundingRect().height()) / (len(self.label_items) -1)
121            self.gradient_item.setRect(10, 0, self.gradient_width, total_height)
122            self.gradient.setStart(10, 0)
123            self.gradient.setFinalStop(10, total_height)
124            self.gradient_item.setBrush(QBrush(self.gradient))
125            self.gradient_item.setPen(QPen(Qt.NoPen))
126            y = 0
127            x = 30
128            for item in self.label_items:
129                move_item_xy(item, x, y, self.parent.graph.animate_plot)
130                y += interval
131            self.rect = QRectF(10, 0, self.gradient_width + max([item.boundingRect().width() for item in self.label_items]), self.label_items[0].boundingRect().height() * max(5, len(self.label_items)))
132        else:
133            width = 50
134            height = max([item.boundingRect().height() for item in self.label_items])
135            total_width = width * max(5, len(self.label_items))
136            interval = (total_width - self.label_items[-1].boundingRect().width()) / (len(self.label_items) -1)
137           
138            self.gradient_item.setRect(0, 0, total_width, self.gradient_width)
139            self.gradient.setStart(0, 0)
140            self.gradient.setFinalStop(total_width, 0)
141            self.gradient_item.setBrush(QBrush(self.gradient))
142            self.gradient_item.setPen(QPen(Qt.NoPen))
143            x = 0
144            y = 30
145            for item in self.label_items:
146                move_item_xy(item, x, y, self.parent.graph.animate_plot)
147                x += interval
148            self.rect = QRectF(0, 0, total_width, self.gradient_width + height)
149 
150    def boundingRect(self):
151        return getattr(self, 'rect', QRectF())
152       
153    def paint(self, painter, option, widget):
154        pass
155       
156       
157class OWLegend(QGraphicsObject):
158    """
159        A legend for :obj:`.OWPlot`.
160       
161        Its items are arranged into a hierarchy by `category`. This is useful when points differ in more than one attribute.
162        In such a case, there can be one category for point color and one for point shape. Usually the category name
163        will be the name of the attribute, while the item's title will be the value.
164       
165        Arbitrary categories can be created, for an example see :meth:`.OWPlot.update_axes`, which creates a special category
166        for unused axes.
167        decimals
168        .. image:: files/legend-categories.png
169       
170        In the image above, `type` and `milk` are categories with 7 and 2 possible values, respectively.
171       
172    """
173    def __init__(self, graph, scene):
174        QGraphicsObject.__init__(self)
175        if scene:
176            scene.addItem(self)
177        self.graph = graph
178        self.curves = []
179        self.items = {}
180        self.attributes = []
181        self.point_attrs = {}
182        self.point_vals = {}
183        self.default_values = {
184                               PointColor : Qt.black, 
185                               PointSize : 8, 
186                               PointSymbol : OWPoint.Ellipse
187                               }
188        self.box_rect = QRectF()
189        self.setFiltersChildEvents(True)
190        self.setFlag(self.ItemHasNoContents, True)
191        self.mouse_down = False
192        self._orientation = Qt.Vertical
193        self.max_size = QSizeF()
194        self._floating = True
195        self._floating_animation = None
196        self._mouse_down_pos = QPointF()
197
198    def clear(self):
199        """
200            Removes all items from the legend
201        """
202        for lst in self.items.itervalues():
203            for i in lst:
204                i.setParentItem(None)
205                if self.scene():
206                    self.scene().removeItem(i)
207        self.items = {}
208        self.update_items()
209       
210
211    def add_curve(self, curve):
212        """
213            Adds a legend item with the same point symbol and name as ``curve``.
214           
215            If the curve's name contains the equal sign (=), it is split at that sign. The first part of the curve
216            is a used as the category, and the second part as the value.
217        """
218        i = curve.name.find('=')
219        if i == -1:
220            cat = ''
221            name = curve.name
222        else:
223            cat = curve.name[:i]
224            name = curve.name[i+1:]
225        self.add_item(cat, name, curve.point_item(0, 0, 0))
226       
227    def add_item(self, category, value, point):
228        """
229            Adds an item with title ``value`` and point symbol ``point`` to the specified ``category``.
230        """
231        if category not in self.items:
232            self.items[category] = [OWLegendTitle(category, self)]
233        self.items[category].append(OWLegendItem(str(value), point, self))
234        self.update_items()
235       
236    def add_color_gradient(self, title, values):
237        if len(values) < 2:
238            # No point in showing a gradient with less that two values
239            return
240        if title in self.items:
241            self.remove_category(title)
242        item = OWLegendGradient(self.graph.contPalette, [str(v) for v in values], self)
243        self.items[title] = [OWLegendTitle(title, self), item]
244        self.update_items()
245       
246    def remove_category(self, category):
247        """
248            Removes ``category`` and all items that belong to it.
249        """
250        if category not in self.items:
251            return
252        if self.scene():
253            for item in self.items[category]:
254                self.scene().removeItem(item)
255        del self.items[category]
256       
257    def update_items(self):
258        """
259            Updates the legend, repositioning the items according to the legend's orientation.
260        """
261        self.box_rect = QRectF()
262        x = y = 0
263       
264        for lst in self.items.itervalues():
265            for item in lst:
266                if hasattr(item, 'text_item'):
267                    item.text_item.setDefaultTextColor(self.graph.color(OWPalette.Text))
268                if hasattr(item, 'rect_item'):
269                    item.rect_item.setBrush(self.graph.color(OWPalette.Canvas))
270                if hasattr(item, 'set_orientation'):
271                    item.set_orientation(self._orientation)
272                   
273        if self._orientation == Qt.Vertical:
274            for lst in self.items.itervalues():
275                for item in lst:
276                    if self.max_size.height() and y and y + item.boundingRect().height() > self.max_size.height():
277                        y = 0
278                        x = x + item.boundingRect().width()
279                    self.box_rect = self.box_rect | item.boundingRect().translated(x, y)
280                    move_item_xy(item, x, y, self.graph.animate_plot)
281                    y = y + item.boundingRect().height()
282        elif self._orientation == Qt.Horizontal:
283            for lst in self.items.itervalues():
284                max_h = max(item.boundingRect().height() for item in lst)
285                for item in lst:
286                    if self.max_size.width() and x and x + item.boundingRect().width() > self.max_size.width():
287                        x = 0
288                        y = y + max_h
289                    self.box_rect = self.box_rect | item.boundingRect().translated(x, y)
290                    move_item_xy(item, x, y, self.graph.animate_plot)
291                    x = x + item.boundingRect().width()               
292                if lst:
293                    x = 0
294                    y = y + max_h
295       
296    def mouseMoveEvent(self, event):
297        self.graph.notify_legend_moved(event.scenePos())
298        if self._floating:
299            p = event.scenePos() - self._mouse_down_pos
300            if self._floating_animation and self._floating_animation.state() == QPropertyAnimation.Running:
301                self.set_pos_animated(p)
302            else:
303                self.setPos(p)
304        event.accept()
305           
306    def mousePressEvent(self, event):
307        self.setCursor(Qt.ClosedHandCursor)
308        self.mouse_down = True
309        self._mouse_down_pos = event.scenePos() - self.pos()
310        event.accept()
311       
312    def mouseReleaseEvent(self, event):
313        self.unsetCursor()
314        self.mouse_down = False
315        self._mouse_down_pos = QPointF()
316        event.accept()
317
318    def boundingRect(self):
319        return self.box_rect
320       
321    def paint(self, painter, option, widget=None):
322        pass
323   
324    def set_orientation(self, orientation):
325        """
326            Sets the legend's orientation to ``orientation``.
327        """
328        self._orientation = orientation
329        self.update_items()
330           
331    def orientation(self):
332        return self._orientation
333           
334    def set_pos_animated(self, pos):
335        if (self.pos() - pos).manhattanLength() < 6 or not self.graph.animate_plot:
336            self.setPos(pos)
337        else:
338            t = 250
339            if self._floating_animation and self._floating_animation.state() == QPropertyAnimation.Running:
340                t = t - self._floating_animation.currentTime()
341                self._floating_animation.stop()
342            self._floating_animation = QPropertyAnimation(self, 'pos')
343            self._floating_animation.setEndValue(pos)
344            self._floating_animation.setDuration(t)
345            self._floating_animation.start(QPropertyAnimation.KeepWhenStopped)
346       
347    def set_floating(self, floating, pos=None):
348        """
349            If floating is ``True``, the legend can be dragged with the mouse.
350            Otherwise, it's fixed in its position.
351           
352            If ``pos`` is specified, the legend is moved there.
353        """
354        if floating == self._floating:
355            return
356        self._floating = floating
357        if pos:
358            if floating:
359                self.set_pos_animated(pos - self._mouse_down_pos)
360            else:
361                self.set_pos_animated(pos)
362       
Note: See TracBrowser for help on using the repository browser.