Qt 繪圖與動畫系統

Qt 提供了內置的繪圖系統以及獨立的QtOpenGL模塊提供對OpenGL的支持。Qt提供了基於狀態機的QPainter系統和麪向對象的Graphics View系統。css

QPainter

基於狀態機的繪圖系統主要包含QPainter、QPaintEngine、QPaintDevice 三個類。算法

QPainter有三個主要參數分別用於設置畫筆(QPen)、畫刷(QBrush)、字體(font),分別由setPen、setBrush、setFont系列方法設定。架構

widget.h:框架

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    void paintEvent(QPaintEvent *event = 0);
    ~Widget();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

widget.cpp:ide

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *parent) {
   // QPainter *painter = new QPainter(this);
    QPainter painter(this);
    painter.setPen(Qt::blue);
    painter. setFont(QFont("Arial", 30));
    painter. drawLine(0,0,100,100);
}

main.cpp:函數

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    w.paintEvent();
    return a.exec();
}

使用QPainter進行繪畫必須重寫QWidget::paintEvent(QPaintEvent *)事件,並在事件中進行繪圖。如Widget.cpp中的void Widget::paintEvent(QPaintEvent *parent)工具

調用可視化組件的update(),repaint()實例方法或者直接調用paintEvent()方法能夠對可視化組件進行重繪。字體

update()容許Qt進行優化從而獲得比調用repaint()更快的速度和更少的閃爍。優化

幾回調用update()的結果一般僅僅是一次paintEvent()調用,repaint()則是當即調用paintEvent()動畫

Qt一般在paintEvent()調用以前擦除這個窗口部件的區域,除非設置了WRepaintNoErase窗口部件標記。

畫筆QPen主要用於線條的繪製,畫筆的樣式能夠在建立QPen對象時指定也可由setStyle()指定。

畫筆主要支持cap、join (結合點)和line三種風格,能夠經過setStyle()、setCapStyle()、setJoinStyle()、setlineStyle()等方法進行設置。

畫刷QBrush用於二維封閉圖形的填充,與QPen相似一樣經過style設定填充風格,還可使用漸變填充。

反走樣

反走樣技術經過對像素的細微調整避免鋸齒狀邊緣出現,可是反走樣算法須要較高計算量且會修改原有圖形,因此與大多數2D繪圖工具同樣QPainter默認不打開反走樣算法。

painter.setRenderHint(QPainter::Antialiasing,true);

使用上述語句將QPainter::Antialiasing設置爲true以後painter就會打開反走樣算法,直到將QPainter::Antialiasing顯式地設爲false反走樣算法纔會關閉。

漸變填充

Qt 的漸變是一個獨立的類,包含QLinearGradient(線性漸變),QRadialGrafient(輻射漸變),QConcialGradient(角度漸變)。

示例:

void Widget::paintEvent(QPaintEvent *parent) {
    QPainter painter(this);
    QLinearGradient gradient(0,0,100,100);
     //初始化漸變對象,左上座標和寬高設定位置
    gradient.setColorAt(0.2,Qt::blue);
    //設定漸變色彩,第一個參數爲參照點的位置比例,第二個參數爲參照點的顏色
    gradient.setColorAt(0.5,Qt::red);
    gradient.setColorAt(0.8,Qt::yellow);
    painter. setBrush(QBrush(gradient));
    //QBrush接受漸變對象作參數,並將其做爲QPainter的繪製工具
    painter. drawEllipse(0,0,100,100);
    //畫圖
}

座標變換

QPainter的座標位於左上角,x軸正方向向右,y軸正方向向下;每一個像素佔據
1×1的座標空間,但像素中心爲與方格中心(x+0.5,y+0.5),其實是一個半像素座標系。

QPainter使用了viewport和window機制,viewport使用物理座標而window使用邏輯座標、QPainter傳遞邏輯座標。二者的座標系一般是相同的,可是QPainter還提供了setViewport()和setWindow()函數用於重置兩個座標系的位置大小,使得能夠在不改變物理實現的狀況下移動畫布的範圍。

QPainter還提供了世界座標用於座標的變換:

void Widget::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    QFont font("Courier",12);//初始化字體對象
    painter.setFont(font);//爲QPainter設置字體

    QTransform transform;//定義座標變換
    transform.rotate(+45);//設置旋轉
    painter.setWorldTransform(transform);//爲QPainter設置座標變換

    painter.drawText(150,0,"Hello World!");//繪製
}

QTransform

