Qt Graphics View 框架

doctorsc發表於2011-09-14

這幾天做迷彩設計相關程式設計用了很多QPainter相關的操作,寫了不少Qt程式碼讓我對Qt又有了進一步的認識。最近翻看Qt 的 Demos and Examples 發現在Graphics view裡面有一個elastic node的示例,程式僅僅定義了背景和小球繪製操作便完成了複雜的滑鼠,鍵盤等互動。在讓我大為驚歎Qt 強大的同時,也決定學一學。查詢了Qt 幫助文件,這一系列操作都用到了Qt Graphics View Framework

 

Graphics View 框架提供了一個介面,用於管理和互動大量的使用者自定義的2D圖形物件,並且有一個view widget(QGraphicsView)使用者視覺化這些物件,支援zoom和rotate。這個框架包含了一個事件傳播構架,允許了double精度的場景中物體互動能力。物件的鍵盤,滑鼠的按下,移動,鬆開,雙擊事件都已經定義好了,並且可以跟蹤滑鼠移動。

 

Graphics view 構架

所有的item都被繪製到了一個場景中用於顯示,這個場景就是QGraphicsScene,這個場景有如下功能:

1、  為管理大量item提供了一個快速互動介面。

2、  能夠將鍵盤,滑鼠等事件傳遞到每個item。

3、  可以管理item的狀態,例如選擇,焦點等。

4、  提供了不變形的繪製,主要用於列印繪畫結果。

 

場景就像一個容易囊括了所有item,可以通過addItem()新增物體,可以通過item()查詢物體,itemAt()返回最上面的item,所有item按照降序堆疊排列。第一個加入的在棧頂,最後一個加入的在棧底。

 

QGraphicsScene的事件傳遞架構非常給力,能夠將場景獲得的事件精確傳遞到相應item,例如滑鼠在某點點選了一下,Scene能夠將這個事件傳遞給在這個點上的item(最上面的那個item),於是這個item被選中或者執行別的動作,具體取決於item的mousePressEvent函式的處理。

 

QGraphicsScene中,你可以用setSelectionArea()選擇區域中的許多個item,僅僅需要呼叫這個函式你就能獲得選中item的結果,真是太強大了,如果要自己寫函式滑鼠選擇的區域選了哪些item,你需要遍歷每個item,對於每個item又要判斷是否全部在區域內,於是item要包圍核,要範圍面積等等變數來表示。想想就覺得真心麻煩。這個函式確實為開發者節約了不少時間。

 

View

QGraphicsView提供了用於顯示的widget,用於顯示scene,你可以將多個view聯絡到同一個scene,給相同的資料提供多個視口,視口支援openGL,甚至可以將QGLWidget作為視口,只要呼叫一下QGraphicsView::setViewport()。View接收互動事件,進行座標轉換後變成scene event交給場景。QGraphicsView::mapToScene(), QGraphicsView::mapFromScene()等多個函式可以在view和scene之間轉換座標系。

QGraphicsScene scene;
myPopulateScene(&scene);
QGraphicsView view(&scene);
view.show();

Item

QGraphicsItem是場景中所有物件的基類,GraphicsView也提供了一些標準item,例如矩形QGraphicsRectItem,橢圓QGraphicsEllipseItem,還有文字,QGraphicsTextItem,最給力的當然是你自定義的item,item支援以下操作:

1、  滑鼠點選,移動,鬆開,雙擊,飛越,滾輪,選單事件。

2、  鍵盤輸入。

3、  拖拽

4、  Group,可以通過父子關係或者QGraphicsItemGroup

5、  碰撞檢測

 

每個Item都有一個區域性座標系,可以通過這個座標系進行選擇,移動等操作,transform()函式通過控制矩陣可以輕鬆實現這些操作。

 

有一個很NB的功能就是item支援碰撞檢測,怎麼檢測呢,首先需要為每一類item定義好它的bounding box,同過boundingRect()函式可以設定這個item外圍矩形包圍盒,而更精確的是用shape()函式定義item外邊框路徑,這個路徑是QPainterPath,這個路徑可以是直接,貝塞爾曲線等任何線條,collidesWith()是一個虛擬函式,定義完成後便能完成碰撞檢測功能了。

 

其他的Graphics類

QGraphicsAnchorLayout是用於將幾個widget以錨點的形式隨意排列的layout。

QGraphicsEffect提供了一些圖形特效。

主要有以下4個標準特效:

Qt provides the following standardeffects:

·        QGraphicsBlurEffect - blurs the item by a given radius

·        QGraphicsDropShadowEffect - renders a dropshadow behind the item

·        QGraphicsColorizeEffect - renders the item in shades of any given color

·        QGraphicsOpacityEffect - renders the item with an opacity

 

Graphics view 座標系

座標系由(x,y)表示,一個單位就是螢幕上一個畫素。在這個框架內一共有3個座標系,item區域性座標系,場景座標系,view座標系,有函式用於在這些座標系之間進行對映。

Item coordinates

Item的座標建立在它的中心點(0,0)附近,這也是所有變形的中心位置,幾何圖元常常由區域性座標系中的點,線,矩形來構建。

構建自定義的item時,僅僅需要考慮區域性座標就行了,QGraphicsView和QGraphicsScene會進行其他所有的變形。滑鼠事件用於item時,會自動將點轉換到item座標系中。Item的bounding rect 和 shape都在區域性座標系中定義。

 

子item的中心位置將會儲存到父item的座標系中用於自動查詢判斷,最上層item的中心座標將會儲存到scene座標系中。

 

父item的旋轉縮放操作將會影響子item跟著一起變,但不會影響子item與父Item的座標系,例如,有父子2個item,在父座標系中,子的位置在(10,0)。於是,子座標系中位置為(0,10)的點在父座標系下的座標是(10,10)。然後,我對父item進行了旋轉和縮放,子座標系下(0,10)的點在父座標系下仍然是(10,10),但是在scene座標系下就會產生變化了,子item會隨著父產生形變,如果父進行了縮放為(2x,2x),在scene座標系下,子的位置為(20,0),子座標系中(10,0)的點在scene下的座標為(40,0)。QGraphicsItem::pos()是少數幾個例外的,它返回item在父座標系下的座標.

 

旋轉和縮放操作相當簡單,在QGraphicsView中呼叫ratate()和scale()函式。

 ————————————————————————————————————————————————————————

光說不練嘴把式,必須隨便搞個test project。

GraphicWidget繼承自QGraphicsView,過載了一個函式,drawBackground().

EllipseItem繼承自QGraphicsItem,過載了若干函式:

QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem*option, QWidget *widget);
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

boundingRect必須得過載,不然編譯器說這個是抽象類,不能夠例項化。

在mousePressEvent()中呼叫QGraphicsItem::mousePressEvent(event);便萬事大吉,方便shi了。

其他雷同。

在EllipseItem中儲存了父節點指標。

在graphicWidget建構函式中new一個scene,設定一下,然後new一個EllipseItem出來,放入scene,指定位置,就可以了。

出來的結果是,你可以任意移動出現的小圓球。

 

待續...

 

相關文章