|  | 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]