第九章- 你可以使用加农炮了

在这里插入图片描述

在这个例子中我们开始画一个蓝色可爱的小加农炮.只cannon.cpp和上一章不同。

  • lcdrange.h包含LCDRange类定义
  • lcdrange.cpp包含LCDRange类实现
  • cannon.h包含CannonField类定义
  • cannon.cpp包含CannonField类实现
  • main.cpp包含MyWidget和main

一行一行地解说

cannon.cpp:

void CannonField::paintEvent(QPaintEvent *)
{
    QPainter p(this);

我们现在开始认真地使用QPainter。我们创建一个绘画工具来操作这个窗口部件。

    p.setBrush(Qt::blue);

当一个QPainter填满一个矩形、圆或者其它无论什么,它会用它的画刷填满这个图形。这里我们把画刷设置为蓝色。(我们也可以使用一个调色板。)

    p.setPen(Qt::NoPen);

并且QPainter使用画笔来画边界。这里我们设置为NoPen,就是说我们在边界上什么都不画,蓝色画刷会在我们画的东西的边界内画满全部。

    p.translate(0, rect().bottom());

QPainter::translate()函数转化QPainter的坐标系统,比如,它通过偏移量来移动。这里我们设置窗口部件的左下角为(0,0)。x和y的方向没有改变,但窗口部件中的所有y坐标现在都是负数(请看坐标系统获得有关Qt的坐标系统更多的信息。)

    p.drawPie(QRect(-35, -35, 70, 70), 0, 90*16);

drawPie()函数使用一个开始角度和弧长在一个指定的矩形内画一个饼型图。角度的度量用的是一度的十六分之一。零度在三点的位置。画的方向是顺时针的。这里我们在窗口部件的左下角画一个四分之一圆。这个饼图被蓝色充满,并且没有边框。

    p.rotate(-ang);

QPainter::rotate()函数绕QPainter坐标系统的初始位置旋转它。旋转的参数是一个按度数给定的浮点数(不是一个像上面那样给的十六分之一的度数)并且是顺时针的。这里我们顺时针旋转ang度数。

    p.drawRect(QRect(33, -4, 15, 8));

QPainter::drawRect()函数画一个指定的矩形。这里我们画的是加农炮的炮筒。

很难想象当坐标系统被转换之后(转化、旋转、缩放或者修剪)的绘画结果。

在这种情况下,坐标系统先被转化后被旋转。如果矩形QRect(33, -4, 15, 8)被画到这个转化后的坐标系统中,它看起来会是这样:
在这里插入图片描述
The cannon translated but not rotated
注意矩形被CannonField窗口部件的边界省略了一部分。当我们选装坐标系统,以60度为例,矩形会以(0,0)为圆心被旋转,也就是左下角,因为我们已经转化了坐标系统。结果会是这样:
在这里插入图片描述
The cannon translated and rotated
我们做完了,除了我们还没有解释为什么Windows在这个时候没有发抖。

行为

当滑块被操作的时候,所画的加农炮的角度会因此而变化。

Quit中的字母Q现在有下划线,并且Alt+Q会实现你所要的。如果你不知道这些,你一定是没有做第八章中的练习。

你也要注意加农炮的闪烁让人很烦,特别是在一个比较慢的机器上。我们将会在下一章修正这一点。 (我电脑没有闪烁)

练习

设置一个不同的画笔代替NoPen。设置一个调色板的画刷。

试着用“Q&uit”或者“Qu&it”作为按钮的文本来提到“&Quit”。发生了什么?

现在你可以进行第十章了。


lcdrange.h:

#ifndef LCDRANGE_H
#define LCDRANGE_H

#include <QWidget>

class QSlider;

class LCDRange : public QWidget
{
    Q_OBJECT
public:
    explicit LCDRange(QWidget *parent = 0, Qt::WindowFlags name = Qt::WindowFlags());
    int value() const;

public slots:
    void setValue(int);
    void setRange(int minVal, int maxVal);

signals:
    void valueChanged(int);

private:
    QSlider *slider;
};

#endif // LCDRANGE_H

lcdrange.cpp:

#include "lcdrange.h"
#include <QVBoxLayout>
#include <QLCDNumber>
#include <QSlider>

LCDRange::LCDRange(QWidget *parent, Qt::WindowFlags name)
    : QWidget(parent, name)
{
    QLCDNumber *lcd  = new QLCDNumber(2);
    slider = new QSlider(Qt::Horizontal);
    slider->setRange(0, 99);
    slider->setValue(10);

    connect(slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)));
    connect(slider, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)));

    setFocusProxy(slider); //设置这个窗口部件的焦点为slider

    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(lcd);
    layout->addWidget(slider);
    setLayout(layout);
}

