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
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><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></head><body bgcolor="#ffffff">
<p>
<table width="100%">
<tr><td><a href="index.html">
<img width="100" height="100" src="qtlogo.png"
alt="Home" border="0"><img width="100"
height="100" src="face.png" alt="Home" border="0">
</a><td valign=top><div align=right><img src="dochead.png" width="472" height="27"><br>
<a href="classes.html"><b>Classes</b></a>
-<a href="annotated.html">Annotated</a>
- <a href="hierarchy.html">Tree</a>
-<a href="functions.html">Functions</a>
-<a href="index.html">Home</a>
-<a href="topicals.html"><b>Structure</b></a>
</div>
</table>
<p>
<h1 align=center>Chapter 12: Hanging in the Air the Way Bricks Don't</h1><br clear="all">
<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">lcdrange.h</a> contains the LCDRange
class definition
<li><a href="t12-lcdrange-cpp.html">lcdrange.cpp</a> contains the LCDRange
implementation
<li><a href="t12-cannon-h.html">cannon.h</a> contains the CannonField class
definition
<li><a href="t12-cannon-cpp.html">cannon.cpp</a> contains the CannonField
implementation
<li><a href="t12-main-cpp.html">main.cpp</a> contains MyWidget and main.
<li><a href="t12-makefile.html">Makefile</a> contains some rules for
generating the meta object information necessary for <a
href="signalsandslots.html">signal/slot creation.</a>
</ul>
<p>
<h2>Line by Line Walk-Through</h2>
<p>
<h3><a href="t12-lcdrange-h.html">lcdrange.h</a></h3>
<p>
The LCDRange now has a text label. <pre>
class QLabel;
</pre>
<p>
We name declare QLabel since we want to use a pointer to it in the class
definition. <pre>
class LCDRange : public QVBox
{
Q_OBJECT
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. <pre>
const char *text() const;
</pre>
<p>
This function returns the label text. <pre>
void setText( const char * );
</pre>
<p>
This slot sets the label text. <pre>
private:
void init();
</pre>
<p>
Since we now have two constructors, we have chosen to put the common
initialization in the private init() function. <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">lcdrange.cpp</a></h3> <pre>
#include <<a href="qlabel-h.html">qlabel.h</a>>
</pre>
<p>
Here we include the QLabel class definition. <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. <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(), then sets the label text. <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" );
slider->setRange( 0, 99 );
slider->setValue( 0 );
label = new <a href="qlabel.html">QLabel</a>( " ", this, "label" );
label->setAlignment( AlignCenter );
<a href="qobject.html#7f8e37">connect</a>( slider, SIGNAL(valueChanged(int)),
lcd, SLOT(display(int)) );
<a href="qobject.html#7f8e37">connect</a>( slider, SIGNAL(valueChanged(int)),
SIGNAL(valueChanged(int)) );
<a href="qwidget.html#cddadb">setFocusProxy</a>( slider );
}
</pre>
<p>
The set up of <code>lcd</code> and <code>slider</code> is the same as in the previous
chapter. Next, we create a QLabel and tell it to align the contents
centered (both vertically and horizontally). The connect() statements
have also been taken from the previous chapter. <pre>
const char *LCDRange::text() const
{
return label->text();
}
</pre>
<p>
This function returns the label text. <pre>
void LCDRange::setText( const char *s )
{
label->setText( s );
}
</pre>
<p>
This function sets the label text.
<p>
<h3><a href="t12-cannon-h.html">cannon.h</a></h3>
<p>
The CannonField now has two new signals: hit() and missed(). In addition
it contains a target. <pre>
void newTarget();
</pre>
<p>
This slot creates a target at a new position. <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 has is certain that it has not and will not
hit the target). <pre>
void paintTarget( <a href="qpainter.html">QPainter</a> * );
</pre>
<p>
This private function paints the target. <pre>
<a href="qrect.html">QRect</a> targetRect() const;
</pre>
<p>
This private function returns the enclosing rectangle of the target. <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">cannon.cpp</a></h3> <pre>
#include <<a href="qdatetime-h.html">qdatetime.h</a>>
</pre>
<p>
We include the QDate, QTime and QDateTime class definitions. <pre>
#include <stdlib.h>
</pre>
<p>
We include the stdlib library because we need the rand() function. <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. Since we are in a constructor, the CannonField
widget is invisible. Qt guarantees that no harm is done when calling
repaint() on a hidden widget. <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 );
srand( midnight.<a href="qtime.html#0b9e38">secsTo</a>(<a href="qtime.html#6091a9">QTime::currentTime</a>()) );
}
<a href="qregion.html">QRegion</a> r( targetRect() );
target = QPoint( 200 + rand() % 190,
10 + rand() % 255 );
<a href="qwidget.html#7569b1">repaint</a>( r.<a href="qrect.html#81e8a3">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 <code>if</code> test will only succeed the first time this function is called,
because we set <code>first_time</code> to FALSE inside the <code>if</code> block.
<p>
Then we create the QTime object <code>midnight</code> 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..390 and y = 35..290) 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() return a random integer >= 0. <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. <pre>
if ( shotR.<a href="qrect.html#5b3d2b">intersects</a>( targetRect() ) ) {
autoShootTimer->stop();
emit hit();
</pre>
<p>
This <code>if</code> statement checks if the shot rectangle intersects the
target rectangle. If it does, the shot has hit the target (ouchh!).
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 since the
CannonField is a component, we leave such decisions to the user of the
component. <pre>
} else if ( shotR.<a href="qrect.html#fccae7">x</a>() > width() || shotR.<a href="qrect.html#f448f7">y</a>() > height() ) {
autoShootTimer->stop();
emit missed();
</pre>
<p>
This <code>if</code> 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. <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: <pre>
if ( updateR.<a href="qrect.html#5b3d2b">intersects</a>( targetRect() ) )
paintTarget( &p );
</pre>
<p>
These two lines make sure the target too is painted, when necessary. <pre>
void CannonField::paintTarget( <a href="qpainter.html">QPainter</a> *p )
{
p-><a href="qpainter.html#3e0cc8">setBrush</a>( red );
p-><a href="qpainter.html#0183e4">setPen</a>( black );
p-><a href="qpainter.html#4c0077">drawRect</a>( targetRect() );
}
</pre>
<p>
This private function paints the target; a rectangle filled with red and
with a black outline. <pre>
<a href="qrect.html">QRect</a> CannonField::targetRect() const
{
<a href="qrect.html">QRect</a> r( 0, 0, 20, 10 );
r.<a href="qrect.html#03af30">moveCenter</a>( <a href="qpoint.html">QPoint</a>(target.x(),<a href="qwidget.html#e3c588">height</a>() - 1 - target.y()) );
return r;
}
</pre>
<p>
This private function returns the enclosing rectangle of the target.
Remember from newTarget() that the <code>target</code> 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#03af30">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">main.cpp</a></h3>
<p>
There are no new members in the MyWidget class, but we have slightly
changed the constructor to set the new LCDRange text labels. <pre>
LCDRange *angle = new LCDRange( "ANGLE", this, "angle" );
</pre>
<p>
We set the angle text label to "ANGLE". <pre>
LCDRange *force = new LCDRange( "FORCE", this, "force" );
</pre>
<p>
We set the force text label to "FORCE".
<p>
<h2>Behavior</h2>
<p>
The cannon can shoot at a target and a new target is automatically created
when one has been hit.
<p>
The LCDRange widgets look a bit strange - the built-in layout
management in QVBox gives the labels too much space and the rest not
enough. We'll fix that in the next chapter.
<p>
<h2>Exercises</h2>
<p>
Make a cheat button that when pressed makes the CannonField display
the shot trajectory for 5 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 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#c0b5fb">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 may now go on to <a href="t13.html">chapter thirteen.</a>
<p>
[<a href="t11.html">Previous tutorial</a>]
[<a href="t13.html">Next tutorial</a>]
[<a href="tutorial.html">Main tutorial page</a>]
<p><address><hr><div align="center">
<table width="100%" cellspacing="0" border="0"><tr>
<td>Copyright 2001 Trolltech<td><a href="http://www.trolltech.com/trademarks.html">Trademarks</a>
<td align="right"><div align="right">Qt version 2.3.2</div>
</table></div></address></body></html>
|