圖形視圖框架(The QGraphics View Framework)(轉)

圖形視圖提供了一個外表(surface來實現大量的客戶所做的2D圖形項的管理和相互的結合;一個視圖窗口部件來使這些項可視化,並支持縮放和旋轉。

該框架包括一個事件傳播體系,可以使得場景中的項的交叉可以達到雙精度的精確控制。其中的項可以處理事件、鼠標按壓、移動、釋放和雙擊事件,它們也可以追蹤鼠標的移動。

圖形視圖使用一個BSP(二進制空間分區Binary Space Partitioning)樹來提供快速的項發現,正因爲如此,它可以使巨大的場景實時地可視化,即便它有上百萬個項(item)。

圖形視圖是在Qt4.2中引入的,取代了以前的QCanvas,如果需要從QCanvas移植,參見“Porting to Graphics View

主題:

l  圖形視圖的體系架構

?  場景(Scene

?  視圖(View

?  項(Item

l  圖形視圖的座標系統

?  項座標

?  場景座標

?  視圖座標

?  座標映射

l  主要的特性

?  縮放和旋轉

?  打印

?  拖放

?  光標和提示

?  動畫

?  OpenGL展示

?  項的成組

?  窗口部件和佈局

?  嵌入式窗口部件支持

 

圖形視圖體系架構

圖形視圖提供了基於項的模型視圖編程方式,很象交互視圖(InterView)的方便的類如QTableWidgetQTreeWidgetQListWidget。多個視圖可以用來觀察一個場景,場景包含了變化的幾何形狀的項。

 

場景(Scene

QGraphicsScene提供了圖形視圖的場景,場景承擔下列的責任:

?  提供一個快速的接口用來管理大量的項。

?  向每個項傳遞事件。

?  管理項的狀態,如選中、焦點處理。

?  提供無變形的展示功能,主要爲了打印。

 

場景作爲QGraphicsItem對象的容器,項可以調用QGraphicsScene::addItem()加入場景。QGraphicsScene::items()和它的重載可以返回所有的項,包括點、長方形、多邊形、通用矢量路徑。QGraphicsScene::itemAt()返回在特定點上最上邊的項。所有項發現函數可以依次返回在堆棧中的項(最先返回的是最上邊的,最後返回的是最下邊的)。

QGraphicsScene scene;

QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100));

 

QGraphicsItem *item = scene.itemAt(50, 50);

// item == rect

QGraphicsScene的事件傳遞架構給場景事件確定時間表,用於傳給項,和管理項之間的傳遞。如果場景在某個位置收到一個鼠標按下事件,場景將把該事件傳遞給在那個位置的項。

QGraphicsScene也管理某些項狀態,如項選擇和聚焦。你可以在場景上通過QGraphics::setSelectionArea()選擇項,傳遞任意的形狀。這個功能也被在QGraphicsView中用做橡皮擦選項。另外一個由QGraphicsScene處理的狀態是一個項是否有鍵盤輸入聚焦。你可以通過調用QGraphics::setFocusItem()QGraphicsItem::setFocus()來聚焦一個項,或者通過調用QGraphicsScene::focusItem()來獲得當前的聚焦。

最後,QGraphicsScene允許通過QGraphicsScene::render()函數將場景的某一部分展示到一個繪畫設備中。你可以在以後的打印段落中看到更多的相關內容。

 

視圖(The View

QGraphicsView提供了視圖窗口部件,它使場景的內容可視化。你可以給一個場景多個視圖,從而針對同樣的數據集提供幾個視圖端口。視圖窗口部件是滾動區域,對大型的場景圖提供滾動的瀏覽方式。爲了支持OpenGL,你可以通過調用QGraphicsView::setViewport()l來設置一個QGLWidget作爲視圖端口。

QGraphicsScene scene;

 myPopulateScene(&scene);

 

 QGraphicsView view(&scene);

view.show();

視圖接收來自鍵盤和鼠標的輸入事件,並在發送事件給可視化的場景之前,將它們轉化爲場景事件(將座標轉化爲適當的場景座標)。

使用變換矩陣,QGraphicsView::matrix(),視圖可以變換場景的座標系統,以便處理高級的瀏覽特性,如縮放和旋轉。爲了方便,QGraphicsView也提供視圖和場景座標之間轉換的函數:QGraphicsView::mapToScene()QGraphicsView::mapFromScene()

 圖形視圖框架(The QGraphics View Framework)(轉) - yleesun - 與青春有關的日子...

項(The Item

QGraphicsItem是場景中圖形項的基類。圖形視圖提供了幾個典型形狀的標準項,如長方形QGraphicsRectItem,橢圓形QGraphicsEllipseItem和文本項QGraphicsTextItem,但是,當你撰寫客戶化項時,QGraphicsItem的強大特性就體現出來了,除此之外,QGraphicsItem還支持下面的特性:

?  鼠標按壓,移動,釋放,雙擊事件,以及鼠標鉤子事件(hover events),滾輪事件(wheel events),上下文事件(context menu events

?  鍵盤輸入聚焦,特定鍵事件

?  拖放

?  成組(Grouping),通過父子關係或者用QGraphicsItemGroup

?  碰撞偵測

項象QGraphicsView一樣,存在於局部座標系統(local coordinate system)中,它提供了很多函數用於在項和場景之間、項與項之間進行座標映射。另外,和QGraphicsView一樣,它通過QGraphicsItem::matrix()函數變換它的座標系統。這在旋轉和縮放單個項時非常有用。

項可以包含另外的項(子項),父項的變換被它的所有子項繼承。不管項的累積變換有多少,它的所有函數(如QGraphicsItem::contains()QGraphicsItem::boundingRect()QGraphicsItem::collidesWith())還是在局部座標系統中操作。

QGraphicsItem通過QGRaphicsItem::shape()函數和QGraphicsItem::collidesWith()函數支持碰撞偵測,這兩個都是虛函數。通過返回項形狀的局部座標,QGRaphicsItem::shape()函數和QGraphicsItem::collidesWith()函數的QPainterPath將處理所有的碰撞偵測。如果你想提供自己的碰撞偵測,則可以重新實現QGraphicsItem::collidesWith()函數。

 圖形視圖框架(The QGraphics View Framework)(轉) - yleesun - 與青春有關的日子...

圖形視圖座標系統(The Graphics View Coordinate System

圖形視圖基於笛卡兒座標系統(Cartesian coordinate system),項的位置和在場景中的幾何形狀由兩個數字組合代表:X座標和Y座標。當使用變換的視圖觀察一個場景時,場景中的一個單元會由屏幕上的一個點表示。

3個有效的座標系統來演繹圖形視圖:項座標、場景座標和視圖座標。爲了簡化你的實現,圖形視圖提供了方便的函數允許你在三個座標系統之間映射。

當圖形展示時,圖形視圖的場景座標對應了QPainter的邏輯座標,視圖座標對應了設備的座標。在座標系統(The Coordinate System)文章中,介紹了邏輯座標(logical coordinate)和設備座標(device coordinate)的關係。

 圖形視圖框架(The QGraphics View Framework)(轉) - yleesun - 與青春有關的日子...

項座標(Item Coordinates

項總是在它們自己的局部座標中。它們的座標一般是圍繞它們的中心點(00),並且這個中心也是左右變換的中心。項座標系統中的簡單幾何件一般是指項點、項線、項長方形。

當創建客戶化項時,項座標是你要考慮的全部。QGraphicsSceneQGraphicsView會爲你實現所有的變換,這讓實現客戶化項變得相當容易。例如,如果你收到了鼠標按下或拖動的事件,該事件的位置點是由項座標系統給出的,QGraphicsItem::contains()得到一個項座標的點參數,若這個點在項中,則返回真,否則,返回假。同樣的,項綁定的矩形或形狀區域也是項座標系統的。

項的位置是指在它的父座標系統中,項的中心點的座標,有時候稱父座標。場景從這個意義上說是所有無父項的“parent”,頂層項的位置是在場景座標中。

子座標是相對於父座標而言的,如果子座標沒有變換,那麼子座標和父座標的差異是和項在父座標中的位置一樣的。如:如果一個沒有變換的子項正好位於其父項的中心點位置,那麼,這兩個項的座標系統是完全一樣的。如果子項的位置是(100),那麼,子項的(010)點對應了父項的(1010)點。

因爲項的位置和變換是相對於父項的,因此,雖然父項的座標變換隱含了子項的變換,但是,子項的座標不受父項座標變換的影響。在上述例子中,即使父項旋轉或縮放了,子項的(010)點始終對應父項的(1010)點。相對於場景,子項將跟隨場景的變換和定位。如果父項放大2倍,(2X2X),子項在場景座標中的位置將是(200),它的(100)點將對應場景中的(400)點。

除了QGraphicsItem::pos()作爲很少的例外,QGraphicsItem的函數操作都是在項座標是操作,而不論項或者它的父項是否已經做了座標變換。如:一個項的綁定矩形(即QGraphicsItem::boundingRect())總是給出項座標。

 

場景座標(Scene Coordinates

場景爲所有的項提供了基本的座標系統。場景座標系統描述了頂層項的位置,並給通過視圖傳遞給場景的所有事件提供了基本的座標體系。場景中的每一個項,除了它自己的局部項位置和綁定矩形外,都有一個場景位置和綁定矩形(QGraphicsItem::scenePos()QGraphicsItem::sceneBoundingRect())。場景位置描述了項在場景座標中的位置,場景綁定矩形使得QGraphicsScene確定場景中需要變化的區域。場景的變化通過QGraphicsScene::changed()信號來通訊,參數就是場景的綁定矩形。

 

 

視圖座標(View Coordinates

視圖座標是窗口部件的座標,視圖座標中的每個座標對應了一個象素。有關這個座標系統特殊的是它是相對於窗口部件的,或視圖端口(viewport),而不被所觀察的場景所影響。QGraphicsView視圖端口的左頂角總是(00),右底角總是(視寬,視高)。所有鼠標事件和拖放事件最初始總是以視圖座標收到的,你需要將它影射成場景座標以方便和項交互。

 

座標映射(Coordinate Mapping

在處理場景中的項時,經常使用從場景到項,從項到項,從視圖到場景的座標和任意形狀的映射。例如:當你在視圖端口按壓鼠標時,可以通過調用QGraphicsView::mapToScene()函數,再跟上QGraphicsScene::itemAt()函數告訴場景那個項在當前光標下。也可以通過在項上調用QGraphicsItem::mapToScene()函數,然後是QGraphicsView::mapToScene()函數,獲知該項在視圖端口的什麼位置。最後,若要知道項是否在視圖橢圓中,你可以向mapToScene()傳遞QPainterPath,向QGraphicsScene::items()函數傳遞mapped path來實現。

通過調用QGraphicsItem::mapToScene()QGraphicsItem::mapFromScene()函數,可以映射任意的座標和形狀。也可以調用QGraphicsItem::mapToParent()QGraphicsItem::mapFromParent ()函數映射到父項。所有的映射函數可以映射點、長方形、多邊形和路徑。

用樣的,在視圖中有同樣的映射函數,將座標和形狀映射到場景。QGraphicsView::mapFromScene()QGraphicsView::mapToScene()。要從視圖映射到項,你需要先映射到場景,再從場景映射到項。

 

主要特性(Key Featue

縮放和旋轉(Zooming and rotating

QGraphicsView通過QGraphicsView::setMatrix(),象QPainter一樣,支持仿射變換。通過給視圖增加變換應用,可以很容易地給普通的瀏覽增加如縮放和旋轉的特性。

下面是一個例子,說明在QGraphicsView子類中如何實現縮放和旋轉的槽。

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保持視圖的中心成直線。

可以參見“Elastic Node”例子,學習如何實現基本的縮放特性。

 

 

 

打印(Printing

圖形視圖通過它的展示函數:QGraphicsScene::render()QGraphicsView::render()提供單線(single-line)打印。

函數提供相同的API,通過將QPainter傳遞給展示函數,你可以打印場景、視圖的全部或部分內容。

例子顯示瞭如何使用QPainter將場景的全部內容打印到整頁紙上。

QGraphicsScene scene;

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

 

 QPrinter printer;

 if (QPrintDialog(&printer).exec() == QDialog::Accepted) {

     QPainter painter(&printer);

     painter.setRenderHint(QPainter::Antialiasing);

     scene.render(&painter);

 }

 

場景和視圖函數展示函數的差異是一個在場景座標,另一個在視圖座標。QGraphicsScene::render()常用於打印無變換的場景的全部內容,如畫幾何數據文檔等。QGraphicsView::render()適合於打印屏幕快照(screenshots),缺省情況下,它展示視圖端口中的當前內容。

QGraphicsScene scene;

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

 

 QPixmap pixmap;

 QPainter painter(&pixmap);

 painter.setRenderHint(QPainter::Antialiasing);

 scene.render(&painter);

 painter.end();

 

 pixmap.save("scene.png");

 

當源區域和目標區域的大小不匹配時,源內容進行伸展以適合目標區域。通過傳遞Qt::AspectRatioMode給你正調用的展示函數,你可以在源內容伸縮時,保持或忽略縱橫比。

 

拖放(Drag and Drop

因爲QGraphicsView間接繼承了QWidget,它也同樣提供QWidget提供的拖放功能。另外,爲了方便,圖形視圖架構給場景、每一個項提供了拖放支持。當視圖收到一個拖動作,它將拖放事件發給QGraphicsSceneDragDropEvent,它再發給場景,場景對事件按時序排列,併發給光標下的第一個項來接受放置。

爲了從一個項上開始拖動作,要產生一個QDrag對象,將指針傳給開始拖的那個窗口部件。在同一時刻,項可以被很多視圖觀察到,但是,只有一個視圖可以開始拖動作。動作在大多數情況下是因爲鼠標按下和提供而觸發開始的,因此,在mousePressEvent()mouseMoveEvent()函數中,你可以從事件中得到起始窗口部件的指針。如下面的例子:

void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)

 {

     QMimeData *data = new QMimeData;

     data->setColor(Qt::green);

 

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

     drag->setMimeData(data);

     drag->start();

 }

 

爲了給場景截取拖放事件,在QGraphicsItem子類中,需要重實現QGraphicsScene::dragEnteEvent()函數,哪個事件處理你特殊的場景需要。你可以在圖形視圖拖放操作文檔中,查看每一個QGraphicsScene的事件句柄來學習更多的知識。

參見“Drag and Drop”例子,它是一個在圖形視圖中支持拖放操作的演示。

 

光標和提示(Cursors and Tooltips

QWidget一樣,QGraphicsItem也提供光標支持(QGraphicsItem::setCursor()),提示支持(QGraphicsItem::setToolTip())。當鼠標的光標進入項的區域時(調用QGraphicsItem::contains()檢測),光標和提示由QGraphicsView激活。

你可以調用QGraphicsView::setCursor()直接在視圖上設置一個缺省光標。

參見“Drag and Drop 例子中,提示和光標形狀處理的實現。

 

動畫(Animation

視圖在幾個層面支持動畫。你可以通過將QGraphicsItemAnimation和項關聯,來簡單地組裝動畫路徑,這種方式允許以時間線方式動畫在所有平臺上都可以以一個穩定的速度操作(幀速會因平臺的性能而改變)。QGraphicsItemAnimation允許爲項的位置、旋轉、尺寸縮放、裁減、變換創建路徑。動畫可以被QSlider控制,或者普遍的是被QTimeLine控制。

另外一個方式是創建一個項,它從QObjectQGraphicsItem繼承而來。這個項可以建立它自己的定時器,在QObject::timeEvent()中控制動畫的進階步驟。

第三種方式,這種方式大部分是爲了和Qt3中的QCanvas保持兼容,調用QGraphicsScene::advance()來推進(advance)場景,進而調用QGraphicsItem::advance()推進項。

參見“Drag and Drop”例子中關於基於時間線技術動畫的演示。

 

OpenGl展示(OpenGL Rendering

爲了使用OpenGL展示,你只要簡單地調用QGraphicsView::setViewport()來設置一個新的QGLWidget作爲QGraphicsView的視圖端口。如果你想要OpenGL具有無鋸齒特性,你需要OpenGL採樣緩衝支持(參見QGLFormat::sampleBuffers())。

例子:

QGraphicsView view(&scene);

view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));

 

項成組(Item Groups

通過使一個項成爲另外一個項的子項,你可以得到有關項成組的關鍵特性:這些項會一起移動,所有的變換都從父項傳遞給子項。QGraphicsItem能夠爲它的子項處理所有的事件(參見QGraphicsItem::setHandlesChildEvents())。這允許父項代表子項動作,可以有效地將所有子項當作一個整體。

另外,QGraphicsItemGroup是一個特殊的項,它聚合了子項事件處理,並有一個有用的接口用來在組中增加和移除項。向QGraphicsItemGroup將保持項的原始位置和變換,而重父化項會引起子項的重定位,因爲新的父項的關係。爲來方便,你可以通過場景調用QGraphicsScene::createItemGroup()來創建QGraphicsItemGroups

 

窗口部件和佈局(Widgets and Layouts

Qt4.4通過QGraphicsWidget引入了對幾何和佈局感應的支持。這個特殊的基類與QWidget相似,但又不同。它不從QPaintDevice繼承,而是從QGraphicsItem繼承。這允許你撰寫具有事件、信號和槽、尺寸線索和策略的完整窗口部件,你也能通過QGraphicsLinearLayoutQGraphicsGridLayout管理佈局中的窗口部件的幾何特性。

 

圖形窗口部件(QGraphicsWidget

QGraphicsWidget創建在QGraphicsItem之上,它提供了幾個方面的結合:相對於QWidget的格外功能,如風格、字體、調色板、佈局方向、幾何表現和從QGraphicsItem繼承的分辨率無關性和變換支持。因爲GraphicsView使用實數座標而不是整數座標,QGraphicsWidget的幾何特性功能也是在QRectFQPointF上操作。這個也應用到框架矩形、空白邊緣和間隙。對於QGraphicsWidget,規定內容邊緣爲(0.50.50.50.5)並不是不平常的(很平常),例如,你可以創建頂層窗口和子窗口部件,在一些情況下,你現在可以用Graphics View寫高級的多文檔應用。

一些QWidget的屬性被支持了,包括窗口標誌和屬性,但不是全部。你可以參見QGraphicsWidget的類文件以全面瞭解它支持或不支持什麼。例如,你可以通過Qt::Window窗口標誌給QGraphicsWidget的構造函數創建裝飾過的窗口,但是,當前的Graphics View不支持在Mac OS X中和普通的Qt::SheetQt::Drawer標誌。

QGraphicsWidget的性能將以來社區的反饋不斷增強。

 

圖形佈局(QGraphicsLayout

QGraphicsLayout是專爲QGraphicsWidget設計的第二代佈局框架。它的APIQLayout很相似。你可以在QGraphicsLinearLayoutQGraphicsGridLayout內管理窗口部件和佈局。你可以通過QGraphicsLayout的子類很容易寫你的佈局,或者通過改編QGraphicsLayoutItem子類給佈局增加你自己的QGraphicsItems項。

 

嵌入式窗口部件支持

Graphics View爲在場景中嵌入任何窗口部件提供了無縫的支持。你可以嵌入簡單窗口部件如QLineEditQPushButton,到複雜的窗口部件如QTabWidget,或是完整的主窗口。爲了將你的窗口部件嵌入場景,可以簡單地調用QGraphicsScene::addWidget(),或創建一個QGraphicsProxyWidget實例來手動嵌入你的窗口部件。

通過QGraphicsProxyWidgetGraphics View將能夠深入地集成客戶窗口部件特性,包括它的光標、提示、鼠標、tablet、鍵盤事件、子窗口部件、動畫、彈出式部件(即QComboBoxQCompleter)、窗口部件的輸入聚焦和激活。QGraphicsProxyWidget也能集成嵌入窗口部件的tab順序。你甚至可以嵌入一個新的QGrasphicsView到場景中形成複雜的嵌套場景。

當對一個嵌入式窗口部件變換座標時,Graphics View需要確信窗口部件被分辨率無關地變換了,而允許字體和風格在放大時保持脆弱的。(注意:分辨率無關的影響依賴於風格。)

 

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