chapter8 二维图形

chapter8 二维图形

这一章的内容是关于二维图形的,这是Qt中相当精彩,但也是相当复杂的一个部分.你有很高的自由度,用Qt提供的方法实现相当完美的效果,一会儿会给出一个时钟的例子,但是用代码来表述,就不是那么简单了.对付复杂的视图,Qt还提供了一个图形项的框架,借助QGraphicsView, QGraphicsSceneQGraphicsItem,实现更加完美的交互,但是这部分的内容,尤其的复杂,我了解的并不多,因此这篇博客暂时不涉及,等到以后钻研清楚,再来补充.

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中的图形绘制属于比较困难的一个部分,设计的领域也比较多,需要有一些物理方面的知识作为背景,目前我手头在写一个'图形编辑'的小程序,基本功能已经具备,还在完善,后期会把代码贴出来.

posted @ 2017-11-29 08:52  plantree  阅读(265)  评论(0编辑  收藏  举报