summaryrefslogtreecommitdiffstats
path: root/doc/html/tutorial1-14.html
blob: dcc50ff0fb126a475938f4169b1abda409d5d496 (plain)
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
<!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:2381 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Qt Tutorial - Chapter 14: Facing the Wall</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&nbsp;Classes</font></a>
 | <a href="mainclasses.html">
<font color="#004faf">Main&nbsp;Classes</font></a>
 | <a href="annotated.html">
<font color="#004faf">Annotated</font></a>
 | <a href="groups.html">
<font color="#004faf">Grouped&nbsp;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 14: Facing the Wall</h1>

 
<p> <center><img src="t14.png" alt="Screenshot of tutorial fourteen"></center> 
<p> This is the final example: a complete game.
<p> We add keyboard accelerators and 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">t14/lcdrange.h</a> contains the LCDRange
class definition.
<li> <a href="t14-lcdrange-cpp.html">t14/lcdrange.cpp</a> contains the LCDRange
implementation.
<li> <a href="t14-cannon-h.html">t14/cannon.h</a> contains the CannonField class
definition.
<li> <a href="t14-cannon-cpp.html">t14/cannon.cpp</a> contains the CannonField
implementation.
<li> <a href="t14-gamebrd-h.html">t14/gamebrd.h</a> contains the GameBoard
class definition.
<li> <a href="t14-gamebrd-cpp.html">t14/gamebrd.cpp</a> contains the GameBoard
implementation.
<li> <a href="t14-main-cpp.html">t14/main.cpp</a> contains MyWidget and main.
</ul>
<p> <h2> Line-by-line Walkthrough
</h2>
<a name="1"></a><p> <h3> <a href="t14-cannon-h.html">t14/cannon.h</a>
</h3>
<a name="1-1"></a><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> 

<p> <pre>    protected:
        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 <a href="qpoint.html">QPoint</a> &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">t14/cannon.cpp</a>
</h3>
<a name="1-2"></a><p> 

