From bd0f3345a938b35ce6a12f6150373b0955b8dd12 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Sun, 10 Jul 2011 15:24:15 -0500 Subject: Add Qt3 development HEAD version --- doc/html/tutorial1-11.html | 250 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 doc/html/tutorial1-11.html (limited to 'doc/html/tutorial1-11.html') diff --git a/doc/html/tutorial1-11.html b/doc/html/tutorial1-11.html new file mode 100644 index 0000000..0414de1 --- /dev/null +++ b/doc/html/tutorial1-11.html @@ -0,0 +1,250 @@ + + + + + +Qt Tutorial - Chapter 11: Giving It a Shot + + + + + + + +
+ +Home + | +All Classes + | +Main Classes + | +Annotated + | +Grouped Classes + | +Functions +

Qt Tutorial - Chapter 11: Giving It a Shot

+ + +

Screenshot of tutorial eleven
+

In this example we introduce a timer to implement animated shooting. +

+

Line-by-line Walkthrough +

+

t11/cannon.h +

+

The CannonField now has shooting capabilities. +

+ +

        void  shoot();
+
+

Calling this slot will make the cannon shoot if a shot is not in the air. +

    private slots:
+        void  moveShot();
+
+

This private slot is used to move the shot while it is in the air, +using a QTimer. +

    private:
+        void  paintShot( QPainter * );
+
+

This private function paints the shot. +

        QRect shotRect() const;
+
+

This private function returns the shot's enclosing rectangle if +one is in the air; otherwise the returned rectangle is undefined. +

        int timerCount;
+        QTimer * autoShootTimer;
+        float shoot_ang;
+        float shoot_f;
+    };
+
+

These private variables contain information that describes the shot. The +timerCount keeps track of the time passed since the shot was fired. +The shoot_ang is the cannon angle and shoot_f is the cannon force +when the shot was fired. +

t11/cannon.cpp +

+

+ +

    #include <math.h>
+
+

We include the math library because we need the sin() and cos() functions. +

    CannonField::CannonField( QWidget *parent, const char *name )
+            : QWidget( parent, name )
+    {
+        ang = 45;
+        f = 0;
+        timerCount = 0;
+        autoShootTimer = new QTimer( this, "movement handler" );
+        connect( autoShootTimer, SIGNAL(timeout()),
+                 this, SLOT(moveShot()) );
+        shoot_ang = 0;
+        shoot_f = 0;
+        setPalette( QPalette( QColor( 250, 250, 200) ) );
+    }
+
+

We initialize our new private variables and connect the QTimer::timeout() signal to our moveShot() slot. We'll move the +shot every time the timer times out. +

    void CannonField::shoot()
+    {
+        if ( autoShootTimer->isActive() )
+            return;
+        timerCount = 0;
+        shoot_ang = ang;
+        shoot_f = f;
+        autoShootTimer->start( 50 );
+    }
+
+

This function shoots a shot unless a shot is in the air. The timerCount +is reset to zero. The shoot_ang and shoot_f are set to the current +cannon angle and force. Finally, we start the timer. +

    void CannonField::moveShot()
+    {
+        QRegion r( shotRect() );
+        timerCount++;
+
+        QRect shotR = shotRect();
+
+        if ( shotR.x() > width() || shotR.y() > height() )
+            autoShootTimer->stop();
+        else
+            r = r.unite( QRegion( shotR ) );
+        repaint( r );
+    }
+
+

moveShot() is the slot that moves the shot, called every 50 +milliseconds when the QTimer fires. +

Its tasks are to compute the new position, repaint the screen with the +shot in the new position, and if necessary, stop the timer. +

First we make a QRegion that holds the old shotRect(). A QRegion +is capable of holding any sort of region, and we'll use it here to +simplify the painting. ShotRect() returns the rectangle where the +shot is now - it is explained in detail later. +

Then we increment the timerCount, which has the effect of moving the +shot one step along its trajectory. +

