1 QPainter
Qt 的繪圖系統允許使用相同的 API 在屏幕和其它打印設備上進行繪製。整個繪圖系統基於QPainter,QPainterDevice和QPaintEngine三個類。
QPainter用來執行繪製的操作;
QPaintDevice是一個二維空間的抽象,這個二維空間允許QPainter在其上面進行繪製,也就是QPainter工作的空間;QPaintEngine提供了畫筆(QPainter)在不同的設備上進行繪製的統一的接口。
QPaintEngine類應用於QPainter和QPaintDevice之間,通常對開發人員是透明的。除非你需要自定義一個設備,否則你是不需要關心QPaintEngine這個類的。我們可以把QPainter理解成畫筆;把QPaintDevice理解成使用畫筆的地方,比如紙張、屏幕等;而對於紙張、屏幕而言,肯定要使用不同的畫筆繪製,爲了統一使用一種畫筆,我們設計了QPaintEngine類,這個類讓不同的紙張、屏幕都能使用一種畫筆。
下圖給出了這三個類之間的層次結構:
頭文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
public:
//繪圖事件
void paintEvent(QPaintEvent *);
int posX;
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//點擊移動按鈕 移動圖片
posX = 10;
connect(ui->pushButton,&QPushButton::clicked,[=](){
posX += 10;
//手動調用繪圖事件
update();
});
}
void Widget::paintEvent(QPaintEvent *)
{
//創建畫家
QPainter painter(this);
//如果出屏幕 強制變回10
if(posX > this->width())
{
posX = 10;
}
//畫圖片
painter.drawPixmap(posX,100,QPixmap(":/Image/OnePiece.png"));
高級設置
painter.drawEllipse( QPoint(100,100),50,50);
//設置抗鋸齒 效率低
painter.setRenderHint(QPainter::Antialiasing);
painter.drawEllipse( QPoint(200,100),50,50);
painter.drawRect( QRect(20,20,50,50));
//移動畫家
painter.translate(QPoint(100,0));
//保存狀態
painter.save();
painter.drawRect( QRect(20,20,50,50));
painter.translate(QPoint(100,0));
//取出狀態
painter.restore();
painter.drawRect( QRect(20,20,50,50));
//設置畫筆顏色
QPen pen(QColor(255,0,0));
//設置筆寬度
pen.setWidth(3);
//設置筆風格
pen.setStyle(Qt::DotLine);
//畫家用這隻筆
painter.setPen(pen);
//畫刷填充顏色
QBrush brush(Qt::cyan);
//讓畫家使用畫刷
brush.setStyle(Qt::Dense4Pattern);
painter.setBrush(brush);
//利用畫家畫畫
//畫線
painter.drawLine(QPoint(0,0),QPoint(100,100));
//畫圓(橢圓)
painter.drawEllipse(QPoint(100,100),50,50);
//畫矩形
painter.drawRect(QRect(10,10,50,50));
//畫字體
painter.drawText(QRect(10,200,150,50),"好好學習,天天向上");
}
Widget::~Widget()
{
delete ui;
}
2 繪圖設備
繪圖設備是指繼承QPainterDevice的子類。Qt一共提供了四個這樣的類,分別是QPixmap、QBitmap、QImage和 QPicture。其中,
- QPixmap專門爲圖像在屏幕上的顯示做了優化
- QBitmap是QPixmap的一個子類,它的色深限定爲1,可以使用 QPixmap的isQBitmap()函數來確定這個QPixmap是不是一個QBitmap。
- QImage專門爲圖像的像素級訪問做了優化。
- QPicture則可以記錄和重現QPainter的各條命令。
QPixmap
QPixmap繼承了QPaintDevice,因此,你可以使用QPainter直接在上面繪製圖形。QPixmap也可以接受一個字符串作爲一個文件的路徑來顯示這個文件,比如你想在程序之中打開png、jpeg之類的文件,就可以使用 QPixmap。使用QPainter的drawPixmap()函數可以把這個文件繪製到一個QLabel、QPushButton或者其他的設備上面。QPixmap是針對屏幕進行特殊優化的,因此,它與實際的底層顯示設備息息相關。注意,這裏說的顯示設備並不是硬件,而是操作系統提供的原生的繪圖引擎。所以,在不同的操作系統平臺下,QPixmap的顯示可能會有所差別。
QBitmap
QBitmap繼承自QPixmap,因此具有QPixmap的所有特性,提供單色圖像。QBitmap的色深始終爲1. 色深這個概念來自計算機圖形學,是指用於表現顏色的二進制的位數。我們知道,計算機裏面的數據都是使用二進制表示的。爲了表示一種顏色,我們也會使用二進制。比如我們要表示8種顏色,需要用3個二進制位,這時我們就說色深是3. 因此,所謂色深爲1,也就是使用1個二進制位表示顏色。1個位只有兩種狀態:0和1,因此它所表示的顏色就有兩種,黑和白。所以說,QBitmap實際上是隻有黑白兩色的圖像數據。
由於QBitmap色深小,因此只佔用很少的存儲空間,所以適合做光標文件和筆刷。
下面我們來看同一個圖像文件在QPixmap和QBitmap下的不同表現:
void PaintWidget::paintEvent(QPaintEvent *)
{
QPixmap pixmap(":/Image/butterfly.png");
QPixmap pixmap1(":/Image/butterfly1.png");
QBitmap bitmap(":/Image/butterfly.png");
QBitmap bitmap1(":/Image/butterfly1.png");
QPainter painter(this);
painter.drawPixmap(0, 0, pixmap);
painter.drawPixmap(200, 0, pixmap1);
painter.drawPixmap(0, 130, bitmap);
painter.drawPixmap(200, 130, bitmap1);
}
注意:
這裏我們給出了兩張png圖片。butterfly1.png是沒有透明色的純白背景,而butterfly.png是具有透明色的背景。我們分別使用QPixmap和QBitmap來加載它們。注意看它們的區別:白色的背景在QBitmap中消失了,而透明色在QBitmap中轉換成了黑色;其他顏色則是使用點的疏密程度來體現的。
QPixmap
QPixmap使用底層平臺的繪製系統進行繪製,無法提供像素級別的操作。
QImage
QImage則是使用獨立於硬件的繪製系統,實際上是自己繪製自己,因此提供了像素級別的操作,並且能夠在不同系統之上提供一個一致的顯示形式。
我們聲明瞭一個QImage對象,大小是300 x 300,顏色模式是RGB32,即使用32位數值表示一個顏色的RGB值,也就是說每種顏色使用8位。然後我們對每個像素進行顏色賦值,從而構成了這個圖像。我們可以把QImage想象成一個RGB顏色的二維數組,記錄了每一像素的顏色。
看一個例子
void PaintWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QImage image(300, 300, QImage::Format_RGB32);
QRgb value;
//將圖片背景填充爲白色
image.fill(Qt::white);
//改變指定區域的像素點的值
for(int i=50; i<100; ++i)
{
for(int j=50; j<100; ++j)
{
value = qRgb(255, 0, 0); // 紅色
image.setPixel(i, j, value);
}
}
//將圖片繪製到窗口中
painter.drawImage(QPoint(0, 0), image);
}
QImage與QPixmap的區別
- QPixmap主要是用於繪圖,針對屏幕顯示而最佳化設計,QImage主要是爲圖像I/O、圖片訪問和像素修改而設計的
- QPixmap依賴於所在的平臺的繪圖引擎,故例如反鋸齒等一些效果在不同的平臺上可能會有不同的顯示效果,QImage使用Qt自身的繪圖引擎,可在不同平臺上具有相同的顯示效果
- 由於QImage是獨立於硬件的,也是一種QPaintDevice,因此我們可以在另一個線程中對其進行繪製,而不需要在GUI線程中處理,使用這一方式可以很大幅度提高UI響應速度。
- QImage可通過setPixpel()和pixel()等方法直接存取指定的像素。
- QImage與QPixmap之間的轉換:
-
- QImage轉QPixmap
使用QPixmap的靜態成員函數: fromImage()
- QImage轉QPixmap
QPixmap fromImage(const QImage & image,
Qt::ImageConversionFlags flags = Qt::AutoColor)
-
- QPixmap轉QImage:
使用QPixmap類的成員函數: toImage()
- QPixmap轉QImage:
QImage toImage() const
QPicture
最後一個需要說明的是QPicture。這是一個可以記錄和重現QPainter命令的繪圖設備。 QPicture將QPainter的命令序列化到一個IO設備,保存爲一個平臺獨立的文件格式。這種格式有時候會是“元文件(meta- files)”。Qt的這種格式是二進制的,不同於某些本地的元文件,Qt的pictures文件沒有內容上的限制,只要是能夠被QPainter繪製的元素,不論是字體還是pixmap,或者是變換,都可以保存進一個picture中。
QPicture是平臺無關的,因此它可以使用在多種設備之上,比如svg、pdf、ps、打印機或者屏幕。回憶下我們這裏所說的QPaintDevice,實際上是說可以有QPainter繪製的對象。QPicture使用系統的分辨率,並且可以調整 QPainter來消除不同設備之間的顯示差異。
如果我們要記錄下QPainter的命令,首先要使用QPainter::begin()函數,將QPicture實例作爲參數傳遞進去,以便告訴系統開始記錄,記錄完畢後使用QPainter::end()命令終止。代碼示例如下:
void PaintWidget::paintEvent(QPaintEvent *)
{
QPicture pic;
QPainter painter;
//將圖像繪製到QPicture中,並保存到文件
painter.begin(&pic);
painter.drawEllipse(20, 20, 100, 50);
painter.fillRect(20, 100, 100, 100, Qt::red);
painter.end();
pic.save("D:\\drawing.pic");
//將保存的繪圖動作重新繪製到設備上
pic.load("D:\\drawing.pic");
painter.begin(this);
painter.drawPicture(200, 200, pic);
painter.end();
}
注意:
就被保存成了後綴爲pic格式的:pic.save("D:\\drawing.pic");
繪圖設備總實例
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPicture>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QPixmap 做繪圖設備,對不同平臺顯示做了優化
QPixmap pix(300,300);
//設置默認填充色
pix.fill(Qt::white);
QPainter painter(&pix);
painter.setPen(QPen(Qt::green));
painter.drawEllipse(QPoint(150,150),100,100);
//保存
pix.save("E:\\pix.png");
QImage 做繪圖設備 對像素級訪問進行了優化
QImage img(300,300,QImage::Format_RGB32);
img.fill(Qt::white);
QPainter painter(&img);
painter.setPen(QPen(Qt::blue));
painter.drawEllipse(QPoint(150,150),100,100);
img.save("E:\\img.png");
//QPicture 繪圖設備
QPicture pic; //用於重現 記錄繪圖指令
QPainter painter;
painter.begin(&pic);
painter.setPen(QPen(Qt::cyan));
painter.drawEllipse(QPoint(150,150),100,100);
painter.end();
//保存
pic.save("E:\\pic.zt");
}
void Widget::paintEvent(QPaintEvent *)
{
QImage可修改xiangsu
QImage img;
img.load(":/Image/Luffy.png");
for(int i = 50 ; i < 100;i++)
{
for(int j = 50 ; j < 100 ;j ++)
{
QRgb value = qRgb(255,0,0);
//設置像素點
img.setPixel(i,j,value);
}
}
QPainter painter(this);
painter.drawImage(QPoint(0,0),img);
//重現繪圖指令 QPicture
QPicture pic;
pic.load("E:\\pic.zt");
QPainter painter(this);
painter.drawPicture(0,0,pic);
}
Widget::~Widget()
{
delete ui;
}