QT開發(三十九)——GraphicsView框架

QT開發(三十九)——GraphicsView框架

    本文主要翻譯自QT 5.6.2GraphicsView官方文檔

一、GraphicsView框架簡介

QT4.2開始引入了Graphics View框架用來取代QT3中的Canvas模塊,並作出了改進,Graphics View框架實現了模型-視圖結構的圖形管理,能對大量圖元進行管理,支持碰撞檢測,座標變換和圖元組等多種方便的功能。

wKioL1hCZ9mzslUwAADgkFOxTRs344.png

    GraphicsView框架結構主要包含三個主要的類QGraphicsScene(場景)、QGraphicsView(視圖)、QGraphicsItem(圖元)。QGraphicsScene本身不可見,是一個存儲圖元的容器,必須通過與之相連的QGraphicsView視圖來顯示及與外界進行交互,主要提供圖元的操作接口、傳遞事件和管理各個圖元狀態,提供無變換的繪製功能(如打印)QGraphicsView提供一個可視的窗口,用於顯示場景中的圖元,一個場景中可以有多個視圖。QGraphicsItem是場景中各個圖元的基礎類QT提供了常用圖形圖元的標準類,如矩形(QGraphicsRectItem)橢(QGraphicsEllipseItem)文本(QGraphicsTextItem)

    GraphicsView是一個基於圖元Model/View架構的框架每一個組件都是一個獨立的元素QPainter採用面向過程的描述方式繪圖;GraphicsView採用面向對象的描述方式繪圖。GraphicsView繪圖時首先創建一個場景,然後創建圖元對象(如一個直線對象一個多邊形對象,再使用場景的add()函數,將圖元對象添加到場景中,最後通過視圖進行顯示對於複雜的圖像來說,如果圖像包含大量的直線、曲線、多邊形等圖元對象,管理圖元對象比管理QPainter的繪製過程語句要容易,並且圖元對象更符合面向對象的思想,圖形的可複用性更好。

二、QGraphicsScene場景

    QGraphicsScene場景是QGraphicsItem對象的容器,主要功能如下 

    A、提供管理大量圖元的快速接口 

    B、傳播鼠標、鍵盤等事件給場景中的每個圖元 

    C、管理圖元狀態,如圖元選擇焦點處理 

    D、提供無變換的渲染功能,如打印 

    通過函數QGraphicsScene::addItem()可以加入一個圖元到場景中圖元可以通過多個函數進行檢索QGraphicsScene::items()重載函數可以返回矩形多邊形或向量路徑相交的所有圖元QGraphicsScene::itemAt()返回指定點的頂層圖元。所有圖元查找函數按照遞減棧順序返回圖元,第一個返回的圖元位置最頂層,最後一個返回的圖元位於最底層。

    QGraphicsScene的事件傳播體系將場景事件發送給圖元,同時也管理圖元之間的事件傳播。如果場景收到了某一點的鼠標單擊事件,場景會把事件傳給在這一點的最頂層圖元。QGraphicsScene負責管理一些圖元的狀態,如圖元選擇和焦點。通過QGraphicsScene::setSeletionArea()函數選擇多個圖元,選擇區域可以是任意的形狀,使用 QPainterPath表示要得到當前選擇的圖元列表可以使用 QGraphicsScene::selectedItems()函數;QGraphicsScene還管理圖元的鍵盤輸入焦點狀態,可以通QGraphicsScene::setFocusItem()函數或者QGraphicsItem::setFoucs()函數來設置圖元的焦點獲得當前具有焦點的圖元使用函數QGraphicsScene::foucsItem()可以使用 QGraphicsScene::render()函數在繪圖設備上繪製場景。 

三、QGraphicsView視圖

    QGraphicsView是視圖窗口部件,使場景內容可視化,可以連接多個視圖到一個場景,也可以爲相同數據源的數據集提供不同的視圖QGraphicsView是可滾動的窗口部件,可以提供滾動條來瀏覽大的場景。如果需要使用OpenGL,可以使用QGraphicsView::setViewport()將視圖設置爲QGLWidget組件。

    視圖接收鍵盤和鼠標的輸入事件,並把事件翻譯爲場景事件(將座標轉換爲場景的座標),再發送到場景。使用變換矩陣函數QGraphicsView::martix()可以變換場景的座標系統,通過變換場景的座標系統可以實現場景的縮放和旋轉。爲了方便,QGraphicsView提供了視圖和場景的座標轉換函數:QGraphicsView::mapToScene()和QGraphicsView::mapFromScene()。 

四、QGraphicsItem圖元

    QGraphicsItem是圖元的基類。QGraphics View框架提供了種標準的圖元 

    QGraphicsEllipseItem 橢圓圖元

 QGraphicsLineItem     直線圖元

    QGraphicsPathItem     路徑圖元
    QGraphicsPixmapItem   圖像圖元

    QGraphicsPolygonItem  多邊形圖元
    QGraphicsRectItem     矩形圖元
    QGraphicsSimpleTextItem 簡單文本圖元
    QGraphicsTextItem     文本瀏覽圖元

    用戶可以繼承QGraphicsItem實現自定義的圖元。

    QGraphicsItem圖元主要特性如下 

    A、支持鼠標按下移動釋放雙擊懸停滾動和右鍵菜單事件。 

    B、支持鍵盤輸入焦點和按鍵事件 

    C、支持拖拽事件

    D、支持分組使用父子關係和QGraphicsItemGroup

    E、支持碰撞檢測 

    圖元存在於本地座標系統上,場景提供了在圖元和場景間、圖元與圖元間進行座標變換的函數。QGraphicsItem::transform()函數可以使用矩陣轉換座標系統。這對於翻轉和縮放圖元是有用的。

    圖元可以包含其他圖元,父圖元的變換會被其所有的子圖元繼承。無論一個圖元本身有多少變換,圖元的所有函數(QGraphicsItem::contains(), QGraphicsItem::boundingRect(), QGraphicsItem::collidesWith())仍舊執行在本地座標系上。

    QGraphicsItem通過虛函數shape()collideWith())來支持碰撞檢測。從shape()返回圖元的形狀(以本地座標QPainterPath表示),QGraphicsItem會處理所有的碰撞檢測。如果要提供自己的碰撞檢測,需要重新實現QGraphicsItem::collideWith()。

    碰撞檢測的方法:

    a、重寫shape()函數來返回圖元的精準輪廓,依靠默認的collidesWithItem()來做外形交集。如果item輪廓和複雜時候,消耗是很大的
    b、重寫collidesWithItem(),提供一個自己的圖元和輪廓碰撞的算法
