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

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

Renaming documentation for widgets developers.

Line 
1<html>
2<head>
3<title>Orange Widgets: Settings and Controls</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>Settings and Controls</H1>
10
11<p>In the <a href="basics.htm">previous section</a> of our tutorial we
12have just built a simple sampling widget. Let us now make this widget
13a bit more useful, by allowing a user to set the proportion of data
14instances to be retained in the sample. Say we want to design a widget
15that looks something like this:</p>
16
17<img src="dataSamplerBWidget.png">
18
19<p>What we added is an Options box, with a spin entry box to set the
20sample size, and a check box and button to commit (send out) any
21change we made in setting. If the check box with "Commit data on
22selection change" is checked, than any change in the sample size will
23make the widget send out the sampled data set. If data sets are large
24(say of several thousands or more) instances, we may want to send out
25the sample data only after we are done setting the sample size, hence
26we left the commit check box unchecked and press "Commit" when we are
27ready for it.</p>
28
29<p>This is a very simple interface, but there is something more to
30it. We want the settings (the sample size and the state of the commit
31button) to be saved. That is, any setting we made, after closing our
32widget (or after going out of Orange application that includes this
33widget, or after closing Orange Canvas), we want to save so that the
34next time we open the widget the settings is there as we have left
35it. There is some complication to it, as widget can be part of an
36application, or part of some schema in the Canvas, and we would like
37to have the settings application- or schema-specific.</p>
38
39<h2>Widgets Settings</h2>
40
41<p>Luckily, since we use the base class <code>OWWidget</code>, the settings
42will be handled just fine. We only need to tell which variables we
43will use for the settings. For Python inspired readers: these
44variables can store any complex object, as long as it is
45picklable. In our widget, we will use two settings variables, and we
46declare this just after the widget class definition.</p>
47
48<xmp class="code">class OWDataSamplerB(OWWidget):
49    settingsList = ['proportion', 'commitOnChange']
50    def __init__(self, parent=None, signalManager=None):
51...
52</xmp>
53
54<p>Any setting has to be initialized, and then we need to call
55<code>loadSettings()</code> to override defaults in case we have used
56the widget before and the settings have been saved:</p>
57
58<xmp class="code">        self.proportion = 50
59        self.commitOnChange = 0
60        self.loadSettings()
61</xmp>
62
63<p>Now anything we do with the two variables (<code>self.proportion</code> and
64<code>self.commitOnChange</code>) will be saved upon exiting our
65widget. In our widget, we won't be setting these variables directly,
66but will instead use them in conjunction with GUI controls.</p>
67
68<h2>Controls and OWGUI</h2>
69
70<p>Now we could tell you how to put different Qt controls on the
71widgets and write callback functions that set our settings
72appropriately. This is what we have done before we got bored with it,
73since the GUI part spanned over much of the widget's code. Instead, we
74wrote a library called OWGUI (I never liked the name, but could never
75come up with something better). With this library, the GUI definition
76part of the options box is a bit dense but rather very short:</p>
77
78<xmp class="code">        box = OWGUI.widgetBox(self.controlArea, "Info")
79        self.infoa = OWGUI.widgetLabel(box, 'No data on input yet, waiting to get something.')
80        self.infob = OWGUI.widgetLabel(box, '')
81
82        OWGUI.separator(self.controlArea)
83        self.optionsBox = OWGUI.widgetBox(self.controlArea, "Options")
84        OWGUI.spin(self.optionsBox, self, 'proportion', min=10, max=90, step=10,
85                   label='Sample Size [%]:', callback=[self.selection, self.checkCommit])
86        OWGUI.checkBox(self.optionsBox, self, 'commitOnChange', 'Commit data on selection change')
87        OWGUI.button(self.optionsBox, self, "Commit", callback=self.commit)
88        self.optionsBox.setDisabled(1)
89</xmp>
90
91<p>We are already familiar with the first part - the Info group
92box. To make widget nicer, we put a separator between this and Options
93box. After defining the option box, here is our first serious OWGUI
94control. Called a <code>spin</code>, we give it place where it is
95drawn (<code>self.optionsBox</code>), and we give it the widget object
96(<code>self</code>) so that it knows where the settings and some other
97variables of our widget are.</p>
98
99<p>Next, we tell the spin box to be
100associated with a variable called <code>proportion</code>. This simply
101means that any change in the value the spin box holds will be directly
102translated to a change of the variable
103<code>self.proportion</code>. No need for a callback! But there's
104more: any change in variable <code>self.proportion</code> will be
105reflected in the look of this GUI control. Say if there would be a
106line <code>self.proportion = 70</code> in your code, our spin box
107control would get updated as well. (I must admit I do not know if you
108appreciate this feature, but trust me, it may really help prototyping
109widgets with some more complex GUI.</p>
110
111<p>The rest of the OWGUI spin box call gives some parameters for the
112control (minimum and maximum value and the step size), tells about the
113label which will be placed on the top, and tells it which functions to
114call when the value in the spin box is changed. We need the first
115callback to make a data sample and report in the Info box what is the
116size of the sample, and a second callback to check if we can send this
117data out. In OWGUI, callbacks are either references to functions, or a
118list with references, just like in our case.</p>
119
120<p>With all of the above, the parameters for the call of
121<code>OWGUI.checkBox</code> should be clear as well. Notice that this
122and a call to <code>OWGUI.spin</code> do not need a parameter which
123would tell the control the value for initialization: upon construction,
124both controls will be set to the value that is pertained in the
125associated setting variable.</p>
126
127<p>That's it. Notice though that we have, as a default, disabled all
128the controls in the Options box. This is because at the start of the
129widget, there is no data to sample from. But this also means that when
130process the input tokens, we should take care for enabling and
131disabling. The data processing and token sending part of our widget
132now is:</p>
133
134<xmp class="code">    def data(self, dataset):
135        if dataset:
136            self.dataset = dataset
137            self.infoa.setText('%d instances in input data set' % len(dataset))
138            self.optionsBox.setDisabled(0)
139            self.selection()
140            self.commit()
141        else:
142            self.send("Sampled Data", None)
143            self.optionsBox.setDisabled(1)
144            self.infoa.setText('No data on input yet, waiting to get something.')
145            self.infob.setText('')
146
147    def selection(self):
148        indices = orange.MakeRandomIndices2(p0=self.proportion / 100.)
149        ind = indices(self.dataset)
150        self.sample = self.dataset.select(ind, 0)
151        self.infob.setText('%d sampled instances' % len(self.sample))
152
153    def commit(self):
154        self.send("Sampled Data", self.sample)
155
156    def checkCommit(self):
157        if self.commitOnChange:
158            self.commit()
159</xmp>
160
161<p>You can now also inspect the <a href="OWDataSamplerB.py">complete code</a> of this
162widget. To distinguish it with a widget we have developed in the
163previous section, we have designed a special <a
164href="DataSamplerB.png">icon</a> for it. If you wish to test is
165widget in the Orange Canvas, put its code in the Test directory we
166have created for the previous widget, update the Canvas registry, and
167try it out using a schema with a File and Data Table widget.</p>
168
169<img src="schemawithdatasamplerB.png">
170
171</body>
172</html>
Note: See TracBrowser for help on using the repository browser.