Qt 4.3: Qt Tutorial 10 - Smooth as Silk

|  | Home · All Classes · Main Classes · Grouped Classes · Modules · Functions |

[Previous: Chapter 9] [Qt Tutorial] [Next: Chapter 11]

# Qt Tutorial 10 - Smooth as Silk

Files:

- tutorial/t10/cannonfield.cpp
- tutorial/t10/cannonfield.h
- tutorial/t10/lcdrange.cpp
- tutorial/t10/lcdrange.h
- tutorial/t10/main.cpp

In this example, we add a force control.

## Line by Line Walkthrough

### t10/cannonfield.h

The `CannonField` now has a force value in addition to the angle.

         int angle() const { return currentAngle; }
         int force() const { return currentForce; }

     public slots:
         void setAngle(int angle);
         void setForce(int force);

     signals:
         void angleChanged(int newAngle);
         void forceChanged(int newForce);

The interface to the force follows the same practice as for the angle.

     private:
         QRect cannonRect() const;

We have put the definition of the cannon's enclosing rectangle in a separate function.

         int currentAngle;
         int currentForce;
     };

The force is stored in the `currentForce` variable.

### t10/cannonfield.cpp

     CannonField::CannonField(QWidget *parent)
         : QWidget(parent)
     {
         currentAngle = 45;
         currentForce = 0;
         setPalette(QPalette(QColor(250, 250, 200)));
         setAutoFillBackground(true);
     }

The force is initialized to zero.

     void CannonField::setAngle(int angle)
     {
         if (angle < 5)
             angle = 5;
         if (angle > 70)
             angle = 70;
         if (currentAngle == angle)
             return;
         currentAngle = angle;
         update(cannonRect());
         emit angleChanged(currentAngle);
     }

We have made a slight change in the `setAngle()` function. It repaints only the portion of the widget that contains the cannon.

     void CannonField::setForce(int force)
     {
         if (force < 0)
             force = 0;
         if (currentForce == force)
             return;
         currentForce = force;
         emit forceChanged(currentForce);
     }

The implementation of `setForce()` is quite similar to that of `setAngle()`. The only difference is that because we don't show the force value, we don't need to repaint the widget.

     void CannonField::paintEvent(QPaintEvent * /* event */)
     {
         QPainter painter(this);

         painter.setPen(Qt::NoPen);
         painter.setBrush(Qt::blue);

         painter.translate(0, height());
         painter.drawPie(QRect(-35, -35, 70, 70), 0, 90 * 16);
         painter.rotate(-currentAngle);
         painter.drawRect(QRect(30, -5, 20, 10));
     }

We paint as in Chapter 9.

     QRect CannonField::cannonRect() const
     {
         QRect result(0, 0, 50, 50);
         result.moveBottomLeft(rect().bottomLeft());
         return result;
     }

The `cannonRect()` function returns the rectangle enclosing the cannon in widget coordinates. First we create a rectangle with the size 50 x 50 and then move it so its bottom-left corner is equal to the widget's own bottom-left corner.

The QWidget::rect() function returns the widget's enclosing rectangle in the widget's own coordinates. The top-left corner of the rectangle is always (0, 0).

### t10/main.cpp

     MyWidget::MyWidget(QWidget *parent)
         : QWidget(parent)
     {

The constructor is mostly the same, but some new bits have been added.

         LCDRange *force = new LCDRange;
         force->setRange(10, 50);

We add a second `LCDRange`, which will be used to set the force.

         connect(force, SIGNAL(valueChanged(int)),
                 cannonField, SLOT(setForce(int)));
         connect(cannonField, SIGNAL(forceChanged(int)),
                 force, SLOT(setValue(int)));

We connect the `force` widget and the `cannonField` widget, just like we did for the `angle` widget.

         QVBoxLayout *leftLayout = new QVBoxLayout;
         leftLayout->addWidget(angle);
         leftLayout->addWidget(force);

         QGridLayout *gridLayout = new QGridLayout;
         gridLayout->addWidget(quit, 0, 0);
         gridLayout->addLayout(leftLayout, 1, 0);
         gridLayout->addWidget(cannonField, 1, 1, 2, 1);
         gridLayout->setColumnStretch(1, 10);

In Chapter 9, we put `angle` in the lower-left cell of the layout. Now we want to have two widgets in that cell, so we make a vertical box, put the vertical box in the grid cell, and put each of `angle` and `range` in the vertical box.

         force->setValue(25);

We initialize the force value to 25.

## Running the Application

We now have a force control.

## Exercises

Make the size of the cannon barrel be dependent on the force.

Put the cannon in the bottom-right corner.

Try adding a better keyboard interface. For example, make + and - increase and decrease the force and enter shoot. If you're bothered by the way the Left and Right keys work, change that too. [Hint: Reimplement QWidget::keyPressEvent().]

[Previous: Chapter 9] [Qt Tutorial] [Next: Chapter 11]