Contains()函數可以調用,用來決定一個圖元是否包含一個點。Contains函數可以重寫,contains()函數默認的方法是通過調用shape()來完成的。
    圖元中也可以包含其他的圖元,也可以被別的圖元包含,所有的圖元可以有一個父類圖元多個子類圖元,除非一個圖元沒有父類,否則圖元的位置是在父類座標中,子類圖元將會繼承父類圖元的位置和轉換。 

    通過調用setVisible(),可以設置圖元是否可見,隱藏一個圖元同時也隱藏了其子類,通過調用 setEnabled()來是指圖元是否可用。如果禁用了圖元,那麼所有的子類都不可用。圖元默認都是可見和可用的。

五、GraphicsView座標系統

    Graphics View座標系基於笛卡爾座標系,圖元的場景中的位置和幾何形狀通過x座標和y座標表示。當使用沒有變換的視圖觀察場景時,場景中的一個單位對應屏幕上的一個像素。

    Graphics View架構中有三個有效的座標系統,圖元座標場景座標和視圖座標。Graphics View提供了三個座標系統之間的轉換函數。在繪製圖形時,QGraphics View的場景座標對應QPainter的邏輯座標,QGraphics View的視圖座標對應QPainter的設備座標。 

1圖元座標 

    圖元存在於自己的本地座標圖元的座標系統通常以圖元中心爲原點,圖元中心也是所有座標變換的原點,圖元座標方向是x軸正方向向右y軸正方向下。創建自定義圖元時需要注意圖元的座標QGraphicsScene和QGraphicsView會完成所有的變換。 例如,如果接受到一個鼠標按下或拖入事件,所給的事件位置是基於圖元座標系的。如果某個點位於圖元內部,使用圖元上的點作爲QGraphicsItem::contains()虛函數的參數,函數會返回true。類似,圖元的邊界矩形和形狀也是基於圖元座標系。

    圖元的位置是圖元的中心點在其父圖元座標系統的座標。按這種說法,場景是所有無父圖元的圖元的父圖元。頂層圖元的位置是場景座標。

wKioL1hCZ_6zyHecAACPjX725s4423.png


子圖元的座標與父圖元的座標相關。如果子圖元無變換,子圖元座標和父圖元座標之間的區別與他們的父圖元的座標相同。例如,如果一個無變換的子圖元精確的位於父圖元的中心點,父子圖元的座標系統是相同的。如果子圖元的位置是(10,0),子圖元上的點(0,10)就是父圖元上的點(10,10)。

