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
|
<!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/doc/tutorial.doc:868 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Qt Tutorial - Chapter 8: Preparing for Battle</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><h1 align=center>Qt Tutorial - Chapter 8: Preparing for Battle</h1>
<p> <center><img src="t8.png" alt="Screenshot of tutorial eight"></center>
<p> In this example, we introduce the first custom widget that can paint
itself. We also add a useful keyboard interface (with two lines of
code).
<p> <ul>
<li> <a href="t8-lcdrange-h.html">t8/lcdrange.h</a> contains the LCDRange class definition.
<li> <a href="t8-lcdrange-cpp.html">t8/lcdrange.cpp</a> contains the LCDRange implementation.
<li> <a href="t8-cannon-h.html">t8/cannon.h</a> contains the CannonField class definition.
<li> <a href="t8-cannon-cpp.html">t8/cannon.cpp</a> contains the CannonField implementation.
<li> <a href="t8-main-cpp.html">t8/main.cpp</a> contains MyWidget and main.
</ul>
<p> <h2> Line-by-line Walkthrough
</h2>
<a name="1"></a><p> <h3> <a href="t8-lcdrange-h.html">t8/lcdrange.h</a>
</h3>
<a name="1-1"></a><p> This file is very similar to the lcdrange.h in Chapter 7. We have added
one slot: setRange().
<p>
<p> <pre> void setRange( int minVal, int maxVal );
</pre>
<p> We now add the possibility of setting the range of the LCDRange.
Until now, it has been fixed at 0..99.
<p> <h3> <a href="t8-lcdrange-cpp.html">t8/lcdrange.cpp</a>
</h3>
<a name="1-2"></a><p>
<p> There is a change to the constructor (we'll discuss that later).
<p> <pre> void LCDRange::setRange( int minVal, int maxVal )
{
if ( minVal < 0 || maxVal > 99 || minVal > maxVal ) {
<a href="qapplication.html#qWarning">qWarning</a>( "LCDRange::setRange(%d,%d)\n"
"\tRange must be 0..99\n"
"\tand minVal must not be greater than maxVal",
minVal, maxVal );
return;
}
<a name="x2334"></a> slider-><a href="qrangecontrol.html#setRange">setRange</a>( minVal, maxVal );
}
</pre>
<p> SetRange() sets the range of the slider in the LCDRange. Because we
have set up the <a href="qlcdnumber.html">QLCDNumber</a> to always display two digits, we want to
limit the possible range of <tt>minVal</tt> and <tt>maxVal</tt> to 0..99 to avoid
overflow of the QLCDNumber. (We could have allowed values down to -9
but chose not to.) If the arguments are illegal, we use Qt's
<a href="qapplication.html#qWarning">qWarning</a>() function to issue a warning to the user and return
immediately. tqWarning() is a printf-like function that by default
sends its output to <tt>stderr</tt>. If you want, you can install your own handler
function using <a href="qapplication.html#qInstallMsgHandler">::qInstallMsgHandler</a>().
<p> <h3> <a href="t8-cannon-h.html">t8/cannon.h</a>
</h3>
<a name="1-3"></a><p> CannonField is a new custom widget that knows how to display itself.
<p>
<p> <pre> class CannonField : public <a href="qwidget.html">QWidget</a>
{
<a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
public:
CannonField( <a href="qwidget.html">QWidget</a> *parent=0, const char *name=0 );
</pre>
<p> CannonField inherits <a href="qwidget.html">QWidget</a>, and we use the same idiom as for LCDRange.
<p> <pre> int angle() const { return ang; }
<a href="qsizepolicy.html">QSizePolicy</a> sizePolicy() const;
public slots:
void setAngle( int degrees );
signals:
void angleChanged( int );
</pre>
<p> For the time being, CannonField only contains an angle value for which we
provide an interface using the same idiom as for value in LCDRange.
<p> <pre> protected:
void paintEvent( <a href="qpaintevent.html">QPaintEvent</a> * );
</pre>
<p> This is the second of the many event handlers in QWidget that we
encounter. This virtual function is called by Qt whenever a widget needs
to update itself (i.e., paint the widget's surface).
<p> <h3> <a href="t8-cannon-cpp.html">t8/cannon.cpp</a>
</h3>
<a name="1-4"></a><p>
<p> <pre> CannonField::CannonField( <a href="qwidget.html">QWidget</a> *parent, const char *name )
: <a href="qwidget.html">QWidget</a>( parent, name )
{
</pre>
<p> Again, we use the same idiom as for LCDRange in the previous chapter.
<p> <pre> ang = 45;
<a href="qwidget.html#setPalette">setPalette</a>( QPalette( QColor( 250, 250, 200) ) );
}
</pre>
<p> The constructor initializes the angle value to 45 degrees and sets a
custom palette for this widget.
<p> This palette uses the indicated color as background and picks other
colors suitably. (For this widget only the background and text
colors will actually be used.)
<p> <pre> void CannonField::setAngle( int degrees )
{
if ( degrees < 5 )
degrees = 5;
if ( degrees > 70 )
degrees = 70;
if ( ang == degrees )
return;
ang = degrees;
<a href="qwidget.html#repaint">repaint</a>();
emit angleChanged( ang );
}
</pre>
<p> This function sets the angle value. We have chosen a legal range of
5..70 and adjust the given number of degrees accordingly. We have
chosen not to issue a warning if the new angle is out of range.
<p> If the new angle equals the old one, we return immediately. It is
important to only emit the signal angleChanged() when the angle <em>really</em> has changed.
<p> Then we set the new angle value and repaint our widget. The <a href="qwidget.html#repaint">QWidget::repaint</a>() function clears the widget (usually filling it with
its background color) and sends a paint event to the widget. This
results in a call to the paint event function of the widget.
<p> Finally, we emit the angleChanged() signal to tell the outside world
that the angle has changed. The <tt>emit</tt> keyword is unique to Qt and
not regular C++ syntax. In fact, it is a macro.
<p> <pre> <a name="x2336"></a>void CannonField::<a href="qwidget.html#paintEvent">paintEvent</a>( <a href="qpaintevent.html">QPaintEvent</a> * )
{
<a href="qstring.html">QString</a> s = "Angle = " + QString::number( ang );
<a href="qpainter.html">QPainter</a> p( this );
<a name="x2335"></a> p.<a href="qpainter.html#drawText">drawText</a>( 200, 200, s );
}
</pre>
<p> This is our first attempt to write a paint event handler. The event
argument contains a description of the paint event. <a href="qpaintevent.html">QPaintEvent</a>
contains the region in the widget that must be updated. For the time
being, we will be lazy and just paint everything.
<p> Our code displays the angle value in the widget at a fixed position.
First we create a <a href="qstring.html">QString</a> with some text and the angle; then we create
a <a href="qpainter.html">QPainter</a> operating on this widget and use it to paint the string.
We'll come back to QPainter later; it can do a great many things.
<p> <h3> <a href="t8-main-cpp.html">t8/main.cpp</a>
</h3>
<a name="1-5"></a><p>
<p> <pre> #include "cannon.h"
</pre>
<p> We include our new class.
<p> <pre> class MyWidget: public <a href="qwidget.html">QWidget</a>
{
public:
MyWidget( <a href="qwidget.html">QWidget</a> *parent=0, const char *name=0 );
};
</pre>
<p> This time we include a single LCDRange and a CannonField in our top-level
widget.
<p> <pre> LCDRange *angle = new LCDRange( this, "angle" );
</pre>
<p> In the constructor, we create and set up our LCDRange.
<p> <pre> angle->setRange( 5, 70 );
</pre>
<p> We set the LCDRange to accept ranges from 5 to 70 degrees.
<p> <pre> CannonField *cannonField
= new CannonField( this, "cannonField" );
</pre>
<p> We create our CannonField.
<p> <pre> <a href="qobject.html#connect">connect</a>( angle, SIGNAL(valueChanged(int)),
cannonField, SLOT(setAngle(int)) );
<a href="qobject.html#connect">connect</a>( cannonField, SIGNAL(angleChanged(int)),
angle, SLOT(setValue(int)) );
</pre>
<p> Here we connect the valueChanged() signal of the LCDRange to the
setAngle() slot of the CannonField. This will update CannonField's angle
value whenever the user operates the LCDRange. We also make the reverse
connection so that changing the angle in the CannonField will update the
LCDRange value. In our example we never change the angle of the
CannonField directly; but by doing the last connect() we ensure that no
future changes will disrupt the synchronization between those two values.
<p> This illustrates the power of component programming and proper
encapsulation.
<p> Notice how important it is to emit the angleChanged() signal only when
the angle actually changes. If both the LCDRange and the CannonField
had omitted this check, the program would have entered an infinite
loop upon the first change of one of the values.
<p> <pre> <a href="qgridlayout.html">QGridLayout</a> *grid = new <a href="qgridlayout.html">QGridLayout</a>( this, 2, 2, 10 );
//2x2, 10 pixel border
</pre>
<p> So far we have used the no-assembly-required <a href="qvbox.html">QVBox</a> and <a href="qgrid.html">QGrid</a> widgets
for geometry management. Now, however, we want to have a little more
control over the layout, and we switch to the more powerful <a href="qgridlayout.html">QGridLayout</a>
class. QGridLayout isn't a widget; it is a different class that can
manage the children of <em>any</em> widget.
<p> As the comment indicates, we create a two-by-two array with ten pixel
borders. (The constructor for <a href="qgridlayout.html">QGridLayout</a> can be a little cryptic,
so it's good to put in such comments.)
<p> <pre> <a name="x2337"></a> grid-><a href="qgridlayout.html#addWidget">addWidget</a>( quit, 0, 0 );
</pre>
<p> We add the Quit button in the top-left cell of the grid: 0, 0.
<p> <pre> grid-><a href="qgridlayout.html#addWidget">addWidget</a>( angle, 1, 0, Qt::AlignTop );
</pre>
<p> We put the angle LCDRange in the bottom-left cell, aligned to the top
of its cell. (This alignment is one of the things QGridLayout allows
but QGrid does not allow.)
<p> <pre> grid-><a href="qgridlayout.html#addWidget">addWidget</a>( cannonField, 1, 1 );
</pre>
<p> We put the CannonField object in the bottom-right cell. (The top-
right cell is empty.)
<p> <pre> <a name="x2338"></a> grid-><a href="qgridlayout.html#setColStretch">setColStretch</a>( 1, 10 );
</pre>
<p> We tell <a href="qgridlayout.html">QGridLayout</a> that the right column (column 1) is stretchable.
Because the left column isn't (it has <a href="layout.html#stretch-factor">stretch factor</a> 0, the default
value), QGridLayout will try to let the left-hand widgets' sizes be
unchanged and will resize just the CannonField when the MyWidget is
resized.
<p> <pre> angle->setValue( 60 );
</pre>
<p> We set an initial angle value. Note that this will trigger the
connection from LCDRange to CannonField.
<p> <pre> <a name="x2339"></a> angle-><a href="qwidget.html#setFocus">setFocus</a>();
</pre>
<p> Our last action is to set <tt>angle</tt> to have <a href="focus.html#keyboard-focus">keyboard focus</a> so that
keyboard input will go to the LCDRange widget by default.
<p> LCDRange does not contain any keyPressEvent(), so that would seem not
to be terribly useful. However, its constructor just got a new line:
<p>
<pre> <a href="qwidget.html#setFocusProxy">setFocusProxy</a>( slider );
</pre>
<p> The LCDRange sets the slider to be its focus proxy. That means that
when someone (the program or the user) wants to give the LCDRange
keyboard focus, the slider should take care of it. <a href="qslider.html">QSlider</a> has a decent
keyboard interface, so with just one line of code we've given LCDRange
one.
<p> <h2> Behavior
</h2>
<a name="2"></a><p> The keyboard now does something - the arrow keys, Home, End, PageUp
and PageDown all do something vaguely sensible.
<p> When the slider is operated, the CannonField displays the new angle
value. Upon resizing, CannonField is given as much space as possible.
<p> On Windows machines with an 8-bit display the new background color is
dithered to death. The next chapter works around this.
<p> (See <a href="tutorial1-07.html#compiling">Compiling</a> for how to create a
makefile and build the application.)
<p> <h2> Exercises
</h2>
<a name="3"></a><p> Try to resize the window. What happens if you make it really narrow
or really squat?
<p> If you remove the AlignTop, what happens to the LCDRange's position
and size? Why?
<p> If you give the left-hand column a non-zero stretch factor, what
happens when you resize the window?
<p> Leave out the setFocus() call. Which behavior do you prefer?
<p> Try to change "Quit" to "&Quit" in the <a href="qbutton.html#setText">QButton::setText</a>() call. How
does the button's look change? What happens if you press Alt+Q while
the program's running? (It is Meta+Q on a few keyboards.)
<p> Center the text in the CannonField.
<p> You're now ready for <a href="tutorial1-09.html">Chapter 9.</a>
<p> [<a href="tutorial1-07.html">Previous tutorial</a>]
[<a href="tutorial1-09.html">Next tutorial</a>]
[<a href="tutorial.html">Main tutorial page</a>]
<p>
<!-- eof -->
<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>
|