📄 t14.html
字号:
<!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 14: Facing the Wall</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> <font face="Arial,Helvetica,Geneva,Swiss,SunSans-Regular" align="center" size=32>Qte</font></a></div></table><p><h1 align=center>Chapter 14: Facing the Wall</h1><br clear="all"><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, introduce mouse events to CannonField. Weput a frame around the CannonField and add a barrier (wall) to make thegame more challenging.<p><ul><li><a href="t14-lcdrange-h.html">lcdrange.h</a> contains the LCDRangeclass definition<li><a href="t14-lcdrange-cpp.html">lcdrange.cpp</a> contains the LCDRangeimplementation<li><a href="t14-cannon-h.html">cannon.h</a> contains the CannonField classdefinition<li><a href="t14-cannon-cpp.html">cannon.cpp</a> contains the CannonFieldimplementation<li><a href="t14-gamebrd-h.html">gamebrd.h</a> contains the GameBoardclass definition<li><a href="t14-gamebrd-cpp.html">gamebrd.cpp</a> contains the GameBoardimplementation<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 forgenerating the meta object information necessary for <ahref="signalsandslots.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 thebarrel by clicking on it and dragging. CannonField also has a barrierwall. <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 implementsthree mouse event handlers. The names say it all. <pre> void paintBarrier( <a href="qpainter.html">QPainter</a> * );</pre><p>This private function paints the barrier wall. <pre> <a href="qrect.html">QRect</a> barrierRect() const;</pre><p>This private function returns the enclosing rectangle of the barrier. <pre> bool barrelHit( const QPoint & ) const;</pre><p>This private function checks if a point is inside the barrel of the cannon. <pre> bool barrelPressed;</pre><p>This private variable is TRUE if the user has pressed the mouse on thebarrel and not released it.<p><h3><a href="t14-cannon-cpp.html">cannon.cpp</a></h3> <pre> barrelPressed = FALSE;</pre><p>This line has been added to the constructor. Initially, the mouse isnot pressed on the barrel. <pre> } else if ( shotR.<a href="qrect.html#fccae7">x</a>() > width() || shotR.<a href="qrect.html#f448f7">y</a>() > height() ||</pre><p>Now that we have a barrier there are three ways to miss. We test forthe third, too. <pre> void CannonField::mousePressEvent( <a href="qmouseevent.html">QMouseEvent</a> *e ) { if ( e->button() != LeftButton ) return; if ( barrelHit( e->pos() ) ) barrelPressed = TRUE; }</pre><p>This is a Qt event handler. It is called when the user presses amouse button when the mouse cursor is over the widget.<p>If the event was not generated by the left mouse button, we returnimmediately. Otherwise, we check if the position of the mouse cursoris within the cannon's barrel. If it is, we set <code>barrelPressed</code> toTRUE.<p>Notice that the pos() function returns a point in the widget'scoordinate system. <pre> void CannonField::mouseMoveEvent( <a href="qmouseevent.html">QMouseEvent</a> *e ) { if ( !barrelPressed ) return; <a href="qpoint.html">QPoint</a> pnt = e->pos(); if ( pnt.<a href="qpoint.html#7e96b2">x</a>() <= 0 ) pnt.<a href="qpoint.html#75b57f">setX</a>( 1 ); if ( pnt.<a href="qpoint.html#e48824">y</a>() >= height() ) pnt.<a href="qpoint.html#c780b4">setY</a>( <a href="qwidget.html#e3c588">height</a>() - 1 ); double rad = atan(((double)rect().bottom()-pnt.<a href="qpoint.html#e48824">y</a>())/pnt.<a href="qpoint.html#7e96b2">x</a>()); setAngle( qRound ( rad*180/3.14159265 ) ); }</pre><p>This is another Qt event handler. It is called when the user alreadyhas pressed the mouse button inside this widget and then moves/dragsthe mouse. (You can make Qt send mouse move events even when nobuttons are pressed too, see <a href="qwidget.html#36406c">QWidget::setMouseTracking()</a>.)<p>This handler repositions the cannon's barrel according to the position ofthe mouse cursor.<p>First, if the barrel is not pressed, we return. Next, we fetch themouse cursor's position. If the mouse cursor is to the left or belowthe widget, we adjust the point to be inside the widget.<p>Then we calculate the angle between the bottom edge of the widget andthe imaginary line between the bottom left corner of the widget andthe cursor position. Finally, we set the cannon's angle to the newvalue converted to degrees.<p>Remember that setAngle() redraws the cannon. <pre> void CannonField::mouseReleaseEvent( <a href="qmouseevent.html">QMouseEvent</a> *e ) { if ( e->button() == LeftButton ) barrelPressed = FALSE; }</pre><p>This Qt event handler is called whenever the user releases a mousebutton and it was pressed inside this widget.<p>If the left button is released, we can be sure that the barrel is nolonger pressed.<p>The paint event has two extra lines: <pre> if ( updateR.<a href="qrect.html#5b3d2b">intersects</a>( barrierRect() ) ) paintBarrier( &p );</pre><p>paintBarrier() does the same sort of thing as paintShot(),paintTarget() and paintCannon(). <pre> void CannonField::paintBarrier( <a href="qpainter.html">QPainter</a> *p ) { p-><a href="qpainter.html#3e0cc8">setBrush</a>( yellow ); p-><a href="qpainter.html#0183e4">setPen</a>( black ); p-><a href="qpainter.html#4c0077">drawRect</a>( barrierRect() ); }</pre><p>This private function paints the barrier as a rectangle filled withyellow and with a black outline. <pre> <a href="qrect.html">QRect</a> CannonField::barrierRect() const { return QRect( 145, <a href="qwidget.html#e3c588">height</a>() - 100, 15, 100 ); }</pre><p>This private function returns the rectangle of the barrier. We fixthe bottom edge of the barrier to the bottom edge of the widget. <pre> bool CannonField::barrelHit( const QPoint &p ) const { <a href="qwmatrix.html">QWMatrix</a> mtx; mtx.<a href="qwmatrix.html#e4b716">translate</a>( 0, <a href="qwidget.html#e3c588">height</a>() - 1 ); mtx.<a href="qwmatrix.html#71020d">rotate</a>( -ang ); mtx = mtx.<a href="qwmatrix.html#4b7815">invert</a>(); return barrelRect.contains( mtx.<a href="qwmatrix.html#83c725">map</a>(p) ); }</pre><p>This function returns TRUE if the point is in the barrel, otherwiseFALSE.<p>Here we use the class <a href="qwmatrix.html">QWMatrix</a>. It is defined in the header fileqwmatrix.h, which is included by qpainter.h.<p>QWMatrix defines a coordinate system mapping. It can perform the sametransformations as the QPainter.<p>Here we perform the same transformation steps as we do when drawingthe barrel in the paintCannon() function. First we translate thecoordinate system, then we rotate it.<p>Now we need to check if the point <code>p</code> (in widget coordinates) liesinside the barrel. To do this, we invert the transformation matrix.The inverted matrix performs the inverse transformation that we usedwhen drawing the barrel. We map the point <code>p</code> using the invertedmatrix and return TRUE if it is inside the original barrel rectangle.<p><h3><a href="t14-gamebrd-cpp.html">gamebrd.cpp</a></h3> <pre> #include <<a href="qaccel-h.html">qaccel.h</a>></pre><p>We include the class definition of <a href="qaccel.html">QAccel</a>. <pre> <a href="qvbox.html">QVBox</a> *box = new <a href="qvbox.html">QVBox</a>( this, "cannonFrame" ); box-><a href="qframe.html#558f79">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<code>cannonField</code> as a child of that box. Since nothing else is in thebox, the effect is that the QVBox will put a frame around theCannonField. <pre> <a href="qaccel.html">QAccel</a> *accel = new <a href="qaccel.html">QAccel</a>( this ); accel-><a href="qaccel.html#2dfde3">connectItem</a>( accel-><a href="qaccel.html#cc688f">insertItem</a>( Key_Enter ), this, SLOT(fire()) ); accel-><a href="qaccel.html#2dfde3">connectItem</a>( accel-><a href="qaccel.html#cc688f">insertItem</a>( Key_Return ), this, SLOT(fire()) );</pre><p>Here we create and set up an accelerator. An accelerator is an objectthat intercepts keyboard events to an application and calls slots ifcertain keys are pressed. This mechanism is also called shortcutkeys. Note that an accelerator is a child of a widget and will bedestroyed when that widget is destroyed. QAccel is <em>not</em> a widgetand has no visible effect on its parent.<p>We define two shortcut keys. We want the slot fire() to be calledwhen the user presses Enter, and we want the application to quit whenkey Control-Q is pressed. Since Enter is sometimes Return and thereare even keyboard with <em>both</em> keys, we make both Enter and Returninvoke fire(). <pre> accel-><a href="qaccel.html#2dfde3">connectItem</a>( accel-><a href="qaccel.html#cc688f">insertItem</a>( CTRL+Key_Q ), qApp, SLOT(quit()) );</pre><p>And then we set up Control-Q to do the same thing as Alt-Q. Somepeople are more used to Control-Q, and anyway it shows how do do it.<p>CTRL, Key_Enter, Key_Return and Key_Q are all constants provided byQt. They're actually Qt::Key_Enter etc. but practically all classesinherit the <a href="qt.html">Qt</a> namespace class. <pre> <a href="qgridlayout.html">QGridLayout</a> *grid = new <a href="qgridlayout.html">QGridLayout</a>( this, 2, 2, 10 ); grid-><a href="qgridlayout.html#dac29c">addWidget</a>( quit, 0, 0 ); grid-><a href="qgridlayout.html#dac29c">addWidget</a>( box, 1, 1 ); grid-><a href="qgridlayout.html#df80c4">setColStretch</a>( 1, 10 );</pre><p>We put <code>box</code> (the QVBox), not the CannonField, in the lower rightcell.<p><h2>Behavior</h2><p>The cannon now shoots when you press Enter. You can also position thecannon's angle using the mouse. The barrier makes it a little morechallenging to play the game. We also have a nice looking framearound the CannonField.<p><h2>Exercises</h2><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:<p>Write a Breakout 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -