source: orange/orange/doc/extend-widgets/plots.htm @ 9398:a6b3d9c13ee0

Revision 9398:a6b3d9c13ee0, 7.1 KB checked in by mitar, 2 years ago (diff)

Renaming documentation for widgets developers.

Line 
1<html>
2<head>
3<title>Orange Widgets: Plots</title>
4<link rel=stylesheet HREF="../style.css" type="text/css">
5<link rel=stylesheet href="style-print.css" type="text/css" media=print>
6</head>
7<body>
8
9<H1>Graphing and Orange Widgets</H1>
10 
11<p>The most fun widgets are of course those that include graphics. For
12this we can use the classes for
13drawing data plots as provided in Qwt library and PyQwt
14interface. This method is described in <a href="graphics.htm">Graphing</a>. </p>
15
16<p>However, Orange provides a new plotting interface via the <code>OrangeWidgets.plot</code>
17module. The OWPlot class use Qt's graphics framework and was written specifically for
18Orange, so it contains methods more suitable to its data structures. It also provides
19most methods also found in the Qwt-based OWGraph, so switching from one base
20to another is quite easy. </p>
21
22<p>Creating a new plot using OWPlot is described in <a href="plots-basic.htm">Plots with OWPlot</a>.
23This topic tries to show the similarities between the plot module and the old Qwt-based graph. </p>
24
25<h2>Plots</h2>
26
27<p>Let us construct a widget with a following appearance:</p>
28
29<img src="learningcurve-owplot.png">
30
31<p>There are two new elements from our previous incarnation of
32a learning curve widget: a control with a list of classifiers, and a
33graph with a plot of learning curves. From a list of classifiers we
34can select those to be displayed in the plot.</p>
35
36<p>The widget still provides learning curve table, but this is now
37offered in a tabbed pane together with a graph. The code for
38definition of the tabbed pane, and initialization of the graph is:</p>
39
40<xmp class="code"># start of content (right) area
41tabs = OWGUI.tabWidget(self.mainArea)
42
43# graph widget
44tab = OWGUI.createTabPage(tabs, "Graph")
45self.graph = OWPlot(tab)
46self.graph.setAxisAutoScale(QwtPlot.xBottom)
47self.graph.setAxisAutoScale(QwtPlot.yLeft)
48tab.layout().addWidget(self.graph)
49self.setGraphGrid()
50</xmp>
51
52<p><code>OWPlot</code> is a convenience subclass of QGraphicsView and is imported from plot.owplot module.
53For the graph, we use <code>set_axis_autoscale</code> to
54request that the axis are automatically set in regard to the data that
55is plotted in the graph. We plot the graph in using the following
56code:</p>
57
58<xmp class="code">def drawLearningCurve(self, learner):
59    if not self.data: return
60    curve = self.graph.add_curve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True)
61   
62    learner.curve = curve
63    self.setGraphStyle(learner)
64    self.graph.replot()
65</xmp>
66
67<p>This is simple. We store the curve returned from <code>add_curve</code> with a
68learner, and use a trick allowed in Orange that we can simply store
69this as a new attribute to the learning object. In this way, each learner
70also stores the current scores, which is a list of numbers to be
71plotted in the graph. The details on how the plot is set are dealt
72with in <code>setGraphStyle</code> function:</code></p>
73
74<xmp class="code">def setGraphStyle(self, learner):
75    curve = learner.curve
76    if self.graphDrawLines:
77        curve.set_style(OWCurve.LinesPoints)
78    else:
79        curve.set_style(OWCurve.Points)
80    curve.set_symbol(OWPoint.Ellipse)
81    curve.set_point_size(self.graphPointSize)
82    curve.set_color(self.graph.color(OWPalette.Data))
83    curve.set_pen(QPen(learner.color, 5))
84</xmp>
85
86<p>Notice that the color of the plot line that is specific to the
87learner is stored in its attribute <code>color</code>
88(<code>learner.color</code>). Who sets it and how? This we discuss in
89the following subsection.</p>
90
91<h2>Colors in Orange Widgets</h2>
92
93<p>Uniform assignment of colors across different widget is an
94important issue. When we plot the same data in different widgets, we
95expect that the color we used in a consistent way; for instance data
96instances of one class should be plotted in scatter plot and parallel
97axis plot using the same color. Developers are thus advised to use
98<code>ColorPaletteHSV</code>, which is provided as a method within
99<code>OWWidget</code> class. <code>ColorPaletteHSV</code> takes an
100integer as an attribute, and returns a list of corresponding number of
101colors. In our learning curve widget, we use it within a function that
102sets the list box with learners:</p>
103
104<xmp class="code">def updatellb(self):
105    self.blockSelectionChanges = 1
106    self.llb.clear()
107    colors = ColorPaletteHSV(len(self.learners))
108    for (i,lt) in enumerate(self.learners):
109        l = lt[1]
110        item = QListWidgetItem(ColorPixmap(colors[i]), l.name)
111        self.llb.addItem(item)
112        item.setSelected(l.isSelected)
113        l.color = colors[i]
114    self.blockSelectionChanges = 0
115</xmp>
116
117<p>The code above sets the items of the list box, where each item
118includes a learner and a small box in learner's color, which is in
119this widget also used as a sort of a legend for the graph. This box is
120returned by <code>ColorPixmap</code> function defined in
121<code>OWColorPalette.py</code>. Else, the classifier's list box control is
122defined in the initialization of the widget using:</p>
123
124<xmp class="code">self.cbox = OWGUI.widgetBox(self.controlArea, "Learners")
125self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", selectionMode=QListWidget.MultiSelection, callback=self.learnerSelectionChanged)
126
127self.llb.setMinimumHeight(50)
128self.blockSelectionChanges = 0
129</xmp>
130
131</p>Now, what is this <code>blockSelectionChanges</code>? Any time
132user makes a selection change in list box of classifiers, we want to
133invoke the procedure called
134<code>learnerSelectionChanged</code>. But we want to perform
135actions there when changes in the list box are invoked from clicking
136by a user, and not by changing list box items from a program. This is
137why, every time we want <code>learnerSelectionChanged</code> not to
138perform its function, we set <code>self.blockSelectionChanges</code>
139to 1.</p>
140
141<p>In our widget, <code>learnerSelectionChanged</code> figures out
142if any curve should be removed from the graph (the user has just
143deselected the corresponding item in the list box) or added to the
144graph (the user just selected a learner):</p>
145
146<xmp class="code">def learnerSelectionChanged(self):
147    if self.blockSelectionChanges: return
148    for (i,lt) in enumerate(self.learners):
149        l = lt[1]
150        if l.isSelected != (i in self.selectedLearners):
151            if l.isSelected: # learner was deselected
152                l.curve.detach()
153            else: # learner was selected
154                self.drawLearningCurve(l)
155            self.graph.replot()
156        l.isSelected = i in self.selectedLearners
157</xmp>
158
159<p>The complete code of this widget is available <a
160href="OWLearningCurve_plot.py">here</a>. This is almost like a typical
161widget that is include in a standard Orange distribution, with a
162typical size just under 300 lines. Just some final cosmetics is needed
163to make this widget a standard one, including setting some graph
164properties (like line and point sizes, grid line control, etc.) and
165saving the graph to an output file.</p>
166
167<h2>Further reading</h2>
168This tutorial only shows one kind of a plot.
169If you wish to use different and more complicated plots,
170refer to the <a href="../Orange/html/OrangeWidgets.plot.html">Plot module documentation</a>.
171
172</body>
173</html>
174
Note: See TracBrowser for help on using the repository browser.