File: t14.html

package info (click to toggle)
qt1g 1%3A1.45-1.1
  • links: PTS
  • area: non-free
  • in suites: potato
  • size: 17,436 kB
  • ctags: 20,174
  • sloc: cpp: 89,153; yacc: 1,273; ansic: 692; makefile: 479; lex: 326; sh: 150; perl: 94
file content (333 lines) | stat: -rw-r--r-- 12,644 bytes parent folder | download
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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html><head><meta name="robots" content="noindex"><title>
Qt Tutorial - Chapter 14
</title></head><body bgcolor="#ffffff">
<p>
<a href=index.html><img width=122 height=65 src=qtlogo.jpg alt="Qt logo" align=left border=0></a>
<center><img src=dochead.gif width=472 height=27></center>
<br clear=all>
<p>
<h1 align=center>Chapter 14: Facing the Wall</h1><br clear="all">
<p>
<center><img src="t14.gif" alt="Screenshot of tutorial fourteen"></center>
<p>
This is the final example, a complete game.
<p>
We add keyboard accelerators, introduce mouse events to CannonField.  We
put a frame around the CannonField and add a barrier (wall) to make the
game more challenging.
<p>
<ul>
<li><a href="t14-lcdrange-h.html">lcdrange.h</a> contains the LCDRange
class definition
<li><a href="t14-lcdrange-cpp.html">lcdrange.cpp</a> contains the LCDRange
implementation
<li><a href="t14-cannon-h.html">cannon.h</a> contains the CannonField class
definition
<li><a href="t14-cannon-cpp.html">cannon.cpp</a> contains the CannonField
implementation
<li><a href="t14-gamebrd-h.html">gamebrd.h</a> contains the GameBoard
class definition
<li><a href="t14-gamebrd-cpp.html">gamebrd.cpp</a> contains the GameBoard
implementation
<li><a href="t14-main-cpp.html">main.cpp</a> contains MyWidget and main.
<li><a href="t14-makefile.html">Makefile</a> contains some rules for
generating the meta object information necessary for <a
href="metaobjects.html">signal/slot creation.</a>
</ul>
<p>
<h2>Line by Line Walk-Through</h2>
<p>
<h3><a href="t14-cannon-h.html">cannon.h</a></h3>
<p>
The CannonField can now receive mouse events to make the user aim the
barrel by clicking on it and dragging. CannonField also has a barrier
wall.
<p>
<pre>
    protected:
        void  timerEvent( <a href="qtimerevent.html">QTimerEvent</a> * );
        void  paintEvent( <a href="qpaintevent.html">QPaintEvent</a> * );
        void  mousePressEvent( <a href="qmouseevent.html">QMouseEvent</a> * );
        void  mouseMoveEvent( <a href="qmouseevent.html">QMouseEvent</a> * );
        void  mouseReleaseEvent( <a href="qmouseevent.html">QMouseEvent</a> * );
</pre>
<p>
In addition to the familiar event handlers, CannonField implements
three mouse event handlers. The names say it all.
<p>
<pre>
        void  paintBarrier( <a href="qpainter.html">QPainter</a> * );
</pre>
<p>
This private function paints the barrier wall.
<p>
<pre>
        <a href="qrect.html">QRect</a> barrierRect() const;
</pre>
<p>
This private function returns the enclosing rectangle of the barrier.
<p>
<pre>
        bool  barrelHit( const QPoint &amp; ) const;
</pre>
<p>
This private function checks if a point is inside the barrel of the cannon.
<p>
<pre>
        bool barrelPressed;
</pre>
<p>
This private variable is TRUE if the user has pressed the mouse on the barrel
and not released it.
<p>
<h3><a href="t14-cannon-cpp.html">cannon.cpp</a></h3>
<p>
<pre>
        barrelPressed = FALSE;
</pre>
<p>
This line has been added to the constructor. Initially, the barrel is
not pressed.
<p>
<pre>
        if ( (shotR.<a href="qrect.html#b2">x</a>() &gt; width() || shotR.<a href="qrect.html#b3">y</a>() &gt; height()) ||
             shotR.<a href="qrect.html#e5">intersects</a>(barrierRect()) ) {
            stopShooting();
            emit missed();
            return;
        }   
</pre>
<p>
In the timerEvent() we now have to test if the shot has hit the barrier,
in addition to checking if the shot has moved beyond the right or bottom
edge of the widget.
<p>
<pre>
        if ( updateR.<a href="qrect.html#e5">intersects</a>( barrierRect() ) )
            paintBarrier( &amp;p );
