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
|
<!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:1711 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Qt Tutorial - Chapter 12: Hanging in the Air the Way Bricks Don't</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 12: Hanging in the Air the Way Bricks Don't</h1>
<p> <center><img src="t12.png" alt="Screenshot of tutorial twelve"></center>
<p> In this example, we extend our LCDRange class to include a text label.
We also provide something to shoot at.
<p> <ul>
<li> <a href="t12-lcdrange-h.html">t12/lcdrange.h</a> contains the LCDRange
class definition.
<li> <a href="t12-lcdrange-cpp.html">t12/lcdrange.cpp</a> contains the LCDRange
implementation.
<li> <a href="t12-cannon-h.html">t12/cannon.h</a> contains the CannonField class
definition.
<li> <a href="t12-cannon-cpp.html">t12/cannon.cpp</a> contains the CannonField
implementation.
<li> <a href="t12-main-cpp.html">t12/main.cpp</a> contains MyWidget and main.
</ul>
<p> <h2> Line-by-line Walkthrough
</h2>
<a name="1"></a><p> <h3> <a href="t12-lcdrange-h.html">t12/lcdrange.h</a>
</h3>
<a name="1-1"></a><p> The LCDRange now has a text label.
<p>
<p> <pre> class QLabel;
</pre>
<p> We name declare <a href="qlabel.html">QLabel</a> because we want to use a pointer to it in the class
definition.
<p> <pre> class LCDRange : public <a href="qvbox.html">QVBox</a>
{
<a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
public:
LCDRange( <a href="qwidget.html">QWidget</a> *parent=0, const char *name=0 );
LCDRange( const char *s, QWidget *parent=0,
const char *name=0 );
</pre>
<p> We have added a new constructor that sets the label text in addition to
the parent and name.
<p> <pre> const char *text() const;
</pre>
<p> This function returns the label text.
<p> <pre> void setText( const char * );
</pre>
<p> This slot sets the label text.
<p> <pre> private:
void init();
</pre>
<p> Because we now have two constructors, we have chosen to put the common
initialization in the private init() function.
<p> <pre> <a href="qlabel.html">QLabel</a> *label;
</pre>
<p> We also have a new private variable: a QLabel. QLabel is one of Qt's
standard widgets and can show a text or a pixmap with or without a
frame.
<p> <h3> <a href="t12-lcdrange-cpp.html">t12/lcdrange.cpp</a>
</h3>
<a name="1-2"></a><p>
<p> <pre> #include <<a href="qlabel-h.html">qlabel.h</a>>
</pre>
<p> Here we include the <a href="qlabel.html">QLabel</a> class definition.
<p> <pre> LCDRange::LCDRange( <a href="qwidget.html">QWidget</a> *parent, const char *name )
: <a href="qvbox.html">QVBox</a>( parent, name )
{
init();
}
</pre>
<p> This constructor calls the init() function, which contains the common
initialization code.
<p> <pre> LCDRange::LCDRange( const char *s, QWidget *parent,
const char *name )
: <a href="qvbox.html">QVBox</a>( parent, name )
{
init();
setText( s );
}
</pre>
<p> This constructor first calls init() and then sets the label text.
<p> <pre> void LCDRange::init()
{
<a href="qlcdnumber.html">QLCDNumber</a> *lcd = new <a href="qlcdnumber.html">QLCDNumber</a>( 2, this, "lcd" );
slider = new <a href="qslider.html">QSlider</a>( Horizontal, this, "slider" );
<a name="x2387"></a> slider-><a href="qrangecontrol.html#setRange">setRange</a>( 0, 99 );
<a name="x2388"></a> slider-><a href="qslider.html#setValue">setValue</a>( 0 );
label = new <a href="qlabel.html">QLabel</a>( " ", this, "label" );
<a name="x2383"></a> label-><a href="qlabel.html#setAlignment">setAlignment</a>( AlignCenter );
<a name="x2389"></a> <a href="qobject.html#connect">connect</a>( slider, SIGNAL(<a href="qslider.html#valueChanged">valueChanged</a>(int)),
<a name="x2386"></a> lcd, SLOT(<a href="qlcdnumber.html#display">display</a>(int)) );
<a href="qobject.html#connect">connect</a>( slider, SIGNAL(<a href="qslider.html#valueChanged">valueChanged</a>(int)),
SIGNAL(valueChanged(int)) );
<a href="qwidget.html#setFocusProxy">setFocusProxy</a>( slider );
}
</pre>
<p> The setup of <tt>lcd</tt> and <tt>slider</tt> is the same as in the previous
chapter. Next we create a <a href="qlabel.html">QLabel</a> and tell it to align the contents
centered (both vertically and horizontally). The connect() statements
have also been taken from the previous chapter.
<p> <pre> const char *LCDRange::text() const
{
<a name="x2385"></a> return label-><a href="qlabel.html#text">text</a>();
}
</pre>
<p> This function returns the label text.
<p> <pre> void LCDRange::setText( const char *s )
{
<a name="x2384"></a> label-><a href="qlabel.html#setText">setText</a>( s );
}
</pre>
<p> This function sets the label text.
<p> <h3> <a href="t12-cannon-h.html">t12/cannon.h</a>
</h3>
<a name="1-3"></a><p> The CannonField now has two new signals: hit() and missed(). In addition
it contains a target.
<p>
<p> <pre> void newTarget();
</pre>
<p> This slot creates a target at a new position.
<p> <pre> signals:
void hit();
void missed();
</pre>
<p> The hit() signal is emitted when a shot hits the target. The missed()
signal is emitted when the shot moves beyond the right or bottom edge
of the widget (i.e., it is certain that it has not and will not
hit the target).
<p> <pre> void paintTarget( <a href="qpainter.html">QPainter</a> * );
</pre>
<p> This private function paints the target.
<p> <pre> <a href="qrect.html">QRect</a> targetRect() const;
</pre>
<p> This private function returns the enclosing rectangle of the target.
<p> <pre> <a href="qpoint.html">QPoint</a> target;
</pre>
<p> This private variable contains the center point of the target.
<p> <h3> <a href="t12-cannon-cpp.html">t12/cannon.cpp</a>
</h3>
<a name="1-4"></a><p>
<p> <pre> #include <<a href="qdatetime-h.html">qdatetime.h</a>>
</pre>
<p> We include the <a href="qdate.html">QDate</a>, <a href="qtime.html">QTime</a>, and <a href="qdatetime.html">QDateTime</a> class definitions.
<p> <pre> #include <stdlib.h>
</pre>
<p> We include the stdlib library because we need the rand() function.
<p> <pre> newTarget();
</pre>
<p> This line has been added to the constructor. It creates a "random"
position for the target. In fact, the newTarget() function will try
to paint the target. Because we are in a constructor, the CannonField
widget is invisible. Qt guarantees that no harm is done when calling
repaint() on a hidden widget.
<p> <pre> void CannonField::newTarget()
{
static bool first_time = TRUE;
if ( first_time ) {
first_time = FALSE;
<a href="qtime.html">QTime</a> midnight( 0, 0, 0 );
<a name="x2399"></a><a name="x2398"></a> srand( midnight.<a href="qtime.html#secsTo">secsTo</a>(QTime::<a href="qtime.html#currentTime">currentTime</a>()) );
}
<a href="qregion.html">QRegion</a> r( targetRect() );
target = QPoint( 200 + rand() % 190,
10 + rand() % 255 );
<a name="x2395"></a> <a href="qwidget.html#repaint">repaint</a>( r.<a href="qrect.html#unite">unite</a>( targetRect() ) );
}
</pre>
<p> This private function creates a target center point at a new "random"
position.
<p> We use the rand() function to fetch random integers. The rand() function
normally returns the same series of numbers each time you run a program.
This would make the target appear at the same position every time. To
avoid this, we must set a random seed the first time this function is
called. The random seed must also be random in order to avoid equal random
number series. The solution is to use the number of seconds that have
passed since midnight as a pseudo-random value.
<p> First we create a static bool local variable. A static variable like
this one is guaranteed to keep its value between calls to the function.
<p> The <tt>if</tt> test will succeed only the first time this function is called
because we set <tt>first_time</tt> to FALSE inside the <tt>if</tt> block.
<p> Then we create the <a href="qtime.html">QTime</a> object <tt>midnight</tt>, which represents the time
00:00:00. Next we fetch the number of seconds from midnight until
now and use it as a random seed. See the documentation for <a href="qdate.html">QDate</a>,
<a href="qtime.html">QTime</a>, and <a href="qdatetime.html">QDateTime</a> for more information.
<p> Finally we calculate the target's center point. We keep it within
the rectangle (x=200, y=35, width=190, height=255), (i.e., the
possible x and y values are x = 200..389 and y = 35..289) in a
coordinate system where we put y position 0 at the bottom edge of the
widget and let y values increase upwards X is as normal, with 0 at
the left edge and with x values increasing to the right.
<p> By experimentation we have found this to always be in reach of the shot.
<p> Note that rand() returns a random integer >= 0.
<p> <pre> void CannonField::moveShot()
{
<a href="qregion.html">QRegion</a> r( shotRect() );
timerCount++;
<a href="qrect.html">QRect</a> shotR = shotRect();
</pre>
<p> This part of the timer event has not changed from the previous chapter.
<p> <pre> if ( shotR.<a href="qrect.html#intersects">intersects</a>( targetRect() ) ) {
<a name="x2400"></a> autoShootTimer-><a href="qtimer.html#stop">stop</a>();
emit hit();
</pre>
<p> This <tt>if</tt> statement checks whether the shot rectangle intersects the
target rectangle. If it does, the shot has hit the target (ouch!).
We stop the shoot timer and emit the hit() signal to tell the outside
world that a target was destroyed, and return.
<p> Note that we could have created a new target on the spot, but because the
CannonField is a component we leave such decisions to the user of the
component.
<p> <pre> <a name="x2397"></a><a name="x2396"></a> } else if ( shotR.<a href="qrect.html#x">x</a>() > width() || shotR.<a href="qrect.html#y">y</a>() > height() ) {
autoShootTimer-><a href="qtimer.html#stop">stop</a>();
emit missed();
</pre>
<p> This <tt>if</tt> statement is the same as in the previous chapter, except that
it now emits the missed() signal to tell the outside world about the
failure.
<p> <pre> } else {
</pre>
<p> And the rest of the function is as before.
<p> CannonField::paintEvent() is as before, except that this has been
added:
<p> <pre> <a name="x2393"></a> if ( updateR.<a href="qrect.html#intersects">intersects</a>( targetRect() ) )
paintTarget( &p );
</pre>
<p> These two lines make sure that the target is also painted when necessary.
<p> <pre> void CannonField::paintTarget( <a href="qpainter.html">QPainter</a> *p )
{
p-><a href="qpainter.html#setBrush">setBrush</a>( red );
p-><a href="qpainter.html#setPen">setPen</a>( black );
p-><a href="qpainter.html#drawRect">drawRect</a>( targetRect() );
}
</pre>
<p> This private function paints the target; a rectangle filled with red and
with a black outline.
<p> <pre> QRect CannonField::targetRect() const
{
<a href="qrect.html">QRect</a> r( 0, 0, 20, 10 );
<a name="x2394"></a> r.<a href="qrect.html#moveCenter">moveCenter</a>( QPoint(target.x(),height() - 1 - target.y()) );
return r;
}
</pre>
<p> This private function returns the enclosing rectangle of the target.
Remember from newTarget() that the <tt>target</tt> point uses y coordinate 0 at
the bottom of the widget. We calculate the point in widget coordinates
before we call <a href="qrect.html#moveCenter">QRect::moveCenter</a>().
<p> The reason we have chosen this coordinate mapping is to fix the distance
between the target and the bottom of the widget. Remember that the widget
can be resized by the user or the program at any time.
<p> <h3> <a href="t12-main-cpp.html">t12/main.cpp</a>
</h3>
<a name="1-5"></a><p>
<p> There are no new members in the MyWidget class, but we have slightly
changed the constructor to set the new LCDRange text labels.
<p> <pre> LCDRange *angle = new LCDRange( "ANGLE", this, "angle" );
</pre>
<p> We set the angle text label to "ANGLE".
<p> <pre> LCDRange *force = new LCDRange( "FORCE", this, "force" );
</pre>
<p> We set the force text label to "FORCE".
<p> <h2> Behavior
</h2>
<a name="2"></a><p> The LCDRange widgets look a bit strange - the built-in layout
management in <a href="qvbox.html">QVBox</a> gives the labels too much space and the rest not
enough. We'll fix that in the next chapter.
<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> Make a cheat button that, when pressed, makes the CannonField display
the shot trajectory for five seconds.
<p> If you did the "round shot" exercise from the previous chapter, try
changing the shotRect() to a shotRegion() that returns a <a href="qregion.html">QRegion</a> so
you can have really accurate collision detection.
<p> Make a moving target.
<p> Make sure that the target is always created entirely on-screen.
<p> Make sure that the widget cannot be resized so that the target isn't
visible. Hint: <a href="qwidget.html#setMinimumSize">QWidget::setMinimumSize</a>() is your friend.
<p> Not easy; make it possible to have several shots in the air at the
same time. Hint: make a Shot object.
<p> You're now ready for <a href="tutorial1-13.html">Chapter 13.</a>
<p> [<a href="tutorial1-11.html">Previous tutorial</a>]
[<a href="tutorial1-13.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>
|