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

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

Remove qDebug messages from widgets in Visualize Qt

Line 
1"""
2    The Axis class display an axis on a graph
3   
4    The axis contains a line with configurable style, possible arrows, and a title
5   
6    .. attribute:: line_style
7        The LineStyle with which the axis line is drawn
8       
9    .. attribute:: title
10        The string to be displayed alongside the axis
11       
12    .. attribute:: title_above
13        A boolean which specifies whether the title should be placed above or below the axis
14        Normally the title would be above for top and left axes.
15       
16    .. attribute:: title_location
17        can either be AxisStart, AxisEnd or AxisMiddle. The default is AxisMiddle
18       
19    .. attribute:: arrows
20        A bitfield containing AxisEnd if an arrow should be drawn at the line's end (line.p2())
21        and AxisStart if there should be an arrows at the first point.
22       
23        By default, there's an arrow at the end of the line
24       
25    .. attribute:: zoomable
26        If this is set to True, the axis line will zoom together with the rest of the graph.
27        Otherwise, the line will remain in place and only tick marks will zoom.
28               
29    .. method:: make_title
30        Makes a pretty title, with the quantity title in italics and the unit in normal text
31               
32    .. method:: label_pos
33        Controls where the axis title and tick marks are placed relative to the axis
34"""
35
36from math import *
37
38from PyQt4.QtGui import QGraphicsItem, QGraphicsLineItem, QGraphicsTextItem, QPainterPath, QGraphicsPathItem, QGraphicsScene, QTransform, QGraphicsRectItem, QPen, QFontMetrics
39from PyQt4.QtCore import QLineF, QPointF, QRectF, Qt
40
41from owconstants import *
42from owtools import resize_plot_item_list
43from owpalette import OWPalette
44
45class OWAxis(QGraphicsItem):
46   
47    Role = OWPalette.Axis
48   
49    def __init__(self, id, title = '', title_above = False, title_location = AxisMiddle, line = None, arrows = AxisEnd, plot = None):
50        QGraphicsItem.__init__(self)
51        self.setFlag(QGraphicsItem.ItemHasNoContents)
52        self.setZValue(AxisZValue)
53        self.id = id
54        self.title = title
55        self.title_location = title_location
56        self.data_line = line
57        self.plot = plot
58        self.graph_line = None
59        self.size = None
60        self.scale = None
61        self.tick_length = (10, 5, 0)
62        self.arrows = arrows
63        self.title_above = title_above
64        self.line_item = QGraphicsLineItem(self)
65        self.title_item = QGraphicsTextItem(self)
66        self.end_arrow_item = None
67        self.start_arrow_item = None
68        self.show_title = False
69        self.scale = None
70        path = QPainterPath()
71        path.setFillRule(Qt.WindingFill)
72        path.moveTo(0, 3.09)
73        path.lineTo(0, -3.09)
74        path.lineTo(9.51, 0)
75        path.closeSubpath()
76        self.arrow_path = path
77        self.label_items = []
78        self.label_bg_items = []
79        self.tick_items = []
80        self._ticks = []
81        self.zoom_transform = QTransform()
82        self.labels = None
83        self.auto_range = None
84        self.auto_scale = True
85       
86        self.zoomable = False
87        self.update_callback = None
88        self.max_text_width = 50
89        self.text_margin = 5
90        self.always_horizontal_text = False
91       
92    def update_ticks(self):
93        self._ticks = []
94        major, medium, minor = self.tick_length
95        if self.labels is not None and not self.auto_scale:
96            for i, text in enumerate(self.labels):
97                self._ticks.append( ( i, text, medium, 1 ) )
98        else:
99            if self.scale and not self.auto_scale:
100                min, max, step = self.scale
101            elif self.auto_range:
102                min, max = self.auto_range
103                if min is not None and max is not None:
104                    step = (max - min)/10
105                else:
106                    return
107            else:
108                return
109           
110            if max == min:
111                return
112               
113            magnitude = int(3*log10(abs(max-min)) + 1)
114            if magnitude % 3 == 0:
115                first_place = 1
116            elif magnitude % 3 == 1:
117                first_place = 2
118            else:
119                first_place = 5
120            magnitude = magnitude / 3 - 1
121            step = first_place * pow(10, magnitude)
122            val = ceil(min/step) * step
123            while val <= max:
124                self._ticks.append( ( val, "%.4g" % val, medium, step ) )
125                val = val + step
126               
127    def update_graph(self):
128        if self.update_callback:
129            self.update_callback()
130           
131
132    def update(self, zoom_only = False):
133        self.update_ticks()
134        line_color = self.plot.color(OWPalette.Axis)
135        text_color = self.plot.color(OWPalette.Text)
136        if not self.graph_line or not self.scene():
137            return
138        self.line_item.setLine(self.graph_line)
139        self.line_item.setPen(line_color)
140        if self.title:
141            self.title_item.setHtml('<b>' + self.title + '</b>')
142            self.title_item.setDefaultTextColor(text_color)
143        if self.title_location == AxisMiddle:
144            title_p = 0.5
145        elif self.title_location == AxisEnd:
146            title_p = 0.95
147        else:
148            title_p = 0.05
149        title_pos = self.graph_line.pointAt(title_p)
150        v = self.graph_line.normalVector().unitVector()
151       
152        dense_text = False
153        if hasattr(self, 'title_margin'):
154            offset = self.title_margin
155        elif self._ticks:
156            if self.should_be_expanded():
157                offset = 55
158                dense_text = True
159            else:
160                offset = 35
161        else:
162            offset = 10
163
164        if self.title_above:
165            title_pos = title_pos + (v.p2() - v.p1())*(offset + QFontMetrics(self.title_item.font()).height())
166        else:
167            title_pos = title_pos - (v.p2() - v.p1())*offset
168        ## TODO: Move it according to self.label_pos
169        self.title_item.setVisible(self.show_title)
170        self.title_item.setRotation(-self.graph_line.angle())
171        c = self.title_item.mapToParent(self.title_item.boundingRect().center())
172        tl = self.title_item.mapToParent(self.title_item.boundingRect().topLeft())
173        self.title_item.setPos(title_pos - c + tl)
174       
175        ## Arrows
176        if not zoom_only:
177            if self.start_arrow_item:
178                self.scene().removeItem(self.start_arrow_item)
179                self.start_arrow_item = None
180            if self.end_arrow_item:
181                self.scene().removeItem(self.end_arrow_item)
182                self.end_arrow_item = None
183
184        if self.arrows & AxisStart:
185            if not zoom_only or not self.start_arrow_item:
186                self.start_arrow_item = QGraphicsPathItem(self.arrow_path, self)
187            self.start_arrow_item.setPos(self.graph_line.p1())
188            self.start_arrow_item.setRotation(-self.graph_line.angle() + 180)
189            self.start_arrow_item.setBrush(line_color)
190            self.start_arrow_item.setPen(line_color)
191        if self.arrows & AxisEnd:
192            if not zoom_only or not self.end_arrow_item:
193                self.end_arrow_item = QGraphicsPathItem(self.arrow_path, self)
194            self.end_arrow_item.setPos(self.graph_line.p2())
195            self.end_arrow_item.setRotation(-self.graph_line.angle())
196            self.end_arrow_item.setBrush(line_color)
197            self.end_arrow_item.setPen(line_color)
198           
199        ## Labels
200               
201        n = len(self._ticks)
202        resize_plot_item_list(self.label_items, n, QGraphicsTextItem, self)
203        resize_plot_item_list(self.label_bg_items, n, QGraphicsRectItem, self)
204        resize_plot_item_list(self.tick_items, n, QGraphicsLineItem, self)
205       
206        test_rect = QRectF(self.graph_line.p1(),  self.graph_line.p2()).normalized()
207        test_rect.adjust(-1, -1, 1, 1)
208       
209        n_v = self.graph_line.normalVector().unitVector()
210        if self.title_above:
211            n_p = n_v.p2() - n_v.p1()
212        else:
213            n_p = n_v.p1() - n_v.p2()
214        l_v = self.graph_line.unitVector()
215        l_p = l_v.p2() - l_v.p1()
216        for i in range(n):
217            pos, text, size, step = self._ticks[i]
218            hs = 0.5 * step
219            tick_pos = self.map_to_graph( pos )
220            if not test_rect.contains(tick_pos):
221                self.tick_items[i].setVisible(False)
222                self.label_items[i].setVisible(False)
223                continue
224            item = self.label_items[i]
225            item.setVisible(True)
226            if not zoom_only:
227                if self.id in XAxes or getattr(self, 'is_horizontal', False):
228                    item.setHtml( '<center>' + Qt.escape(text.strip()) + '</center>')
229                else:
230                    item.setHtml(Qt.escape(text.strip()))
231           
232            item.setTextWidth(-1)
233            text_angle = 0
234            if dense_text:
235                w = min(item.boundingRect().width(), self.max_text_width)
236                item.setTextWidth(w)
237                if self.title_above:
238                    label_pos = tick_pos + n_p * (w + self.text_margin) + l_p * item.boundingRect().height()/2
239                else:
240                    label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect().height()/2
241                text_angle = -90 if self.title_above else 90
242            else:
243                w = min(item.boundingRect().width(), QLineF(self.map_to_graph(pos - hs), self.map_to_graph(pos + hs) ).length())
244                label_pos = tick_pos + n_p * self.text_margin - l_p * w/2
245                item.setTextWidth(w)
246               
247            if not self.always_horizontal_text:
248                if self.title_above:
249                    item.setRotation(-self.graph_line.angle() - text_angle)
250                else:
251                    item.setRotation(self.graph_line.angle() - text_angle)
252
253            item.setPos(label_pos)
254            item.setDefaultTextColor(text_color)
255           
256            self.label_bg_items[i].setRect(item.boundingRect())
257            self.label_bg_items[i].setPen(QPen(Qt.NoPen))
258            self.label_bg_items[i].setBrush(self.plot.color(OWPalette.Canvas))
259           
260            item = self.tick_items[i]
261            item.setVisible(True)
262            tick_line = QLineF(v)
263            tick_line.translate(-tick_line.p1())
264            tick_line.setLength(size)
265            if self.title_above:
266                tick_line.setAngle(tick_line.angle() + 180)
267            item.setLine( tick_line )
268            item.setPen(line_color)
269            item.setPos(self.map_to_graph(pos))
270           
271    @staticmethod
272    def make_title(label, unit = None):
273        lab = '<i>' + label + '</i>'
274        if unit:
275            lab = lab + ' [' + unit + ']'
276        return lab
277       
278    def set_line(self, line):
279        self.graph_line = line
280        self.update()
281   
282    def set_title(self, title):
283        self.title = title
284        self.update()
285       
286    def set_show_title(self, b):
287        self.show_title = b
288        self.update()
289       
290    def set_labels(self, labels):
291        self.labels = labels
292        self.graph_line = None
293        self.auto_scale = False
294        self.update_ticks()
295        self.update_graph()
296       
297    def set_scale(self, min, max, step_size):
298        self.scale = (min, max, step_size)
299        self.graph_line = None
300        self.auto_scale = False
301        self.update_ticks()
302        self.update_graph()
303   
304    def set_tick_length(self, minor, medium, major):
305        self.tick_length = (minor, medium, major)
306        self.update()
307       
308    def map_to_graph(self, x):
309        min, max = self.plot.bounds_for_axis(self.id)
310        if min == max:
311            return QPointF()
312        line_point = self.graph_line.pointAt( (x-min)/(max-min) )
313        end_point = line_point * self.zoom_transform
314        return self.projection(end_point, self.graph_line)
315       
316    @staticmethod
317    def projection(point, line):
318        norm = line.normalVector()
319        norm.translate(point - norm.p1())
320        p = QPointF()
321        type = line.intersect(norm, p)
322        return p
323       
324    def continuous_labels(self):
325        min, max, step = self.scale
326        magnitude = log10(abs(max-min))
327       
328    def paint(self, painter, option, widget):
329        pass
330   
331    def boundingRect(self):
332        return QRectF()
333       
334    def ticks(self):
335        if not self._ticks:
336            self.update_ticks()
337        return self._ticks
338       
339    def bounds(self):
340        if self.labels:
341            return -0.2, len(self.labels) -0.8
342        elif self.scale:
343            min, max, _step = self.scale
344            return min, max
345        elif self.auto_range:
346            return self.auto_range
347        else:
348            return 0, 1
349           
350    def should_be_expanded(self):
351        self.update_ticks()
352        return self.id in YAxes or self.always_horizontal_text or sum(len(t[1]) for t in self._ticks) * 12 > self.plot.width()
353
Note: See TracBrowser for help on using the repository browser.