QTransform是對座標變換矩陣的封裝,一般使用:旋轉rotate,放縮scale,裁剪shear,平移translate等方法設定相關變換。一個QTransform對象能夠依次設置多個座標變換操做。

Qtransform也可使用setmatrix()等方法直接操做變換矩陣。

繪圖設備

繪圖設備是指QPaintDevice的派生類,包括QPixmap、QBitmap、QImage和QPicture。QPainter提供了drawPixmap()方法能夠將圖像畫到不一樣設備上。

QPixmap爲圖片在屏幕顯示作了優化,QPixmap與繪圖設備底層相關,不提供像素級支持,不一樣設備上圖像有所不一樣。

能夠直接使用QPainter在QPixmap上繪製也能夠接受一個圖片文件在屏幕上顯示。

QBitmap是QPixmap的派生類,只能繪製黑白圖像(色深爲1)。

QImage是與硬件無關的繪圖設備,提供了像素級操做支持。

QPicture用於記錄QPainter的操做,並保存到一個序列化的平臺獨立的二進制文件中。

示例:

void Widget::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    QPicture picture;
    painter.begin(&picture); //開始記錄
    painter.drawText(100,100,"Hello World!");//繪製
    painter.end();//終止記錄
    picture.save("Hello.pic");//保存到文件

    picture.load("Hello.pic");//加載文件
    painter.drawPicture(0,0,picture);//重畫
}

Qt Graphics View

Graphics View是一種採用MV架構,面向對象的繪圖系統。與QPainter狀態機使用繪圖語句繪製的模式不一樣,Gaphics View中每一個圖形元素都是一個對象。

Graphics View的架構中Model負責存儲對象結構View則提供觀察窗口,MV架構能夠很容易的實現轉換視角,攝像機,碰撞檢測等功能。

Grphics View框架採用BSP樹管理item,能夠對大量items作出快速響應。

示例:

QGraphicsScene scene;
scene.addText("Hello, world!");
QGraphicsView view(&scene);
view.show();

QGraphicsScene

QGraphicsScene類做爲容器(MV中的模型Model),用於存儲全部的圖形元素;QGraphicsView則是觀察窗口,能夠顯示場景的一部分或者全局;全部的圖形元素均繼承自QGraphicsItem。

QGraphicsScene的addItem(QGraphicsItem * item);方法能夠向容器中添加圖形元素。

addEllipse(), addLine(), addPath(), addPixmap(), addPolygon(), addRect(), or addText()能夠方便地添加圖形元素並返回指向圖形元素的指針。QGraphicsScene::removeItem(QGraphicsItem * item)用於移除相應的Item。

QGraphicsScene使用下標來高效的管理item的位置,默認的使用BSP樹,適用於一個大型的場景,其中的item都是靜止不變的,能夠選擇調用setItemIndexMethod().來禁用下標,能夠查看itemIndexMethod來獲取更多的信息。

items()函數能夠在數微秒內找到item的位置,item()有一些重載函數。itemAt()函數能夠根據提供的位置返回所在位置處的最上層的item指針。

場景的邊界可使用setSceneRect()來設置,item能夠放置在場景中任何位置,場景默認的大小是不受限制的。若是場景邊界矩形沒有設置,QgrapbhicsScene將會使用全部item圖元的邊界,使用函數itemsBoundingRect()返回。

經過繼承QGraphicsScene並重寫事件響應函數能夠對Scene中的Item的點擊、拖動等事件進行響應。在場景變換時,QGraphicsScene將會發射changed()信號,通知相應槽函數進行處理。

QGraphicsView提供了用於顯示的widget,用於顯示Scene,可使用構造函數或void setScene(QGraphicsScene * scene)將多個View聯繫到同一個scene,給相同的數據提供多個View,視口支持openGL,甚至能夠將QGLWidget做爲視口,只要調用一下QGraphicsView::setViewport()

QGraphicsView::mapToScene(), QGraphicsView::mapFromScene()等多個函數能夠在view和scene之間轉換座標系。

QGraphicsView

QGraphicsView是一個觀察窗口,它將QGraphicsScene中item顯示出來,並將用戶的鼠標和鍵盤事件映射到QGraphicsScene事件,且將座標轉換爲QGraphicsScene的座標。

  • setScence

QGraphicsView的構造函數能夠接受一個QGraphicsScene的指針做爲參數或者使用void QGraphicsView::setScene ( QGraphicsScene * scene )

  • transform()

