天天看點

QT開發(四十)——GraphicsView程式設計

qt開發(四十)——graphicsview程式設計

qgraphicsscene繼承自qobject,是一個管理圖元的容器,與qgraphicsview合用可以在2d螢幕上顯示如線、三角形、文本、自定義圖元等圖元。

qgraphicsscene是不可見的,隻用于管理圖元。為了檢視場景,需要建立一個視圖元件。

    一個場景分為三個層:圖元層、前景層和背景層。場景的繪制總是從背景層開始,然後是圖形項層,最後是前景層。

    qgraphicsscene的責任之一是傳播來自視圖的事件。要發送一個事件到場景,需要構造一個繼承自qevent的事件,使用qapplication::sendevent()函數發送事件。event()函數負責派發事件到各個圖元。常用的事件會被便利事件處理函數處理,如滑鼠按下事件會被mousepressevent()函數處理。

    按鍵事件會被派發到焦點圖元。為了設定焦點圖元,可以調用setfocusitem()函數,或是圖元自身調用qgraphicsitem::setfocus()函數。調用focusitem()函數可以擷取目前的焦點圖元。為了相容圖形元件,場景維護着自己的焦點資訊。預設場景并沒有焦點,并且所有的按鍵事件會别丢棄。如果setfocus()函數被調用,或是場景中一個圖元獲得了焦點,場景會自動獲得焦點。如果場景有焦點,hasfocus()函數會傳回true,按鍵事件會被發送到焦點圖元。如果場景失去了焦點,而圖元有焦點(如調用clearfocus()函數),場景會維護圖元的焦點資訊,一旦場景重新獲得焦點,會確定最後一個有焦點的圖元獲得焦點。

    對于懸停效果,qgraphicsscene會派發懸停事件,如果某個圖元接受了懸停事件(調用qgraphicsitem::accepthoverevents()),當滑鼠進入圖元的區域時,圖元會接收到一個graphicsscenehoverenter事件。當滑鼠繼續在圖元内部移動時,qgraphicsscene會發送graphicsscenehovermove事件。當滑鼠離開圖元的區域時,圖元會收到一個graphicsscenehoverleave事件。

    所有滑鼠事件會被傳播到目前滑鼠擷取的圖元。如果一個圖元接收了滑鼠事件,并收到滑鼠按下,圖元就是場景的滑鼠擷取圖元。這個圖元會一直被滑鼠擷取,直到圖元收到一個滑鼠釋放事件。調用mousegrabberitem()函數可以知道目前滑鼠擷取的圖元。

場景可以傳遞來自視圖的事件,将事件傳遞給該點最頂層的圖元。如果一個圖元要接收鍵盤事件,那麼它必須獲得焦點。而且,如果在場景中重寫了事件處理函數,那麼在該函數的最後必須調用場景預設的事件處理函數,隻有這樣,圖元才能接收到該事件。

a、拖拽事件

[virtual protected] void dragenterevent(qgraphicsscenedragdropevent *event)

拖入事件處理函數

[virtual protected] void dragleaveevent(qgraphicsscenedragdropevent *event)

拖離事件梳理函數

[virtual protected] void dragmoveevent(qgraphicsscenedragdropevent *event)

拖動事件處理函數

[virtual protected] void dropevent(qgraphicsscenedragdropevent *event)

drop事件處理函數

在以上拖拽事件處理函數中的末尾需要調用qgraphicsscene類相應的事件處理函數。

qgraphicsscene::dragenterevent(event);

qgraphicsscene::dragleaveevent(event);

qgraphicsscene::dragmoveevent(event);

qgraphicsscene::dropevent(event);

b、滑鼠事件

[virtual protected] void mousemoveevent(qgraphicsscenemouseevent *mouseevent)

滑鼠移動處理函數

[virtual protected] void mousepressevent(qgraphicsscenemouseevent *mouseevent)

滑鼠按下處理函數

[virtual protected] void mousereleaseevent(qgraphicsscenemouseevent *mouseevent)

滑鼠釋放處理函數

在以上滑鼠事件處理函數中的末尾需要調用qgraphicsscene類相應的事件處理函數。

qgraphicsscene::mousemoveevent(event);

qgraphicsscene::mousepressevent(event);