int LCDRange::value() const
{
    return slider->value();
}

void LCDRange::setValue(int value)
{
    slider->setValue(value);
}

void LCDRange::setRange(int minVal, int maxVal)
{
    if (minVal < 0 || maxVal > 99 || minVal > maxVal)
    {
        qWarning("LCDRange::setRange(%d,%d)\n"
                 "\tRange must be 0~99\n"
                 "\tand minVal must not be greater than maxVal",
                 minVal, maxVal);
        return;
    }
    slider->setRange(minVal, maxVal);
}

cannon.h:

#ifndef CANNON_H
#define CANNON_H

#include <QWidget>

class CannonField : public QWidget
{
    Q_OBJECT
public:
    CannonField(QWidget *parent = 0, Qt::WindowFlags name = Qt::WindowFlags());
    int angle() const { return ang; }

public slots:
    void setAngle(int degrees);

signals:
    void angleChanged(int);

protected:
    void paintEvent(QPaintEvent *);

private:
    int ang;
};

#endif // CANNON_H

cannon.cpp:

#include "cannon.h"
#include <QPainter>

CannonField::CannonField(QWidget *parent, Qt::WindowFlags name)
    : QWidget(parent, name)
{
    ang = 45;
    setAutoFillBackground(true);
    setPalette(QPalette(QColor(250, 250, 200)));
}

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

void CannonField::paintEvent(QPaintEvent *)
{
    QPainter p(this);

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

    p.translate(0, rect().bottom()); // 将绘图坐标原点移动到窗口部件的左下角
    p.drawPie(QRect(-35, -35, 70, 70), 0, 90*16); // 绘制一个圆形扇形,圆心在 (-35, -35),半径为 35,从0度开始,弧长90*16(四分之一圆) 绘制90度的角度
    p.rotate(-ang);  // 根据变量 ang 的值逆时针旋转坐标系统
    p.drawRect(QRect(33, -4, 15, 8)); // 绘制一个矩形,左上角顶点在 (33, -4),宽度为 15,高度为 8
}

main.cpp:

#include <QApplication>
#include <Qpushbutton>
#include <QVBoxLayout>
#include <QGridLayout>
#include <Qfont>

#include "lcdrange.h"
#include "cannon.h"

class MyWidget : public QWidget
{
public:
    MyWidget(QWidget *parent=0, Qt::WindowFlags name = Qt::WindowFlags());
};

MyWidget::MyWidget(QWidget *parent, Qt::WindowFlags name)
    : QWidget(parent, name)
{
    QPushButton *quit = new QPushButton("Quit");
    quit->setFont(QFont("Times", 18, QFont::Bold));
    connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));

    LCDRange *lcdRange = new LCDRange;
    lcdRange->setRange(5, 70);

    CannonField *cannonField = new CannonField;

    connect(lcdRange, SIGNAL(valueChanged(int)), cannonField, SLOT(setAngle(int)));
    connect(cannonField, SIGNAL(angleChanged(int)), lcdRange, SLOT(setValue(int)));

    QGridLayout *grid = new QGridLayout;

    grid->addWidget(quit, 0, 0);
    grid->addWidget(lcdRange, 1, 0, Qt::AlignTop);
    grid->addWidget(cannonField, 1, 1);
    grid->setColumnStretch(1, 10);

    lcdRange->setValue(60);
    lcdRange->setFocus(); //设置lcdRange获得键盘焦点

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addLayout(grid);
    setLayout(layout);
}

int main(int argc, char **argv)
{
    //QApplication::setColorSpec(QApplication::CustomColor);
    QApplication a(argc, argv);

    MyWidget w;
    w.setGeometry(100, 100, 500, 355);
    w.show();
    return a.exec();
}
  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值