Qt GraphicsView框架


一、GraphicsView框架簡介

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

 

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

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

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

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

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

2、場景坐標

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

 

3、視圖坐標

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

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()適合用於截屏,默認會使用繪圖設備精確渲染視口的內容。

 1 QGraphicsScene scene;  2 
 3 scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));  4 
 5 QPixmap pixmap;  6 
 7 QPainter painter(&pixmap);  8 
 9 painter.setRenderHint(QPainter::Antialiasing); 10 
11 scene.render(&painter); 12 
13 painter.end(); 14 
15 pixmap.save("scene.png");

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

3、拖拽

  由於QGraphicsView繼承自QWidget,GraphicsView同樣提供了拖拽功能。此外,為了方便,GraphicsView框架也為場景、圖元提供拖拽支持。當視圖接收到拖拽事件,GraphicsView框架會將拖拽事件翻譯為QGraphicsSceneDragDropEvent事件,再發送到場景,場景接管事件,再把事件發送到光標下接受拖拽的第一個圖元。

  為了開啟圖元拖拽,創建一個QDrag對象,傳遞啟動拖拽的QWidget的指針。圖元可以同時被多個視圖觀察,但只有一個視圖可以拖拽圖元。通常,拖拽是從按下鼠標或是移動鼠標開始的,在mousePressEvent()或mouseMoveEvent()中,可以從事件中得到原始的QWidget指針。

1 void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event) 2 { 3     QMimeData *data = new QMimeData; 4     data->setColor(Qt::green); 5     QDrag *drag = new QDrag(event->widget()); 6     drag->setMimeData(data); 7     drag->start(); 8 }

  要在場景中取拖拽事件,需要重新實現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采樣緩沖支持。

1 QGraphicsView view(&scene); 2 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,甚至是主窗口。

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

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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM