source: orange/Orange/doc/extend-widgets/owgui.htm @ 9671:a7b056375472

Revision 9671:a7b056375472, 37.4 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Moved orange to Orange (part 2)

Line 
1<html>
2<head>
3<title>Orange Widgets: OWGUI</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
7
8<script type="text/javascript">
9function toggleVisibility(obj)
10{
11   var exampleStyle = obj.parentNode.getElementsByTagName('div')[0].style;
12   var buttonText = obj.firstChild;
13
14   if ((exampleStyle.visibility == "hidden") || (exampleStyle.visibility=="")) {
15     exampleStyle.visibility = "visible";
16     exampleStyle.height = "auto";
17     buttonText.data = "Hide Example";
18     if (navigator.appName == "Microsoft Internet Explorer")
19       exampleStyle.position = "fixed";
20   }
21   else {
22     exampleStyle.visibility = "hidden";
23     exampleStyle.height = "1%";
24     buttonText.data = "Show Example";
25     if (navigator.appName == "Microsoft Internet Explorer")
26       exampleStyle.position = "absolute";
27   }
28}
29
30function onLoad()
31{
32   if (navigator.appName == "Microsoft Internet Explorer") {
33       try {
34            for(var x = 0, ss = document.styleSheets; ss[x]; x++) {
35                for (var y=0, rr = ss[x].rules; rr[y]; y++) {
36                    if (rr[y].selectorText == ".example") {
37                        rr[y].style.position = "absolute";
38                        rr[y].style.marginTop = "0px";
39                    }
40                    if (rr[y].selectorText == ".hideShow")
41                        rr[y].style.marginBottom = "0px";
42                }
43            }
44       }
45       catch (e) {}
46   }
47}
48
49</script>
50
51<style>
52P.function { font-family: monospace; font-weight: bold; font-size: 8pt; margin-top: 1em }
53
54xmp.code { font-size: 8pt; background-color: white; }
55
56.example { visibility: hidden; height: 1%; border: 1px solid #000000; padding-left: 6px; margin-top: -7px; padding-top: 0px; background-color: #f8f8f8; }
57
58.hideshow { border: 1px dotted #000000; padding: 2px; margin-bottom: -7px; background-color: #eeeeee; margin-right: 6px; }
59
60/*.example { visibility: hidden; height: 1%; border: 1px solid #000000; padding-left: 6px; margin-top: -5px; padding-top: 0px; background-color: #f8f8f8; z-index: -1;}
61
62.hideshow { border: 1px dotted #000000; padding: 2px; margin-bottom: -5px; background-color: #eeeeee; margin-right: 6px; z-index: 1;}
63*/
64
65.header {margin: 0px; padding: 0px;  }
66
67.function { background-color: #eeeeee; padding: 2px; }
68
69H1 { margin-top: 48px; font-size: 12pt; background-color: white; color: black; }
70H2 { margin-top: 36px; font-size: 10pt; background-color: white; font-weight: bold; color: blue; }
71
72</style>
73
74</head>
75<body onLoad="onLoad();">
76
77
78<h1>OWGUI: Library of Common GUI Controls</h1>
79
80<p>Orange Widgets wrap Orange's classes in an easy to use interactive graphical interface. As such, much of their code is about the interface, event control and maintaining the state of the GUI controls.</p>
81
82<p>In the spirit of constructive laziness, we wrote a library using which a single line of code can construct a check box, line edit or a combo, make it being synchronized with a Python object's attribute (which, by the way, gets automatically saved and retrieved when the widgets is closed and reopened), attaches a callback function to the control, make it disable or enable other controls...</p>
83
84<hr>
85
86<h1>Common Attributes</h1>
87
88<P>Many functions that construct controls share one or more common arguments, usually in the same order. These are described here. Descriptions of individual controls will only list their specific arguments, while the arguments which are presented here will only be described in cases where they have a different meaning.</P>
89
90<DL class="attributes">
91<DT>widget (required)</DT>
92<DD>Widget on which control will be drawn - can be widget's <code>controlArea</code> or another box.</DD>
93
94<DT>master (required)</DT>
95<DD>Object which includes an attribute that are used to store control's
96state, most often the object which called the function that
97initialized the control.</DD>
98
99<DT>value (required)</DT>
100<DD>String with the name of the master's attribute that synchronizes with the
101state of the control (and vice-versa - when this attribute is changed, the control changes as well). This attribute should usually be also included the master's <code>settingsList</code>, so that it is automatically saved and retrieved.</DD>
102
103<DT>box (default: None)</DT>
104<DD>Indicates if there should be a box that is drawn around the control. If <code>box</code> is <code>None</code>, no box is drawn; if it is a string, it is also used as box's name. If <code>box</code> is any other true value (such as <code>True</code> :), an unlabeled box is drawn.</DD>
105
106<DT>callback (default: None)</DT>
107<DD>A function to be called when the state of the control is changed. Can include a single function, or a list of functions that will be called in the order provided. If callback function changes the value of the controlled attribute (the one given as the <code>value</code> argument described above) it may trigger a cycle; a simple trick to avoid this is shown in the description of <a href="#listBox">listBox function</a>.</DD>
108
109<DT>tooltip (default: None)</DT>
110<DD>A string that is displayed in a tooltip that appears when mouse is over the control.</DD>
111
112<DT>label (default: None)</DT>
113<DD>A string that is displayed as control's label.</DD>
114
115<DT>labelWidth (default: None)</DT>
116<DD>Sets the label's width. This is useful for aligning the controls.</DD>
117
118<DT>orientation (default: "vertical")</DT>
119<DD>When label is used, determines the relative placement of the label and the control. Label can be above the control, "vertical", or in the same line with control, "horizontal". Instead of "vertical" and "horizontal" you can also use <code>True</code> and <code>False</code> or 1 and 0, respectively. (Remember this as "vertical" being the usual order of controls in the widgets, so vertical is "true".)</DD>
120
121<DT>disabled (default: False)</DT>
122<DD>Tells whether the control be disabled upon the initialization.</DD>
123
124<dt>addSpace (default: False)</dt>
125<dd>If true, a space of 8 pixels is added after the widget by calling <code>OWGUI.separator</code>. <code>addSpace</code> can also be an integer specifying the height of the added space.</dd>
126</DL>
127
128
129<h1>Controls</h1>
130
131<P>This section describes the OWGUI wrappers for controls like check boxes, buttons and similar. All the important Qt's controls can be constructed through this functions. You should always use them instead of calling Qt directly, not only because they are convenient, but also because they set up a lot of things that happen in behind.</P>
132
133<h2>Check Box</h2>
134
135<P>Check box, a wrapper around QCheckBox, adding a label, box, tooltip, callback and synchronization with the designated widget's attribute.</P>
136
137<p class="function">
138checkBox(widget, master, value, label[, box, tooltip, callback, disabled, labelWidth, disables])
139</p>
140<dl class="attributes">
141
142<DT>disables (default: [])</DT>
143<DD>If the check box needs to disable some other controls they can be given in list  <code>disables</code>, e.g. <code>disables=[someOtherCheckBox, someLineEdit]</code>. If the other control should be disabled when the checkbox is checked, do it like this: <code>disables=[someOtherCheckBox, (-1, someLineEdit)]</code> - now <code>someOtherCheckBox</code> will be enabled when this check box is checked, while <code>someLineEdit</code> will be enabled when the check box is unchecked.</DD>
144
145<dt>labelWidth (default: None)</dt>
146<dd><code>labelWidth</code> can be used to align this widget with others.</dd>
147</dl>
148
149<span>
150<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
151<span class="hideshow"><a href="gui_check.py">Download example (gui_check.py)</a></span>
152<div class="example">
153
154<p>In the following code we define two check boxes. The second one
155disables (enables) the box with the spin box, depending on the state
156of the checkbox.</p>
157
158<table><tr>
159<td width="600px">
160<xmp class=code>self.spinval = 10
161self.chkA = 1
162self.chkB = 0
163self.dx = 15
164
165box = OWGUI.widgetBox(self.controlArea, "Settings")
166gridbox = OWGUI.widgetBox(self.controlArea, "Grid Opions")
167gridbox.setEnabled(self.chkB)
168OWGUI.checkBox(box, self, "chkA", "Verbose")
169OWGUI.checkBox(box, self, "chkB", "Display Grid", disables=[gridbox])
170OWGUI.spin(gridbox, self, "dx", 10, 20)
171</xmp>
172</td>
173<td valign="top"><img src="gui_check.png"></td>
174</tr></table>
175</div></span>
176
177<a name="lineedit"></a>
178<h2>Line Edit</h2>
179
180<P>Edit box, a wrapper around QLineEdit.</P>
181
182<P class="function">lineEdit(widget, master, value[, label, labelWidth, orientation, box, tooltip, callback, valueType, validator, controlWidth])</P>
183
184<dl class="attributes">
185<dt>valueType (default: str)</dt>
186<dd>A type into which the value is cast.</dd>
187
188<dt>validator (default: None)</DT>
189<DD>A standard Qt validator that can be associated with the control.</DD>
190</dl>
191
192<span><span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
193<span class="hideshow"><a href="gui_lineedit.py">Download example (gui_lineedit.py)</a></span>
194<div class="example">
195<table><tr valign="top">
196<td width="600px">
197<xmp class=code>self.val1 = "Enter text ..."
198self.val2 = "Some more text ..."
199self.valF = 10.2
200
201OWGUI.lineEdit(self.controlArea, self, "val1", box="Text Entry")
202box = OWGUI.widgetBox(self.controlArea, "Options (with lineEdit)")
203OWGUI.lineEdit(box, self, "val2",
204               label="Name:", orientation="horizontal", labelWidth=40)
205OWGUI.lineEdit(box, self, "valF", label="Float:",
206               orientation="horizontal", labelWidth=40, valueType=float)
207</xmp>
208</td>
209<td valign="top"><img src="gui_lineedit.png"></td></tr></table>
210</div></span>
211
212<h2>Button</h2>
213
214<P>A wrapper around QPushButton, just to be able to define a button
215and its callback in a single line.</p>
216
217<p class="function">button(widget, master, label[, callback, disabled, tooltip])</p>
218
219
220<h2>Radio Buttons</h2>
221
222<P>OWGUI can create an individual radio button or a box of radio buttons or an individual radio button.</P>
223
224<P>An individual radio button is created by <code>radioButton</code>.</P>
225
226<p class="function">radioButton(widget, master, value, label[, box, tooltip, callback, addSpace])</p>
227
228<P>The function provides the usual capabilities of OWGUI controls. It is though your responsibility to put it in something like a <code>QVButtonGroup</code>.</P>
229
230<P>A box of radio buttons is created by function <code>radioButtonsInBox</code>.</P>
231
232<p class="function">
233radioButtonsInBox(widget, master, value, btnLabels[, box, tooltips, callback)</p>
234
235<dl class="attributes">
236<dt>value (required)</dt>
237<dd>Synchronized with the index of the selected radio button.</dd>
238
239<dt>btnLabels (required)</dt>
240<dd>A list with labels for radio buttons. Labels can be strings or pixmaps.</dd>
241
242<dt>tooltips (default: None)</dt>
243<dd>A list of tooltips, one for each button.</dd>
244</dl>
245
246<span>
247<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
248<span class="hideshow"><a href="gui_radiobuttons.py">Download example (gui_radiobuttons.py)</a></span>
249<div class="example">
250<table><tr valign="top">
251<td width="600px">
252<xmp class=code>self.method = 0
253
254OWGUI.radioButtonsInBox(self.controlArea, self, "method",
255              box = "Probability estimation",
256              btnLabels = ["Relative", "Laplace", "m-estimate"],
257              tooltips = ["Relative frequency of the event",
258                          "Laplace-corrected estimate",
259                          "M-estimate of probability"])
260</xmp></td>
261<td><img src="gui_radiobuttons.png"></td></tr></table>
262</div></span>
263
264
265<h2>Combo Box</h2>
266
267<P>A wrapper around QComboBox.</P>
268
269<p class="function">comboBox(widget, master, value[, box, label, labelWidth, orientation, items, tooltip, callback, sendSelectedValue, valueType, control2attributeDict, emptyString])</p>
270
271<dl class="attributes">
272<dt>items (default: [])</dt>
273<dd>A list of combo box's items. Unlike most OWGUI, <code>items</code> have one Orange-specific quirk: its element can be either a string, in which case it is used as a label, or a tuple, where the first element is a label name and the last is the attribute type which is used to create an icon. Most attribute lists in Orange Widgets are constructed this way.</dd>
274
275<dt>sendSelectedValue (default: 0)</dt>
276<dd>If false, attribute <code>value</code> will be assigned the index of the selected item. Otherwise, it is assigned the currently selected item's label.</dd>
277
278<dt>control2attributeDict (default: {})</dt>
279<dd>A dictionary for translating the item's label into <code>value</code>. It is used only is <code>sendSelectedValue</code> is true, and even then a label is translated only if an item with such a key is found in the dictionary; otherwise, label is written to <code>value</code> as it is. </dd>
280
281<dt>emptyString (default: "")</dt>
282<dd>Tells which combo box's item corresponds to an empty <code>value</code>. This is typically used when combo box's labels are attribute names and an item "(none)", which allows user to select no attribute. If we give <code>emptyString="(none)"</code>, <code>value</code> will be an empty string when the user selects "(none)". This is equivalent to specifying <code>control2attributeDict = {"(none)": ""}</code> (and is actually implemented like that), but far more convenient.</dd>
283
284<dt>valueType (default: str or unicode)</dt>
285<dd>A function through which the currently selected item's label is converted prior to looking into <code>control2attributeDict</code>. Needed to convert Qt's QString.</dd>
286</dl>
287
288
289<span>
290<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
291<span class="hideshow"><a href="gui_combobox.py">Download example (gui_combobox.py)</a></span>
292<div class="example">
293<P>We shall create a widget with two combo boxes: one will contain a simple choice of three colors, while the other will let the user select an attribute or no attribute (none).</P>
294
295<table><tr valign="top">
296<td width="600px">
297<xmp class=code>self.chosenColor = 1
298self.chosenAttribute = ""
299
300box = OWGUI.widgetBox(self.controlArea, "Color & Attribute")
301
302OWGUI.comboBox(box, self, "chosenColor", label="Color: ",
303               items=["Red", "Green", "Blue"])
304
305self.attrCombo = OWGUI.comboBox(box, self, "chosenAttribute", label="Attribute: ",
306               sendSelectedValue = 1, valueType=str)
307</xmp></td>
308<td><img src="gui_combobox.png"></td></tr></table>
309
310<P>The widget's snapshot on the right is a cheat: the above code leaves the bottom list box empty. To be able to fill it later on, when the widget gets some data, we stored the control (<code>self.attrCombo</code>).</P>
311
312<P>Say that the widget received an example table and stored it in <code>self.data</code>. This is how we fill out the combo.</P>
313
314<table><tr valign="top">
315<td width="600px">
316<xmp class=code>self.attrCombo.clear()
317self.attrCombo.insertItem("(none)")
318icons = OWGUI.getAttributeIcons()
319for attr in self.data.domain:
320    self.attrCombo.insertItem(icons[attr.varType], attr.name)
321
322self.chosenAttribute = self.data.domain[0].name
323</xmp></td>
324</tr></table>
325
326<P>The first item is "(none)". When the user selects it, the value of <code>chosenAttribute</code> is set to "", since we specified <code>emptyString="(none)"</code> when initializing the combo. Next we insert items using Qt QComboBox's function <code>insertItem</code>, where the first argument is a QPixmap (an icon for the corresponding attribute type) and the second is an attribute name. Finally, we set <code>self.chosenAttribute</code> (and with that the selection in the combo box) to the first attribute. (For simplicity, we here left out testing whether the data contains any attribute.)</P>
327
328</div>
329</span>
330
331<a name="listBox"></a>
332<h2>List Box</h2>
333
334<P>This control, which might be the most complicated control in OWGUI, is a sophisticated wrapper around QListBox. It's complexity arises from synchronization.</P>
335
336<p class="function">
337listBox(widget, master, value, labels[, box, tooltip, callback, selectionMode])</p>
338
339<dl class="attributes">
340<dt>value (required)</dt>
341<dd>The name of master's attribute containing indices of all selected values.</dd>
342
343<dt>labels (required)</dt>
344<dd>The name of master's attribute containing the list box's labels. Similar to <code>items</code> in combo box, list <code>labels</code> have one Orange-specific quirk: its element can be either a string, in which case it is used as a label, or a tuple, where the first element is a label name and the second can be either an icon on an integer, representing the attribute type which is used to create an icon. Most attribute lists in Orange Widgets are constructed this way.</dd>
345
346<dt>selectionMode (default: QListWidget.SingleSelection)</dt>
347<dd>Tells whether the user can select a single item (<code>QListWidget.SingleSelection</code>), multiple items (<code>QListWidget.MultiSelection</code>, <code>QListWidget.ExtendedSelection</code>) or nothing (<code>QListWidget.NoSelection</code>).</dd>
348</dl>
349
350<P><code>value</code> is automatically cast to <code>OWGUI.ControlledList</code> (this is needed because the list should report any changes to the control, the list box; <code>OWGUI.ControlledList</code> is like an ordinary Python <code>list</code> except that it triggers synchronization with the list box at every change).</P>
351
352<P><code>labels</code> is only partially synchronized with the list box: if a new list is assigning to <code>labels</code> attribute, the list will change. If elements of the existing list are changed or added, the list box won't budge. You should never change the list, but always assign a new list (or reassign the same after it's changed). If the labels are stored in <code>self.listLabels</code> and you write <code>self.listLabels[1]="a new label"</code>, the list box won't change. To trigger the synchronization, you should continue by <code>self.listLabels = self.listLabels</code>. This may seem awkward, but by our experience a list of selected items is seldom changed changed "per-item", so we were too lazy to write the annoyingly complex backward callbacks.</P>
353
354<span>
355<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
356<span class="hideshow"><a href="gui_listbox.py">Download example (gui_listbox.py)</a></span>
357<span class="hideshow"><a href="gui_listbox_attr.py">Download example (gui_listbox_attr.py)</a></span>
358
359<div class="example">
360<P>In first example, we will set up to list boxes, one with single and one with multiple selection. To see what is going on, we provide two labels that show the values of the corresponding attributes. Besides that, if all items in the second list box are chosen, the value of the first list box will change to red and the user will be prevented to change it from red until she deselects some of the items in the second list.</P>
361
362<table><tr valign="top">
363<td width="600px">
364<xmp class=code>self.colors = ["Red", "Green", "Blue"]
365self.chosenColor = [2]
366self.numbers = ["One", "Two", "Three", "Four"]
367self.chosenNumbers = [0, 2, 3]
368
369OWGUI.listBox(self.controlArea, self, "chosenColor", "colors",
370              box="Color", callback=self.checkAll)
371OWGUI.listBox(self.controlArea, self, "chosenNumbers", "numbers",
372              box="Number", selectionMode=QListWidget.MultiSelection, callback=self.checkAll)
373
374box = OWGUI.widgetBox(self.controlArea, "Debug info")
375OWGUI.label(box, self, "Color: %(chosenColor)s")
376OWGUI.label(box, self, "Numbers: %(chosenNumbers)s", labelWidth=100)
377</xmp>
378</td><td valign="top">
379<img src="gui_listbox.png">
380</td></tr></table>
381
382<P>Selecting the red color and not allowing any other color but red when the entire second list is selected is ensured by function <code>checkAll</code> which is used as a callback for both list box. An important point not to be overlooked here is <code>self.chosenColor != [0]</code> in condition here. If omitted, setting <code>self.chosenColor</code> would trigger a change in the list box selection, change in the list box selection causes the callback function <code>self.chosenColor</code> to be called again and ... you see where this leads.</P>
383
384<table><tr>
385<td width="600px"><xmp class="code">def checkAll(self):
386    if len(self.chosenNumbers) == len(self.numbers) and self.chosenColor != [0]:
387        self.chosenColor = [0]
388</xmp>
389</td></tr></table>
390
391
392<P>The second example shows a construction of an attribute list. If a discrete attribute is selected, the second list box will let the user select one or more of its values.</P>
393
394<table><tr valign="top">
395<td width="600px">
396<xmp class=code>self.attributes = []
397self.chosenAttribute = []
398self.values = []
399self.chosenValues = []
400
401OWGUI.listBox(self.controlArea, self, "chosenAttribute", "attributes",
402              box="Attributes", callback=self.setValues)
403OWGUI.separator(self.controlArea)
404OWGUI.listBox(self.controlArea, self, "chosenValues", "values",
405              box="Values", selectionMode=QListWidget.MultiSelection)
406
407
408# The following assignments usually don't take place in __init__
409# but later on, when the widget receives some data
410
411self.data = orange.ExampleTable(r"..\datasets\horse-colic.tab")
412self.attributes = [(attr.name, attr.varType) for attr in self.data.domain]
413self.chosenAttribute = [0]
414</xmp>
415</td><td valign="top">
416<img src="gui_listbox_attr.png">
417</td></tr></table>
418
419<P>We avoided the temptation of writing <code>self.attributes = self.chosenAttribute = self.values = self.chosenValues = []</code>. This would assign all attributes the <em>same</em> empty list and would, through <code>self.chosenAttributes</code> and <code>self.chosenValues</code> keep the selection in two lists synchronized (try it, it looks funny).</P>
420
421<P>The second list box is filled every time the selection in the first is changed. This is done through the callback <code>setValues</code>.</P>
422
423<table><tr valign="top">
424<td width="600px"><xmp class="code">def setValues(self):
425    attrIndex = self.chosenAttribute[0]
426    attr = self.data.domain[attrIndex]
427    if attr.varType == orange.VarTypes.Discrete:
428        self.values = attr.values
429    else:
430        self.values = []
431    self.chosenValues = []
432</xmp>
433</td></tr></table>
434
435<P>How does the list fill when the widget gets the data? In the above code, <code>self.chosenAttribute = [0]</code> changes the selection in the first lists and causes the callback, <code>setValues</code> to be called. That's how.</P>
436</div></span>
437
438<h2>Spin</h2>
439
440<P>Spin control, a wrapper around QSpinBox.</P>
441
442<p class="function">spin(widget, master, value, min, max[, step, box, label, labelWidth, orientation, tooltip, callback, controlWidth])</p>
443
444<DL class=attributes>
445<dt>min, max, step=1</dt>
446<dd>Minimal and maximal value, and step.</dd>
447</DL>
448
449<span>
450<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
451<span class="hideshow"><a href="gui_spin.py">Download example (gui_spin.py)</a></span>
452<div class="example">
453
454<P>Following is an excerpt of the code in the initialization part of
455the widget. It defines three spin boxes, where the second and third
456invoke the callback which sets the text in the info box.</p>
457
458<table><tr>
459<td width="600px">
460<xmp class=code>self.spinval = 10
461OWGUI.spin(self.controlArea, self, "spinval", 0, 100, box="Value A")
462box = OWGUI.widgetBox(self.controlArea, "Options")
463self.alpha = 30
464self.beta = 4
465OWGUI.spin(box, self, "alpha", 0, 100, label="Alpha:", labelWidth=60,
466           orientation="horizontal", callback=self.setInfo)
467OWGUI.spin(box, self, "beta", -10, 10, label="Beta:", labelWidth=60,
468           orientation="horizontal", callback=self.setInfo)
469
470box = OWGUI.widgetBox(self.controlArea, "Info")
471self.info = OWGUI.widgetLabel(box, "")
472self.setInfo()
473</xmp>
474</td><td valign="top">
475<img src="gui_spin.png">
476</td></tr></table>
477
478<p>The callback for setting the info box is simple. Notice that OWGUI
479associates each control's state with some value, which is updated
480automatically on any change of the control.</p>
481
482<table><tr>
483<td width="600px">
484<xmp class=code>def setInfo(self):
485    self.info.setText("Alpha=%d, Beta=%d" % (self.alpha, self.beta))
486</xmp>
487</td></tr></table>
488
489<P>By the way, such functions are needed only when updating the label requires more than simply inserting some attributes' values. This code becomes redundant if we use <code>OWGUI.label</code> instead of <code>QLabel</code>.</P>
490</div></span>
491
492<h2>Slider</h2>
493
494<P>A wrapper around QSlider that allows user setting a numerical value between the given bounds.</P>
495
496<p class="function">hSlider(widget, master, value[, box, minValue, maxValue, step, callback, labelFormat, ticks, divideFactor])</p>
497
498<dl class="attributes">
499<dt>minValue (default: 0), maxValue (default: 10), step (default: 1)</dt>
500<dd>Minimal and maximal value for the spin control, and its step.</dd>
501
502<dt>ticks (default: 0)</dt>
503<dd>If non-zero, it gives the interval between two ticks. The ticks will appear below the groove.</dd>
504
505<dt>labelFormat (default: " %d")</dt>
506<dd>Defines the look of the label on the righthand side of the slider. It has to contain one format character (like %d in the default), but can contain other text as well.</dd>
507
508<dt>divideFactor (default: 1.0)</dt>
509<dd>The value printed in the label is divided by <code>divideFactor</code>.</dd>
510</dl>
511
512<P>For an example of usage, see the second example in the description of <a href="#labels-example">labels</a>.</P>
513
514
515<h2>Check Box with Spin</h2>
516
517<P>Check box with spin, or, essentially, a wrapper around
518OWGUI.checkBox and OWGUI.spin.</P>
519
520<p class="function">checkWithSpin(widget, master, label, min, max, checked, value[, posttext, step, tooltip, checkCallback, spinCallback, labelWidth])</p>
521
522<dl class="attributes">
523<dt>min, max, step (required)</dt>
524<dd>Minimal and maximal value for the spin control, and its step.
525
526<dt>checked (required)</dt>
527<dd>Master's attribute that is synchronized with the state of the check box.</dd>
528
529<dt>value (required)</dt>
530<dd>The attribute that is synchronized with the spin.</dd>
531
532<dt>posttext (default: None)</dt>
533<dd>Text which appears on the right-hand side of the control.</dd>
534
535<dt>checkCallback (default: None), spinCallback (default: None)</dt>
536<dd>Function that are called when the state of the check box or spin changes.</dd>
537</dd>
538</dl>
539
540<span>
541<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
542<span class="hideshow"><a href="gui_checkspin.py">Download example (gui_checkspin.py)</a></span>
543<div class="example">
544<table><tr>
545<td width="600px">
546<xmp class=code>self.val = 20
547self.chk = 1
548OWGUI.checkWithSpin(self.controlArea, self, "Prunning, m=", 0, 100,
549                    "chk", "val", posttext = "%")
550</xmp>
551</td><td valign="top">
552<img src="gui_checkspin.png">
553</td></tr></table>
554</div></span>
555
556
557<h2>Labels</h2>
558
559<P>There are two functions for constructing labels. The first is a simple wrapper around QLabel which differs only in allowing to specify a fixed width without needing an extra line. Note that unlike most other OWGUI widgets, this one does not have the argument <code>master</code>.</P>
560
561<p class="function">widgetLabel(widget, label[, labelWidth])</p>
562
563<P>The second is a label which can synchronize with values of master widget's attributes.</P>
564
565<p class="function">label(widget, master, label[, labelWidth])</p>
566
567<dl class="attributes">
568<dt>label</dt>
569<dd><code>label</code> is a format string following Python's syntax (see the corresponding Python documentation): the label's content is rendered as <code>label % master.__dict__</code>.</dd>
570</dl>
571
572<span>
573<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
574<span class="hideshow"><a href="gui_label.py">Download example (gui_label.py)</a></span>
575<div class="example">
576<P>The following code, taken from <code>OWPurgeDomain</code>, construct four labels that change whenever the value of <code>self.removedAttrs</code>, <code>self.reducedAttrs</code>, <code>self.resortedAttrs</code> or <code>self.classAttr</code> changes in the code.</P>
577
578<table><tr>
579<td width="600px">
580<xmp class="code">box3 = OWGUI.widgetBox(self.controlArea, 'Statistics')
581OWGUI.label(box3, self, "Removed attributes: %(removedAttrs)s")
582OWGUI.label(box3, self, "Reduced attributes: %(reducedAttrs)s")
583OWGUI.label(box3, self, "Resorted attributes: %(resortedAttrs)s")
584OWGUI.label(box3, self, "Class attribute: %(classAttr)s")
585</xmp>
586</td>
587</tr></table>
588
589<P>Each label can contain any number of attributes, which can be strings (as in <code>%(name)s</code>) or of any type supported by Python (e.g. <code>%(name)5.3f</code>), like in the toy example below.</P>
590
591<a name="labels-example"></a>
592<table><tr>
593<td width="600px">
594<xmp class=code>self.val = 5
595self.line = "a parrot"
596
597OWGUI.spin(self.controlArea, self, "val", 1, 10, label="Value")
598OWGUI.lineEdit(self.controlArea, self, "line", label="Line: ", orientation=0)
599
600OWGUI.label(self.controlArea, self,
601            "Value is %(val)i and line edit contains %(line)s")
602</xmp>
603</td><td valign="top">
604<img src="gui_label.png">
605</td></tr></table>
606</div></span>
607
608<h1>Utilities</h1>
609
610<h2>Widget box</h2>
611
612<dl>
613<dt>widgetBox(widget, box=None, orientation='vertical', addSpace=False)</dt>
614<dd>Creates a box in which other widgets can be put. If <code>box</code> is given and not false, the box will be framed. If <code>box</code> is a string, it will be used for the box name (don't capitalize each word; spaces in front or after the string will be trimmed and replaced with a single space). Argument <code>orientation</code> can be <code>"vertical"</code> or <code>"horizontal"</code> (or <code>True</code> and <code>False</code>, or <code>1</code> and <code>0</code>, respectively).</dd>
615</dl>
616
617<h2>Idented box</h2>
618
619<dl>
620<dt>indentedBox(widget, sep=20)</dt>
621<dd>Creates an indented box. Widgets which are subsequently put into that box will be arranged vertically and aligned with an indentation of <code>sep</code>.</dd>
622</dl>
623
624<span>
625<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
626<span class="hideshow"><a href="gui_indentedBox.py">Download example (gui_indentedBox.py)</a></span>
627<div class="example">
628<P>The first box in this example widget contains three check boxes: the first enables and disables the other two, which are for this reason indented. Note that we are disabling both check boxes at once by disabling the indented box.</P>
629
630<P>The second box contains only two check boxes, the first disabling the second. Note the shortcut we used - the indented check box is created on the fly.</P>
631
632<table><tr>
633<td width="600px">
634<xmp class="code">box = OWGUI.widgetBox(self.controlArea, "Redundant values")
635remRedCB = OWGUI.checkBox(box, self, "removeRedundant", "Remove redundant values")
636
637iBox = OWGUI.indentedBox(box)
638OWGUI.checkBox(iBox, self, "removeContinuous", "Reduce continuous attributes")
639OWGUI.checkBox(iBox, self, "removeClasses", "Reduce class attribute")
640remRedCB.disables.append(iBox)
641
642
643box = OWGUI.widgetBox(self.controlArea, "Noise")
644noiseCB = OWGUI.checkBox(box, self, "addNoise", "Add noise to data")
645OWGUI.checkBox(OWGUI.indentedBox(box), self, "classOnly", "Add noise to class only")
646</xmp>
647</td><td valign="top">
648<img src="gui_indentedBox.png">
649</td></tr></table></div></span>
650
651
652
653<h2>Inserting Space between Widgets</h2>
654
655<P>Most widgets look better if we insert some vertical space between the controls or groups of controls. A few functions have an optional argument <code>addSpace</code> by which we can request such space to be added. For other occasions, we can use the following two functions.</P>
656
657<p class="function">separator(widget, width=0, height=8)</p>
658
659<P>Function <code>separator</code> inserts a fixed amount of space into <code>widget</code>. Although the caller can specify the amount, leaving the default will help the widgets having uniform look.</p>
660
661<p class="function">rubber(widget[, orientation="vertical"])</p>
662
663<P>Similar to separator, except that the size is (1, 1) and that it expands in the specified direction if the widget is expanded. Most widgets should have rubber somewhere in their <code>controlArea</code>.</p>
664
665
666<h2>Attribute Icons</h2>
667
668<p class="function">getAttributeIcons()</p>
669
670<p>Returns a dictionary with attribute types (<code>orange.VarTypes.Discrete</code>, <code>orange.VarTypes.Continuous</code>, <code>orange.VarTypes.String</code>, -1) as keys and colored pixmaps as values. The dictionary can be used in list and combo boxes showing attributes for easier distinguishing between attributes of different types.</p>
671
672
673<h2>Send automatically / Send</h2>
674
675<p>Many widgets have a "Send" button (perhaps named "Apply", "Commit"...) accompanied with a check box "Send automatically", having the same effect as if the user pressed the button after each change. A well behaved widget cares to:
676<ul>
677<li>disable the button, when the check box is checked;</li>
678<li>when the user checks the check box, the data needs to be send (or the changes applied), but only if there is any pending change which has not been (manually) sent yet.</li>
679</ul>
680Programming this into every widget is annoying and error-prone; at the time when the function described here was written, not many widgets actually did this properly.</p>
681
682<p class="function">setStopper(master, sendButton, stopCheckbox, changedFlag, callback)</p>
683<dl class="attributes">
684<dt>sendButton</dt>
685<dd>The button that will be disabled when the check box is checked.</dd>
686
687<dt>stopCheckbox</dt>
688<dd>Check box that decides whether the changes are sent/commited/applied automatically.</dd>
689
690<dt>changedFlag</dt>
691<dd>The name of the <code>master</code>'s attribute which tells whether there is a change which has not been sent/applied yet.</dd>
692
693<dt>callback</dt>
694<dd>The function that sends the data or applies the changes. This is typically the function which is also used as the <code>sendButton</code>'s callback.</dd>
695</dl>
696
697<!--<P>Although this may all look complicated, it's actually simple. <code>setStopper</code> is only three lines long:
698<ul>
699<li>it puts <code>(-1, sendButton)</code> in the <code>stopCheckbox</code>'s <code>disables</code> list, which will automatically disable the button when the box is checked (see the documentation on <code>OWGUI.checkBox</code>)</li>
700<li>disables the button if the box is checked</li>
701<li>to <code>stopCheckBox</code> it assigns a callback function which is called whenever the box's state is changed; the function checks whether the check box is checked and <code>changedFlag</code> is set; if so, it calls <code>callback</code></li>
702</ul></P>
703-->
704
705<P><code>setStopper</code> is a trivial three lines long function which connects a few signals. Its true importance is in enforcing the correct procedure for implementing such button-check box combinations. Make sure to carefully observe and follow the example provided below.</P>
706
707<span>
708<span onclick="toggleVisibility(this);" class="hideshow">Show Example</span>
709<span class="hideshow"><a href="gui_stopper.py">Download example (gui_stopper.py)</a></span>
710<div class="example">
711<P>In this somewhat lengthy example we shall write a widget for a new learning method XLearner, which will feature a few radio buttons and check boxes, and an "apply box", with an Apply button and a check box for automatic application of each change.</P>
712
713<P>Except for two import statements and the construction of learner, which typically takes one line, this is a complete source. This is how much code a typical learner-wrapping widget takes.</P>
714
715<table><tr>
716<td width="600px">
717<xmp class=code>class OWXLearner(OWWidget):
718  settingsList = ["method", "maxi", "cheat", "autoApply"]
719  def __init__(self, parent=None):
720      OWWidget.__init__(self, parent, title='X Learner')
721
722      self.method= 0
723      self.maxi = 1
724      self.cheat = 0
725      self.autoApply = True
726
727      self.settingsChanged = False
728
729      OWGUI.radioButtonsInBox(self.controlArea, self, "method",
730                     ["Vanishing", "Disappearing", "Invisibilisation"],
731                     box="Minimization technique",
732                     callback = self.applyIf)
733      OWGUI.separator(self.controlArea)
734
735      box = OWGUI.widgetBox(self.controlArea, "Settings")
736      OWGUI.checkBox(box, self, "maxi", "Post-maximize", callback = self.applyIf)
737      OWGUI.checkBox(box, self, "cheat", "Quasi-cheating", callback = self.applyIf)
738      OWGUI.separator(self.controlArea)
739
740      box = OWGUI.widgetBox(self.controlArea, "Apply")
741      applyButton = OWGUI.button(box, self, "Apply", callback = self.apply)
742      autoApplyCB = OWGUI.checkBox(box, self, "autoApply", "Apply automatically")
743
744      OWGUI.setStopper(self, applyButton, autoApplyCB, "settingsChanged", self.apply)
745
746      self.adjustSize()
747
748  def applyIf(self):
749      if self.autoApply:
750          self.apply()
751      else:
752          self.settingsChanged = True
753
754  def apply(self):
755      # Constructs and sends the learner; here we shall show only a message box
756      QMessageBox.information(self, "Applied", "New settings applied")
757      self.settingsChanged = False
758</xmp></td>
759<td valign="top">
760<img src="gui_stopper.png">
761</td>
762</tr>
763</table>
764
765<P>First note the two apply functions. The first, <code>applyIf</code> is a conditional apply; if automatic application is enabled, the "true" apply function is called, elsewhere we just set the flag that settings have been changed since the last application. The other apply function, <code>apply</code> would construct a learner and send the signal to widget's outputs, while we just show a message box. In either case, we have to clear the flag that settings have been changed.</P>
766
767<P>All controls that set the learner's properties specify <code>applyIf</code> as the callback function. If other code needs to be executed when the control is changed, this is OK, but at the end it needs to call <code>applyIf</code>. Apply button naturally calls <code>apply</code> function, which unconditionally applies the changes.</P>
768
769<P>The callback function that <code>setStopper</code> attaches to the check box "Apply automatically" does this and which is called at each change of the check box, verifies whether the check box has been just checked and whether any settings have been changed (<code>settingsChanged</code>). If so, <code>apply</code> is called. Otherwise, it does nothing. (Button Apply gets disabled, of course, but this happens using another, more general OWGUI's mechanism.)</P>
770</div></span>
771
772</body>
773</html>
774
Note: See TracBrowser for help on using the repository browser.