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
|
<!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-subclassing.leaf:3 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Subclassing and Dynamic 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-5.html">Prev: The Designer Approach</a>] [<a href="designer-manual.html">Home</a>] [<a href="designer-manual-7.html">Next: Creating Custom Widgets</a>]</p>
<h2 align="center">Subclassing and Dynamic Dialogs</h2>
<!-- index Subclassing --><!-- index Dynamic Dialogs --><!-- index Dialogs!Dynamic --><p>This chapter describes two different approaches that you can take to creating forms with <em>TQt Designer</em>. Subclassing is used to extend the functionality of a form by creating your own class based upon a form you create in <em>TQt Designer</em>. Dynamic dialogs are<!-- index .ui --> <tt>.ui</tt> files which can be executed by a TQt application; this keeps the GUI design and the code separate and is useful in environments where the GUI may have to change more often than the underlying application logic.</p>
<h3><a name="1"></a>Subclassing</h3>
<p>We'll start with a general description of how to subclass a form and follow with a short example. Note that subclassing has some disadvantages compared with putting your code into a form directly; see <a href="designer-manual-5.html#3">Extending the functionality of a form</a> in <a href="designer-manual-5.html#the-designer-approach">The Designer Approach</a> chapter for details.</p>
<h4><a name="1-1"></a>Generating Source Code from <em>TQt Designer</em> .ui Files</h4>
<p><em>TQt Designer</em> reads and writes <tt>qmake</tt><!-- index .pro --> <tt>.pro</tt> (project) files which are used to record the files used to build the application and from which Makefiles are generated. <em>TQt Designer</em> also reads and writes<!-- index .ui --> <tt>.ui</tt> (user interface) files. These are XML files that record the widgets, layouts, source code and settings you've used for a form. Every<!-- index .ui --> <tt>.ui</tt> file is converted by the <tt>uic</tt> (user interface compiler) into a C++<!-- index .h --> <tt>.h</tt> file and a C++<!-- index .cpp --> <tt>.cpp</tt> file. These C++ files are then read by <tt>moc</tt> (meta object compiler), and finally compiled by your compiler into a working application.</p>
<!-- index Makefiles --><!-- index Projects!Adding Files --><!-- index Adding!Files to Projects --><p>If you create applications wholly within <em>TQt Designer</em> you only need to create a<!-- index main.cpp --> <tt>main.cpp</tt>.</p>
<p>If you create the <tt>main.cpp</tt> file within <em>TQt Designer</em>, it will automatically be added to your project file by <em>TQt Designer</em>. If you create the <tt>main.cpp</tt> file outside of <em>TQt Designer</em> you must add it to the project file manually by adding the following line at the end of your project's<!-- index .pro --> <tt>.pro</tt> file:</p>
<pre>
SOURCES += main.cpp
</pre>
<p>You can then use <tt>qmake</tt> to generate the Makefile. (For example <tt>qmake -o Makefile myproject.pro</tt>.) Running <tt>make</tt> (Linux, Unix or Borland compilers), or <tt>nmake</tt> (Visual C++), will then call <tt>uic</tt>, <tt>moc</tt> and your compiler as necessary to build your application.</p>
<!-- index Errors!Undefined reference --><!-- index Undefined references, Error --><!-- index qmake!HEADERS --><!-- index qmake!SOURCES --><p>If you use <em>TQt Designer</em> to create your main window and dialogs, but also add other C++ files, or if you subclass any of your forms you will need to add these files to the<!-- index .pro --> <tt>.pro</tt> file so that they are compiled with the rest of your application's source files. Each<!-- index .h --> <tt>.h</tt> file that you create separately from <em>TQt Designer</em> should be added to the <tt>HEADERS</tt> line, and each<!-- index .cpp --> <tt>.cpp</tt> file should be added to the <tt>SOURCES</tt> line, just as we've done for<!-- index main.cpp --> <tt>main.cpp</tt>. If you get undefined reference errors it is worth checking that you've added the names of all your header and implementation files to the<!-- index .pro --> <tt>.pro</tt> file.</p>
<h4><a name="1-2"></a>Subclassing a Form</h4>
<!-- index Subclassing --><p>When subclassing a form it is helpful to use a naming convention to help us identify which files are generated from <em>TQt Designer</em>'s<!-- index .ui --> <tt>.ui</tt> files and which are hand coded.</p>
<p>Suppose, for example, that we are developing a dialog and writing the code directly in <em>TQt Designer</em>. We might call our dialog 'OptionsForm' and the<!-- index .ui --> <tt>.ui</tt> file, <tt>optionsform.ui</tt>. The automatically generated files will be <tt>optionsform.h</tt> and <tt>optionsform.cpp</tt>.</p>
<p>If we were developing another dialog, but this time one that we intended to subclass, we want to make it easy to distinguish between the automatically generated files and our hand coded files. For example, we might call our dialog 'SettingsFormBase' and the<!-- index .ui --> <tt>.ui</tt> file <tt>settingsformbase.ui</tt>. The automatically generated files would then be called <tt>settingsformbase.h</tt> and <tt>settingsformbase.cpp</tt>. We would then call our subclass 'SettingsForm' and code it in the files <tt>settingsform.h</tt> and <tt>settingsform.cpp</tt>.</p>
<!-- index TQ_OBJECT!Macros --><!-- index Macros!TQ_OBJECT --><!-- index Signals and Slots!TQ_OBJECT --><p>Any subclass of a form should include the <tt>TQ_OBJECT</tt> macro so that slots and signals will work correctly. Once you've created your subclass be sure to add the<!-- index .h --> <tt>.h</tt> and the<!-- index .cpp --> <tt>.cpp</tt> files to the<!-- index .pro --> <tt>.pro</tt> project file. For example we would add the following lines for our subclassed 'SettingsForm' at the end of the<!-- index .pro --> <tt>.pro</tt> file:</p>
<pre>
HEADERS += settingsform.h
SOURCES += settingsform.cpp
</pre>
<p>The simplest way to create a new source file is by clicking <b>File|New</b> to invoke the 'New File' dialog, then click 'C++ Source' or 'C++ Header' as appropriate, then click <b>OK</b>. A new empty source window will appear. You don't need to manually edit the <tt>.pro</tt> file since <em>TQt Designer</em> will add them for you automatically.</p>
<p><em>TQt Designer</em> will have added</p>
<pre>
FORMS = settingsformbase.ui
</pre>
<p>to the project file. The <tt>settingsformbase.h</tt> and <tt>settingsformbase.cpp</tt> files will be generated from the<!-- index .ui --> <tt>.ui</tt> file automatically.</p>
<h4><a name="1-3"></a>A Subclassing Example</h4>
<p>We will write a small example dialog to show the use of subclassing in practice. The dialog will present a choice of customer credit ratings with an option of choosing a 'special' rating for which a specific amount must be given. We'll implement the functionality in a subclass. We'll start by creating the base form and connecting its signals and slots, then we'll create the subclass and a simple<!-- index main.cpp --> <tt>main.cpp</tt> so that we can test it.</p>
<h5><a name="1-3-1"></a>Designing the Form</h5>
<!-- index Projects!Creating New --><p>We'll begin by creating a new project. Click <b>File|New</b>, then click the 'C++ Project' icon to invoke the <em>Project Settings</em> dialog. Click the ellipsis button to invoke the <em>Save As</em> dialog; navigate to the project's directory (creating it if necessary). Make sure you're in the project's directory, then enter a project name of 'credit.pro'. Click the <b>Save</b> button to return to the <em>Project Settings</em> dialog, then click <b>OK</b>. Now we'll add a form to the project. Click <b>File|New</b> to invoke the <em>New File</em> dialog. The default form is Dialog which is what we want; click <b>OK</b>. Resize the form to make it smaller; it should be about 2 inches (5 cm) square. Change the form's name to 'CreditFormBase' and the caption to 'Credit Rating'. Save the form as <tt>creditformbase.ui</tt>.</p>
<p>We'll now add the widgets we need.</p>
<ol type=1><li><p>Click the <b>Button Group</b> toolbar button, then click near the top left of the form. Resize the button group so that it takes up approximately half the form. Change the button group's <em>name</em> to 'creditButtonGroup' and its <em>title</em> property to 'Credit Rating'.</p>
<li><p>We'll now add some radio buttons. <em>Double</em> click the <b>Radio Button</b> toolbar button. Click towards the top of the Credit Rating button group and a radio button will appear. Click below this button, to create a second radio button, then click below the second button to create a third. Now we will switch off the effect of the <em>double</em> click by clicking the <b>Pointer</b> (arrow) toolbar button. The pointer will now behave normally, i.e. clicking the form will no longer create more radio buttons. Change the first radio button's <em>name</em> to 'stdRadioButton' and its text to '&Standard'. Change its <em>checked</em> property to True. Change the second button's name to 'noneRadioButton' and its text to '&None'. Change the third radio button's properties to 'specialRadioButton' and 'Sp&ecial' respectively.</p>
<li><p>If the user chooses the special credit rating they must specify an amount. Click the <b>SpinBox</b> toolbar button and click the form just below the button group. Change the spin box's <em>name</em> to 'amountSpinBox'. Change its <em>prefix</em> to '$ ' (note the space), its <em>maxValue</em> to '100000' and its <em>lineStep</em> to '10000'. Change its <em>enabled</em> property to False.</p>
<li><p>Click the <b>Push Button</b> toolbar button and click the form below the spin box. Change the button's <em>name</em> to 'okPushButton', its <em>text</em> to 'OK' and its <em>default</em> property to 'True'. Add a second button to the right of the first. Change the second button's <em>name</em> to 'cancelPushButton' and its <em>text</em> to 'Cancel'.</p>
</ol><p>We'll now lay out the widgets and connect up the slots we need.</p>
<ol type=1><li><p>Click the credit rating group box then press <b>Ctrl+L</b> (lay out vertically).</p>
<li><p>Click the form so that the button group is no longer selected. <b>Ctrl+Click</b> the OK button and drag the rubber band to touch the Cancel button, then release. Press <b>Ctrl+H</b>.</p>
<li><p>Click the form, then press <b>Ctrl+L</b>.</p>
<!-- index Layouts!Spacers --><p>The widgets will be laid out vertically, each one stretching to fill up the maximum space both vertically and horizontally. The buttons look rather large since they've expanded to take up the full width of the form. It might look more attractive to make the buttons smaller using spacers. Click the OK button, then press <b>Ctrl+B</b> (break layout). Resize both buttons to make them narrower leaving space on either side of them. Click the <b>Spacer</b> toolbar button then click to the left of the OK button; click Horizontal from the pop up spacer menu. Copy this spacer and place the copy between the two buttons. Copy the spacer again and place the copy to the right of the Cancel button. (For the second and third spacers, click on the first spacer, press <b>Ctrl+C</b> then <b>Ctrl+V</b>. Drag the new spacer to the desired position.) Ctrl+Click the left most spacer and drag the rubber band so that it touches the buttons and the spacers, then release. Press <b>Ctrl+H</b>. Click the form then press <b>Ctrl+L</b>.</p>
</ol><!-- index Signals and Slots --><p>We'll now connect the signals and slots. Click <b>Edit|Connections</b> to invoke the <em>View and Edit Connections</em> dialog.</p>
<p>Create a new connection that connects the OK button's clicked() signal to the form's accept() slot. Create a second connection that connects the Cancel button's clicked() signal to the form's reject() slot. (See <a href="designer-manual-3.html#creating-signals-and-slots-connections-sidebar">Creating Signals and Slots Connections</a>.)</p>
<p>We want the amount spin box to be enabled only if the special radio button is checked. Create another connection, this time connecting the special radio button's toggled() signal to the amount spin box's setEnabled() slot.</p>
<p>If the user checks the standard or none radio buttons we want to set the amount accordingly. Connect the credit rating button group's clicked() signal to a new custom setAmount() slot (which you create by clicking the <b>Edit Slots...</b> button).</p>
<p>We'll subclass the form to set the amount in the spin box depending on which radio button is checked. Save the form as 'creditformbase.ui' (press <b>Ctrl+S</b>).</p>
<h5><a name="1-3-2"></a>Creating the Test Harness</h5>
<!-- index Forms!Creating Test Harnesses --><!-- index Creating Test Harnesses for Forms --><!-- index Subclassing --><p>Although we intend our dialog to be used within an application it is useful to create a test harness so that we can develop and test it stand-alone. Click <b>File|New</b> to invoke the 'New File' dialog, then click 'C++ Source', then click <b>OK</b>. In the editor window that pops up, enter the following code:</p>
<pre>
#include <ntqapplication.h>
#include "creditformbase.h"
int main( int argc, char *argv[] )
{
TQApplication app( argc, argv );
CreditFormBase creditForm;
app.setMainWidget( &creditForm );
creditForm.show();
return app.exec();
}
</pre>
<p>Note that we're including <tt>creditformbase.h</tt> and instantiating a CreditFormBase object; once we've written our subclass we'll replace the header with our subclass, <tt>creditform.h</tt>, and instantiate a CreditForm.</p>
<p>We can now generate the application with <tt>qmake</tt>, e.g. <tt>qmake -o Makefile credit.pro</tt>, make it and run it. The form should run fine, but doesn't yet have the behaviour we require.</p>
<h5><a name="1-3-3"></a>Creating the Subclass</h5>
<p>We need to create a header and an implementation file for our subclass. The code for our subclass is minimal. The header file is <tt>qt/tools/designer/examples/credit/creditform.h</tt>:</p>
<pre> #include "creditformbase.h"
class CreditForm : public CreditFormBase
{
TQ_OBJECT
public:
CreditForm( <a href="ntqwidget.html">TQWidget</a>* parent = 0, const char* name = 0,
bool modal = FALSE, WFlags fl = 0 );
~CreditForm();
public slots:
void setAmount();
};
</pre>
<!-- index Macros!TQ_OBJECT --><!-- index TQ_OBJECT --> <p>We've declared the slot, <tt>setAmount()</tt>, that we created in <em>TQt Designer</em>. The <tt>TQ_OBJECT</tt> macro is included because it is essential for classes that use signals and slots.</p>
<p>The implementation in <tt>qt/tools/designer/examples/credit/creditform.cpp</tt> is simple:</p>
<pre> #include <<a href="qradiobutton-h.html">ntqradiobutton.h</a>>
#include <<a href="qspinbox-h.html">ntqspinbox.h</a>>
#include "creditform.h"
CreditForm::CreditForm( <a href="ntqwidget.html">TQWidget</a>* parent, const char* name,
bool modal, WFlags fl )
: CreditFormBase( parent, name, modal, fl )
{
setAmount();
}
CreditForm::~CreditForm() { /* NOOP */ }
void CreditForm::setAmount()
{
if ( stdRadioButton->isChecked() )
amountSpinBox->setValue( amountSpinBox->maxValue() / 2 );
else if ( noneRadioButton->isChecked() )
amountSpinBox->setValue( amountSpinBox->minValue() );
}
</pre>
<p>We call <tt>setAmount()</tt> in the constructor to ensure that the correct amount is shown when the form starts based on whichever radio button we checked in <em>TQt Designer</em>. In <tt>setAmount()</tt> we set the amount if the standard or none radio button is checked. If the user has checked the special radio button they are free to change the amount themselves.</p>
<!-- index Makefiles --><!-- index qmake!HEADERS --><!-- index qmake!SOURCES --><p>To be able to test our subclass we change<!-- index main.cpp --> <tt>main.cpp</tt> to include <tt>creditform.h</tt> rather than <tt>creditformbase.h</tt> and change the instantiation of the creditForm object:</p>
<pre> #include <<a href="qapplication-h.html">ntqapplication.h</a>>
#include "creditform.h"
int main( int argc, char *argv[] )
{
<a href="ntqapplication.html">TQApplication</a> app( argc, argv );
CreditForm creditForm;
app.<a href="ntqapplication.html#setMainWidget">setMainWidget</a>( &creditForm );
creditForm.show();
return app.<a href="ntqapplication.html#exec">exec</a>();
}
</pre>
<p>If you created the <tt>creditform.h</tt> and <tt>creditform.cpp</tt> files in <em>TQt Designer</em>, they are already in the project file, but if you created them manually you must also update the project file by adding these two new lines at the end:</p>
<pre>
HEADERS += creditform.h
SOURCES += creditform.cpp
</pre>
<p>To test the form rerun <tt>qmake</tt> to regenerate the Makefile, then make and run.</p>
<p>The subclassing example we've used is simple, but this reflects subclassing forms in TQt: it is easy to do.</p>
<a name="dynamicdialogs"></a><h3><a name="2"></a>Creating Dynamic Dialogs from .ui Files</h3>
<!-- index Dynamic Dialogs --><!-- index Dialogs!Dynamic --><!-- index Code Editing!Not in Dynamic Dialogs --><p>TQt programs are capable of loading <em>TQt Designer</em><!-- index .ui --> <tt>.ui</tt> files and instantiating the forms represented by the<!-- index .ui --> <tt>.ui</tt> files. Since the<!-- index .ui --> <tt>.ui</tt> file is not compiled it cannot include any C++ code, (e.g. slot implementations). In this section we will explain how to load a dynamic dialog and how to create a class that can be used to implement the dynamic dialog's custom slots.</p>
<p>We will use the credit form that we created in the subclassing section as our example form. We will start by simply instantiating and running the form and then we'll cover how to implement custom slots.</p>
<p>We'll create a<!-- index main.cpp --> <tt>main.cpp</tt> file to use as a test harness, and manually create a project file.</p>
<h4><a name="2-1"></a>Creating the Project File</h4>
<p>The project file <tt>qt/tools/designer/examples/receiver1/receiver.pro</tt> looks like this:</p>
<pre>
TEMPLATE = app
CONFIG += qt warn_on release
TARGET = receiver
SOURCES += main.cpp
unix:LIBS += -ltqui
win32:LIBS += $(QTDIR)/lib/tqui.lib
FORMS = mainform.ui
LANGUAGE = C++
INCLUDEPATH += $(QTDIR)/tools/designer/uilib
</pre>
<p>We do <em>not</em> include the <tt>creditformbase.ui</tt> file since this file will be read at runtime, as we'll see shortly. We must include the <tt>tqui</tt> library since the functionality we require is not part of the standard TQt library.</p>
<h4><a name="2-2"></a>Creating main.cpp</h4>
<p>The<!-- index main.cpp --> <tt>main.cpp</tt> is quite standard. It will invoke the form we're going to create in <em>TQt Designer</em> as its main form. This form will then load and execute the dynamic dialog.</p>
<pre> #include <<a href="qapplication-h.html">ntqapplication.h</a>>
#include "mainform.h"
int main( int argc, char *argv[] )
{
<a href="ntqapplication.html">TQApplication</a> app( argc, argv );
MainForm *mainForm = new MainForm;
app.<a href="ntqapplication.html#setMainWidget">setMainWidget</a>( mainForm );
mainForm->show();
return app.<a href="ntqapplication.html#exec">exec</a>();
}
</pre>
<p>We create a new instance of our MainForm class, set it to be the main widget, show it and enter the event loop in the <tt>app.exec()</tt> call.</p>
<h4><a name="2-3"></a>Creating the Main Form</h4>
<h5><a name="2-3-1"></a>Designing the Form</h5>
<ol type=1><li><p>Open the<!-- index receiver.pro --> <tt>receiver.pro</tt> project file in <em>TQt Designer</em>. We'll create a dialog as our main window which we'll use to invoke the dynamic dialog. Press <b>Ctrl+N</b> to launch the <em>New File</em> dialog and click <b>OK</b> to get the default which is a dialog. Change the dialog's name to 'MainForm' and its caption to 'Main Form'. Add two buttons, one called 'creditPushButton' with the text '&Credit Dialog', and the other called 'quitPushButton' with the text '&Quit'. (For each button click the <b>Push Button</b> toolbar button, then click the form. Change the properties in the property window to those we've just described.)</p>
<li><p>We will now add a couple of labels so that we can show the settings the user chose in the dynamic dialog. Click the <b>Text Label</b> toolbar button, then click the form below the Credit Dialog button. Change the label's <em>text</em> to 'Credit Rating'. Add another text label below the Quit button. Change its <em>name</em> to 'ratingTextLabel' and its <em>text</em> to 'Unrated'.</p>
<li><p>We'll now lay out the widgets. Click the form then press <b>Ctrl+G</b> (lay out in a grid).</p>
<li><!-- index Signals and Slots!Connecting to Close a Dialog --><p>We'll now handle the signals and slots connections. Invoke the <em>View and Edit Connections</em> dialog and connect the credit dialog button's clicked() signal to a new creditDialog() custom slot (which is created by clicking the <b>Edit Slots...</b> button). Now connect the Quit button's<!-- index clicked() --> <tt>clicked()</tt> signal to the dialog's<!-- index accept() --> <tt>accept()</tt> function.</p>
</ol><p>Save the form and call it <tt>mainform.ui</tt>. (Press <b>Ctrl+S</b> and enter the filename.) In the next section we'll write the code for loading and launching the dynamic dialog directly in <em>TQt Designer</em>.</p>
<h5><a name="2-3-2"></a>Loading and Executing a Dynamic Dialog</h5>
<!-- index Dynamic Dialogs!Loading and Executing --><!-- index Dialogs!Dynamic --><!-- index Forms!Forward declarations --><!-- index Forward declarations --><!-- index Includes --><p>We'll now add the code to invoke the credit dialog. Before we can do this we need to add the widget factory's header file to the form. Click the Source tab in the Object Hierarchy. Right click Includes (in Implementation), then click <b>New</b>. Type in '<tt><ntqwidgetfactory.h></tt>', then press <b>Enter</b>. Because we will need to access the spin box in the dynamic dialog we must add its header file. Right click Includes (in Implmentation), then click <b>New</b>. Type in '<tt><ntqspinbox.h></tt>', then press <b>Enter</b>.</p>
<p>In our main form we created a slot called <tt>creditDialog()</tt>. We will implement this slot directly in <em>TQt Designer</em> and use it to load and execute the dynamic dialog. The code is taken from <tt>qt/tools/designer/examples/receiver1/mainform.ui.h</tt> which contains the C++ implementation of <tt>mainform.ui</tt>'s slots.</p>
<pre> void MainForm::creditDialog()
{
<a href="ntqdialog.html">TQDialog</a> *creditForm = (TQDialog *)
TQWidgetFactory::<a href="ntqwidgetfactory.html#create">create</a>( "../credit/creditformbase.ui" );
// Set up the dynamic dialog here
if ( creditForm-><a href="ntqdialog.html#exec">exec</a>() ) {
// The user accepted, act accordingly
<a href="ntqspinbox.html">TQSpinBox</a> *amount = (TQSpinBox *) creditForm-><a href="ntqobject.html#child">child</a>( "amountSpinBox", "TQSpinBox" );
if ( amount )
ratingTextLabel->setText( amount-><a href="ntqspinbox.html#text">text</a>() );
}
delete creditForm;
}
</pre>
<p>The<!-- index create() --> <tt>create()</tt> function is a static <a href="ntqwidgetfactory.html">TQWidgetFactory</a> function. It loads the specified<!-- index .ui --> <tt>.ui</tt> file and returns a pointer to the toplevel <a href="ntqwidget.html">TQWidget</a> created from the<!-- index .ui --> <tt>.ui</tt> file. We have cast the pointer to <a href="ntqdialog.html">TQDialog</a> since we know that the <tt>creditformbase.ui</tt> file defines a <a href="ntqdialog.html">TQDialog</a>. After creating the dialog we<!-- index exec() --> <tt>exec()</tt> it. If the user clicked <b>OK</b> the dialog returns Accepted and we enter the body of the <tt>if</tt> statement. We want to know the amount of credit that the user selected. We call the<!-- index child() --> <tt>child()</tt> function on the dialog passing it the name of the widget we're interested in. The<!-- index child() --> <tt>child()</tt> function returns a pointer to the widget with the name we passed, or returns 0 if no widget of that name was found. In the example we call<!-- index child() --> <tt>child()</tt> to get a pointer to the 'amountSpinBox'. If the pointer we get back is not 0 we set the rating text to the amount in the dialog's spin box. At the end we delete the dynamic dialog. Deleting the dialog ensures that we free up its resources as soon as it is no longer required.</p>
<p>We used the<!-- index child() --> <tt>child()</tt> to gain access to a widget within the dynamic dialog, passing it the name of the widget we were interested in. In some situations we might not know what a widget is called. We can access the first widget of a specified class by calling<!-- index child() --> <tt>child()</tt> with a null widget name and a classname, e.g. <tt>child(0,"TQPushButton")</tt>. This will return a pointer to the first <a href="ntqpushbutton.html">TQPushButton</a> it finds (or 0 if there isn't one). If you want pointers to all the widgets of a given class you can call the<!-- index TQObject::queryList() --> <tt>TQObject::queryList()</tt> function, passing it the name of the class. It returns a <a href="ntqobjectlist.html">TQObjectList</a> pointer which points to every object in the dialog that is derived from the given class. See the online <a href="http://doc.trolltech.com/ntqobject.html">TQObject</a> documentation for further details.</p>
<h5><a name="2-3-3"></a>Implementing Slots for Dynamic Dialogs</h5>
<!-- index Signals and Slots!Dynamic Dialogs --><!-- index Dynamic Dialogs --><p>There is one outstanding issue that we haven't addressed: the dynamic dialog does not have the behaviour of the original credit dialog because we have not implemented the <tt>setAmount()</tt> slot. We can implement slots for dynamic dialogs by creating a <a href="ntqobject.html">TQObject</a> subclass. We then create an instance of this subclass and pass a pointer to it to the<!-- index TQWidgetFactory::create() --> <tt>TQWidgetFactory::create()</tt> function which will connect the dynamic dialog's signals to the slots implemented in our subclass.</p>
<p>We need to create a <a href="ntqobject.html">TQObject</a> subclass and change our <tt>creditDialog()</tt> to create an instance of our subclass that can be passed to the<!-- index TQWidgetFactory::create() --> <tt>TQWidgetFactory::create()</tt> function. Here is the modified <tt>creditDialog()</tt> function from the <tt>qt/tools/designer/examples/receiver2/mainform.ui.h</tt> file that contains the code for <tt>mainform.ui</tt>'s slots:</p>
<pre> void MainForm::creditDialog()
{
Receiver *receiver = new Receiver;
<a href="ntqdialog.html">TQDialog</a> *creditForm = (TQDialog *)
TQWidgetFactory::<a href="ntqwidgetfactory.html#create">create</a>( "../credit/creditformbase.ui", receiver );
receiver->setParent( creditForm );
// Set up the dynamic dialog here
if ( creditForm-><a href="ntqdialog.html#exec">exec</a>() ) {
// The user accepted, act accordingly
<a href="ntqspinbox.html">TQSpinBox</a> *amount = (TQSpinBox *) creditForm-><a href="ntqobject.html#child">child</a>( "amountSpinBox", "TQSpinBox" );
if ( amount )
ratingTextLabel->setText( amount-><a href="ntqspinbox.html#text">text</a>() );
}
delete receiver;
delete creditForm;
}
</pre>
<p>We create a new instance of our 'Receiver' subclass. (We'll write the code for this class shortly.) We then create the <a href="ntqdialog.html">TQDialog</a> using<!-- index TQWidgetFactory::create() --> <tt>TQWidgetFactory::create()</tt>. This call differs from our previous example because we pass in the subclass object so that the<!-- index create() --> <tt>create()</tt> function can set up the signals/slots connections automatically for us. Since our slot must access the widgets in the dynamic form we pass a pointer to the form to the receiver object through our <tt>setParent()</tt> function. The remainder of the function is the same as before except that we delete our receiver object.</p>
<p>Since we are using the 'Receiver' subclass in our main form we must include its header file. In <em>Object Explorer</em>'s Members tab, right click Includes (in Implmentation), then click <b>New</b>. Type in '<tt>receiver.h</tt>', then press <b>Enter</b>.</p>
<!-- index Dynamic Dialogs --><p>We'll now look at the implementation of our 'Receiver' subclass. The code is taken from <tt>qt/tools/designer/examples/receiver2/receiver.h</tt> and the corresponding <tt>receiver.cpp</tt> file. We'll start with the header file.</p>
<pre>#include <<a href="qobject-h.html">ntqobject.h</a>>
#include <<a href="qdialog-h.html">ntqdialog.h</a>>
class Receiver : public <a href="ntqobject.html">TQObject</a>
{
TQ_OBJECT
public:
void setParent( <a href="ntqdialog.html">TQDialog</a> *parent );
public slots:
void setAmount();
private:
<a href="ntqdialog.html">TQDialog</a> *p;
};
</pre>
<!-- index Macros!TQ_OBJECT --><!-- index TQ_OBJECT!Macros --><p>Our class must be a <a href="ntqobject.html">TQObject</a> subclass and because we're using signals and slots it must include the <tt>TQ_OBJECT</tt> macro. We declare a function and the <tt>setAmount()</tt> slot that we wish to implement as well as a private <a href="ntqdialog.html">TQDialog</a> pointer.</p>
<p>The implementation requires the header files of the classes it uses:</p>
<pre> #include <<a href="qradiobutton-h.html">ntqradiobutton.h</a>>
#include <<a href="qspinbox-h.html">ntqspinbox.h</a>>
#include "receiver.h"
</pre>
<p>We'll discuss the implementation of each function in <tt>receiver.cpp</tt> separately.</p>
<pre> void Receiver::setParent( <a href="ntqdialog.html">TQDialog</a> *parent )
{
p = parent;
setAmount();
}
</pre>
<p>The <tt>setParent()</tt> function assigns a pointer to the dynamic dialog to our private pointer. We could not do this in a constructor call because we have to construct our Receiver object before we call<!-- index TQWidgetFactory::create() --> <tt>TQWidgetFactory::create()</tt>, since we must pass the Receiver object to the<!-- index create() --> <tt>create()</tt> function. Once we've called<!-- index create() --> <tt>create()</tt> we then have a pointer to the dynamic dialog which we can then pass via <tt>setParent()</tt> to our Receiver class. In the subclass version of this example we called <tt>setAmount()</tt> in the constructor; but we cannot do that here because the implementation of <tt>setAmount()</tt> depends on knowledge of the dynamic dialog which is not available at construction time. Because of this we call <tt>setAmount()</tt> in the <tt>setParent()</tt> function.</p>
<pre> void Receiver::setAmount()
{
<a href="ntqspinbox.html">TQSpinBox</a> *amount =
(TQSpinBox *) p->child( "amountSpinBox", "TQSpinBox" );
<a href="ntqradiobutton.html">TQRadioButton</a> *radio =
(TQRadioButton *) p->child( "stdRadioButton", "TQRadioButton" );
if ( radio && radio-><a href="ntqradiobutton.html#isChecked">isChecked</a>() ) {
if ( amount )
amount-><a href="ntqspinbox.html#setValue">setValue</a>( amount-><a href="ntqspinbox.html#maxValue">maxValue</a>() / 2 );
return;
}
radio =
(TQRadioButton *) p->child( "noneRadioButton", "TQRadioButton" );
if ( radio && radio-><a href="ntqradiobutton.html#isChecked">isChecked</a>() )
if ( amount )
amount-><a href="ntqspinbox.html#setValue">setValue</a>( amount-><a href="ntqspinbox.html#minValue">minValue</a>() );
}
</pre>
<p>Since we may be updating the amount spin box we need to get a pointer to it. We call<!-- index child() --> <tt>child()</tt> on the pointer <tt>p</tt> which points to the dynamic dialog assigned in the <tt>setParent()</tt> call. We cast the resulting pointer to the correct type so that we can call any functions relevant to that type. In the example we call<!-- index child() --> <tt>child()</tt> to get a pointer to the amount spin box, and then call<!-- index child() --> <tt>child()</tt> again to get a pointer to the 'stdRadioButton'. If we get a pointer to the radio button and the button is checked we set the amount providing we have a pointer to the amount spin box. If this radio button was checked we're finished so we return. If the 'stdRadioButton' isn't checked we get a pointer to the 'noneRadioButton' and set the amount if this button is checked. We do nothing if the 'specialRadioButton' is checked because the user is free to enter a value of their choice.</p>
<blockquote>
<p align="center"><b> Compiling vs Dynamically Loading Dialogs</b></p>
<!-- index Dynamic Dialogs!Compared with Compiling --><!-- index Dynamic Dialogs!Subclassing --><!-- index Subclassing!Dynamic Dialogs --><p>The differences between using a 'compiled in'<!-- index .ui --> <tt>.ui</tt> file and a dynamically loaded<!-- index .ui --> <tt>.ui</tt> file are these:</p>
<ul><li><p>Dynamic dialogs cannot have any C++ code in the<!-- index .ui --> <tt>.ui</tt> file; any custom slots must be implemented via a <a href="ntqobject.html">TQObject</a> subclass. Compiled dialogs can contain code either in the<!-- index .ui --> <tt>.ui</tt> file or in a subclass.</p>
<li><p>Dynamic dialogs will load slower because the<!-- index .ui --> <tt>.ui</tt> file must be read and a <a href="ntqwidget.html">TQWidget</a> instance instantiated based on the<!-- index .ui --> <tt>.ui</tt> file's parse tree. Compiled code will load much faster because no file reading or parsing is necessary. Note that the user may not notice any difference in speed since the difference may be mere fractions of a second.</p>
<li><p>Dynamic dialogs allow you to change the<!-- index .ui --> <tt>.ui</tt> file independently of the code so long as none of the changes impact the code. This means that you can change the appearance of the form, e.g. move widgets and lay them out differently. If you want to change a compiled dialog you must change the<!-- index .ui --> <tt>.ui</tt> file and recompile. If you are building an application and want your customers to be able to customize aspects of the user interface you can give them a copy of <em>TQt Designer</em> (if your license permits this) and use dynamic dialogs.</p>
</ul></blockquote>
<!-- eof -->
<p align="right">[<a href="designer-manual-5.html">Prev: The Designer Approach</a>] [<a href="designer-manual.html">Home</a>] [<a href="designer-manual-7.html">Next: Creating Custom Widgets</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>TQt 3.3.8</div>
</table></div></address></body>
</html>
|