qgraphicsscene::mousereleaseevent(event);

    索引算法,是指在場景中進行圖元查找的算法。qgraphicsscene中提供了兩種選擇,在一個枚舉變量qgraphicsscene::itemindexmethod中定義,分别是:

    qgraphicssecne::bsptreeindex :應用binary space partition tree,适合于大量的靜态圖元,是預設值。

    qgraphicsscene::noindex :不用索引,搜尋場景中所有的圖元,适合于經常進行圖元的添加、移動和删除等操作的情況。

    使用setitemindexmethod()函數進行索引算法的更改。

    圖元可以放到場景的任何位置,場景的大小預設是沒有限制的。而場景的邊界矩形僅用于場景内部進行索引的維護。因為如果沒有邊界矩形,場景就要搜尋所有的圖元,然後确定出其邊界,這是十分費時的。是以如果要操作一個較大的場景,應該給出它的邊界矩形。

    設定邊界矩形,可以使用setscenerect()函數。

    場景最大的優勢之一就是可以快速的鎖定圖元的位置,即使有上百萬個圖元,items()函數也能在數毫秒的時間内鎖定一個圖元的位置。items()函數有幾個重載函數來友善的進行圖元的查找。如果在場景的一個點可能重疊着幾個圖元,可以使用itemat()函數傳回最上面的一個圖元。

    qgraphicsitem是圖元的基類。

    自定義圖元,首先應該繼承qgraphicsitem,然後重寫他的兩個純虛公共函數boundingrect()和paint(),boundingrect()函數傳回繪制圖元大概的區域,paint()函數用來繪制圖元内容。

    boundingrect()函數有很多用處,場景在boundingrect()來建立它的圖元的index,視圖使用boundingrect來剪切可見的圖元,在重新繪制圖元時候,來決定互相重疊的部分,此外,圖元的碰撞檢測機制也使用的boundingrect()來提供一個高效的定點,在collideswithitem()更好的碰撞算法建立在調用函數shape(),shape()函數以qpainterpath類型傳回圖元的精準的輪廓。

    場景不希望圖元的boundingrect()和shape()變化,除非該圖元被通告,如果想通過一些方法改變圖元的形狀,首先應該調用qgraphicsscene()來允許場景qgraphicsscene來重新整理它的圖元記錄。

    圖元沒有獲得焦點時,事件隻能從視圖傳遞到場景,不能傳遞到圖元。清除圖元的焦點函數為clearfocus()。

    paint()函數被qgrapicsview類調用來繪制圖元的内容,圖元預設是沒有背景或者填充顔色的。在函數中沒有被繪制的所有區域都将會發亮,可以調用update()來重繪圖元,可以選擇傳遞需要重繪的矩形區域(不是必須的)。取決于圖元在視圖中是否可見,圖元可能會也可能不會重繪,qgraphicsitem裡面沒有和 qwidget::repaint()函數等價的圖元通過視圖來繪制,從父類圖元開始,然後是圖元自身,以上升的棧的順序,可以通過調用setzvalue()設定圖元的棧順序,通過zvalue()來測試,具有低z-values的圖元比具有高z-value 的圖元先繪制,棧順序應用于兄弟圖元,父類圖元總是比子類圖元更早繪制。

    所有的圖元都按照一個已經聲明的穩定的順序來繪制,聲明的順序決定了當在場景中點選滑鼠時候,哪個圖元最先接受滑鼠的輸入。通常情況下,不需要擔心圖元排序的問題,因為所有的圖元都按照一個在場景中聲明的自然的順序。

    在一個棧中,子類圖元在父類圖元的上面,兄弟圖元按照插入場景的順序來入棧,如果你先添加圖元a ,然後是圖元b,然後是圖元c ,棧中的順序從下往上就是a、b、c。可以調用setzvalue()來設定一個圖元的相對于另一個圖元向上、向下或者兄弟棧順序。預設的z值是0,具有同樣的z值的圖元會按照插入的順序來入棧。可以調用stackbefore()來備份子類圖元的清單,直接更正圖元的順序。

    如果想讓子類圖元在父類圖元的後面,也就是先繪制子類圖元,然後再繪制父類圖元。可以利用函數setflag()設定itemstacksbehindparent屬性給圖元。

    qgraphicsitem從場景中通過sceneevent()函數來接受事件,sceneevent()函數通過一些友善的操作分散大部分事件。

    contextmenuevent()函數接受上下文菜單事件,

    focusinevent()和focusoutevent()函數接受焦點進出事件, 

    hoverenterevent()、hovermoveevent()、hoverleaveevent() 接受滑鼠懸浮移動和離開事件。

    inputmethodevent()函數處理輸入法事件,

    keypressevent()和keyreleaseevent()事件處理鍵盤按下和釋放事件

    mousepressevent()、mousemoveevent()、mousereleaseevent()、    mousedoubleclickevent()處理滑鼠按下、移動、釋放、輕按兩下事件

    通過安裝過濾器,可以為圖元過濾一些事件,與qt一般的事件過濾器不一樣,一般的過濾器隻工作在qobject及其子類。通過調用 installsceneeventfilter()為圖元安裝事件過濾器後,被過濾的事件将會被虛函數sceneeventfilter()捕捉 到,可以通過調用函數removesceneeventfilter()來去除掉事件過濾器。

    graphicsview架構為視圖、場景、圖元提供拖拽支援。當視圖接收到拖拽事件,graphicsview架構會将拖拽事件翻譯為qgraphicsscenedragdropevent事件,再發送到場景,場景接管事件,把事件發送到光标下接受拖拽的第一個圖元。

    從圖元開始拖拽時,建立一個qdrag對象,傳遞開始拖拽的qwidget的指針。圖元可以同時被多個視圖觀察,但隻有一個視圖可以開始拖拽。拖拽在多數情況下是從按下滑鼠或是移動滑鼠開始的,在mousepressevent()或mousemoveevent()中,可以從事件中得到原始的qwidget指針。

    要在場景中取拖拽事件,需要重新實作qgraphicsscene::dragenterevent()和qgraphicsitem子類裡任何與特定場景需要的事件處理器。圖元也可以通過調用qgraphicsitem::setacceptdrops()獲得拖拽支援,為了處理将要進行的拖拽,需要重新實作qgraphicsitem的dragenterevent()、dragmoveevent()、

