Qt 的繪圖系統容許使用相同的 API 在屏幕和其它打印設備上進行繪製。整個繪圖系統基於QPainter
,QPainterDevice
和QPaintEngine
三個類。函數
QPainter
用來執行繪製的操做;QPaintDevice
是一個二維空間的抽象,這個二維空間容許QPainter
在其上面進行繪製,也就是QPainter
工做的空間;QPaintEngine
提供了畫筆(QPainter
)在不一樣的設備上進行繪製的統一的接口。QPaintEngine
類應用於QPainter
和QPaintDevice
之間,一般對開發人員是透明的。除非你須要自定義一個設備,不然你是不須要關心QPaintEngine
這個類的。咱們能夠把QPainter
理解成畫筆;把QPaintDevice
理解成使用畫筆的地方,好比紙張、屏幕等;而對於紙張、屏幕而言,確定要使用不一樣的畫筆繪製,爲了統一使用一種畫筆,咱們設計了QPaintEngine
類,這個類讓不一樣的紙張、屏幕都能使用一種畫筆。this
下圖給出了這三個類之間的層次結構(出自 Qt API 文檔):翻譯
上面的示意圖告訴咱們,Qt 的繪圖系統其實是,使用QPainter
在QPainterDevice
上進行繪製,它們之間使用QPaintEngine
進行通信(也就是翻譯QPainter
的指令)。設計
下面咱們經過一個實例來介紹QPainter
的使用:指針
//!!! Qt4/Qt5 class PaintedWidget : public QWidget { Q_OBJECT public: PaintedWidget(QWidget *parent = 0); protected: void paintEvent(QPaintEvent *); };
注意咱們重寫了QWidget
的paintEvent()
函數。這或許是咱們在理解了 Qt 事件系統以後首次實際應用。接下來就是PaintedWidget
的源代碼:code
//!!! Qt4/Qt5 PaintedWidget::PaintedWidget(QWidget *parent) : QWidget(parent) { resize(800, 600); setWindowTitle(tr("Paint Demo")); } void PaintedWidget::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawLine(80, 100, 650, 500); painter.setPen(Qt::red); painter.drawRect(10, 10, 100, 400); painter.setPen(QPen(Qt::green, 5)); painter.setBrush(Qt::blue); painter.drawEllipse(50, 150, 400, 200); }
在構造函數中,咱們僅僅設置了窗口的大小和標題。而paintEvent()
函數則是繪製的代碼。首先,咱們在棧上建立了一個QPainter
對象,也就是說,每次運行paintEvent()
函數的時候,都會重建這個QPainter
對象。注意,這一點可能會引起某些細節問題:因爲咱們每次重建QPainter
,所以第一次運行時所設置的畫筆顏色、狀態等,第二次再進入這個函數時就會所有丟失。有時候咱們但願保存畫筆狀態,就必須本身保存數據,不然的話則須要將QPainter
做爲類的成員變量。對象
QPainter
接收一個QPaintDevice
指針做爲參數。QPaintDevice
有不少子類,好比QImage
,以及QWidget
。注意回憶一下,QPaintDevice
能夠理解成要在哪裏去繪製,而如今咱們但願畫在這個組件,所以傳入的是 this 指針。接口
QPainter
有不少以 draw 開頭的函數,用於各類圖形的繪製,好比這裏的drawLine()
,drawRect()
以及drawEllipse()
等。當繪製輪廓線時,使用QPainter
的pen()
屬性。好比,咱們調用了painter.setPen(Qt::red)
將 pen 設置爲紅色,則下面繪製的矩形具備紅色的輪廓線。接下來,咱們將 pen 修改成綠色,5 像素寬(painter.setPen(QPen(Qt::green, 5))
),又設置了畫刷爲藍色。這時候再調用 draw 函數,則是具備綠色 5 像素寬輪廓線、藍色填充的橢圓。事件
運行一下咱們的程序,能夠看到最終效果:ip
咱們會在後面的章節詳細介紹畫筆QPen
和畫刷QBrush
的屬性。
另外要說明一點,請注意咱們的繪製順序,首先是直線,而後是矩形,最後是橢圓。按照這樣的繪製順序,能夠看到直線是第一個繪製,位於最下一層;矩形是第二個繪製,在中間一層;橢圓是最後繪製,在最上層。
若是瞭解 OpenGL,確定據說過這麼一句話:OpenGL 是一個狀態機。所謂狀態機,就是說,OpenGL 保存的只是各類狀態。好比,將畫筆顏色設置成紅色,那麼,除非你從新設置另外的顏色,它的顏色會一直是紅色。QPainter
也是這樣,它的狀態不會本身恢復,除非你使用了各類設置函數。所以,若是在上面的代碼中,咱們在橢圓繪製以後再畫一個矩形,它的樣式還會是綠色 5 像素的輪廓線以及藍色的填充,除非你顯式地調用了設置函數進行狀態的更新。這是大多數繪圖系統的實現方式,包括 OpenGL、QPainter
以及 Java2D。正由於QPainter
是一個狀態機,纔會引出咱們前面曾經介紹過的一個細節問題:因爲paintEvent()
是須要重複進入的,所以,須要注意第二次進入時,QPainter
的狀態是否是和第一次一致,不然的話可能會形成閃爍的現象。這個閃爍並非因爲雙緩衝的問題,而是因爲繪製狀態的快速切換。