<p> <pre>        barrelPressed = FALSE;
</pre>
<p> This line has been added to the constructor. Initially, the mouse is
not pressed on the barrel.
<p> <pre>    <a name="x2429"></a>    } else if ( shotR.<a href="qrect.html#x">x</a>() &gt; width() || shotR.<a href="qrect.html#y">y</a>() &gt; height() ||
                    shotR.<a href="qrect.html#intersects">intersects</a>(barrierRect()) ) {
</pre>
<p> Now that we have a barrier, there are three ways to miss. We test for
the third, too.
<p> <pre>    void CannonField::<a href="qwidget.html#mousePressEvent">mousePressEvent</a>( <a href="qmouseevent.html">QMouseEvent</a> *e )
    {
        if ( e-&gt;<a href="qmouseevent.html#button">button</a>() != LeftButton )
            return;
    <a name="x2418"></a>    if ( barrelHit( e-&gt;<a href="qmouseevent.html#pos">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 <tt>barrelPressed</tt> to
TRUE.
<p> Notice that the pos() function returns a point in the widget's
coordinate system.
<p> <pre>    void CannonField::<a href="qwidget.html#mouseMoveEvent">mouseMoveEvent</a>( <a href="qmouseevent.html">QMouseEvent</a> *e )
    {
        if ( !barrelPressed )
            return;
        <a href="qpoint.html">QPoint</a> pnt = e-&gt;<a href="qmouseevent.html#pos">pos</a>();
    <a name="x2424"></a>    if ( pnt.<a href="qpoint.html#x">x</a>() &lt;= 0 )
    <a name="x2422"></a>        pnt.<a href="qpoint.html#setX">setX</a>( 1 );
    <a name="x2425"></a>    if ( pnt.<a href="qpoint.html#y">y</a>() &gt;= <a href="qwidget.html#height">height</a>() )
    <a name="x2423"></a>        pnt.<a href="qpoint.html#setY">setY</a>( <a href="qwidget.html#height">height</a>() - 1 );
        double rad = atan(((double)<a href="qwidget.html#rect">rect</a>().bottom()-pnt.<a href="qpoint.html#y">y</a>())/pnt.<a href="qpoint.html#x">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#setMouseTracking">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>    <a name="x2432"></a>void CannonField::<a href="qwidget.html#mouseReleaseEvent">mouseReleaseEvent</a>( <a href="qmouseevent.html">QMouseEvent</a> *e )
    {
    <a name="x2417"></a>    if ( e-&gt;<a href="qmouseevent.html#button">button</a>() == LeftButton )
            barrelPressed = FALSE;
    }
</pre>
<p> This Qt event handler is called whenever the user releases a mouse
button and it was pressed inside this widget.
<p> If the left button is released, we can be sure that the barrel is no
longer pressed.
<p> The paint event has two extra lines:
<p> <pre>    <a name="x2427"></a>    if ( updateR.<a href="qrect.html#intersects">intersects</a>( barrierRect() ) )
            paintBarrier( &amp;p );
</pre>
<p> paintBarrier() does the same sort of thing as paintShot(),
paintTarget(), and paintCannon().
<p> <pre>    void CannonField::paintBarrier( <a href="qpainter.html">QPainter</a> *p )
    {
        p-&gt;<a href="qpainter.html#setBrush">setBrush</a>( yellow );
        p-&gt;<a href="qpainter.html#setPen">setPen</a>( black );
        p-&gt;<a href="qpainter.html#drawRect">drawRect</a>( barrierRect() );
    }
</pre>
<p> This private function paints the barrier as a rectangle filled with
yellow and with a black outline.
<p> <pre>    QRect CannonField::barrierRect() const
    {
        return QRect( 145, height() - 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 <a href="qpoint.html">QPoint</a> &amp;p ) const
    {
        <a href="qwmatrix.html">QWMatrix</a> mtx;
    <a name="x2436"></a>    mtx.<a href="qwmatrix.html#translate">translate</a>( 0, height() - 1 );
    <a name="x2435"></a>    mtx.<a href="qwmatrix.html#rotate">rotate</a>( -ang );
    <a name="x2433"></a>    mtx = mtx.<a href="qwmatrix.html#invert">invert</a>();
    <a name="x2434"></a><a name="x2426"></a>    return barrelRect.<a href="qrect.html#contains">contains</a>( mtx.<a href="qwmatrix.html#map">map</a>(p) );
    }
</pre>
<p> This function returns TRUE if the point is in the barrel; otherwise it returns
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> <a href="qwmatrix.html">QWMatrix</a> defines a coordinate system mapping. It can perform the same
transformations as the <a href="qpainter.html">QPainter</a>.
<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 and then we rotate it.
<p> Now we need to check whether the point <tt>p</tt> (in widget coordinates) lies
inside the barrel. To do this, we invert the <a href="qwmatrix.html#TransformationMode">transformation matrix</a>.
The inverted matrix performs the inverse transformation that we used
when drawing the barrel. We map the point <tt>p</tt> using the inverted
matrix and return TRUE if it is inside the original barrel rectangle.
<p> <h3> <a href="t14-gamebrd-cpp.html">t14/gamebrd.cpp</a>
</h3>
<a name="1-3"></a><p> 

<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>        <a href="qvbox.html">QVBox</a> *box = new <a href="qvbox.html">QVBox</a>( this, "cannonFrame" );
        box-&gt;<a href="qframe.html#setFrameStyle">setFrameStyle</a>( QFrame::WinPanel | QFrame::Sunken );
        cannonField = new CannonField( box, "cannonField" );
</pre>
<p> We create and set up a <a href="qvbox.html">QVBox</a>, set its frame style, and then create
<tt>CannonField</tt> as a child of that box. Because nothing else is in the
box, the effect is that the <a href="qvbox.html">QVBox</a> will put a frame around the
CannonField.
<p> <pre>        <a href="qaccel.html">QAccel</a> *accel = new <a href="qaccel.html">QAccel</a>( this );
    <a name="x2438"></a><a name="x2437"></a>    accel-&gt;<a href="qaccel.html#connectItem">connectItem</a>( accel-&gt;<a href="qaccel.html#insertItem">insertItem</a>( Key_Enter ),
                            this, SLOT(fire()) );
        accel-&gt;<a href="qaccel.html#connectItem">connectItem</a>( accel-&gt;<a href="qaccel.html#insertItem">insertItem</a>( Key_Return ),
                            this, SLOT(fire()) );
</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 is a child of a widget and will be
destroyed when that widget is destroyed. <a href="qaccel.html">QAccel</a> 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 Enter, and we want the application to quit when
key Ctrl+Q is pressed. Because Enter is sometimes Return and there
are even keyboards with <em>both</em> keys, we make both Enter and Return
invoke fire().
<p> <pre>        accel-&gt;<a href="qaccel.html#connectItem">connectItem</a>( accel-&gt;<a href="qaccel.html#insertItem">insertItem</a>( CTRL+Key_Q ),
                            qApp, SLOT(<a href="qapplication.html#quit">quit</a>()) );
</pre>
<p> And then we set up Ctrl+Q to do the same thing as Alt+Q. Some
people are more used to Ctrl+Q (and anyway it shows how do do it).
<p> CTRL, Key_Enter, Key_Return and Key_Q are all constants provided by
Qt. They're actually Qt::Key_Enter, etc., but practically all classes
inherit the <a href="qt.html">Qt</a> namespace class.
<p> <pre>        <a href="qgridlayout.html">QGridLayout</a> *grid = new <a href="qgridlayout.html">QGridLayout</a>( this, 2, 2, 10 );
    <a name="x2441"></a>    grid-&gt;<a href="qgridlayout.html#addWidget">addWidget</a>( quit, 0, 0 );
        grid-&gt;<a href="qgridlayout.html#addWidget">addWidget</a>( box, 1, 1 );
    <a name="x2442"></a>    grid-&gt;<a href="qgridlayout.html#setColStretch">setColStretch</a>( 1, 10 );
</pre>
<p> We put <tt>box</tt> (the <a href="qvbox.html">QVBox</a>), not the CannonField, in the lower-right
cell.
<p> <h2> Behavior
</h2>
<a name="2"></a><p> The cannon now shoots when you press Enter. 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> (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> Write a space invaders game.
<p> (This exercise was first done by
<a href="mailto:igorr@ifi.uio.no">Igor Rafienko</a>. You can
<a href="http://www.stud.ifi.uio.no/~igorr/download.html">download his game</a>.)
<p> The new exercise is: Write a Breakout game.
<p> Final exhortation: Go forth now and create <em>masterpieces of the programming art!</em>
<p> 
<p> [<a href="tutorial1-13.html">Previous tutorial</a>]
[<a href="tutorial1-01.html">First 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 &copy; 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>