source: orange/docs/extend-widgets/rst/plots.rst @ 11049:f4dd8dbc57bb

Revision 11049:f4dd8dbc57bb, 6.7 KB checked in by Miha Stajdohar <miha.stajdohar@…>, 16 months ago (diff)

From HTML to Sphinx.

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