</pre>
<p>
In the paint event, we have added painting of the barrier.
<p>
<pre>
    void CannonField::mousePressEvent( <a href="qmouseevent.html">QMouseEvent</a> *e )
    {
        if ( e-&gt;<a href="qmouseevent.html#a8">button</a>() != LeftButton )
            return;
        if ( barrelHit( e-&gt;<a href="qmouseevent.html#a2">pos</a>() ) )
            barrelPressed = TRUE;
    }
</pre>
<p>
This is a Qt event handler.  It is called when the user presses a mouse
button when the mouse cursor is over the widget.
<p>
If the event was not generated by the left mouse button, we return
immediately. Otherwise, we check if the position of the mouse cursor is
within the cannon's barrel. If it is, we set <code>barrelPressed</code> to TRUE.
<p>
Notice that the pos() function returns a point in the widget's coordinate
system.
<p>
<pre>
    void CannonField::mouseMoveEvent( <a href="qmouseevent.html">QMouseEvent</a> *e )
    {
        if ( !barrelPressed )
            return;
        <a href="qpoint.html">QPoint</a> pnt = e-&gt;<a href="qmouseevent.html#a2">pos</a>();
        if ( pnt.<a href="qpoint.html#a3">x</a>() &lt;= 0 )
            pnt.<a href="qpoint.html#a5">setX</a>( 1 );
        if ( pnt.<a href="qpoint.html#a4">y</a>() &gt;= height() )
            pnt.<a href="qpoint.html#a6">setY</a>( <a href="qwidget.html#c0">height</a>() - 1 );
        double rad = atan( ((double) rect().bottom() - pnt.<a href="qpoint.html#a4">y</a>()) /  pnt.<a href="qpoint.html#a3">x</a>() );
        setAngle( qRound ( rad*180/3.14159265 ) );
    }
</pre>
<p>
This is another Qt event handler. It is called when the user already has
pressed the mouse button inside this widget and then moves/drags the
mouse.  You can make Qt send mouse move events even when no buttons are
pressed, see <a href="qwidget.html#h5">QWidget::setMouseTracking()</a>.
<p>
This handler repositions the cannon's barrel according to the position of
the mouse cursor.
<p>
First, if the barrel is not pressed, we return.  Next, we fetch the mouse
cursor's position. If the mouse cursor is to the left or below the widget,
we adjust the point to be inside the widget.
<p>
Then we calculate the angle between the bottom edge of the widget and the
imaginary line between the bottom left corner of the widget and the cursor
position. Finally, we set the cannon's angle to the new value converted
to degrees.
<p>
Remember that setAngle() redraws the cannon.
<p>
<pre>
    void CannonField::mouseReleaseEvent( <a href="qmouseevent.html">QMouseEvent</a> *e )
    {
        if ( e-&gt;<a href="qmouseevent.html#a8">button</a>() == LeftButton )
            barrelPressed = FALSE;
    }
</pre>
<p>
This Qt event handler is called whenever the user releases a mouse button
after it has been pressed inside this widget.
<p>
If the left button is released, we can be sure that the barrel is no longer
pressed.
<p>
<pre>
    void CannonField::paintBarrier( <a href="qpainter.html">QPainter</a> *p )
    {
        p-&gt;<a href="qpainter.html#c8">setBrush</a>( yellow );
        p-&gt;<a href="qpainter.html#c4">setPen</a>( black );
        p-&gt;<a href="qpainter.html#i0">drawRect</a>( barrierRect() );
    }
</pre>
<p>
This private function paints the barrier as a rectangle filled with yellow
and with a black outline.
<p>
<pre>
    <a href="qrect.html">QRect</a> CannonField::barrierRect() const
    {
        return QRect( 145, <a href="qwidget.html#c0">height</a>() - 100, 15, 100 );
    }
</pre>
<p>
This private function returns the rectangle of the barrier.
We fix the bottom edge of the barrier to the bottom edge of the widget.
<p>
<pre>
    bool CannonField::barrelHit( const QPoint &amp;p ) const
    {
        <a href="qwmatrix.html">QWMatrix</a> mtx;
        mtx.<a href="qwmatrix.html#b5">translate</a>( 0, <a href="qwidget.html#c0">height</a>() - 1 );
        mtx.<a href="qwmatrix.html#b8">rotate</a>( -ang );
        mtx = mtx.<a href="qwmatrix.html#b9">invert</a>();
        return barrel_rect.contains( mtx.<a href="qwmatrix.html#a9">map</a>(p) );
    }
