#(29):繪制設備
繪圖設備是繼承`QPainterDevice`的類。`QPaintDevice`就是能夠進行繪制的類,也就是說,`QPainter`可以在任何`QPaintDevice`的子類上進行繪制。現在,Qt 提供了若干這樣的類:
Qt4:
[](http://files.devbean.net/images/2012/12/paint-device-qt4.png)
Qt5:
[](http://files.devbean.net/images/2012/12/paint-device-qt5.png)
上面的是 Qt4 的相關類圖,下面是 Qt5。這兩部分大致相同,只是在 Qt5 中,`QGLPixelBuffer`已經被廢棄。本章我們關注的是`QPixmap`、`QBitmap`、`QImage`和`QPicture`這幾個類。另外的部分,`QWidget`就是所有組件的父類,我們已經在前面的章節中使用過,這里不再贅述。`QGLWidget`和`QGLFramebufferObject`,顧名思義,就是關于 OpenGL 的相關類。在 Qt 中,我們可以方便地結合 OpenGL 進行繪制。由于這部分需要牽扯到 OpenGL 的相關內容,現在也不再深入。在我們選擇的幾個類中,大多與圖像密切相關。
`QPixmap`專門為圖像在屏幕上的顯示做了優化;`QBitmap`是`QPixmap`的一個子類,它的色深限定為1,你可以使用`QPixmap`的`isQBitmap()`函數來確定這個`QPixmap`是不是一個`QBitmap`。`QImage`專門為圖像的像素級訪問做了優化。`QPicture`則可以記錄和重現`QPainter`的各條命令。下面我們將分兩部分介紹這四種繪圖設備。
`QPixmap`繼承了`QPaintDevice`,因此,你可以使用`QPainter`直接在上面繪制圖形。`QPixmap`也可以接受一個字符串作為一個文件的路徑來顯示這個文件,比如你想在程序之中打開 png、jpeg 之類的文件,就可以使用`QPixmap`。使用`QPainter::drawPixmap()`函數可以把這個文件繪制到一個`QLabel`、`QPushButton`或者其他的設備上面。正如前面所說的那樣,`QPixmap`是針對屏幕進行特殊優化的,因此,它與實際的底層顯示設備息息相關。注意,這里說的顯示設備并不是硬件,而是操作系統提供的原生的繪圖引擎。所以,在不同的操作系統平臺下,`QPixmap`的顯示可能會有所差別。
`QPixmap`提供了靜態的`grabWidget()`和`grabWindow()`函數,用于將自身圖像繪制到目標上。同時,在使用`QPixmap`時,你可以直接使用傳值的形式,不需要傳指針,因為`QPixmap`提供了“隱式數據共享”。關于這一點,我們會在以后的章節中詳細描述。簡單來說,就是一般對于大型數據(圖像無疑就是這種“大型數據”),為性能起見,通常會采用傳指針的方式,但是由于`QPixmap`內置了隱式數據共享,所以只要知道傳遞`QPixmap`。
前面說過,`QBitmap`繼承自`QPixmap`,因此具有`QPixmap`的所有特性。不同之處在于,`QBitmap`的色深始終為 1。色深這個概念來自計算機圖形學,是指用于表現顏色的二進制的位數。我們知道,計算機里面的數據都是使用二進制表示的。為了表示一種顏色,我們也會使用二進制。比如我們要表示 8 種顏色,需要用 3 個二進制位,這時我們就說色深是 3。因此,所謂色深為 1,也就是使用 1 個二進制位表示顏色。1 個位只有兩種狀態:0 和 1,因此它所表示的顏色就有兩種,黑和白。所以說,`QBitmap`實際上是只有黑白兩色的圖像數據。由于`QBitmap`色深小,因此只占用很少的存儲空間,所以適合做光標文件和筆刷。
下面我們來看同一個圖像文件在`QPixmap`和`QBitmap`下的不同表現:
~~~
void paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPixmap pixmap("qt-logo.png");
QBitmap bitmap("qt-logo.png");
painter.drawPixmap(10, 10, 250, 125, pixmap);
painter.drawPixmap(270, 10, 250, 125, bitmap);
QPixmap whitePixmap("qt-logo-white.png");
QBitmap whiteBitmap("qt-logo-white.png");
painter.drawPixmap(10, 140, 250, 125, whitePixmap);
painter.drawPixmap(270, 140, 250, 125, whiteBitmap);
}
~~~
先來看一下運行結果:
[](http://files.devbean.net/images/2012/12/qpixmap-qbitmap-demo.png)
這里我們給出了兩張 png 圖片。qt-logo.png 具有透明背景,qt-logo-white.png 具有白色背景。我們分別使用`QPixmap`和`QBitmap`來加載它們。注意看它們的區別:白色的背景在`QBitmap`中消失了,而透明色在`QBitmap`中轉換成了黑色(“黑色”,記住,`QBitmap`只有兩種顏色:黑色和白色);其他顏色則是使用點的疏密程度來體現的。
`QPixmap`使用底層平臺的繪制系統進行繪制,無法提供像素級別的操作,而`QImage`則是使用獨立于硬件的繪制系統,實際上是自己繪制自己,因此提供了像素級別的操作,并且能夠在不同系統之上提供一個一致的顯示形式。
[](http://files.devbean.net/images/2012/12/qimage-32bit.png)
`QImage`與`QPixmap`相比,最大的優勢在于能夠進行像素級別的操作。我們通過上面的示意圖可以看到,我們聲明一個 3 x 3 像素的`QImage`對象,然后利用`setPixel()`函數進行顏色的設置。你可以把`QImage`想象成一個 RGB 顏色的二維數組,記錄了每一像素的顏色。值得注意的是,在`QImage`上進行繪制時,不能使用`QImage::Format_Indexed8`這種格式。
最后一種`QPicture`是平臺無關的,因此它可以使用在多種設備之上,比如 svg、pdf、ps、打印機或者屏幕。回憶下我們曾經說的`QPaintDevice`,實際上是說可以由`QPainter`進行繪制的對象。`QPicture`使用系統分辨率,并且可以調整`QPainter`來消除不同設備之間的顯示差異。如果我們要記錄下`QPainter`的命令,首先要使用`QPainter::begin()`函數,將`QPicture`實例作為參數傳遞進去,以便告訴系統開始記錄,記錄完畢后使用`QPainter::end()`命令終止。代碼示例如下:
~~~
QPicture picture;
QPainter painter;
painter.begin(&picture); // 在 picture 進行繪制
painter.drawEllipse(10, 20, 80, 70); // 繪制一個橢圓
painter.end(); // 繪制完成
picture.save("drawing.pic"); // 保存 picture
~~~
如果我們要重現命令,首先要使用 QPicture::load() 函數進行裝載:
~~~
QPicture picture;
picture.load("drawing.pic"); // 加載 picture
QPainter painter;
painter.begin(&myImage); // 在 myImage 上開始繪制
painter.drawPicture(0, 0, picture); // 在 (0, 0) 點開始繪制 picture
painter.end();
~~~
我們也可以直接使用`QPicture::play()`進行繪制。這個函數接受一個`QPainter`對象,也就是進行繪制的畫筆。
- (1)序
- (2)Qt 簡介
- (3)Hello, world!
- (4)信號槽
- (5)自定義信號槽
- (6)Qt 模塊簡介
- (7)MainWindow 簡介
- (8)添加動作
- (9)資源文件
- (10)對象模型
- (11)布局管理器
- (12)菜單欄、工具欄和狀態欄
- (13)對話框簡介
- (14)對話框數據傳遞
- (15)標準對話框 QMessageBox
- (16)深入 Qt5 信號槽新語法
- (17)文件對話框
- (18)事件
- (19)事件的接受與忽略
- (21)事件過濾器
- (22)事件總結
- (23)自定義事件
- (24)Qt 繪制系統簡介
- (25)畫刷和畫筆
- (26)反走樣
- (27)漸變
- (28)坐標系統
- (29)繪制設備
- (30)Graphics View Framework
- (31)貪吃蛇游戲(1)
- (32)貪吃蛇游戲(2)
- (33)貪吃蛇游戲(3)
- (34)貪吃蛇游戲(4)
- (35)文件
- (36)二進制文件讀寫
- (37)文本文件讀寫
- (38)存儲容器
- (39)遍歷容器
- (40)隱式數據共享
- (41)model/view 架構
- (42)QListWidget、QTreeWidget 和 QTableWidget
- (43)QStringListModel
- (44)QFileSystemModel
- (45)模型
- (46)視圖和委托
- (47)視圖選擇
- (48)QSortFilterProxyModel
- (49)自定義只讀模型
- (50)自定義可編輯模型
- (51)布爾表達式樹模型
- (52)使用拖放
- (53)自定義拖放數據
- (54)剪貼板
- (55)數據庫操作
- (56)使用模型操作數據庫
- (57)可視化顯示數據庫數據
- (58)編輯數據庫外鍵
- (59)使用流處理 XML
- (60)使用 DOM 處理 XML
- (61)使用 SAX 處理 XML
- (62)保存 XML
- (63)使用 QJson 處理 JSON
- (64)使用 QJsonDocument 處理 JSON
- (65)訪問網絡(1)
- (66)訪問網絡(2)
- (67)訪問網絡(3)
- (68)訪問網絡(4)
- (69)進程
- (70)進程間通信
- (71)線程簡介
- (72)線程和事件循環
- (73)Qt 線程相關類
- (74)線程和 QObject
- (75)線程總結
- (76)QML 和 QtQuick 2
- (77)QML 語法
- (78)QML 基本元素
- (79)QML 組件
- (80)定位器
- (81)元素布局
- (82)輸入元素
- (83)Qt Quick Controls
- (84)Repeater
- (85)動態視圖
- (86)視圖代理
- (87)模型-視圖高級技術
- (88)Canvas
- (89)Canvas(續)