在这个例子中我们开始画一个蓝色可爱的小加农炮.只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()
函数转化QPainte
r的坐标系统,比如,它通过偏移量来移动。这里我们设置窗口部件的左下角为(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();
}