dropevent()、dragleaveevent() 。

    要在自定義圖元類中處理滑鼠事件,需要重寫qgraphicsitem類中滑鼠按下、滑鼠移動、滑鼠釋放的事件。

[virtual protected] void mousemoveevent(qgraphicsscenemouseevent *event)

[virtual protected] void mousepressevent(qgraphicsscenemouseevent *event)

[virtual protected] void mousereleaseevent(qgraphicsscenemouseevent *event)

    實作圖元的動畫效果,也可以在不同的層面進行。如果隻想控制一兩個圖元的動畫,一般在場景或視圖中實作。但是要是想讓一個圖元類的多個對象都進行同樣的動畫,那麼我們就可以在圖元類的構造函數中進行實作。

    //圖元可獲得焦點

    setflag(qgraphicsitem::itemisfocusable);

    //圖元可移動

    setflag(qgraphicsitem::itemismovable);

    qgraphicsitemanimation *anim = new qgraphicsitemanimation;

    //将圖元加入動畫對象中

    anim->setitem(this);

    //建立長為1秒的時間線

    qtimeline *timeline = new qtimeline(1000);

    //動畫循環次數為0,表示無限循環

    timeline->setloopcount(0);

    //将時間線加入動畫類對象中

    anim->settimeline(timeline);

    //在動畫時間的一半時圖形項旋轉180度

    anim->setrotationat(0.5,180);

    //在動畫執行完時圖形項旋轉360度

    anim->setrotationat(1,360);

    //開始動畫

    timeline->start();

    圖元的移動,有多種方法實作,可以在視圖或場景上控制,但對于不同類型的大量圖元,怎樣能一起控制呢?在圖形視圖架構中提供了advance()槽函數,advance()函數在qgraphicsscene和qgraphicsitem中都有定義,在圖元類中的原型是advance(int phase)。實作流程是,利用qgraphicsscene類的對象調用qgraphicsscene的advance()函數,會執行兩次場景中所有圖元的advance(int phase)函數,第一次phase為0,告訴所有圖形項即将要移動;第二次phase的值為1,執行移動。

    qtimer timer;

    qobject::connect(&timer, signal(timeout()),scene, slot(advance()));

    timer.start(1000);

    至于圖元如何移動,需要重寫圖元類的advance()函數。

    如果在自定義圖元類的構造函數中設定為可移動,則圖元可以直接使用滑鼠拖拽。