QGraphicsView::setTransform()使用轉換矩陣來改變視圖座標系,達到旋轉或縮放的目的。並經過QGraphicsView::transform()訪問QTransform。

示例:

QTransform transform;//定義座標變換
transform.rotate(+45);//設置旋轉
view.setTransform(transform);
view.show()

固然能夠用void QGraphicsView::rotate(qreal angle)或者void QGraphicsView::scale(qreal sx, qreal sy)

示例:

QGraphicsScene scene;
scene.addText("GraphicsView rotated clockwise");

QGraphicsView view(&scene);
view.rotate(90); // the text is rendered with a 90 degree clockwise rotation
view.show();

QGraphicsView使用ViewportAnchor屬性來決定當轉換矩陣修改和座標系統修改時候如何擺放場景的在viewport中的位置。

默認的是 AnchorViewCenter,這樣使場景點在變換時候保持在view中心點不變。例如當旋轉時候,場景將會圍繞着view中心點來旋轉。

只有場景中的一部分可見時候這個屬性才顯而易見的。例如:當view中有滾動條時候,不然整個場景都在view中,場景將會使用QGraphicsView::aligenment來擺放它的位置。

void QGraphicsView::render ( QPainter * painter)將QGraphicsView經過QPainter映射到QPaintDevice上顯示。

來自官方文檔的示例:

QGraphicsScene scene;
scene.addItem(...
...

QGraphicsView view(&scene);
view.show();
...

QPrinter printer(QPrinter::HighResolution);
printer.setPageSize(QPrinter::A4);
QPainter painter(&printer);

// print, fitting the viewport contents into a full page
view.render(&painter);

// print the upper half of the viewport into the lower.
// half of the page.
QRect viewport = view.viewport()->rect();
view.render(&painter,
            QRectF(0, printer.height() / 2,
                   printer.width(), printer.height() / 2),
            viewport.adjusted(0, 0, 0, -viewport.height() / 2));

QGraphicsItem

QGraphicsItem是全部item的基類,要自定義item須要繼承QGraphicsItem並實現兩個純虛函數:

  • QRectF boundingRect() const

返回一個表明形狀QRectF對象。

  • void paint(QPainter painter, const QStyleOptionGraphicsItem option,
    QWidget *widget)

控制繪圖過程。

QGraphicsScene認爲全部item的boundingRect函數與shape函數都是不發生改變的,除非用戶進行通知。若是想改變一個item,必需先調用prepareGeometryChange以容許QGraphicsScene進行更新。

Graphics View框架使用shape()collidesWithPath()實現碰撞檢測。

  • QPainterPath QGraphicsItem::shape() const

函數返回碰撞箱形狀,默認實現爲調用boundingRect()並返回簡單的矩形。

重寫該函數,定義更復雜的碰撞箱形狀,但複雜的形狀會使計算量增大。

默認實現:

QPainterPath RoundItem::shape() const
{
    QPainterPath path;
    path.addEllipse(boundingRect());
    return path;
}
  • bool QGraphicsItem::collidesWithPath(const QPainterPath & path)

當item與參數指定的PainterPath發生碰撞時返回true,不然爲false。

碰撞箱形狀決定於shape(),可使用shape()函數進行兩個item的碰撞檢測:

item1.collidesWithPath(item2.shape());

Qt提供了默認實現,能夠重寫該方法實現更復雜的碰撞檢測。

Qt Animation

QPropertyAnimation

示例:

QPushButton button("Animated Button");  

button.show();  

QPropertyAnimation animation(&button, "geometry"); 
 //初始化Animation對象,指定要動畫的屬性

animation.setDuration(10000);  
//設定持續時間,單位爲毫秒

animation.setStartValue(QRect(0, 0, 0, 0));  
//設定初始值

animation1->setKeyValueAt(0.4, QRect(20, 250, 20, 30));  
//設定中間關鍵值,第一個參數爲時間百分比,第二個參數爲關鍵值

animation.setEndValue(QRect(250, 250, 100, 30));
//設定結束值  

animation.start();
//啓動動畫

QPropertyAnimation是Qt動畫框架提供的一個實現類,它按照設置對某個Qt屬性進行線性插值,並在時間軸的控制下動態調節該屬性達到動畫的效果。

QPropertyAnimation動畫的屬性必須是Qt屬性,Qt屬性由Q_PROPERTY宏來聲明,而且類必須繼承QObject。標準的Qt Widgets組件均使用Q_PROPERTY屬性,能夠直接進行動畫,若自定義組件使用動畫效果則須要使用Q_PROPERTY進行聲明。

Q_PROPERTY宏的原型爲:

Q_PROPERTY(type name
       (READ getFunction [WRITE setFunction] |
        MEMBER memberName [(READ getFunction | WRITE setFunction)])
       [RESET resetFunction]
       [NOTIFY notifySignal]
       [REVISION int]
       [DESIGNABLE bool]
       [SCRIPTABLE bool]
       [STORED bool]
       [USER bool]
       [CONSTANT]
       [FINAL])
  • 屬性類型必須有一個read函數。它用來讀取屬性值。所以用Const限定。它的返回值類型必須爲屬性類型或者屬性類型的引用或者指針。不能是其餘類型例如:QWidget::hasFocus()。

  • 有一個可選的write函數。它用來設置屬性值,它的返回值必須爲void型,而起必需要含有一個參數。例如:QWidget::setEnabled()。

  • 一個reset函數可以把property設置成其默認狀態,它也是可選的。復位功能必須返回void,而且不帶參數。

  • 一個可選的NOTIFY信號, 它提供了一個在值發生改變時會自動被觸發信號。

  • 若是定義了"STODE"屬性代表這是一直存在的。

  • 一個"DESIGNABLE"屬性代表該property能在GUI builder(通常爲Qt Designer)可見。

  • USER 屬性 表面是否能夠被用戶所編輯

  • CONST設定屬性是不可修改的 因此不能跟WRITE或者NOTIFY同時出現

  • FINAL代表該屬性不會被派生類中重寫

示例:

class Test : public QObject {

Q_OBJECT

Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)

public:

Test(QObject *parent = 0) : QObject(parent) {}

virtual ~Test(){}

void setEnabled(bool e) { enabled = e; }

bool isEnabled() const { return enabled; }

private:

bool enabled;

};

