1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- /home/espenr/tmp/qt-3.3.8-espenr-2499/qt-x11-free-3.3.8/tools/designer/book/chap-dialogs.leaf:3 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Creating Dialogs</title>
<style type="text/css"><!--
fn { margin-left: 1cm; text-indent: -1cm; }
a:link { color: #004faf; text-decoration: none }
a:visited { color: #672967; text-decoration: none }
body { background: #ffffff; color: black; }
--></style>
</head>
<body>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr bgcolor="#E5E5E5">
<td valign=center>
<a href="index.html">
<font color="#004faf">Home</font></a>
| <a href="classes.html">
<font color="#004faf">All Classes</font></a>
| <a href="mainclasses.html">
<font color="#004faf">Main Classes</font></a>
| <a href="annotated.html">
<font color="#004faf">Annotated</font></a>
| <a href="groups.html">
<font color="#004faf">Grouped Classes</font></a>
| <a href="functions.html">
<font color="#004faf">Functions</font></a>
</td>
<td align="right" valign="center"><img src="logo32.png" align="right" width="64" height="32" border="0"></td></tr></table><p align="right">[<a href="designer-manual-3.html">Prev: Creating a Main Window Application</a>] [<a href="designer-manual.html">Home</a>] [<a href="designer-manual-5.html">Next: The Designer Approach</a>]</p>
<h2 align="center">Creating Dialogs</h2>
<p>In this chapter we will create the dialogs necessary to complete the <tt>colortool</tt> application. All the dialogs are launched from the main window we created in the previous chapter. We will learn how to create and use both modal and modeless dialogs, and more about using Qt's layout classes in <em>Qt Designer</em> to produce forms that are well proportioned and that scale well.</p>
<h3><a name="1"></a>Adding Colors</h3>
<p>Qt already has a static function that launches a "choose a color" dialog. But we need to not only choose a color, but to give it a name too. So we'll present the user with the choose a color dialog, and if they choose a color, we'll then ask them to name it.</p>
<p align="center"><img align="middle" src="qd-colortool-name.png" alt="The Color Name Dialog" width="356" height="134">
</p>
<h4><a name="1-1"></a>Making the Connection</h4>
<p>When we created the main form we made an action called "editAddAction". We added this action to the menubar (in the "Edit" menu), and to the toolbar. Now we need to connect this action to a slot so that we can make it add a color.</p>
<p>Click <b>Edit|Connections</b> to invoke the <em>View and Edit Connections</em> dialog. Click <b>New</b> to create a new connection. Change the Sender to "editAddAction", the signal to "activated()" and the receiver to "MainForm". We need to create a new slot to connect to. Click <b>Edit Slots</b> to invoke the <em>Edit Functions</em> dialog. Click <b>New Function</b> and change the slot's name to "editAdd()", then click <b>OK</b>. Now change the slot we connect to "editAdd()", then click <b>OK</b> to close the dialog.</p>
<blockquote>
<p align="center"><b> Selecting Widgets</b></p>
<!-- index Object Explorer --><!-- index Selecting Widgets --><!-- index Rubber band, Selecting --><!-- index Selecting!Rubber band --><!-- index Grouping Widgets --><!-- index Widgets!Grouping --><p>To select an individual widget, either click the widget itself or click its Name in Object Explorer. To select a group either click a fraction outside its red outline or click its Name in Object Explorer.</p>
<p>If you want to insert a widget into a gap between widgets which are in a layout we can click the toolbar button for the new widget and then click in the gap. <em>Qt Designer</em> will ask us if we want to break the layout and if we click <b>Break Layout</b> the layout will be broken and our widget inserted. We can then select the widgets and groups we want to lay out and lay them out again. The same effect can be achieved by clicking the group and either clicking the <b>Break Layout</b> toolbar button or pressing <b>Ctrl+B</b>.</p>
<p>Multiple widgets can be selected in five different ways:</p>
<ol type=1><li><p>Click the first widget, then <b>Shift+Click</b> the other widgets.</p>
<li><p><b>Ctrl+Click</b> the first widget, then <b>Ctrl+Click</b> the other widgets; this is like the first technique but allows you to select widgets that are <em>inside</em> another widget (e.g. inside a group).</p>
<li><p>Click the form and drag the rectangular rubber band to <em>touch</em> all the widgets you are interested in.</p>
<li><p><b>Ctrl+Click</b> the first widget, then drag the rectangular rubber band to <em>touch</em> the other widgets. This differs from the previous technique in that it allows you to select widgets that are <em>inside</em> another widget (e.g. inside a group).</p>
<li><p>Click Object Explorer's Objects tab. Click the first widget in Object Explorer, then <b>Shift+Click</b> the other widgets. This is especially useful for picking out widgets when you have lots of complex layouts.</p>
</ol><p>When multiple widgets are selected their common properties are shown in the <a href="designer-manual-3.html#using-the-property-editor-sidebar">Property Editor</a>. Any changes made in the Property Editor will be applied to <em>all</em> the selected widgets. This is particularly useful for setting common minimum and maximum sizes, colors, size policies, cursors, fonts, etc.</p>
</blockquote>
<h4><a name="1-2"></a>Creating the Dialog</h4>
<a name="layouts-subsection"></a><h5><a name="1-2-1"></a>Layouts</h5>
<!-- index Layouts --><!-- index Scaling widgets and application windows --><!-- index Automatically scaling widgets and application windows --><!-- index Absolute positioning --><p>Layouts provide a means of laying out widgets, groups of widgets, and layouts into horizontal and vertical pairs and into grids. If you use layouts your forms, and the widgets they contain, will scale automatically when the user resizes the window. This is better than using absolute sizes and positions since you don't have to write any code to achieve the scaling and your users can make the most of their screen size whether they have a laptop or a very large screen desktop machine. Layouts use standard sizes for margins and widget spacing which helps give your applications a consistent and proportional look without requiring any effort on your part. Layouts are also easier and faster to use than absolute positioning; you can just place your widgets on the form in approximate positions and leave the layout tools to size and scale the widgets correctly.</p>
<a name="adding-the-widgets-subsection"></a><h5><a name="1-2-2"></a>Adding the Widgets</h5>
<p>We can use one of Qt's static dialogs to get the user to choose a color, but we need our own dialog to get them to give it a name. We'll create that dialog now.</p>
<p>Click <b>File|New</b> to invoke the <em>New File</em> dialog, then click "Dialog", then click <b>OK</b>. Drag a corner of the new form to make it a lot smaller. Change the form's name (in the <a href="designer-manual-3.html#using-the-property-editor-sidebar">Property Editor</a>) to "ColorNameForm", and change its caption to "Color Tool -- Color Name". Click <b>File|Save</b>, then click <b>Save</b> to save it.</p>
<p>We'll now add some widgets to the dialog. Don't worry about precise positioning or sizing; we'll get <em>Qt Designer</em> to lay the form out perfectly for us shortly.</p>
<p>We need to create a <a href="qlabel.html">QLabel</a> that will show the color the user has chosen. Click the TextLabel tool in the Toolbox, then click on the left hand side of the form. Change the label's <em>name</em> property to "colorLabel", and delete the text in the <em>text</em> property. Click the <em>pixmap</em> property's ellipsis button and choose the "editraise.png" image. Change the <em>minimumSize</em> property's <em>width</em> sub-property to 80, and set the <em>scaledContents</em> property to True.</p>
<p>Click the TextLabel tool again, then click to the right of the colorLabel, towards the top of the form. Change the <em>text</em> property to "&Name". Note that the ampersand is displayed; this is because a <a href="qlabel.html">QLabel</a> cannot accept focus and we haven't specified a focus widget ("buddy") yet.</p>
<p>Click the LineEdit tool, then click to the right of the "Name" label, again towards the top of the form. Change the <em>name</em> property to "colorLineEdit".</p>
<p>Click on the "Name" label and change its <em>buddy</em> property to "colorLineEdit". The ampersand has now disappeared and <b>Alt+N</b> will set the focus in the colorLineEdit.</p>
<p>Click the PushButton tool, then click below the "colorLabel". Change the button's <em>name</em> property to "okPushButton", its <em>text</em> property to "OK", and its <em>default</em> property to True.</p>
<p align="center"><img align="middle" src="dialog1addwidg.png" width="425" height="228">
</p>
<p>Click the PushButton tool again, then click to the right of the OK button. Change this button's <em>name</em> property to "cancelPushButton" and its <em>text</em> property to "Cancel".</p>
<a name="laying-out-the-widgets-subsection"></a><h5><a name="1-2-3"></a>Laying Out the Widgets</h5>
<p>Now that we've created and roughly placed the widgets we need we are ready to lay them out.</p>
<p>The process of laying out widgets is essentially two steps:</p>
<ol type=1><li><p>select two or more widgets (or layouts)</p>
<li><p>apply a layout method (vertical, horizontal, grid) to the selected items</p>
</ol><p><em>Qt Designer</em> provides several different ways of selecting widgets and layouts. It doesn't matter which you use; some are more convenient in certain situations that others. We'll show several different approaches to selecting widgets as we lay out this form.</p>
<p>We will lay out the "Name" label and the line edit side by side. Then we'll do the same for the buttons, and finally we'll lay out these two layouts in relation to the colorLabel.</p>
<p>Click the form to deselect any selected widgets. Click the form above the "Name" label and drag the black rectangular elastic band so that it touches both the "Name" label and the line edit; then release. (It only has to be touching them when you release.) The "Name" label and the line edit are now selected. Click the <b>Lay Out Horizontally</b> toolbar button. A thin red rectangle appears to indicate the layout (this only shows up as a visual cue in <em>Qt Designer</em>, it doesn't appear in preview mode or in the running form).</p>
<p align="center"><img align="middle" src="dialog1selewidg.png" alt="Selecting Widgets with the Rubber Band" width="425" height="228">
</p>
<p align="center"><img align="middle" src="dialog1selewidg1.png" alt="Selected Widgets" width="426" height="229">
</p>
<p>Click the form to deselect any selected widgets. In Object Explorer's Objects tab click the "cancelPushButton" to select it. Now <b>Shift+Click</b> the OK button in the Objects tab. (The Objects tab only ever highlights <em>one</em> object; but the form shows that both buttons are selected.) Click the <b>Lay Out Horizontally</b> toolbar button.</p>
<p>Click the form to deselect any selected widgets. In Objects Explorer, click "Layout1" (which contains the "Name" label and the line edit), then <b>Shift+Click</b> "Layout2" (which contains the buttons) so that both layouts are selected on the form. Click the <b>Lay Out Vertically</b> toolbar button.</p>
<p>Now that we've got our two layouts (and the colorLabel widget), we'll lay them all out in relation to the form.</p>
<p>Click the form to deselect any selected widgets. Click the <b>Lay Out Horizontally</b> toolbar button to lay out the form. (The form gets laid out because no widgets or layouts are selected.)</p>
<p align="center"><img align="middle" src="dialog1layout1.png" width="424" height="229">
</p>
<p>Preview the form (<b>Ctrl+T</b>), and try resizing it. Unfortunately, when you make the form larger the buttons expand too much and look unattractive. Furthermore they don't stay at the bottom of the form as we would like. The problem is that when we enlarge the form there is a lot of unused space, and we need to tell the buttons not to make use of that space. This is achieved by inserting "spacers"; these have no visible appearance to the user, but consume excess space.</p>
<p>We need to break the form layout so that we can insert spacers and lay out the form once more. Click the red rectangle of "Layout3", the layout that lays out the entire form. (We could just have easily have clicked the layout in Object Explorer.) Click the <b>Break Layout</b> toolbar button.</p>
<p>We could add a spacer to the left of the buttons and lay that out with the buttons. But to save creating an extra layout, instead we'll break the layout that contains the buttons and lay them out in a single layout with the spacer. Click one of the buttons, then click <b>Break Layout</b> (or press <b>Ctrl+B</b>). Roughly resize the OK button so that it is about half as wide, leaving a gap on its left. Click the Spacer tool (in the Toolbox or on the toolbar), then click the form to the left of the OK button and drag horizontally. A horizontal Spacer (indicated by a blue "spring") is now left of the OK button.</p>
<p>Since the Spacer is already selected, we'll simply extend the selection to include the buttons. <b>Shift+Click</b> the OK and cancel buttons in turn so that both buttons and the Spacer are selected. Now click <b>Lay Out Horizontally</b> (or press <b>Ctrl+H</b>). The Spacer is now in place and able to consume excess space.</p>
<p>Now we'll put a Spacer between the layout containing the line edit and the layout we've just created (containing the buttons) to consume any excess space between them. Click the Spacer tool, then click the form in-between the line edit and the buttons and drag vertically. The Spacer is already selected but we need to extend the selection to include both the layouts. <b>Shift+Click</b> the layouts in Object Explorer's Objects tab. Now click <b>Lay Out Vertically</b> (or press <b>Ctrl+L</b>).</p>
<p>Now we'll lay out the form itself. Click the form to deselect any selected widgets or layouts. Then click <b>Lay Out Horizontally</b>.</p>
<p align="center"><img align="middle" src="dialog1layout2.png" width="425" height="226">
</p>
<p>So far the form doesn't look very different from before. Try previewing it (<b>Ctrl+T</b>) and resizing it. No matter how large or small you make it, the form always remains nicely proportioned.</p>
<p>This is the beauty of using layouts rather than fixed sizes. It is especially useful if your program is translated into different languages since the labels will automatically and proportionally resize without any coding whatsoever. And your users will be able to use your forms on a wide variety of screen sizes from giant desktop screens to small laptop screens. (And, you can, of course, use fixed sizes and positions if you wish.)</p>
<p>Resize the form in <em>Qt Designer</em>. Notice that the form has a minimum size; this is because all the widgets it contains have minimum sizes. (You can override this if you wish.) The size you make the form in <em>Qt Designer</em> will become the form's default size.</p>
<p>When previewing the form, try pressing <b>Tab</b> to move between widgets that can accept focus. It is most likely that the tab order is correct. If it isn't, we can easily change it: see the <a href="designer-manual-4.html#changing-the-tab-order-sidebar">Changing the Tab Order</a> sidebar.</p>
<blockquote>
<p align="center"><b> Changing the Tab Order</b></p>
<a name="changing-the-tab-order-sidebar"></a><!-- index Tab Order --><!-- index Tab Order Mode!Tab Order --><p>Keyboard users press the <b>Tab</b> key to move the focus from widget to widget as they use a form. The order in which the focus moves is called the tab order.</p>
<p>To change a form's tab order click the <b>Tab Order</b> toolbar button. This will put <em>Qt Designer</em> into tab order mode; a number in a blue circle will appear next to every widget that can accept focus.</p>
<p>To change the tab order, click every widget in turn in the order you want them to accept focus. If a widget already has the right tab order number you must <em>still click it</em> unless that widget and <em>all</em> the following widgets have the correct tab order numbers; in which case you can stop. Press <b>Esc</b> to leave tab order mode. You can test the tab order by previewing (<b>Ctrl+T</b>) and pressing the <b>Tab</b> key.</p>
<p>If you prefer the tab order that you had before this one, click <b>Edit|Undo</b> (or press <b>Ctrl+Z</b>).</p>
<p align="center"><img align="middle" src="dialog1tab.png" width="425" height="227">
</p>
</blockquote>
<h5><a name="1-2-4"></a>Connecting the Widgets</h5>
<p>We need to handle two buttons; the OK button and the cancel button. If the user clicks <b>OK</b>, we will only accept their color name if it isn't empty, and if it isn't already in use. (The <tt>rgb.txt</tt> format allows duplicate colors, but we will choose not to allow them to be added.) If the user clicks <b>Cancel</b> we'll just close the dialog.</p>
<p>Click <b>Ctrl+T</b> to preview the form. Click the <b>Cancel</b> button; notice that it does nothing.</p>
<p>We'll connect the cancel button first because it is the easiest. Click <b>Edit|Connections</b> to invoke the <em>View and Edit Connections</em> dialogs. Click <b>New</b> to create a new connection. Set the Sender to "cancelPushButton", the Signal to "clicked()", the Receiver to "ColorNameForm", and the slot to "reject()". Because this functionality is achieved purely through signal and slot connections using a predefined signal and a predefined slot, it will work in preview mode. Click <b>OK</b> to close the dialog, then click <b>Ctrl+T</b> to preview. Clicking the <b>Cancel</b> button will now close the form, even in preview mode.</p>
<p>Now we'll connect the OK button. Click <b>Edit|Connections</b> to invoke the <em>View and Edit Connections</em> dialogs. Click <b>New</b> to create a new connection. Set the Sender to "okPushButton", the Signal to "clicked()" and the Receiver to "ColorNameForm". We want to invoke our own custom slot so that we can validate what the user has entered. Click <b>Edit Slots</b> to invoke the <em>Edit Functions</em> dialog. Click <b>New Function</b> and change the function's name to "validate()", then click <b>OK</b>. Now change the receiver's slot to our newly created "validate()" slot. Click <b>OK</b> to close the dialog.</p>
<h5><a name="1-2-5"></a>Coding the Dialog</h5>
<p>We must code our custom "validate()" slot. This slot will check to see if the color name entered already exists, so we must also provide a function through which a form-global list of color names can be set.</p>
<p>Click "colornameform.ui.h" in the Project Overview window to invoke the code editor. The editor shows a single empty slot, <tt>validate()</tt>.</p>
<p>We need to add some include files and also a form-global variable to hold the color names. We could add this information in the same way as we did for the main form, by right-clicking the appropriate sections in Objects Explorer's Members list and adding them. Instead we'll take an alternative approach; we'll add what we need directly in the form. Which approach you take is mostly a matter of personal preference; and you can use both approaches if you wish.</p>
<blockquote>
<p align="center"><b> Adding Members vs Coding in .ui.h</b></p>
<p>For includes there is no difference between adding an include to Object Explorer's Members "Includes (in Implementation)" and typing the include directly in the <tt>.ui.h</tt> file.</p>
<p>The situation is different for variables. If you add these to Members, Class Variables, they will be included as private variables in the class definition. If you type them at the top of the <tt>.ui.h</tt> file they will be form-global variables.</p>
</blockquote>
<p>We'll start by adding some includes.</p>
<pre> #include <<a href="qcolor-h.html">qcolor.h</a>>
#include <<a href="qmap-h.html">qmap.h</a>>
#include <<a href="qstring-h.html">qstring.h</a>>
</pre>
<p>Enter these above the <tt>validate()</tt> function.</p>
<p>We also need a variable to hold the list of colors.</p>
<pre> QMap<QString,QColor> m_colors;
</pre>
<p>Add this line; we'll store the colors in a local <tt>m_colors</tt> map.</p>
<p>We also need a function that the caller can call to populate the <tt>m_colors</tt> map with the current colors.</p>
<pre> void ColorNameForm::setColors( const <a href="qmap.html">QMap</a><QString,QColor>& colors )
{
m_colors = colors;
}
</pre>
<p>Now that we've got a means of obtaining the list of color names we are ready to write the <tt>validate()</tt> function.</p>
<pre> void ColorNameForm::validate()
{
<a href="qstring.html">QString</a> name = colorLineEdit->text();
if ( ! name.<a href="qstring.html#isEmpty">isEmpty</a>() &&
( m_colors.isEmpty() || ! m_colors.contains( <a href="qobject.html#name-prop">name</a> ) ) )
accept();
else
colorLineEdit->selectAll();
}
</pre>
<p>The function examines the text that the user has entered. If they've entered something and it doesn't exist in the list of colors we call <tt>accept()</tt>; this closes the form and returns a true value to the caller. (<tt>reject()</tt>, which is called if the user clicks <b>Cancel</b>, returns a false value.) If the color already exists we simply select it; an alternative would be to pop up a message box.</p>
<p>The dialog is now complete. The next stage is to make use of it from the main form by coding the <tt>editAdd()</tt> slot.</p>
<h4><a name="1-3"></a>Using the Dialog</h4>
<p>The ColorNameForm dialog will be called from the main form. The caller will firstly call one of Qt's static "choose a color" dialogs, and if the user chooses a color, will then invoke our custom dialog. Since we're going to use a "choose a color" dialog we will need the appropriate header file. We'll also be accessing the dialog's colorLabel (to set it to the chosen color), and the dialog's line edit (to retrieve the color name), so we'll need appropriate headers for these too.</p>
<p>Click "MainForm" in the Project Overview window so that Object Explorer shows the main form's objects.</p>
<p>Click Object Explorer's Members tab. Right click "Includes (in Implementation)", then click <b>Edit</b> to invoke the <em>Edit Includes (in Implementation)</em> dialog. Click <b>Add</b> then enter "qcolordialog.h". Click <b>Add</b> again, and enter "qlabel.h". Similarly add "qlineedit.h". We also need to include the header for the form we've just created, so add "colornameform.h", and since it is our last entry press <b>Enter</b>, then click <b>Close</b>.</p>
<p>You should now have added the following declarations to your includes (in implementation):</p>
<ul><li><p>"qcolordialog.h"</p>
<li><p>"qlabel.h"</p>
<li><p>"qlineedit.h"</p>
<li><p>"colornameform.h"</p>
</ul><p>Now we're ready to enter the <tt>editAdd()</tt> slot's code. Click "mainform.ui.h" in the Project Overview to invoke the code editor.</p>
<h5><a name="1-3-1"></a>editAdd()</h5>
<pre> void MainForm::editAdd()
{
<a href="qcolor.html">QColor</a> color = white;
if ( ! m_colors.isEmpty() ) {
<a href="qwidget.html">QWidget</a> *visible = colorWidgetStack->visibleWidget();
if ( <a href="qwidget.html#visible-prop">visible</a> == tablePage )
color = colorTable->text( colorTable->currentRow(),
colorTable->currentColumn() );
else
color = colorIconView->currentItem()->text();
}
color = QColorDialog::<a href="qcolordialog.html#getColor">getColor</a>( color, this );
if ( color.<a href="qcolor.html#isValid">isValid</a>() ) {
<a href="qpixmap.html">QPixmap</a> pixmap( 80, 10 );
pixmap.<a href="qpixmap.html#fill">fill</a>( color );
ColorNameForm *colorForm = new ColorNameForm( this, "color", TRUE );
colorForm->setColors( m_colors );
colorForm->colorLabel->setPixmap( pixmap );
if ( colorForm->exec() ) {
<a href="qstring.html">QString</a> name = colorForm->colorLineEdit->text();
m_colors[name] = color;
<a href="qpixmap.html">QPixmap</a> pixmap( 22, 22 );
pixmap.<a href="qpixmap.html#fill">fill</a>( color );
int row = colorTable->currentRow();
colorTable->insertRows( row, 1 );
colorTable->setText( row, COL_NAME, name );
colorTable->setPixmap( row, COL_NAME, pixmap );
colorTable->setText( row, COL_HEX, color.<a href="qcolor.html#name">name</a>().upper() );
if ( m_show_web ) {
<a href="qchecktableitem.html">QCheckTableItem</a> *item = new <a href="qchecktableitem.html">QCheckTableItem</a>( colorTable, "" );
item-><a href="qchecktableitem.html#setChecked">setChecked</a>( isWebColor( color ) );
colorTable->setItem( row, COL_WEB, item );
}
colorTable->setCurrentCell( row, 0 );
(void) new <a href="qiconviewitem.html">QIconViewItem</a>( colorIconView, name,
colorSwatch( color ) );
m_changed = TRUE;
}
}
}
</pre>
<p>The code for this function is quite long, but it isn't difficult. We start by setting a default color to white. If there are any colors in the <tt>m_colors</tt> map we set the default color to be the current color showing in the current view. We then invoke Qt's static <a href="qcolordialog.html#getColor">getColor()</a> dialog, passing it the default color. (If the user cancels an invalid color is returned.)</p>
<p>If the user chose a color we want to show their chosen color in our custom dialog, so we create a pixmap and fill it with their chosen color. We create an instance of our ColorNameForm as a modal dialog (third argument is TRUE). We then call its <tt>setColors()</tt> function to set the colors in the <tt>m_colors</tt> map (so that the <tt>validate()</tt> function will work correctly). We set its colorLabel's pixmap to the pixmap we've just created, i.e. to a rectangle in the user's chosen color.</p>
<p>We execute (<tt>exec()</tt>) the dialog. If the user clicks OK (and the color name they've entered is valid), the call will return a true value. In this case we retrieve the name they've entered from the line edit and create a new entry in the <tt>m_colors</tt> map using the name the user has given and the color they chose.</p>
<p>At this point we could simply mark the views "dirty" and call repopulate. Instead we'll add the new color to each view directly and save the overhead of a full update (which might be considerable if we have thousands of colors).</p>
<p>We create a pixmap and fill it with the new color. We then insert a new row in the table and set the columns to the new color's values, in the same way as we've already seen in the <tt>populate()</tt> function. Similarly we create a new icon for the icon view. Finally we mark the data as changed so that the user will be prompted to save if they attempt to exit or load another color file before they've saved this one.</p>
<p>Try building and running the application. You should now be able to add your own colors. (If it doesn't build see the <a href="designer-manual-4.html#6">Troubleshooting</a> section.)</p>
<p>The application is essentially complete. We can load and save color files, we can show them in the table or icon view and users can add and delete colors. But if the user has a color file with hundreds or thousands of colors, scrolling to look for a particular color would be tedious. Also whenever the user runs the application it starts up with the default window size, view and other settings; it would be much nicer if we remembered how the user left the application and reinstated their choices. In the following two sections we'll address both these issues through the creation of a modeless find dialog and a modal options dialog.</p>
<h3><a name="2"></a>Finding Colors</h3>
<p>The approach to this option is similar to adding colors. We'll design a dialog, make its connections, write its code, then code the slot that invokes it. (We already have the connection set up in the main form; it was created automatically by the main window wizard.)</p>
<p align="center"><img align="middle" src="dialog2sample.png" width="320" height="122">
</p>
<h4><a name="2-1"></a>Creating the Dialog</h4>
<p>Click <b>File|New</b> to invoke the <em>New File</em> dialog, then click "Dialog", then click <b>OK</b>. Drag a corner of the form to make it smaller. Use the <a href="designer-manual-3.html#using-the-property-editor-sidebar">Property Editor</a> to change the form's <em>name</em> property to "FindForm", and its <em>caption</em> property to "Color Tool -- Find Color".</p>
<p>Press <b>Ctrl+S</b> (or click <b>File|Save</b>) to save the form, then click <b>Save</b>.</p>
<h5><a name="2-1-1"></a>Adding the Widgets</h5>
<p>Click the TextLabel tool in the Toolbox, then click on the form on the left hand side. Change the <em>text</em> property to "&Look for". (The ampersand will show on the form because we haven't yet provided a focus widget "buddy" for the <b>Alt+L</b> keyboard shortcut; we'll do that in a moment.)</p>
<p>Click the LineEdit tool, then click on the form to the right of the "Look for" label. Change its <em>name</em> property to "findLineEdit". Click on the "Look for" label and change its <em>buddy</em> property to "findLineEdit".</p>
<p>Click the PushButton tool and click on the form below the left-end of the line edit. Change the <em>name</em> property to "findPushButton", its <em>text</em> property to "&Find" and its <em>default</em> property to True.</p>
<p>Click the PushButton tool again and click on the form to the right of the "Find" button. Change the <em>name</em> property to "closePushButton" and the <em>text</em> property to "Close".</p>
<p>We know from experience with the previous form that we'll need a spacer above the buttons and to the left of the buttons. Click on the Spacer tool, then click to the left of the "Find" button; drag horizontally to the right a little, then release. Click on the Spacer tool again, then click above the "Find" button; drag vertically a little, then release.</p>
<p align="center"><img align="middle" src="dialog2setwidg.png" width="467" height="207">
</p>
<h5><a name="2-1-2"></a>Laying Out the Widgets</h5>
<p>Now that we've created and roughly placed the widgets we need we are ready to lay them out.</p>
<p>Click the form to deselect any selected widgets. Click to the right and below the "Close" button and drag the rubber band rectangle so that it <em>touches</em> both buttons and the spacer to the <em>left</em> of the "Find" button; then release. There's no need to be too precise, so long as you touch the three items we're interested in and nothing else. If you make a mistake and haven't selected everything or have selected something else, just click the form and try again. Once the Spacer (to the left of the "Find" button) and both buttons are selected, click the <b>Lay Out Horizontally</b> toolbar button.</p>
<p>Lay out the "Look for" label and the line edit horizontally. (Click the "Look for" label, then <b>Shift+Click</b> the line edit, the press <b>Ctrl+H</b>.)</p>
<p>We can now lay out the form itself. Click the form, then press <b>Ctrl+L</b> (lay out vertically). You may want to resize the form slightly, perhaps to make it smaller. Try previewing the form (<b>Ctrl+T</b>) and resizing it to see how everything resizes nicely.</p>
<p align="center"><img align="middle" src="dialog2laywidg.png" width="343" height="165">
</p>
<h5><a name="2-1-3"></a>Connecting the Widgets</h5>
<p>There are two approaches we can take for a find dialog. One approach is to use a modal dialog: the user enters a term, clicks "Find" and we then close the form with the found item highlighted. The other approach is to use a modeless dialog: the user can enter the term and click "Find" as often as they like; each time they click the next matching term is found. We are going to use the second approach.</p>
<p>Since the searching takes place through the data held by the main form, and since we want any found term to be highlighted by the main form, we'll put most of the code for the search in the main form. To achieve this we'll have the FindForm emit a signal whenever the user clicks the "Find" button, and we'll provide a slot that the main form can call to notify the FindForm that no matching color was found.</p>
<p>Invoke the <em>View and Edit Connections</em> dialog. (Click <b>Edit|Connections</b>.)</p>
<p>Connect the closePushButton's <tt>clicked()</tt> signal to the form's <tt>accept()</tt> slot. (Click <b>New</b>, then change the Sender to "closePushButton", change the Signal to "clicked()", change the form to "FindForm" and change the slot to "accept()".) Since this functionality is achieved purely through signal and slot connections it will work in preview mode, i.e. if you preview and click the "Close" button, the form will close.</p>
<p>Connect the findPushButton's <tt>clicked()</tt> signal to a newly created "find()" slot. (Click <b>New</b>, then change the Sender to "findPushButton", change the Signal to "clicked()", change the form to "FindForm". Click <b>Edit Slots</b> to invoke the <em>Edit Functions</em> dialog; change the slot's name to "find()", then click <b>OK</b>. Back in the <em>View and Edit Connections</em> dialog, change the slot to the newly created "find()" slot.)</p>
<p>Close the <em>View and Edit Connections</em> dialog. (Click <b>OK</b>.)</p>
<p>When the user clicks the "Find" button we want to emit a signal so that the main form can look for the text.</p>
<p>Click Object Explorer's Members tab, then right click Signals and click <b>New</b>. Enter "lookfor(const QString&)". We'll emit this signal when we implement the code for the <tt>find()</tt> slot.</p>
<h5><a name="2-1-4"></a>Coding the Dialog</h5>
<p>Click "findform.ui.h" in the Project Overview to invoke the code editor. We'll implement the <tt>find()</tt> function and also a "notfound" function that the caller can use to signify that the search failed.</p>
<pre> void FindForm::find()
{
emit lookfor( findLineEdit->text() );
}
</pre>
<p>When the user clicks the "Find" button we emit the text that's in the line edit. It is up to the caller to connect to this signal and perform the search.</p>
<pre> void FindForm::notfound()
{
findLineEdit->selectAll();
}
</pre>
<p>If the term isn't found all we'll do is highlight the text. An alternative would be to popup a message box.</p>
<p>The dialog's code is simple because we're pushing all the work onto the main form.</p>
<h4><a name="2-2"></a>Using the Dialog</h4>
<p>When the user clicks <b>Edit|Find</b> in the application we want to invoke the FindForm dialog. And whenever they click the <b>Find</b> button we want to look for the text they've entered in the current view, starting at the color after the color they're on. We will create just one instance of the FindForm and keep a pointer to it so that we can show and hide it as necessary.</p>
<p>We need to include the FindForm, and we also need to declare the "findForm" variable we put (commented out) in the <tt>init()</tt> function.</p>
<p>Click on "mainform.ui.h" in the Project Overview. This will invoke the code editor and set Object Explorer to show objects in the MainForm.</p>
<p>Add "findform.h" to "Includes (in Declaration)". (Click Object Explorer's Members tab, right click "Includes (in Declaration)", click <b>New</b>, enter "findform.h", then press <b>Enter</b>.)</p>
<p>You should now have added the following declaration to your includes (in declaration):</p>
<ul><li><p>"findform.h"</p>
</ul><p>In the class variables add "FindForm *findForm;". (Right click "Class Variables", click <b>Edit</b> and then click <b>Add</b>. Enter "FindForm *findForm;", then press <b>OK</b>.)</p>
<p>You should now have added the following variable to your class variables:</p>
<ul><li><p>FindForm *findform;</p>
</ul><p>Uncomment the line <tt>findForm = 0;</tt> in the <tt>init()</tt> function.</p>
<p>We can now implement the <tt>editFind()</tt> slot.</p>
<h5><a name="2-2-1"></a>editFind()</h5>
<pre> void MainForm::editFind()
{
if ( ! findForm ) {
findForm = new FindForm( this );
<a href="qobject.html#connect">connect</a>( findForm, SIGNAL( lookfor(const <a href="qstring.html">QString</a>&) ),
this, SLOT( lookfor(const <a href="qstring.html">QString</a>&) ) );
}
findForm->show();
}
</pre>
<p>If we haven't created the FindForm, we create it and connect its <tt>lookfor()</tt> signal to a corresponding <tt>lookfor()</tt> slot that we'll create in the main form. We then show the FindForm so that the user can enter their search text and click find.</p>
<h5><a name="2-2-2"></a>lookfor()</h5>
<pre> void MainForm::lookfor( const <a href="qstring.html">QString</a>& text )
{
if ( text.<a href="qstring.html#isEmpty">isEmpty</a>() )
return;
<a href="qstring.html">QString</a> ltext = text.<a href="qstring.html#lower">lower</a>();
<a href="qwidget.html">QWidget</a> *visible = colorWidgetStack->visibleWidget();
bool found = FALSE;
if ( <a href="qwidget.html#visible-prop">visible</a> == tablePage && colorTable->numRows() ) {
int row = colorTable->currentRow();
for ( int i = row + 1; i < colorTable->numRows(); ++i )
if ( colorTable->text( i, 0 ).lower().contains( ltext ) ) {
colorTable->setCurrentCell( i, 0 );
colorTable->clearSelection();
colorTable->selectRow( i );
found = TRUE;
break;
}
if ( ! found )
colorTable->setCurrentCell( row, 0 );
}
else if ( <a href="qwidget.html#visible-prop">visible</a> == iconsPage ) {
<a href="qiconviewitem.html">QIconViewItem</a> *start = colorIconView->currentItem();
for ( <a href="qiconviewitem.html">QIconViewItem</a> *item = start-><a href="qiconviewitem.html#nextItem">nextItem</a>(); item; item = item-><a href="qiconviewitem.html#nextItem">nextItem</a>() )
if ( item-><a href="qtableitem.html#text">text</a>().lower().contains( ltext ) ) {
colorIconView->setCurrentItem( item );
colorIconView->ensureItemVisible( item );
found = TRUE;
break;
}
if ( ! found && start )
colorIconView->setCurrentItem( start );
}
if ( ! found ) {
statusBar()->message( QString( "Could not find '%1' after here" ).
arg( text ) );
findForm->notfound();
}
}
</pre>
<p>This slot is invoked when the user clicks the "Find" button in the FindForm. The text the user entered in the FindForm's line edit is passed in as the <tt>text</tt> parameter. If there is no text we simply return.</p>
<p>We take a lower case copy of the text because we want to do a case-insensitive search. We find out which view the user is using (so that we can look in the right one), and set a flag <tt>found</tt>, that we'll use further on.</p>
<p>If the user is using the table view we start looking from the row following the row they're on. If we get a match we select the row containing the match, set <tt>found</tt> to TRUE and stop looking. If we didn't find a match we set the current cell back to the cell we started from.</p>
<p>If the user is using the icon view, we start looking from the item following the current item. If we find a match we select the corresponding item and ensure that it is visible. Again, if we didn't find a match we set the current item to be the item we started looking from.</p>
<p>If we found the text, the relevant item is highlighted (because we have selected it) in the user's view. If we didn't find the text we issue a message on the status bar and call the FindForm's <tt>notfound()</tt> function (which simply selects the search text).</p>
<p>Functions that are typed directly into the code editor become public functions (unless their return value is <tt>void</tt> in which case they become public slots). These can be changed later by editing the function's properties. We need <tt>lookfor()</tt> to be a slot because we connect to it. Click Object Explorer's Members tab, then right click <tt>lookfor()</tt>, then click <b>Properties</b>. This invokes the <em>Edit Functions</em> dialog. Change the Type to "slot", then click <b>OK</b>.</p>
<p>Save the application (<b>Ctrl+S</b>), then build it and try out some searches. (If it doesn't build see the <a href="designer-manual-4.html#6">Troubleshooting</a> section.)</p>
<h3><a name="3"></a>User Options</h3>
<p>We want to give the user the choice of whether or not to indicate web colors in the table view, and what they want to copy to the clipboard when the click <b>Ctrl+C</b> to copy a color. We'll also automatically save and restore their view, window size and position.</p>
<p align="center"><img align="middle" src="dialog3sample.png" width="328" height="315">
</p>
<h4><a name="3-1"></a>Creating the Dialog</h4>
<p>Create a new dialog. (Click <b>File|New</b> to invoke the <em>New File</em> dialog, then click "Dialog", then click <b>OK</b>.) Change the form's <em>name</em> property to "OptionsForm", and its <em>caption</em> property to "Color Tool -- Options". Drag a corner of the form to make it a bit smaller. Now save the form and accept the default name.</p>
<p>Click the GroupBox tool (in the Containers toolbox) and click towards the top left of the form. Roughly drag the group box to make it wider. Change its <em>title</em> property to "Table View".</p>
<p>Click the CheckBox tool (in the Common Widgets toolbox), and click <em>inside</em> the group box. Change the checkbox's <em>name</em> to "webCheckBox", its <em>text</em> property to "Indicate &Web Colors" and its <em>checked</em> property to True.</p>
<p>Up to now we've always placed all our widgets and then laid them out at the end. But we can lay out widgets as we go along if we prefer. Click the group box, then press <b>Ctrl+H</b> (lay out horizontally).</p>
<p align="center"><img align="middle" src="dialog3grpbox.png" width="472" height="371">
</p>
<p>Click the ButtonGroup tool and click below the table view group box. Change this group's <em>title</em> property to "Copy to Clipboard As" and drag a corner to make it larger.</p>
<p>We want to add three radio buttons to the clipboard group. When there are a lot of identical widgets to add we can use <em>Qt Designer</em>'s multiple placement mode to work more quickly.</p>
<p><em>Double</em> click the RadioButton tool to enter multiple placement mode. Now every click on the form will create a radio button. Click inside, and towards the top of, the clipboard group box. Click below the first radio button. Click for a third time, below the second radio button. Now click the Pointer toolbar button to cancel multiple placement mode.</p>
<p align="center"><img align="middle" src="dialog3clipgrp.png" width="473" height="370">
</p>
<p>Click the first (top-most) radio button. Change its <em>name</em> property to "hexRadioButton", its <em>text</em> property to "&Hex, e.g. #AB52F7", and its <em>checked</em> property to True.</p>
<p>Click the second (middle) radio button. Change its <em>name</em> property to "nameRadioButton" and its text to "&Name, e.g. light blue".</p>
<p>Click the third (bottom) radio button. Change its <em>name</em> property to "rgbRadioButton" and its text to "&RGB, e.g. 51,255,102".</p>
<p>Click the clipboard group to select it, then click <b>Ctrl+L</b> (lay out vertically).</p>
<p align="center"><img align="middle" src="dialog3layclip.png" width="474" height="371">
</p>
<p>Now create an OK button and a Cancel button. (Click the PushButton tool, then click the form below the clipboard group. Change the button's name to "okPushButton", its text to "OK" and its <em>default</em> property to True. Click the PushButton tool again, then click to the right of the OK button. Change this button's name to "cancelPushButton" and its text to "Cancel")</p>
<p>To lay out the buttons add a horizontal Spacer to the left of the OK button, then lay out the Spacer and the buttons horizontally. (Click the Spacer tool, then click the form to the left of the OK button; drag horizontally to the left a little then release. <b>Shift+Click</b> the OK button, then <b>Shift+Click</b> the Cancel button. Press <b>Ctrl+H</b>.)</p>
<p align="center"><img align="middle" src="dialog3buttons.png" width="474" height="369">
</p>
<p>Lay out the form. (Click the form, then press <b>Ctrl+L</b> -- lay out vertically.) Resize the form until it is a pleasing size and shape.</p>
<p align="center"><img align="middle" src="dialog3layout.png" width="302" height="276">
</p>
<h5><a name="3-1-1"></a>Connecting the Widgets</h5>
<p>The options dialog is a traditional dialog: the caller will create it, set its widgets and if the user clicks <b>OK</b>, the caller will read the data from the form's widgets and act accordingly. All we need to do is connect up the OK and Cancel buttons; there's no code to write at all.</p>
<p>Connect the OK button to the form's <tt>accept()</tt> slot and the Cancel button to the form's <tt>reject()</tt> slot. (Click <b>Edit|Connections</b>, then click <b>New</b>. Change the Sender to "okPushButton", the signal to "clicked()", the Receiver to "OptionsForm" and the slot to "accept()". Click <b>New</b> again. Change the Sender to "cancelPushButton", the signal to "clicked()", the Receiver to "OptionsForm" and the slot to "reject()". Click <b>OK</b> to close the connections dialog.)</p>
<h4><a name="3-2"></a>Using the Dialog</h4>
<p>Click on "mainform.ui.h" in the Project Overview to invoke the code editor and to set the MainForm as <em>Qt Designer</em>'s current form.</p>
<p>We need to create a slot that will invoke the options form and to connect the optionsAction action to this slot.</p>
<p>Invoke the <em>View and Edit Connections</em> dialog and create a new connection from the optionsAction to a new slot called "editOptions". (Click <b>Edit|Connections</b>, then click <b>New</b>. Change the Sender to "optionsAction", the Signal to "activated()" and the Receiver to "MainForm". Click the <b>Edit Slots</b> button to invoke the <em>Edit Functions</em> dialog. Click <b>New Function</b> and change the slot's name to "editOptions()", then click <b>OK</b>. Make the Slot function the newly created "editOptions()" slot. Click <b>OK</b>.)</p>
<p>Now we're ready to code the slot.</p>
<h5><a name="3-2-1"></a>editOptions()</h5>
<pre> void MainForm::editOptions()
{
OptionsForm *options = new OptionsForm( this, "options", TRUE );
switch ( m_clip_as ) {
case CLIP_AS_HEX:
options->hexRadioButton->setChecked( TRUE );
break;
case CLIP_AS_NAME:
options->nameRadioButton->setChecked( TRUE );
break;
case CLIP_AS_RGB:
options->rgbRadioButton->setChecked( TRUE );
break;
}
options->webCheckBox->setChecked( m_show_web );
if ( options->exec() ) {
if ( options->hexRadioButton->isChecked() )
m_clip_as = CLIP_AS_HEX;
else if ( options->nameRadioButton->isChecked() )
m_clip_as = CLIP_AS_NAME;
else if ( options->rgbRadioButton->isChecked() )
m_clip_as = CLIP_AS_RGB;
m_table_dirty = m_show_web !=
options->webCheckBox->isChecked();
m_show_web = options->webCheckBox->isChecked();
populate();
}
}
</pre>
<p>We create a new options form, passing it TRUE to make it modal. We set the radio buttons depending on the current setting of the <tt>m_clip_as</tt> variable. We set the check box to correspond with the <tt>m_show_web</tt> variable. We execute the form, and if the user clicks <b>OK</b>, we reflect their choices back into the relevant main form variables. If the user changed the <tt>m_show_web</tt> variable (by clicking the webCheckBox), we mark the table as "dirty" since it will need updating. We then call <tt>populate()</tt> which will update the table view if required.</p>
<p>Because we use our OptionsForm and access its radio buttons and checkbox we must add "optionsform.h", "qradiobutton.h" and "qcheckbox.h" to our includes in implementation. (Click Object Explorer's Members tab, right click "Includes (in Implementation)", then click <b>Edit</b>. Click <b>Add</b> and enter "optionsform.h"; click <b>Add</b> again and enter "qradiobutton.h"; click <b>Add</b> again and enter "qcheckbox.h". Press <b>Enter</b>, then click <b>Close</b>.)</p>
<p>You should now have added the following declarations to your includes (in implementation):</p>
<ul><li><p>"optionsform.h"</p>
<li><p>"qcheckbox.h"</p>
<li><p>"qradiobutton.h"</p>
</ul><p>Now the user can change options to suit their own preferences. But these option settings will be lost when they exit the application. We'll finish off by adding functions to load and save the user's settings.</p>
<h3><a name="4"></a>Saving and Loading Settings</h3>
<p>Logically we think of loading settings first, e.g. at application start up, and of saving settings last, e.g. at application termination. But we will code saving settings first, since then we'll know what it is that we must load.</p>
<p>Qt 3.0 introduced a new class <a href="qsettings.html">QSettings</a>, that handles user settings in a platform independent way (e.g. it uses the registry on windows and rc files on Unix). Add the "qsettings.h" header to the includes in implementation. (Click Object Explorer's Members tab, right click "Includes (in Implementation)", click <b>New</b>, enter "qsettings.h", then press <b>Enter</b>.)</p>
<p>You should now have added the following declaration to your includes (in implementation):</p>
<ul><li><p>"qsettings.h"</p>
</ul><h4><a name="4-1"></a>saveSettings()</h4>
<pre> void MainForm::saveSettings()
{
<a href="qsettings.html">QSettings</a> settings;
settings.<a href="qsettings.html#insertSearchPath">insertSearchPath</a>( QSettings::Windows, WINDOWS_REGISTRY );
settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowWidth", width() );
settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowHeight", height() );
settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowX", x() );
settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "WindowY", y() );
settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "ClipAs", m_clip_as );
settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "ShowWeb", m_show_web );
settings.<a href="qsettings.html#writeEntry">writeEntry</a>( APP_KEY + "View",
colorWidgetStack->visibleWidget() == tablePage );
}
</pre>
<p>The <tt>insertSearchPath()</tt> call should be made on all platforms (it simply returns if called on a platform where it doesn't apply). We save the main form's window dimensions, plus the user's preferences for the clipboard and web color indicator. We also record the user's view. We'll call this function when the user exits the application, so now we'll produce our third and final version of the <tt>fileExit()</tt> function.</p>
<h5><a name="4-1-1"></a>fileExit()</h5>
<pre> void MainForm::fileExit()
{
if ( okToClear() ) {
saveSettings();
QApplication::<a href="qapplication.html#exit">exit</a>( 0 );
}
}
</pre>
<p>If the exit takes place we automatically save the user's settings.</p>
<h4><a name="4-2"></a>loadSettings()</h4>
<pre> void MainForm::loadSettings()
{
<a href="qsettings.html">QSettings</a> settings;
settings.<a href="qsettings.html#insertSearchPath">insertSearchPath</a>( QSettings::Windows, WINDOWS_REGISTRY );
int windowWidth = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowWidth", 550 );
int windowHeight = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowHeight", 500 );
int windowX = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowX", 0 );
int windowY = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "WindowY", 0 );
m_clip_as = settings.<a href="qsettings.html#readNumEntry">readNumEntry</a>( APP_KEY + "ClipAs", CLIP_AS_HEX );
m_show_web = settings.<a href="qsettings.html#readBoolEntry">readBoolEntry</a>( APP_KEY + "ShowWeb", TRUE );
if ( ! settings.<a href="qsettings.html#readBoolEntry">readBoolEntry</a>( APP_KEY + "View", TRUE ) ) {
colorWidgetStack->raiseWidget( iconsPage );
viewIconsAction->setOn( TRUE );
}
</pre>
<p>We read in the settings using default values if there are not settings (i.e. if the settings were deleted or if this is the first time the user has run the application). Again, the <tt>insertSearchPath()</tt> call should be made on all platforms.</p>
<p>In response to the settings we switch to the view the user was looking at when they last exited the program. We also resize and move the main window to the size and position it was last used in.</p>
<p>Uncomment the "loadSettings();" line in the <tt>init()</tt> function.</p>
<p>Build the application and run it. Change to the icon view and change one or two of the options in the options dialog. Move the window and resize it. Exit the application and restart it; your view, window size and position and your options are all restored. (If it doesn't build see the <a href="designer-manual-4.html#6">Troubleshooting</a> section.)</p>
<h3><a name="5"></a>Wrapping Up</h3>
<p>The <tt>colortool</tt> application is now complete. There are many possible extensions to its functionality that you might like to try, for example:</p>
<ul><li><p>Adding additional columns to the table view to show RGB values (0..255), scaled RGB values (0.00..1.00), HSV values, etc.</p>
<li><p>Providing a case sensitive checkbox in the Find dialog and updating the code to make use of it.</p>
<li><p>Creating your own color selection dialog that provides the same functionality as the built-in color selection dialog, but which also allows the user to name the color.</p>
</ul><p><em>Qt Designer</em>'s primary benefit is that it makes designing (and redesigning) forms both fast and easy. Laying out widgets is a simple two step process: select two or more widgets or layouts, then apply a layout (vertical, horizontal or grid) to them. If a layout doesn't look right, simply press <b>Ctrl+Z</b> to undo immeditately, or click the layout and press <b>Ctrl+B</b> (break layout) later. <em>Qt Designer</em> fully supports unlimited undo and redo, so it is easy and safe to experiment. (If you're interested in how <em>Qt Designer</em>'s layouts translate into code, look at the <tt>.cpp</tt> files that are generated when you build the application.)</p>
<p>The rest of this part of the manual covers more advanced features and the second part is a reference section covering all <em>Qt Designer</em>'s dialogs, menu options and toolbar buttons. We recommend that you spend some time experiementing with <em>Qt Designer</em>'s layouts. Try for example, to reproduce a form that has the look and resizing characteristics of the multiclip example (in <tt>qt/tools/designer/examples/multiclip</tt>).</p>
<h3><a name="6"></a>Troubleshooting</h3>
<p>The most likely source of error is if you missed out or misspelled an include file, a forward declaration or a variable. Check them against the following lists and insert or correct them as appropriate. (In all cases look at <em>Object Explorer</em>'s Members tab; if you need to edit, right click the relevant section, then click Edit to invoke the relevant dialog.)</p>
<h4><a name="6-1"></a>The MainForm Members</h4>
<p>Class Variables:</p>
<ul><li><p>FindForm *findForm;</p>
<li><p>QClipboard *clipboard;</p>
<li><p>QMap<QString,QColor> m_colors;</p>
<li><p>bool m_show_web;</p>
<li><p>int m_clip_as;</p>
<li><p>bool m_icons_dirty;</p>
<li><p>bool m_table_dirty;</p>
<li><p>bool m_changed;</p>
<li><p>QString m_filename;</p>
<li><p>QStringList m_comments;</p>
</ul><p>Forward Declarations:</p>
<ul><li><p>class QColor;</p>
<li><p>class QString;</p>
</ul><p>Includes (in Declaration):</p>
<ul><li><p>"findform.h"</p>
</ul><p>Includes (in Implementation):</p>
<ul><li><p>"optionsform.h"</p>
<li><p>"qlineedit.h"</p>
<li><p>"qlabel.h"</p>
<li><p>"qclipboard.h"</p>
<li><p>"qmessagebox.h"</p>
<li><p>"qstatusbar.h"</p>
<li><p>"qpainter.h"</p>
<li><p>"qstring.h"</p>
<li><p>"qcolor.h"</p>
<li><p>"qapplication.h"</p>
<li><p>"qfiledialog.h"</p>
<li><p>"qfile.h"</p>
<li><p>"qregexp.h"</p>
<li><p>"qcolordialog.h"</p>
<li><p>"colornameform.h"</p>
<li><p>"qcheckbox.h"</p>
<li><p>"qradiobutton.h"</p>
<li><p>"qsettings.h"</p>
</ul><h4><a name="6-2"></a>ColorNameForm Members</h4>
<p>We put all the ColorNameForm declarations in the source code file. The file <tt>colornameform.ui.h</tt> should begin with the following declarations:</p>
<pre> #include <<a href="qcolor-h.html">qcolor.h</a>>
#include <<a href="qmap-h.html">qmap.h</a>>
#include <<a href="qstring-h.html">qstring.h</a>>
QMap<QString,QColor> m_colors;
</pre>
<h4><a name="6-3"></a> FindForm Members</h4>
<p>Signals:</p>
<ul><li><p>lookfor(const QString&)</p>
</ul><h4><a name="6-4"></a>OptionsForm Members</h4>
<p>The OptionsForm has no members.</p>
<h4><a name="6-5"></a>main.cpp Members</h4>
<p>This file should begin with the following declarations:</p>
<pre> #include <<a href="qapplication-h.html">qapplication.h</a>>
#include "mainform.h"
</pre>
<!-- eof -->
<p align="right">[<a href="designer-manual-3.html">Prev: Creating a Main Window Application</a>] [<a href="designer-manual.html">Home</a>] [<a href="designer-manual-5.html">Next: The Designer Approach</a>]</p>
<p><address><hr><div align=center>
<table width=100% cellspacing=0 border=0><tr>
<td>Copyright © 2007
<a href="troll.html">Trolltech</a><td align=center><a href="trademarks.html">Trademarks</a>
<td align=right><div align=right>Qt 3.3.8</div>
</table></div></address></body>
</html>
|