由於圖元的位置和變換與父圖元相關,但子圖元的座標並不會被父圖元的變換影響,雖然父圖元的變換會隱式地變換子圖元。在上例中,即使父圖元被翻轉和縮放,子圖元上的點(0,10)仍舊是父圖元上的點(10,10)。

    如果調用QGraphicsItem類的paint()函數重繪圖元時,則以圖元座標系爲基準。

2場景座標 

    場景座標是所有圖元的基礎座標系統。場景座標系統描述了頂層圖元的位置,並且構成從視圖傳播到場景的所有場景事件的基礎。每個圖元在場景上都有場景座標和邊界矩形。場景座標的原點在場景中心座標原點是X軸正方向向右,Y軸正方向向下。

wKiom1hCaBzznyLgAABQlPpPquk204.png

3、視圖座標 

    視圖座標是窗口部件的座標,視圖座標的單位是像素,QGraphicsView的左上角是(0,0)所有鼠標事件、拖拽事件最開始都使用視圖座標,爲了和圖元交互,需要轉換座標爲場景座標。

wKiom1hCaDmQoeO3AABAsC66bUs857.png

4座標變換 

    在Graphics View框架中,經常需要將多種座標變換,從場景到圖元,從圖元到圖元,從視圖到場景 QGraphics View框架座標變換函數如下:

QGraphicsView::mapToScene()視圖到場景

QGraphicsView::mapFromScene() 場景到視圖

QGraphicsItem::mapFromScene()  場景到圖元

QGraphicsItem::mapToScene()  圖元到場景

QGraphicsItem::mapToParent()  子圖元到父圖元

QGraphicsItem::mapFromParent() 父圖元到子圖元

QGraphicsItem::mapToItem()本圖元到其他圖元

QGraphicsItem::mapFromItem()其他圖元到本圖元

    在場景中處理圖元時,從場景到圖元、從圖元到圖元、從視圖到場景進行座標和圖形變換是有用的。當在QGraphicsView的視口中擊鼠標時,應該通過調用QGraphicsView::mapToScence()與QGraphicsScene::itemAt()來獲知光標下是場景中的哪個圖元;如果想獲知一個圖元位於視口中的位置,應該先在圖元上調用QGraphicsItem::mapToScene(),然後調用QGraphicsView::mapFromScene();如果獲知在一個視圖橢圓中有哪些圖元,應該把QPainterPath傳遞到mapToScene(),然後再把映射後的路徑傳遞到QGraphicsScene::items()。 可以調用QGraphicsItem::mapToScene()與QGraphicsItem::mapFromScene()在圖元與場景之間進行座標與形狀的映射也可以在子圖元與其父圖元之間通過QGraphicsItem::mapToParent()與QGraphicsItem::mapFromItem()進行映射。所有映射函數可以包括點矩形多邊形路徑。視圖與場景之間的映射也與此類似。對於視圖與圖元之間的映射,應該先從視圖映射到場景,然後再從場景圖映射到圖元

六、GraphicsView框架特性

1、縮放與旋轉

    QGraphicsView通過QGraphicsView::setMatrix()支持同QPainter一樣的座標變換,通過對一個視圖應用變換,可以很容易地支持普通的導航特性如縮放與旋轉

2、打印

    圖形視圖架構通過渲染函數QGraphicsScene::render()QGraphicsView::render()支持單行打印

場景和視圖的渲染函數的不同在於QGraphicsScene::render()使用場景座標,QGraphicsView::render()使用視圖座標。QGraphicsScene::render()經常用於打印未變換場景中的整塊,例如一塊圖形數據或是打印一個文本文檔。    QGraphicsView::render()適合用於截屏,默認會使用繪圖設備精確渲染視口的內容。

QGraphicsScene scene;

scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

QPixmap pixmap;

QPainter painter(&pixmap);

painter.setRenderHint(QPainter::Antialiasing);

scene.render(&painter);

painter.end();

pixmap.save("scene.png");

    當源和目標區尺寸不匹配時,源的內容會比例縮放適合目標區。

3、拖拽

    由於QGraphicsView繼承QWidget,GraphicsView同樣提供了拖拽功能。此外,爲了方便,GraphicsView框架也爲場景、圖元提供拖拽支持。當視圖接收到拖拽事件,GraphicsView框架會將拖拽事件翻譯QGraphicsSceneDragDropEvent事件,再發送到場景場景接管事件,事件發送到光標下接受拖拽的第一個圖元
    爲了開啓圖元拖拽,創建一個QDrag對象,傳遞啓動拖拽的QWidget的指針。圖元可以同時被多個視圖觀察,但只有一個視圖可以拖拽圖元通常,拖拽是從按下鼠標或是移動鼠標開始的,在mousePressEvent()或mouseMoveEvent()中,可以從事件中得到原始的QWidget指針

