天天看點

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

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

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

一、GraphicsView架構簡介

QT4.2開始引入了Graphics View架構用來取代QT3中的Canvas子產品,并作出了改進,Graphics View架構實作了模型-視圖結構的圖形管理,能對大量圖元進行管理,支援碰撞檢測,坐标變換和圖元組等多種友善的功能。

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

    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。類似,圖元的邊界矩形和形狀也是基于圖元坐标系。

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

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

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

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

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

2、場景坐标 

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

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

3、視圖坐标 

    視圖坐标是視窗部件的坐标,視圖坐标的機關是像素,QGraphicsView的左上角是(0,0)。所有滑鼠事件、拖拽事件最開始都使用視圖坐标,為了和圖元互動,需要轉換坐标為場景坐标。

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

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()獲得拖拽支援,為了處理将要進行的拖拽,需要重新實作QGraphicsItem的dragEnterEvent()、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()。

6、OpenGL渲染

    為了使用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繼承,而是繼承自QGraphicsItem。QGraphicsWidget支援事件、信号與槽、大小和政策。通過QGraphicsLinearLayout、QGraphicsGridLayout可以對圖形元件進行布局管理。

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

    QGraphicsLayout是專為QGraphicsWidget特殊設計的第二代布局架構。QGraphicsLayout的API類似于QLayout。通過QGraphicsLinearLayout和QGraphicsGridLayout可以管理元件與子布局。

9、嵌入元件

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

繼續閱讀