Next we fetch the new shot rectangle. +

If the shot has moved beyond the right or bottom edge of the widget, we +stop the timer or we add the new shotRect() to the QRegion. +

Finally, we repaint the QRegion. This will send a single paint event +for just the one or two rectangles that need updating. +

    void CannonField::paintEvent( QPaintEvent *e )
+    {
+        QRect updateR = e->rect();
+        QPainter p( this );
+
+        if ( updateR.intersects( cannonRect() ) )
+            paintCannon( &p );
+        if ( autoShootTimer->isActive() &&
+             updateR.intersects( shotRect() ) )
+            paintShot( &p );
+    }
+
+

The paint event function has been split in two since the previous +chapter. Now we fetch the bounding rectangle of the region that +needs painting, check whether it intersects either the cannon and/or +the shot, and if necessary, call paintCannon() and/or paintShot(). +

    void CannonField::paintShot( QPainter *p )
+    {
+        p->setBrush( black );
+        p->setPen( NoPen );
+        p->drawRect( shotRect() );
+    }
+
+

This private function paints the shot by drawing a black filled rectangle. +

We leave out the implementation of paintCannon(); it is the same as +the paintEvent() from the previous chapter. +

    QRect CannonField::shotRect() const
+    {
+        const double gravity = 4;
+
+        double time      = timerCount / 4.0;
+        double velocity  = shoot_f;
+        double radians   = shoot_ang*3.14159265/180;
+
+        double velx      = velocity*cos( radians );
+        double vely      = velocity*sin( radians );
+        double x0        = ( barrelRect.right()  + 5 )*cos(radians);
+        double y0        = ( barrelRect.right()  + 5 )*sin(radians);
+        double x         = x0 + velx*time;
+        double y         = y0 + vely*time - 0.5*gravity*time*time;
+
+        QRect r = QRect( 0, 0, 6, 6 );
+        r.moveCenter( QPoint( qRound(x), height() - 1 - qRound(y) ) );
+        return r;
+    }
+
+

This private function calculates the center point of the shot and returns +the enclosing rectangle of the shot. It uses the initial cannon force and +angle in addition to timerCount, which increases as time passes. +

The formula used is the classical Newtonian formula for frictionless +movement in a gravity field. For simplicity, we've chosen to +disregard any Einsteinian effects. +

We calculate the center point in a coordinate system where y +coordinates increase upward. After we have calculated the center +point, we construct a QRect with size 6x6 and move its center point to +the point calculated above. In the same operation we convert the +point into the widget's coordinate system (see The +Coordinate System). +

The qRound() function is an inline function defined in qglobal.h (included +by all other Qt header files). qRound() rounds a double to the closest +integer. +

t11/main.cpp +

+

+ +

    class MyWidget: public QWidget
+    {
+    public:
+        MyWidget( QWidget *parent=0, const char *name=0 );
+    };
+
+

The only addition is the Shoot button. +

        QPushButton *shoot = new QPushButton( "&Shoot", this, "shoot" );
+        shoot->setFont( QFont( "Times", 18, QFont::Bold ) );
+
+

In the constructor we create and set up the Shoot button exactly like we +did with the Quit button. Note that the first argument to the constructor +is the button text, and the third is the widget's name. +

        connect( shoot, SIGNAL(clicked()), cannonField, SLOT(shoot()) );
+
+

Connects the clicked() signal of the Shoot button to the shoot() slot +of the CannonField. +

Behavior +

+

The cannon can shoot, but there's nothing to shoot at. +

(See Compiling for how to create a +makefile and build the application.) +

Exercises +

+

Make the shot a filled circle. Hint: QPainter::drawEllipse() may +help. +

Change the color of the cannon when a shot is in the air. +

You're now ready for Chapter 12. +

[Previous tutorial] +[Next tutorial] +[Main tutorial page] +

+ +


+ +
Copyright © 2007 +TrolltechTrademarks +
Qt 3.3.8
+
+ -- cgit v1.2.3