Qt 提供了內建的繪圖系統以及獨立的QtOpenGL模組提供對OpenGL的支援。Qt提供了基於狀態機的QPainter系統和麵向物件的Graphics View系統。
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:
#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.餘弦曲線式