最近 Heresy 在寫程式的圖形介面,都是用 Nokia 的 Qt(官網)在寫的;而這一篇,大概來講一下 Qt Graphics View 架構的概念,主要參考文件,是 Qt 官方的《Graphics View Framework》一文。
基本上,Qt 的 Graphics View 是一個用來做大量 2D 物件的繪製、互動、管理的框架(framework)。透過這個架構裡的 Scene、View 和 item 的組合,Graphics View 這個框架可以提供非常有效率、而且方便使用大量物件的 2D 繪製效果;同時,也還支援旋轉、縮放、選取、動畫等等的顯示與互動效果。
所以如果是使用 Qt 當作圖形介面,而且需要畫出一些可能會有互動、需要選取的物件的時候,Graphics View 應該算是個不錯的選擇。
而接下來,就大概介紹一下 Qt Graphics View 的架構了∼
Graphics View 架構
Qt Graphics View 的架構,基本上主要有三種元件:Scene(場景)、View(顯示區域)、Item(場景內的物件),他 們的型別分別是 QGraphicsScene、QGraphicsView 和 QGraphicsItem;同時,也都可能包含其他繼承出來的衍生型別的物件。
而這三者的功能,則大致如下:
-
Scene(QGraphicsScene)
整個要顯示的場景,它提供了一個標準的介面,來配置、管理場景內的所有物體(item)。
Qt 預設會採用 Binary Space Partitioning Tree(BSP Tree)來做物件的管理,號稱既使 scene 裡面有上百萬個 item 也可以即時地顯示、互動。
-
View(QGraphicsView)
實際用來顯示 scene 的 Qt 元件,它本身不包含 item 的資料、而僅只是根據 scene 把所配置好的 item 呈現出來。他也有提供基本的卷軸功能、可以快速地用在大型的 scene 上;如果進一步透過設定 View 的參數,也可以做到縮放、移動等顯示上效果。
而在使用上,一個 scene 可以有多個不同的 view,也就是可以設定多組不同的視角、來觀看同一組資料。另外,View 也包含了處理使用者操作、把這些指令轉譯給 scene 的功能。
-
Item(QGraphicsItem)
scene 裡面最基本的單元,在 Scene 裡面的物體,都必須要繼承自 QGraphicsItem 這個基礎型別;這些 item 可以支援鍵盤滑鼠的互動、拖曳、item 間的群組、以及 item 間的碰撞偵測等功能。
Qt 本身已經內建了許多不同基礎的 item,有需要也可以自行定義出新的 item 來使用。當然,有必要的話,也是可以建立 item 的群組的(QGraphicsItemGroup)。
簡單的使用範例
而在有 scene、view 和 item 三種最主要的元素的情況下,Qt Graphive View 最基本的使用方法,大概就會是像下面的範例:
#include <QtGui/QtGui>
int main( int argc, char **argv )
{
// Qt Application
QApplication app( argc, argv );
// 1. Qt Scene
QGraphicsScene scene;
scene.setSceneRect( -200, -100, 400, 300 );// 2. insert Qt Item
scene.addEllipse( 0, 0, 100, 20 );
QGraphicsTextItem* qText = scene.addText( "Hello, world!" );
qText->setPos( -10, 100 );// 3. Qt View// start!
QGraphicsView view( &scene );
view.resize( 320, 240 );
view.show();
return app.exec();
}
上面範例程式裡、黃底的部分,就是 Grpahics View 的相關程式了∼
其中,第一段「1. Qt Scene」就是建立一個 QGraphicsScene 的物件出來,當作場景;同時,這邊也設定好它的邊界範圍(在概念上,scene 可以是無窮大的)。
而第二段的「2. insert Qt Item 」則是透過 QGraphicsScene 提供的函式,在這個場景內加上一些東西;Heresy 這邊是簡單地加了一個橢圓形、和一段文字。
第三段「3. Qt View」則是使用目前的場景,建構出一個 QGraphicsView 的物件,當作主要顯示的元件;而它的操作方法,也就和一般的 Qt Widget 大致相同了∼
而這樣的程式的執行結果,就會是一個長得像右圖這樣子的視窗。而由於這個場景的範圍大於 view 的顯示範圍,所以 View 的元件會自動出現卷軸,讓使用者可以調整要看的範圍。這點算是相當方便的∼而如果要進行 view 的其他控制的話,也可以透過它內建的 scale()、translate()、rotate() 等函式,來輕易地做到。
另外,如果在畫面裡想要有更多的東西的話,也只要繼續把東西丟到 scene 裡就可以了∼基本上,Qt 已經提供了一些常見的 item 類型了(參考),如果只是要做簡單的處理,應該是夠用;但是在實務上應該還是會有自訂新的 item 的需求,這時候只要自己去繼承 QGraphicsItem、定義出新的物件出來,就可以放到 scene 裡面,讓 QGraphicsScene 來做管理、使用了∼而操作的方法,也都會和其他的物件一樣!所以在這個架構下,就算是有自定物件的需求,也是相當方便的∼
 
座標系統
在 Qt 的 Graphic View 架構裡,座標系統主要分成三種:Item 座標系統、scene 座標系統、view 座標系統;而這三種座標系統,也就是對應到他基本的三類物件了。而為了操作上的便利性,Qt 也有提供這三種座標系統間轉換的函式可以使用,所以在轉換上也相當地方便。
其中,Item 座標系統主要是在自定義新的 QGraphicsItem 時,才比較會用到的,大部分情況下,他會是以物體的中心為原點。
而 Scene 座標系統,則就是用來管理整個 scene 的座標系統,各個 item 在 scene 裡面的位置,就是用這個座標系統來表示的,所以基本上應該會是最常用到的一個∼
View 座標系統基本上就是 QGraphicsView 這個 Qt Widget 本身的座標系統,他的左上角永遠都是 (0, 0),右下角永遠是 ( 寬, 高 )。而他本身滑鼠的事件所得到的座標都是 view 座標系統的,要拿來控制物體的話,還需要透過 QGraphicsView::mapToScene() 這個函式把座標對應到 scene 座標系統上才可以。
 
事件的傳遞
在事件的處理上,一般來說是由有實際顯示出來的 view 來接收絕大部分的外部事件(event),像是鍵盤、滑鼠的事件,都是由 view 來統一接收的。而當 view 接收到這些輸入的事件後,他會把這些事件、做一些轉換(例如座標系統)後,交付給它所顯示的 scene 做處理。而 scene 在收到了 view 傳來的事件後,會再根據目前的狀況,將這些事件傳給對應的 item、讓 item 各自做處理。
所以,如果要寫自己的事件處理方法的話,其實可以在 view、scene、item 三個層次上都是可以做的!不過要實作的話,就是得要去繼承原有的類別,寫一個新的類別出來用了。至於要在哪一個層級做這個動作,就是看需求而定了。
 
這篇概論性的東西就先寫道這了,再接下來比較進一步的使用,就之後再用別篇來寫了∼