QT 繪圖設備和Graphics View Framework QT 繪圖設備和Graphics View Framework

QT 繪圖設備和Graphics View Framework

             <div id="article_content" class="article_content clearfix">
        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-211130ba7a.css">
                        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-211130ba7a.css">
            <div class="htmledit_views" id="content_views">
                                        <div><span style="font-size:small;">繪圖設備是指繼承QPainterDevice的子類。Qt一共提供了四個這樣的類,分別是QPixmap、QBitmap、QImage和QPicture。其中,QPixmap專門爲圖像在屏幕上的顯示做了優化,而QBitmap是QPixmap的一個子類,它的色深限定爲1,你可以使用QPixmap的isQBitmap()函數來確定這個QPixmap是不是一個QBitmap。QImage專門爲圖像的像素級訪問做了優化。QPicture則可以記錄和重現QPainter的各條命令。下面我們將分兩部分介紹這四種繪圖設備。</span></div>


QPixmap繼承了QPaintDevice,因此,你可以使用QPainter直接在上面繪製圖形。QPixmap也可以接受一個字符串作爲一個文件的路徑來顯示這個文件,比如你想在程序之中打開png、jpeg之類的文件,就可以使用QPixmap。使用QPainter的drawPixmap()函數可以把這個文件繪製到一個QLabel、QPushButton或者其他的設備上面。QPixmap是針對屏幕進行特殊優化的,因此,它與實際的底層顯示設備息息相關。注意,這裏說的顯示設備並不是硬件,而是操作系統提供的原生的繪圖引擎。所以,在不同的操作系統平臺下,QPixmap的顯示可能會有所差別。
 
QPixmap提供了靜態的grabWidget()和grabWindow()函數,用於將自身圖像繪製到目標上。同時,在使用QPixmap時,你可以直接使用傳值也不需要傳指針,因爲QPixmap提供了“隱式數據共享”。關於這一點,我們會在以後的章節中詳細描述,這裏只要知道傳遞QPixmap不必須使用指針就好了。
 
QBitmap繼承自QPixmap,因此具有QPixmap的所有特性。QBitmap的色深始終爲1. 色深這個概念來自計算機圖形學,是指用於表現顏色的二進制的位數。我們知道,計算機裏面的數據都是使用二進制表示的。爲了表示一種顏色,我們也會使用二進制。比如我們要表示8種顏色,需要用3個二進制位,這時我們就說色深是3. 因此,所謂色深爲1,也就是使用1個二進制位表示顏色。1個位只有兩種狀態:0和1,因此它所表示的顏色就有兩種,黑和白。所以說,QBitmap實際上是隻有黑白兩色的圖像數據。
 
由於QBitmap色深小,因此只佔用很少的存儲空間,所以適合做光標文件和筆刷。
 
下面我們來看同一個圖像文件在QPixmap和QBitmap下的不同表現:
 
void PaintedWidget::paintEvent(QPaintEvent *event)
{
        QPainter painter(this);
        QPixmap pixmap("Cat.png");
        QBitmap bitmap("Cat.png");
        painter.drawPixmap(10, 10, 128, 128, pixmap);
        painter.drawPixmap(140, 10, 128, 128, bitmap);
        QPixmap pixmap2("Cat2.png");
        QBitmap bitmap2("Cat2.png");
        painter.drawPixmap(10, 140, 128, 128, pixmap2);
        painter.drawPixmap(140, 140, 128, 128, bitmap2);
}
 
先來看一下運行結果:

 

這裏我們給出了兩張png圖片。Cat.png是沒有透明色的純白背景,而Cat2.png是具有透明色的背景。我們分別使用QPixmap和QBitmap來加載它們。注意看它們的區別:白色的背景在Qbitmap中消失了,而透明色在QBitmap中轉換成了黑色;其他顏色則是使用點的疏密程度來體現的。
 
QPixmap使用底層平臺的繪製系統進行繪製,無法提供像素級別的操作,而QImage則是使用獨立於硬件的繪製系統,實際上是自己繪製自己,因此提供了像素級別的操作,並且能夠在不同系統之上提供一個一致的顯示形式。
 
如上圖所示(出自Qt API文檔),我們聲明瞭一個QImage對象,大小是3 x 3,顏色模式是RGB32,即使用32位數值表示一個顏色的RGB值,也就是說每種顏色使用8位。然後我們對每個像素進行顏色賦值,從而構成了這個圖像。你可以把QImage想象成一個RGB顏色的二維數組,記錄了每一像素的顏色。
 
最後一個需要說明的是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()命令終止。代碼示例如下:
 
QPicture picture;
QPainter painter;
painter.begin(&picture);              // paint in picture
painter.drawEllipse(10,20, 80,70); // draw an ellipse
painter.end();                           // painting done
picture.save("drawing.pic");         // save picture
 
