source: orange/docs/extend-widgets/rst/graphing.rst @ 11593:6edc44eb9655

Revision 11593:6edc44eb9655, 5.1 KB checked in by Ales Erjavec <ales.erjavec@…>, 10 months ago (diff)

Updated Widget development tutorial.

RevLine 
[11049]1###########################
2Graphing and Orange Widgets
3###########################
4
5The most fun widgets are of course those that include graphics. For
[11593]6this we either use Qt's :class:`QGraphicsScene` (widgets for tree and
7heat map visualizations, for instance, use this), or use a special
8control for drawing data plots as provided in Qwt library and :mod:`PyQwt`
[11049]9interface. Here we look at the latter, and extend our learning curve
10widget with a control that plots the curve.
11
12*****
13Plots
14*****
15
16Let us construct a widget with a following appearance:
17
18.. image:: learningcurve-plot.png
19
20There are two new elements from our previous incarnation of
21a learning curve widget: a control with a list of classifiers, and a
22graph with a plot of learning curves. From a list of classifiers we
23can select those to be displayed in the plot.
24
25The widget still provides learning curve table, but this is now
26offered in a tabbed pane together with a graph. The code for
[11593]27definition of the tabbed pane, and initialization of the graph is
[11049]28
[11593]29.. literalinclude:: OWLearningCurveC.py
30   :start-after: # ~SPHINX start main area tabs~
31   :end-before: # ~SPHINX end main area tabs~
[11049]32
[11593]33:class:`~OWGraph.OWGrap` is a convenience subclass of :class:`QwtPlot`
34and is imported from OWGraph module. For the graph, we use
35:func:`setAxisAutoScale` to request that the axis are automatically
36set in regard to the data that is plotted in the graph. We plot
37the graph in using the following code
[11049]38
[11593]39.. literalinclude:: OWLearningCurveC.py
40   :pyobject: OWLearningCurveC.drawLearningCurve
[11049]41
42This is simple. We store the curve returned from :obj:`addCurve` with a
[11593]43learner.
[11439]44
45.. warning::
46
[11593]47   This is a very bad design. Please do **not** store widget specific
48   data in the input objects.
[11439]49
50
[11593]51In this way, each learner also stores the current scores, which is a
52list of numbers to be plotted in Qwt graph. The details on how the
53plot is set are dealt with in :obj:`setGraphStyle` function:
[11049]54
[11593]55.. literalinclude:: OWLearningCurveC.py
56   :pyobject: OWLearningCurveC.setGraphStyle
57
[11049]58
59Notice that the color of the plot line that is specific to the
60learner is stored in its attribute :obj:`color`
61(:obj:`learner.color`). Who sets it and how? This we discuss in
62the following subsection.
63
64************************
65Colors in Orange Widgets
66************************
67
68Uniform assignment of colors across different widget is an
69important issue. When we plot the same data in different widgets, we
70expect that the color we used in a consistent way; for instance data
71instances of one class should be plotted in scatter plot and parallel
72axis plot using the same color. Developers are thus advised to use
[11593]73:obj:`ColorPaletteHSV`, which can be imported from :mod:`OWWidget`
74module. :obj:`ColorPaletteHSV` takes an
75integer as an parameter, and returns a list of corresponding number of
[11049]76colors. In our learning curve widget, we use it within a function that
[11593]77sets the list box with learners
[11049]78
[11593]79.. literalinclude:: OWLearningCurveC.py
80   :pyobject: OWLearningCurveC.updatellb
[11049]81
82The code above sets the items of the list box, where each item
83includes a learner and a small box in learner's color, which is in
84this widget also used as a sort of a legend for the graph. This box is
85returned by :obj:`ColorPixmap` function defined in
86:obj:`OWColorPalette.py`. Else, the classifier's list box control is
[11593]87defined in the initialization of the widget using
[11049]88
[11593]89.. literalinclude:: OWLearningCurveC.py
90   :start-after: # ~SPHINX start color cb~
91   :end-before: # ~SPHINX end color cb~
[11049]92
93Now, what is this :obj:`blockSelectionChanges`? Any time
94user makes a selection change in list box of classifiers, we want to
95invoke the procedure called
[11593]96:func:`learnerSelectionChanged`. But we want to perform
[11049]97actions there when changes in the list box are invoked from clicking
98by a user, and not by changing list box items from a program. This is
[11593]99why, every time we want :func:`learnerSelectionChanged` not to
[11049]100perform its function, we set :obj:`self.blockSelectionChanges`
101to 1.
102
[11593]103In our widget, :func:`learnerSelectionChanged` figures out
[11049]104if any curve should be removed from the graph (the user has just
105deselected the corresponding item in the list box) or added to the
[11593]106graph (the user just selected a learner)
[11049]107
[11593]108.. literalinclude:: OWLearningCurveC.py
109   :pyobject: OWLearningCurveC.learnerSelectionChanged
110
111..
[11049]112    def learnerSelectionChanged(self):
[11408]113        if self.blockSelectionChanges:
114            return
[11049]115        for (i,lt) in enumerate(self.learners):
116            l = lt[1]
117            if l.isSelected != (i in self.selectedLearners):
118                if l.isSelected: # learner was deselected
119                    l.curve.detach()
120                else: # learner was selected
121                    self.drawLearningCurve(l)
122                self.graph.replot()
123            l.isSelected = i in self.selectedLearners
124
[11439]125The complete code of this widget is available :download:`here <OWLearningCurveC.py>`.
[11049]126This is almost like a typical
127widget that is include in a standard Orange distribution, with a
128typical size just under 300 lines. Just some final cosmetics is needed
129to make this widget a standard one, including setting some graph
130properties (like line and point sizes, grid line control, etc.) and
131saving the graph to an output file.
Note: See TracBrowser for help on using the repository browser.