chapter8 二维图形
chapter8 二维图形
这一章的内容是关于二维图形的,这是Qt中相当精彩,但也是相当复杂的一个部分.你有很高的自由度,用Qt提供的方法实现相当完美的效果,一会儿会给出一个时钟的例子,但是用代码来表述,就不是那么简单了.对付复杂的视图,Qt还提供了一个图形项的框架,借助QGraphicsView
, QGraphicsScene
和QGraphicsItem
,实现更加完美的交互,但是这部分的内容,尤其的复杂,我了解的并不多,因此这篇博客暂时不涉及,等到以后钻研清楚,再来补充.
Qt的二维图形引擎是基于QPainter
类的,QPainter既可以绘制几何形状(点,线,矩形,椭圆,弧形等),也可以绘制图像和文字,支持一些高级特性,如反走样,渐变等,也可以平移和缩放,属于最底层的控制类.
使用QPainter,只需要重新实现QWidget::paintEvent()
事件,就可以天马行空的绘制自己的图形了.
1.用QPainter绘图
框架一般是这个样子:
void MyWidget::paintEvent(QPaintEvent *Event){
QPainter painter(this);
...
}
this指针传递的是一个绘图设备,这里让QPainter绘制在窗体上,也可以绘制在图像上等其他"绘图设备".
基本的绘制流程大致是这样:
1.设置画笔:QPen类,构造函数如下, QPen(const QBrush &brush, qreal width, Qt::PenStyle style = Qt::SolidLine, Qt::PenCapStyle cap = Qt::SquareCap, Qt::PenJoinStyle join = Qt::BevelJoin)
,其中包含了画笔的颜色,线宽,线的类型以及拐点等.
2.设置画刷:QBrush类,可以填充颜色,也可以填充图像,用于图形的填充.
3.调用QPainter的draw()函数,可以绘制矩形,椭圆等等.
QPainterPath也可以试着使用,但是没有那么的方便.
QPainter还提供了线性渐变
,辐射渐变
以及锥形渐变
,都不是很复杂,看官方文档即可.
QPainter的save()
和restore()
方法,提供了一个回滚的机制,必要的时候可以使用.
2.坐标系统变换
这个坐标系统的变换,也是图形绘制中比较复杂的一部分,理解的很浅显,这里有三个概念:窗口
,视口
和世界矩阵
.
解释一下:视口是物理坐标系下指定的人依据性,窗口也是指同意举行,但是实在逻辑坐标系下,绘制图形时使用的坐标是逻辑坐标,会以线性代数的方式转换为物理坐标,然后完成绘制.这种视口-窗口分离的机制,带来的最大优势就是:可以独立的绘制图形,而不必考虑它们如何在窗口中表达.
世界变化就是在窗口-视口转换之外使用的变换矩阵,设计移动,缩放,旋转等,主要是靠QTransform
类来实现.
下面看个具体的例子:
OvenTime.h
#ifndef OVENTIMER_H
#define OVENTIMER_H
#include <QWidget>
#include <QDateTime>
namespace Ui {
class OvenTimer;
}
class OvenTimer : public QWidget
{
Q_OBJECT
public:
explicit OvenTimer(QWidget *parent = 0);
~OvenTimer();
void setDuration(int secs);
int duration() const;
void draw(QPainter *painter);
private:
Ui::OvenTimer *ui;
QDateTime finishTime;
QTimer *updateTimer;
QTimer *finishTimer;
signals:
void timeout();
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
};
#endif // OVENTIMER_H
OvenTime.cpp
#include <QPainter>
#include <QPen>
#include <QDateTime>
#include <QTimer>
#include <QPaintEvent>
#include "oventimer.h"
#include "ui_oventimer.h"
const double DegreesPerMinute = 7.0;
const double DegreesPerSecond = DegreesPerMinute / 60;
const int MaxMinutes = 45;
const int MaxSeconds = MaxMinutes * 60;
const int UpdateInterval = 5;
OvenTimer::OvenTimer(QWidget *parent) :
QWidget(parent),
ui(new Ui::OvenTimer)
{
//ui->setupUi(this);
finishTime = QDateTime::currentDateTime();
updateTimer = new QTimer(this);
connect(updateTimer, SIGNAL(timeout()),
this, SLOT(update()));
finishTimer = new QTimer(this);
finishTimer->setSingleShot(true);
connect(finishTimer, SIGNAL(timeout()),
this, SIGNAL(timeout()));
connect(finishTimer, SIGNAL(timeout()),
updateTimer, SLOT(stop()));
QFont font;
font.setPointSize(8);
setFont(font);
}
void OvenTimer::setDuration(int secs){
secs = qBound(0, secs, MaxSeconds);
finishTime = QDateTime::currentDateTime().addSecs(secs);
if(secs > 0){
updateTimer->start(UpdateInterval * 1000);
finishTimer->start(secs * 1000);
}else{
updateTimer->stop();
finishTimer->stop();
}
update();
}
OvenTimer::~OvenTimer()
{
delete ui;
}
int OvenTimer::duration() const{
int secs = QDateTime::currentDateTime().secsTo(finishTime);
if(secs < 0){
secs = 0;
}
return secs;
}
void OvenTimer::mousePressEvent(QMouseEvent *event){
QPointF point = event->pos() - rect().center();
double theta = std::atan2(-point.x(), -point.y()) * 180.0 / M_PI;
setDuration(duration() + int(theta / DegreesPerSecond));
}
void OvenTimer::paintEvent(QPaintEvent *event){
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
int side = qMin(width(), height());
painter.setViewport((width() - side) / 2,
(height() - side) / 2,
side, side);
painter.setWindow(-50, -50, 100, 100);
draw(&painter);
}
void OvenTimer::draw(QPainter *painter){
static const int triangle[3][2] = {
{-2, -49}, {2, -49}, {0, -47}
};
QPen thickPen(palette().foreground(), 1.5);
QPen thinPen(palette().foreground(), 0.5);
QColor niceBlue(150, 150, 200);
painter->setPen(thinPen);
painter->setBrush(palette().foreground());
painter->drawPolygon(QPolygon(3, &triangle[0][0]));
QConicalGradient coneGradient(0, 0, -90.0);
coneGradient.setColorAt(0.0, Qt::darkGray);
coneGradient.setColorAt(0.2, niceBlue);
coneGradient.setColorAt(0.5, Qt::white);
coneGradient.setColorAt(1.0, Qt::darkGray);
painter->setBrush(coneGradient);
painter->drawEllipse(-46, -46, 92, 92);
QRadialGradient haloGradient(0, 0, 20, 0, 0);
haloGradient.setColorAt(0.0, Qt::lightGray);
haloGradient.setColorAt(0.8, Qt::darkGray);
haloGradient.setColorAt(0.9, Qt::white);
haloGradient.setColorAt(1.0, Qt::black);
painter->setPen(Qt::NoPen);
painter->setBrush(haloGradient);
painter->drawEllipse(-20, -20, 40, 40);
QLinearGradient knobGradient(-7, -25, 7, -25);
knobGradient.setColorAt(0.0, Qt::black);
knobGradient.setColorAt(0.2, niceBlue);
knobGradient.setColorAt(0.3, Qt::lightGray);
knobGradient.setColorAt(0.8, Qt::white);
knobGradient.setColorAt(1.0, Qt::black);
painter->rotate(duration() * DegreesPerSecond);
painter->setBrush(knobGradient);
painter->setPen(thinPen);
painter->drawRoundedRect(-7, -25, 14, 50, 99, 49);
for(int i = 0; i <= MaxMinutes; ++i){
painter->save();
painter->rotate(-i * DegreesPerMinute);
if(i % 5 == 0){
painter->setPen(thickPen);
painter->drawLine(0, -41, 0, -44);
painter->drawText(-15, -41, 30, 30,
Qt::AlignHCenter | Qt::AlignTop,
QString::number(i));
}else{
painter->setPen(thinPen);
painter->drawLine(0, -42, 0, -44);
}
painter->restore();
}
}
这个例子相当综合,上面所说的几乎所有的功能都使用到了,但是理解起来有些吃力,慢慢的看.
3.绘制图像
Qt中关于图像的类有很多,比如QImage
,QPixmap
等,使用的时候也比较简单,就不多说了.
总结:博客写的比较简单,因为自己目前的理解也比较浅显,只是把最基础的一个概念给理了一下,这只是刚开始的学习,随着学习的不但深入,我会在修订这些之前的博客,完善自己的理解.Qt中的图形绘制属于比较困难的一个部分,设计的领域也比较多,需要有一些物理方面的知识作为背景,目前我手头在写一个'图形编辑'的小程序,基本功能已经具备,还在完善,后期会把代码贴出来.