void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)

  {

      QMimeData *data = new QMimeData;

      data->setColor(Qt::green);

      QDrag *drag = new QDrag(event->widget());

      drag->setMimeData(data);

      drag->start();

  }

    要在場景中拖拽事件,需要重新實現QGraphicsScene::dragEnterEvent()QGraphicsItem子類裏特定場景需要的事件處理器。

    圖元也可以通過調用QGraphicsItem::setAcceptDrops()獲得拖拽支持,爲了處理將要進行的拖拽,需要重新實現QGraphicsItemdragEnterEvent()dragMoveEvent()dropEvent()dragLeaveEvent() 。

4、光標與工具提示

    QGraphicsItem支持光標(QgraphicsItem::setCursor)與工具提示(QGraphicsItem::setToolTip())。當光標進入到圖元的區域,光標與工具提示被QGraphicsView激活(通過調用QGraphicsItem::contains()檢測)也可以直接在視圖上設置一個缺省光標(QGraphicsView::setCursor)。

5、動畫

    GraphicsView框架支持多種層次的動畫。使用動畫框架可以很容易製作出動畫。

GraphicsView框架支持動畫實現種類如下:

    A、圖元需要繼承自QGraphicsObject,並且需要聯結QPropertyAnimation屬性。

    B、創建繼承自QObject和QGraphicsItem的圖元圖元可以設置自己的定時器,在QObject::timeEvent()中增加步進的方式來控制動畫。
    C、通過調用QGraphicsScene::advance()來推進場景,依次調用QGraphicsItem::advance()

6OpenGL渲染

    爲了使用OpenGL渲染,要設置一個新的QGLWidget作爲QGraphicsView的視口:QGraphicsView::setViewPort()。如果需要OpenGL提供反鋸齒功能,則需要OpenGL採樣緩衝支持。
QGraphicsView view(&scene);
view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));

7、圖元組

    通過把一個圖元做爲另一個圖元的孩子,可以得到圖元組的大多數本質特性:所有圖元會一起移動,所有變換會從父到子傳遞。

    另外,QGraphicsItemGroup是一個特殊的圖元。爲了增加和刪除圖元,使用一個有用接口合併了子圖元的事件處理。把一個圖元加到QGraphicsItemGroup仍會保留圖元的原始位置與變換,而給一個圖元重新指定父圖元則會讓圖元根據其新的父親重新定位。可以用QGraphicsScene::createItemGroup()創建圖元組

8、圖形組件和佈局

    QT4.4通過QGraphicsWidget支持圖形和圖元佈局。QGraphicsWidget類似於QWidget,但QGraphicsWidget並不從QPaintDevice繼承,而是繼承自QGraphicsItemQGraphicsWidget支持事件、信號與槽、大小和策略。通過QGraphicsLinearLayoutQGraphicsGridLayout可以對圖形組件進行佈局管理。

    QGraphicsWidget繼承了QWidget和QGraphicsItem的優點,如QWidget的樣式、字體、調色板、佈局方向以及QGraphicsItem圖形、獨立精度和變換支持。

    QGraphicsLayout是專爲QGraphicsWidget特殊設計的第二代佈局框架。QGraphicsLayoutAPI類似於QLayout。通過QGraphicsLinearLayoutQGraphicsGridLayout可以管理組件與子佈局。

9、嵌入組件

圖形視圖框架爲嵌入任何組件到場景提供了無縫支持。可以嵌入簡單的組件,如QLineEdit、QPushButton,或是複雜的組件如QTableWidget,甚至是主窗口。

要嵌入組件到場景,只需要調用QGraphicsScene::addWidget(),或是創建一個QGraphicsProxyWidget實例,手動嵌入組件。

    通過QGraphicsProxyWidget,圖形視圖框架可以深度整合客戶組件特性,如光標、工具提示、鼠標、平板和鍵盤事件、子組件、動畫、彈拉框、組件輸入焦點和激活。QGraphicsProxyWidget甚至整合了嵌入組件的tab順序,可以通過tab選擇嵌入的組件。甚至可以嵌入一個QGraphicsView到場景。

    當變換和嵌入組件時,圖形視圖框架會確保組件會被獨立變換。

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