Qt 5——繪圖和繪圖設備

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()
QPixmap	fromImage(const QImage & image, 
Qt::ImageConversionFlags flags = Qt::AutoColor)
    • QPixmap轉QImage:
      使用QPixmap類的成員函數: toImage()
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;
}

    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章