Home | All Classes | Main Classes | Annotated | Grouped Classes | 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 ang; }
            int force() const { return f; }

        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 ang;
            int f;
        };

The force is stored in the integer `f`.

### t10/cannonfield.cpp

        CannonField::CannonField(QWidget *parent)
            : QWidget(parent)
        {
            ang = 45;
            f = 0;
            setPalette(QPalette(QColor(250, 250, 200)));
        }

The force (`f`) is initialized to zero.

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

We have made a slight change in the `setAngle()` function. It repaints only the portion of the widget that contains the cannon. The `false` argument indicates that the specified rectangle should not be erased before a paint event is sent to the widget. This speeds up and smooths the drawing a little bit.

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

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(-ang);
            painter.drawRect(QRect(33, -4, 15, 8));
        }

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(this);
            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(this);
            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.

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