如果我們要重現命令,首先要使用QPicture::load()函數進行裝載:
 
QPicture picture;
picture.load("drawing.pic");          // load picture
QPainter painter;
painter.begin(&myImage);            // paint in myImage
painter.drawPicture(0, 0, picture); // draw the picture at (0,0)
painter.end();
Graphics View Framework
現在我們就要來看看在繪圖部分功能最強大的Graphics View。我們經常說KDE桌面,新版本的KDE桌面就是建立在Graphics View的基礎之上,可見其強大之處。
 
Qt的白皮書裏面這樣寫道:“Qt Graphics View 提供了用於管理和交互大量定製的 2D 圖形對象的平面以及可視化顯示對象的視圖 widget,並支持縮放和旋轉功能。Graphics View 使用 BSP(二進制空間劃分)樹形可非常快速地找到對象,因此即使是包含百萬個對象的大型場景,也能實時圖形化顯示。”
 
Graphics View是一個基於item的M-V架構的框架。
 
基於item意思是,它的每一個組件都是一個item。這是與QPainter的狀態機不同。回憶一下,使用QPainter繪圖多是採用一種面向過程的描述方式,首先使用drawLine()畫一條直線,然後使用drawPolygon()畫一個多邊形;而對於Graphics View來說,相同的過程可以是,首先創建一個場景scene,然後創建一個line對象和一個polygon對象,再使用scene的add()函數將line和polygon添加到scene,最後通過視口view就可以看到了。乍看起來,後者似乎更加複雜,但是,如果你的圖像中包含了成千上萬的直線、多邊形之類,管理這些對象要比管理QPainter的draw語句容易得多。並且,這些圖形對象也更加符合面向對象的設計要求:一個很複雜的圖形可以很方便的複用。
 
M-V架構的意思是,Graphics View提供一個model和一個view。所謂model就是我們添加的種種對象,所謂view就是我們觀察這些對象的視口。同一個model可以由很多view從不同的角度進行觀察,這是很常見的需求。使用QPainter就很難實現這一點,這需要很複雜的計算,而Qt的Graphics View就可以很容易的實現。
 
Graphics View提供了一個QGraphicsScene作爲場景,即是我們添加圖形的空間,相當於整個世界;一個QGraphicsView作爲視口,也就是我們觀察的窗口,相當於照相機的取景框,這個取景框可以覆蓋整個場景,也可以是場景的一部分;一些QGraphicsItem作爲圖形元件,以便scene添加,Qt內置了很多圖形,比如line、polygon等,都是繼承自QGraphicsItem。
 
下面我們來看一下代碼:
 
#include <QtGui>

class DrawApp : public QWidget {
public:
        DrawApp();
protected:
        void paintEvent(QPaintEvent *event);
};

DrawApp::DrawApp()
{

}

void DrawApp::paintEvent(QPaintEvent *event)
{
        QPainter painter(this);
        painter.drawLine(10, 10, 150, 300);
}

int main(int argc, char *argv[])
{
        QApplication a(argc, argv);
        QGraphicsScene *scene = new QGraphicsScene;
        scene->addLine(10, 10, 150, 300);
        QGraphicsView *view = new QGraphicsView(scene);
        view->resize(500, 500);
        view->setWindowTitle("Graphics View");
        view->show();

        DrawApp *da = new DrawApp;
        da->resize(500, 500);
        da->setWindowTitle("QWidget");
        da->show();
        return a.exec();
}
 
爲了突出重點,我們就直接include了QtGui,不過在實際應用中不建議這麼做。這裏提供了直線的兩種實現:一個是DrawApp使用我們前面介紹的技術,重寫paintEvent()函數,這裏就不在贅述,重點來看main()函數裏面的實現。
 
首先,我們創建了一個QGraphicsScene作爲場景,然後在scene中添加了一個直線,這樣就把我們需要的圖形元件放到了scene中。然後創建一個QGraphicsView對象進行觀察。就這樣,我們就是用Graphics View搭建了一個最簡單的應用。運行這個程序來看結果:
第一張圖是Graphics View的,第二個是DrawApp的。雖然這兩個直線是同樣的座標,但是,DrawApp按照原始座標繪製出了直線,而Graphics View則按照座標繪製出直線之後,自動將直線居中顯示在view視口。你可以通過拖動Graphics View來看直線是一直居中顯示的。
 
這裏僅僅是一個很簡單的對比,不過你已經可以看到Graphics View功能的強大。僅這一個居中的操作,如果你是用QPainter,就需要很大的計算量了!當然,如果你不需要這種居中,Graphics View也是可以像QPainter繪製的一樣進行顯示的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章