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

    却道,此心安处是吾乡
  • 相关阅读:
    C#中Dictionary的用法及用途 原文转载自:http://www.cnblogs.com/linzheng/archive/2010/12/13/1904709.html
    权限管理数据表设计说明 转载自:http://kobe6111.iteye.com/blog/241122
    SQL 游标(最简单的游标说明)
    由于前面的错误,Microsoft.Data.Entity.Design.Package.MicrosoftDataEntityDesignPackage, Microsoft.Data.Entity.Design.Package, Ver
    获取DataGird的列名
    asp.net中gridview、datalist、datagrid三个数据控件的foreach遍历方法
    WinForm控件设计:DataGridView的行统计实现 原文来自:http://www.cnblogs.com/yyj/archive/2010/10/21/1857518.html
    eclipse内存设置参数(转)
    (转)使用WCF 4.0 构建 REST Service
    使用EF Power Tools 错误参数 80070057解决方法
  • 原文地址:https://www.cnblogs.com/lucifer25/p/7918456.html
Copyright © 2011-2022 走看看