06/13/13 18:12:28 (10 months ago)
Ales Erjavec <ales.erjavec@…>

Updated Widget development tutorial.

1 edited


  • docs/extend-widgets/rst/graphing.rst

    r11439 r11593  
    55The most fun widgets are of course those that include graphics. For 
    6 this we either use control called canvas, which is Qt's general 
    7 control for doing any graphics of choice (widgets for tree and heat map 
    8 visualizations, for instance, use this), or use a special control for 
    9 drawing data plots as provided in Qwt library and PyQwt 
     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` 
    109interface. Here we look at the latter, and extend our learning curve 
    1110widget with a control that plots the curve. 
    2625The widget still provides learning curve table, but this is now 
    2726offered in a tabbed pane together with a graph. The code for 
    28 definition of the tabbed pane, and initialization of the graph is:: 
     27definition of the tabbed pane, and initialization of the graph is 
    30     # start of content (right) area 
    31     tabs = OWGUI.tabWidget(self.mainArea) 
     29.. literalinclude:: OWLearningCurveC.py 
     30   :start-after: # ~SPHINX start main area tabs~ 
     31   :end-before: # ~SPHINX end main area tabs~ 
    33     # graph widget 
    34     tab = OWGUI.createTabPage(tabs, "Graph") 
    35     self.graph = OWGraph(tab) 
    36     self.graph.setAxisAutoScale(QwtPlot.xBottom) 
    37     self.graph.setAxisAutoScale(QwtPlot.yLeft) 
    38     tab.layout().addWidget(self.graph) 
    39     self.setGraphGrid() 
     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 
    41 :obj:`OWGrap` is a convenience subclass of QwtPlot and is imported from 
    42 OWGraph module. For the graph, we use :obj:`setAxisAutoScale` to 
    43 request that the axis are automatically set in regard to the data that 
    44 is plotted in the graph. We plot the graph in using the following 
    45 code:: 
    47     def drawLearningCurve(self, learner): 
    48         if not self.data: return 
    49         curve = self.graph.addCurve(learner.name, xData=self.curvePoints, yData=learner.score, autoScale=True) 
    51         learner.curve = curve 
    52         self.setGraphStyle(learner) 
    53         self.graph.replot() 
     39.. literalinclude:: OWLearningCurveC.py 
     40   :pyobject: OWLearningCurveC.drawLearningCurve 
    5542This is simple. We store the curve returned from :obj:`addCurve` with a 
    56 learner, and use a trick allowed in Orange that we can simply store 
    57 this as a new attribute to the learning object. By default, Orange 
    58 would give a warning of the type:: 
    60     c:\Python23\Lib\site-packages\orange\OrangeWidgets\Test\OWLearningCurveC.py:269: 
    61      AttributeWarning: 'curve' is not a builtin attribute of 'kNNLearner' 
    62       setattr(learner, "curve", curve) 
    64 but we surpress such warnings with a line:: 
    66     warnings.filterwarnings("ignore", ".*builtin attribute.*", orange.AttributeWarning) 
    6945.. warning:: 
    71    This is a very bad design. Please do **not** store widget data in the 
    72    input objects. 
     47   This is a very bad design. Please do **not** store widget specific 
     48   data in the input objects. 
    75 in the initialization part of the widget. In this way, each learner 
    76 also stores the current scores, which is a list of numbers to be 
    77 plotted in Qwt graph. The details on how the plot is set are dealt 
    78 with in :obj:`setGraphStyle` function:` :: 
     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: 
    80     def setGraphStyle(self, learner): 
    81         curve = learner.curve 
    82         if self.graphDrawLines: 
    83             curve.setStyle(QwtPlotCurve.Lines) 
    84         else: 
    85             curve.setStyle(QwtPlotCurve.NoCurve) 
    86         curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse, \ 
    87           QBrush(QColor(0,0,0)), QPen(QColor(0,0,0)), 
    88           QSize(self.graphPointSize, self.graphPointSize))) 
    89         curve.setPen(QPen(learner.color, 5)) 
     55.. literalinclude:: OWLearningCurveC.py 
     56   :pyobject: OWLearningCurveC.setGraphStyle 
    9159Notice that the color of the plot line that is specific to the 
    10371instances of one class should be plotted in scatter plot and parallel 
    10472axis plot using the same color. Developers are thus advised to use 
    105 :obj:`ColorPaletteHSV`, which is provided as a method within 
    106 :mod:`OWWidget` module. :obj:`ColorPaletteHSV` takes an 
    107 integer as an attribute, and returns a list of corresponding number of 
     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 
    10876colors. In our learning curve widget, we use it within a function that 
    109 sets the list box with learners:: 
     77sets the list box with learners 
    111     def updatellb(self): 
    112         self.blockSelectionChanges = 1 
    113         self.llb.clear() 
    114         colors = ColorPaletteHSV(len(self.learners)) 
    115         for (i,lt) in enumerate(self.learners): 
    116             l = lt[1] 
    117             item = QListWidgetItem(ColorPixmap(colors[i]), l.name) 
    118             self.llb.addItem(item) 
    119             item.setSelected(l.isSelected) 
    120             l.color = colors[i] 
    121         self.blockSelectionChanges = 0 
     79.. literalinclude:: OWLearningCurveC.py 
     80   :pyobject: OWLearningCurveC.updatellb 
    12382The code above sets the items of the list box, where each item 
    12685returned by :obj:`ColorPixmap` function defined in 
    12786:obj:`OWColorPalette.py`. Else, the classifier's list box control is 
    128 defined in the initialization of the widget using:: 
     87defined in the initialization of the widget using 
    130     self.cbox = OWGUI.widgetBox(self.controlArea, "Learners") 
    131     self.llb = OWGUI.listBox(self.cbox, self, "selectedLearners", 
    132                              selectionMode=QListWidget.MultiSelection, 
    133                              callback=self.learnerSelectionChanged) 
    135     self.llb.setMinimumHeight(50) 
    136     self.blockSelectionChanges = 0 
     89.. literalinclude:: OWLearningCurveC.py 
     90   :start-after: # ~SPHINX start color cb~ 
     91   :end-before: # ~SPHINX end color cb~ 
    13893Now, what is this :obj:`blockSelectionChanges`? Any time 
    13994user makes a selection change in list box of classifiers, we want to 
    14095invoke the procedure called 
    141 :obj:`learnerSelectionChanged`. But we want to perform 
     96:func:`learnerSelectionChanged`. But we want to perform 
    14297actions there when changes in the list box are invoked from clicking 
    14398by a user, and not by changing list box items from a program. This is 
    144 why, every time we want :obj:`learnerSelectionChanged` not to 
     99why, every time we want :func:`learnerSelectionChanged` not to 
    145100perform its function, we set :obj:`self.blockSelectionChanges` 
    146101to 1. 
    148 In our widget, :obj:`learnerSelectionChanged` figures out 
     103In our widget, :func:`learnerSelectionChanged` figures out 
    149104if any curve should be removed from the graph (the user has just 
    150105deselected the corresponding item in the list box) or added to the 
    151 graph (the user just selected a learner):: 
     106graph (the user just selected a learner) 
     108.. literalinclude:: OWLearningCurveC.py 
     109   :pyobject: OWLearningCurveC.learnerSelectionChanged 
    153112    def learnerSelectionChanged(self): 
    154113        if self.blockSelectionChanges: 
Note: See TracChangeset for help on using the changeset viewer.