EasyCurving

QPropertyAnimation默認進行線性插值,也可使用寬鬆曲線(EasyCurvig)設置屬性變化規律,如產生組件彈跳進入的效果等。

QPushButton button("Animated Button");  

button.show();  

QPropertyAnimation animation(&button, "geometry"); 
 //初始化Animation對象,指定要動畫的屬性

animation.setDuration(10000);  
//設定持續時間,單位爲毫秒

animation.setStartValue(QRect(0, 0, 0, 0));  
//設定初始值

animation1->setKeyValueAt(0.4, QRect(20, 250, 20, 30));  
//設定中間關鍵值,第一個參數爲時間百分比,第二個參數爲關鍵值

animation.setEndValue(QRect(250, 250, 100, 30));
//設定結束值  

animation1->setEasingCurve(QEasingCurve::OutBounce);  
//使用彈跳曲線

animation.start();
//啓動動畫

QEasyCurving::Type枚舉類型中定義了各類曲線,能夠從中選擇或者自行實現而後註冊到EasyCurving。

QTimeLine

Qt的 動畫效果離不開時間軸的控制,Animation框架依賴QTimeLine提供時間軸。咱們可使用QTimeLine製做本身的動畫。

QTimeLine接受一個以毫秒爲單位的參數,表明動畫運行的總時間。
例:timeline = new QTimeLine(1000);

在計時結束後將會發出finished()信號,或者調用stop()方法手動使QTimeLine進入NotRunning狀態。

調用start方法,QTimeLine開始計時:timeLine->start();

QTimeLine進入Running狀態後,默認每隔40ms發送一個frameChanged()信號,間隔規律能夠手動設置:setUpdateInterval(int interval);

默認狀況下QTimeLine均勻的發送信號,也能夠手動設置間隔規律:timeline->setCurveShape(QTimeLine::LinearCurve);

目前,Qt支持的模式有:

  • QTimeLine::EaseInCurve = 0

The value starts growing slowly, then increases in speed.先慢後快

  • QTimeLine::EaseOutCurve = 1

The value starts growing steadily, then ends slowly.先勻加速,後減速

  • QTimeLine::EaseInOutCurve = 2

The value starts growing slowly, then runs steadily, then grows slowly again.
先慢,中間穩定,最後慢

  • QTimeLine::LinearCurve = 3

The value grows linearly (e.g., if the duration is 1000 ms, the value at time 500 ms is 0.5).勻速的

  • QTimeLine::SineCurve = 4

The value grows sinusoidally.正選曲線式

  • QTimeLine::CosineCurve = 5

The value grows cosinusoidally.餘弦曲線式

相關文章
相關標籤/搜索