</pre>
<p>
This function returns TRUE if the point is in the barrel, otherwise FALSE.
<p>
Here we use the class <a href="qwmatrix.html">QWMatrix</a>.  It is defined
in the header file qwmatrix.h, which is included by qpainter.h.
<p>
QWMatrix defines a coordinate system mapping.  It can perform the same
transformations as the QPainter.
<p>
Here we perform the same transformation steps as we do when drawing the
barrel in the paintCannon() function. First we translate the coordinate
system, then we rotate it.
<p>
Now we need to check if the point <code>p</code> (in widget coordinates) lies inside
the barrel. To do this, we invert the transformation matrix.  The inverted
matrix performs the inverse transformation that we used when drawing the
barrel.  We map the point <code>p</code> using the inverted matrix and return TRUE
if it is inside the original barrel rectangle.
<p>
<h3><a href="t14-gamebrd-h.html">gamebrd.h</a></h3>
<p>
The only change is that a frame has been added.
<p>
<pre>
    class QFrame;
</pre>
<p>
We name declare QFrame...
<p>
<pre>
        <a href="qframe.html">QFrame</a>      *frame;
</pre>
<p>
and add a QFrame to this widget.
<p>
<h3><a href="t14-gamebrd-cpp.html">gamebrd.cpp</a></h3>
<p>
<pre>
    #include &lt;<a href="qaccel-h.html">qaccel.h</a>&gt;
</pre>
<p>
We include the class definition of <a href="qaccel.html">QAccel</a>.
<p>
<pre>
        frame = new <a href="qframe.html">QFrame</a>( this, "cannonFrame" );
        frame-&gt;setFrameStyle( QFrame::WinPanel | QFrame::Sunken );
</pre>
<p>
We create and set up a frame. We want this frame around the CannonField
widget. Since widgets cannot be transparent, we have to put the frame
behind the CannonField.  We create the frame before we create the
CannonField.  Qt guarantees that the last created widget always will be
on top of the others.
<p>
The frame style is set to a sunken WinPanel. See the <a href="qframe.html">QFrame
documentation</a> for more information.
<p>
<pre>
        <a href="qaccel.html">QAccel</a> *accel = new <a href="qaccel.html">QAccel</a>( this );
        accel-&gt;<a href="qaccel.html#b2">connectItem</a>( accel-&gt;<a href="qaccel.html#a5">insertItem</a>( Key_Space), this, SLOT(fire()) );
        accel-&gt;<a href="qaccel.html#b2">connectItem</a>( accel-&gt;<a href="qaccel.html#a5">insertItem</a>( Key_Q), qApp, SLOT(quit()) );
</pre>
<p>
Here we create and set up an accelerator. An accelerator is an object that
intercepts keyboard events to an application and calls slots if certain
keys are pressed. This mechanism is also called shortcut keys.  Note that
an accelerator can be a child of a widget and will be destroyed when the
parent is destroyed.  QAccel is <em>not</em> a widget and has no visible effect
on its parent.
<p>
We define two shortcut keys. We want the slot fire() to be called when the
user presses space, and we want the application to quit when key "Q" is
pressed.  The <code>Key_Space</code> and <code>Key_Q</code> constants are defined in the
header file qkeycode.h (included by qaccel.h). Also note that <code>Key_Q</code>
signifies the Q key on the keyboard. The slot will be called when typing
both "q" and "Q".
<p>
<pre>
        frame-&gt;move( angle-&gt;x() + angle-&gt;width() + 10, angle-&gt;y() );
        cannonField-&gt;move( frame-&gt;x() + 2, frame-&gt;y() + 2 );
</pre>
<p>
We set the initial position of the frame to the same position we
previously used for the CannonField. Next, we set the position of the
CannonField relative to the frame.
<p>
<pre>
    void GameBoard::resizeEvent( <a href="qresizeevent.html">QResizeEvent</a> * )
    {
        frame-&gt;resize( <a href="qwidget.html#b9">width</a>()  - frame-&gt;x() - 10,
                       <a href="qwidget.html#c0">height</a>() - frame-&gt;y() - 10 );
        cannonField-&gt;resize( frame-&gt;width() - 4, frame-&gt;height() - 4 );
    }
</pre>
<p>
We must now incorporate the frame widget in our resize handler.  First we
resize the frame exactly as we did with the CannonField in the previous
chapter. Then we simply resize the CannonField relative to the frame.
<p>
<h2>Behavior</h2>
<p>
The cannon now shoots when you press space. You can also position the
cannon's angle using the mouse.  The barrier makes it a little more
challenging to play the game.  We also have a nice looking frame around
the CannonField.
<p>
<h2>Excercises</h2>
<p>
Write a space invaders game and give it to the 
<a href="http://www.kde.org/">KDE project.</a>
<p>
You may now go on to write your own Qt applications.
<p>
[<a href="t13.html">Previous tutorial</a>]
[<a href="t1.html">First 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  1999 Troll Tech<td><a href="trademarks.html">Trademarks</a>
<td align="right"><div align="right">Qt version 1.45</div>
</table></div></address></body></html>