setflag(qgraphicsitem::itemismovable);

    qgraphicsitem支援坐标轉換,對于簡單的轉換,可以調用函數setrotation()或者setscale(),可以傳遞一個轉換矩陣給函數settransform(),對于一些更複雜的轉換,可以通過調用函數settransformations()來設定一系列組合的轉換。

    圖元轉換從父類到子類進行聚集,是以如果一個父類圖元和子類圖元都旋轉90度,那麼子類圖元就旋轉了180度;如果父類圖元和子類圖元都放大了2x倍,那麼子類圖元就被放大4x倍,圖元的轉換不影響圖元的外觀,所有和外觀有關的函數(例如contains(),update()和所有的映射mapping函數)将會在本地坐标中操作,qgraphicsitem提供函數scenetransform(),将會傳回圖元所有的轉換矩陣,scenepos()将會傳回圖元在場景坐标中的位置,重新設定圖元的矩陣,調用函數resettransform()。

    一般的轉換回産生一個不同的結果,取決于轉換應用的順序,轉換順序不同得到結果将不同。

qvariant itemchange(graphicsitemchange change, const qvariant & value)

    itemchange函數被qgraphicsitem調用用來辨別圖元的狀态改變了,通過重載itemchange函數,可以對自己定義事件響應。參數change是改變的圖元的改變狀态參數,value是一個新的資料,類型取決于change,change是qgraphicsitem::graphicsitemchange枚舉變量。

    在itemchange函數内部調用函數時候要謹慎,不能在itemchange函數裡面調用setpos(),參數change是itempositionchange時,setpos()函數将會再次調用itemchange(itempositionchange),形成死循環。

    void setflag(graphicsitemflag flag, bool enabled = true)

     void setflags(graphicsitemflags flags)

    flags設定為圖元的屬性,如果圖元獲得了光标,但flags沒有使能itemsfocusable,圖元将會丢失光标,當圖元被選擇,但沒有使能itemsselectable,圖元會自動的失去選擇。

     qpainterpath shape () const 

    以qpainterpath傳回圖元在本地坐标中的形狀,形狀可以用來做很多事情,包括碰撞偵測,打擊測試,還有用來 qgraphicsscene::items() 函數

    預設的函數調用boundingrect()傳回一個簡單的矩形形狀,子類可以重載boundingrect函數,為非矩形的圖元傳回一個更加精準的形狀,例如一個圓形的圖元可以選擇傳回一個橢圓形,用來獲得更好的碰撞偵測效果。

    qgraphicsview繼承自qabstractscrollarea,繼承了qwidget的特性。

    qgraphicsview提供了視圖視窗部件,使場景的内容可視化。可以給一個場景關聯多個視圖,進而給一個資料集提供多個視口。視圖部件是一個滾動區域,可以提供一個滾動條來顯示大型的場景。

    在圖形視圖架構中,滑鼠鍵盤等事件是從視圖進入的,視圖将事件傳遞給場景,場景再将事件傳遞給該點的圖元,如果該點有多個圖元,那麼就傳給最上面的圖元。為了使事件能進一步傳播到場景,需要在重新實作事件處理函數時,在其最後将event參數傳給預設的事件處理函數。比如重寫了視圖的滑鼠按下事件處理函數,那麼就在該函數的最後寫上qgraphicsview::mousepressevent(event);

    在qgraphicview中提供了三種拖拽模式,分别是:

    qgraphicsview::nodrag :忽略滑鼠事件,不可以拖動。

    qgraphicsview::scrollhanddrag :光标變為手型,可以拖動場景進行移動。

    qgraphicsview::rubberbanddrag :使用橡皮筋效果,進行區域選擇,可以選中一個區域内的所有圖元。

    可以利用setdragmode()函數進行相應設定。

[virtual protected] void dragenterevent(qdragenterevent *event)

[virtual protected] void dragleaveevent(qdragleaveevent *event)

[virtual protected] void dragmoveevent(qdragmoveevent *event)

[virtual protected] void dropevent(qdropevent *event)

在以上拖拽事件處理函數中的末尾需要調用qgraphicsview類相應的事件處理函數。

qgraphicsview::dragenterevent(event);

qgraphicsview::dragleaveevent(event);

qgraphicsview::dragmoveevent(event);

qgraphicsview::dropevent(event);

[virtual protected] void mousemoveevent(qmouseevent *event)

[virtual protected] void mousepressevent(qmouseevent *event)

[virtual protected] void mousereleaseevent(qmouseevent *event)

void setmousetracking(bool enable)

在以上滑鼠事件處理函數中的末尾需要調用qgraphicsview類相應的事件處理函數。

qgraphicsview::mousemoveevent(event);

qgraphicsview::mousepressevent(event);

qgraphicsview::mousereleaseevent(event);

customview.h檔案:

customview.cpp檔案:

customscene.h檔案:

customscene.cpp檔案:

customitem.h檔案:

customitem.cpp檔案:

繼續閱讀