Qt繪圖之QGraphicsScene QGraphicsView QGraphicsItem詳解
Graphics View提供了一個界面,它既可以管理大數量的定制2D graphical items,又可與它們交互,有一個view widget可以把這些項繪制出來,並支持旋轉與縮放。這個櫃架也包含一個事件傳播結構,對於在scene中的這些items,它具有雙精度的交互能力。Items能處理鍵盤事件,鼠標的按,移動、釋放、雙擊事件,也可以跟蹤鼠標移動。Graphics View使用BSP樹來提供對item的快速查找,使用這種技術,它可以實時地繪制大規模場景,甚至以百萬items計。Graphics View在Qt 4.2中被引用,它替代了它的前輩QCanvas。
Graphics View的體系結構
Graphics View提供的是一種類似於Qt model-view的編程。多個views可以監視同一個場景,而場景包含多個具有多種幾何外形的items。
場景
QGraphicsScene 表示Graphics View中的場景,它有以下職責: 為管理大量的items提供一個快速的接口。 傳播事件到每個item。 管理item的狀態,例如選擇,焦點處理。 提供未經變換的渲染功能,主要用於打印。 場景作為QGraphicsItem對象的容器。通過調用QgraphicsScene::addItem()把這些Items加入到場景中。可以使用眾多的查找函數來獲取特定的items。QGraphicsScene:items()與它的許多重載函數可獲取那些與點、矩形,多邊形,向量路徑等相交或是有包含有關系的items。QGraphicsScene::itemAt()返回特定上最頂端的item。所有的item查找函數都以出棧序列返回(也就是說,第一個返回的是最頂端的,最后一個返回的是最底端的)。 QGraphicsScene scene; QGraphicsRectItem *rect=scene.addRect(QRectF(0,0,100,100)); QGraphicsItem *item=scene.itemAt(50,50); //item==rect; QGraphicsScene的事件傳播結構會把場景事件投遞到items,也管理多個items之間的傳遞。假如場景收到了鼠標在某個位置press事件,場景會把這個事件投遞給處在那個位置的item。QGraphicsScene也管理某種item狀態,像選擇與焦點。你可以通過調用QGraphicsScene::setSelectionArea()來選擇items,它需要提供一個任意的形狀為參數。這個函數也作為在QGraphicsView實現橡皮筋選擇功能的一個基礎。為得到這些已經被選擇的items,調用QGraphicsScene::selectedItem()。另一個狀態處理是是否一個item擁有鍵盤輸入焦點。你可以調用QGraphicsScene::setFocusItem()或QGraphics::setFocus()來設定焦點,也可用QGraphicsScene::focusItem()來得到當前擁有焦點的那個item。最后,QGraphicsScene允許你通過調用QGraphicsScene::render()函數把部分場景送到繪圖設備進行渲染。
視圖
QGraphicsView提供了視圖部件,它可視化場景中的內容。你可以聯結多個視圖到同一個場景,對這個相同的數據集提供幾個視口。視口部件是一個滾動區域,它提供了滾動條以對大場景進行瀏覽。為了使用OpenGL,你應該調用QGraphicsView::setViewport()來把一個QGLWidget設為視口。視圖從鍵盤,鼠標接收輸入事件,在發送這些事件到場景之前,會對這些事件進行適當的翻譯(把事件坐標轉換成對應的場景坐標)。
利用轉換矩陣,QGraphicsView::matrix(),視圖可變換場景的坐標系統。這允許高級的導航特性,如縮放,旋轉。為了方便,QGraphicsView也提供了在視圖與場景之間進行坐標轉換的函數:QGraphicsView::mapToScene(),QGraphicsView::mapForScene()。 
The Item QGraphicsItem 是場景中圖形items的基類。Graphics View 提供了一些標准的、用於典型形狀的items。像矩形(QGraphicsRectItem),橢圓(QGraphicsEllipseItem),文本(QGraphicsTextItem),當你寫定制的item時,那些最有用的一些QGraphicsItem特性也是有效的。除此這外,QGraphicsItem支持以下特性: *鼠標按、移動、釋放、雙擊事件,鼠標懸停事件,滾輪事件,彈出菜單事件。 *鍵盤輸入焦點,鍵盤事件。 *拖拽 *組,包括父子關系,使用QGraphicsItemGroup *碰撞檢測 Items如同QGraphicsView一樣,位於本地坐標系,它也為item與場景之間,item與item之間的坐標轉換提供許多工具函數。而且,也像QGraphicsView一樣,它使用矩陣來變換它的坐標系統:QGraphicsItem::matrix()。它對旋轉與縮放單個的Item比較有用。 Items可以包含別的items(孩子)。父items的轉換被它的子孫所繼承。然而,它的所有函數(也就是,QGraphicsItem::contains(),QGraphicsItem::boundingRect(),QGraphicsItem::collidesWith()),不會積累這些轉換,依然在本地坐標下工作。 QGraphicsItem通過QGraphicsItem::shape(),QGraphicsItem::collideWith())來支持碰撞檢測。這兩個都是虛函數。從shape()返回你的item的形狀(以本地坐標QPainterPath表示),QGraphicsItem會為你處理所有的碰撞檢測。假如你想提供自己的碰撞檢測,你應該重新實現QGraphicsItem::collideWith()。
Graphics View 坐標系統
圖形視圖框架是基於笛卡爾坐標系,視圖的中心相交處為場景的原點(0,0)。場景的大小隨着最大圖元項大小改變,而場景中心坐標顯示在視圖的中心位置。
setSceneRect函數作用相當於在場景的原點添加了一個虛擬的矩形。該矩形的中心,定義了場景中心坐標。視圖的旋轉只會改變場景,自身不會發生改變。
圖元繪制的時候參考的是QGraphicsScene的坐標系,圖元創建后產生自己的坐標系,移動圖元參考的是自身的坐標原點,圖元位置始終坐標不變。
圖元雖然沒有繼承QObject,但底層實現了內存管理機制會自動釋放內存。子圖元的坐標參考的是父圖元自身的坐標系。
坐標映射
經常,處理場景中item時,在場景與item之間,item與item之間,視圖與場景之間進行坐標映射,形狀映射是非常有用的。舉例來講,當你在QGraphicsView的視口中點擊鼠標時,你應該通過調用QGraphicsView::mapToScence()與QGraphicsScene::itemAt()來獲知光標下是場景中的哪個item。假如你想獲知一個item位於視口中的什么位置,你應該先在item上調用QGraphicsItem::mapToScene(),然后調用QGraphicsView::mapFromScene()。最后,假如你想在一個視圖橢圓中有哪些items,你應該把QPainterPath傳遞到mapToScene(),然后再把映射后的路徑傳遞到QGraphicsScene::items()。 你可以調用QGraphicsItem::mapToScene()與QGraphicsItem::mapFromScene()在item與場景之間進行坐標與形狀的映射。也可以在item與其父item之間通過QGraphicsItem::mapToParent()與QGraphicsItem::mapFromItem()進行映射。所有映射函數可以包括點,矩形,多邊形,路徑。視圖與場景之間的映射也與此類似。對於從視圖與item之間的映射,你應該首先映射到場景,然后再從場景向item進行映射。
關鍵特性
縮放與旋轉
QGraphicsView通過QGraphicsView::setMatrix()支持同QPainter一樣的仿射變換,通過對一個視圖應用變換,你可以很容易地支持普通的導航特性如縮放與旋轉。下面是一個例子: class View:;public QGraphicsView { Q_OBJECT //….. public slots: void zoomIn() {scale(1.2,1.2);} void zoomOut() {scale(1/1.2,1/1.2);} void rotateLeft() {rotate(-10);} void rotateRight() {rotate(10);} }; 這些槽應與QToolButtons聯接,並使autoRepeat有效。當對視圖變換時,QGraphicsView會對視圖中心進行校正。
拖拽
因為QGraphicsView繼承自QWidget,它也提供了像QWidget那樣的拖拽功能,另處,為了方便,Graphics View櫃架也為場景,每個item提供拖拽支持。當視圖接收到拖拽事件,它可翻譯為QGraphicsSceneDragDropEvent,再發送到場景。場景接管這個事件,把它發送到光標下接受拖拽的第一個item。 從一個item開始拖拽時,創建一個QDrag對象,傳遞開始拖拽的那個widget的指針。Items可以同時被多個視圖觀察,但只有一個視圖可以開始拖拽。拖拽在多數情況下是從按下鼠標或是移動鼠標開始的,因此,在 mousePressEvent()或mouseMoveEvent()中,你可以從事件中得到那個原始的widget指針,例如: 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的子類里任何與你特定場景需要的事件處理器。items也可以通過調用QGraphicsItem::setAcceptDrops()獲得拖拽支持,為了處理將要進行的拖拽,你需要重新實現QGraphicsItem::dragEnterEvent(),QGraphicsItem::dragMoveEvent(),QGraphicsItem::dragLeaveEvent()和QGraphicsItem::dropEvent()。
光標與工具提示
像QWidget一樣,QGraphicsItem也支持光標(QgraphicsItem::setCursor)與工具提示(QGraphicsItem::setToolTip())。當光標進入到item的區域,光標與工具提示被QGraphicsView激活(通過調用QGraphicsItem::contains()檢測)。你也可以直接在視圖上設置一個缺省光標(QGraphicsView::setCursor)。
動畫
Graphics View支持幾種級別的動畫。你可以很容易地通過把QGraphicsItemAnimatoin與你的item聯結來 裝配出動畫路徑,這允許以時間線來控制動畫,在所有平台上以穩定的速率運作。QGraphicsItemAnimation允許你為item的位置,旋轉,縮放,剪切,變換等產生一條路徑,動畫可以用QSlider來控制,或更為普遍使用的QTimeLine。 另一種是從QObject和QGraphicsItem繼承,item可以設置自己的定時器,以在QObject::timeEvent()中增加步進的方式來控制動畫。 第三種,是通過調用QGraphicsScene::advance()來推進場景,它又依次調用QGraphicsItem::advance().
OpenGL渲染
為了使用OpenGL渲染,你要設置一個新的QGLWidget作為QGraphicsView的視口:QGraphicsView::setViewPort()。假如你讓OpenGL提供反鋸齒功能,你需要OpenGL采樣緩沖支持。 QGraphicsView view(&scene); view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
Item組
通過把一個item做為另一個item的孩子,你可以得到item組的大多數本質特性:這些items會一起移動,所有變換 會從父到子傳遞。QGraphicsItem也可以為它的孩子處理所有的事件,這樣就允許以父親代表它所有的孩子,可以有效地把所有的items看作一個整體。 另外,QGraphicsItemGroup是一個特殊的item,它既對孩子事件進行處理又有一個接口把items從一個組中增加和刪除。把一個item加到 QGraphicsItemGroup仍會保留item的原始位置與變換,而給一個item重新指定父item則會讓item根據其新的父親重新定位。可以用QGraphicsScene::createItemGroup()建組。
