Qt QGraphicsScene QGraphicsView QGraphicsItem學習記錄


一.場景(QGraphicsScene)

QGraphicsScene 提供了圖形視圖框架的場景,相當於一塊畫布,並具有以下功能。

1.一個管理大量圖形項的快速接口。

2.向每個圖形項傳播事件

3.管理圖形項的狀態,比如選擇,焦點處理等

4.提供無轉換的渲染功能,主要用於打印

簡單地一個場景使用

1 QGraphicsScene scene;//場景
2 scene.addText("Hello, world!");//添加文本圖形項
3 QGraphicsView view(&scene);//設置視圖
4 view.show();
1 addText("Hello, world!") 相當於執行了以下兩句 2 
3 QGraphicsTextItem *item = new QGraphicsTextItem("Hello, world!"); 4 scene.addItem(item);

如果場景要刪除一個圖形項,可以使用

removeItem(item)

函數。

一般而言,場景層從下到上共分為3層,分別為背景層(backgroundLayer), 圖形項層(Itemlayer)與前景層(ForegroundLayer)。場景繪制總是從背景層開始,然后是圖形層,最后是前景層。

1 scene.setForegroundBrush(QColor(255,255,255,100));//前景層顏色為白色半透明
2 scene.setBackgroundBrush(Qt::green);//背景色設置為綠色

對於前景層,我們一般不進行設置,或者像上面這樣設置為半透明的白色。對於背景層,這里設置為了綠色,當然,我們也可以將一張圖片設置為背景。

scene.setBackgroundBrush(QPixmap(":/background.jpg"));

2. 場景邊界矩形

場景大小默認是沒有限制的,圖形項可以放到場景的任何位置。而場景的邊界矩形僅用於場景內部進行索引的維護。

因為如果沒有邊界矩形,場景就要搜索所有的圖形項,然后確定出其邊界,這是十分費時的。所以如果要操作一個較大的場景,我們應該給出它的邊界矩形。設置邊界矩形,可以使用setSceneRect()函數。

3.圖形項查找

場景最大的優勢之一就是可以快速的鎖定圖形項的位置,即使有上百萬個圖形項,items()函數也能在數毫秒的時間內鎖定一個圖形項的位置。items()函數有幾個重載函數來方便的進行圖形項的查找。但是有時在場景的一個點可能重疊着幾個圖形項,這時我們可以使用itemAt()函數返回最上面的一個圖形項。對於這些函數的使用,我們到后面講視圖時再舉例講解。

4.事件處理和傳播

場景可以傳播來自視圖的事件,將事件傳播給該點最頂層的圖形項。但是就像我們在講圖形項時所說的那樣,如果一個圖形項要接收鍵盤事件,那么它必須獲得焦點。而且,如果我們在場景中重寫了事件處理函數,那么在該函數的最后,必須調用場景默認的事件處理函數,只有這樣,圖形項才能接收到該事件。這一點我們也到后面講視圖時再細講。

二.視圖(QGraphicsView)

QGraphicsView 提供了視圖窗口部件,它使場景的內容可視化。你可以給一個場景關聯多個視圖,從而給一個數據集提供多個視口。視圖部件是一個滾動區域,就是說,它可以提供一個滾動條來顯示大型的場景。如果要使用OpenGL,你可以使用QGraphicsView::setViewport()函數來添加QGLWidget 。

(一)縮放與旋轉

1 QGraphicsView::scale(xScale, yScale);//在分別在x,y方向上縮放xScale,yScale倍。若為1.0倍,則不進行縮放。
2 QGraphicsView::rotate(90);//順時針旋轉90度
(二)場景邊框與場景對齊方式
我們在上面講場景時就提到了場景邊框(SceneRect),這里再說說它在視圖中的作用。我們前面說過,視圖是可以提供滾動條的,但是,這只是在視圖窗口小於場景時才自動出現的。如果我們不定義場景邊框,那么當場景中的圖形項移動到視圖可視窗口以外的地方時,視圖就會自動出現滾動條,但是即使是圖形項再次回到可視區域,滾動條也不會消失。為了解決這個問題,我們可以為場景設置邊框,這樣,當圖形項移動到場景邊框以外時,視圖是不會提供額外的滾動區域的。
       而當整個場景都可視時,也就是說視圖沒有滾動條時,我們可以通過setAlignment()函數來設置場景在視圖中的對齊方式,如左對齊Qt::AlignLeft ,向上對齊Qt::AlignTop ,中心對齊Qt::AlignCenter。更多的對齊方式,可以查看幫助中Qt::Alignment 關鍵字。默認的對齊方式是Qt::AlignCenter 。而且幾種對齊方式可以通過“按位或”操作一起使用。我們在上面的程序中的myitem.cpp文件中的構造函數最后添加一行代碼:
setAlignment(Qt::AlignLeft | Qt::AlignTop);
(三)拖動模式
在QGraphicView中提供了三種拖動模式,分別是:
1 QGraphicsView::NoDrag :忽略鼠標事件,不可以拖動。
2 QGraphicsView::ScrollHandDrag :光標變為手型,可以拖動場景進行移動。
3 QGraphicsView::RubberBandDrag :使用橡皮筋效果,進行區域選擇,可以選中一個區域內的所有圖形項。
我們可以利用setDragMode()函數進行相應設置。
下面我們更改上面的程序。在myview.cpp中的構造函數中的最后添加一行代碼:
setDragMode(QGraphicsView::ScrollHandDrag);//手型拖動
並將場景外框放大一點:
scene->setSceneRect(0,0,800,800);
這時運行程序,雖然出現了小手,但是並不能拖動場景。為什么呢?我們在mousePressEvent()函數中添加一行代碼:
QGraphicsView::mousePressEvent(event);
這時再運行程序,發現已經成功了。效果如下:
我們在事件函數的最后添加了一行:
QGraphicsView::mousePressEvent(event);
這樣程序才能執行默認的事件。這也是我們下面要說的事件傳播的內容的一部分。
(四)事件傳遞
在上面我們看到必須在事件函數的最后將event參數傳遞出去,才能執行默認的事件操作。其實不止上面那一種情況,在圖形視圖框架中,鼠標鍵盤等事件是從視圖進入的,視圖將它們傳遞給場景,場景再將事件傳遞給該點的圖形項,如果該點有多個圖形項,那么就傳給最上面的圖形項。所以要想使這個事件能一直傳播下去,我們就需要在重新實現事件處理函數時,在其最后將event參數傳給默認的事件處理函數。比如我們重寫了場景的鍵盤按下事件處理函數,那么我們就在該函數的最后寫上
QGraphicsScene::keyPressEvent(event);
一行代碼。

(五)背景緩存
如果場景的背景需要大量耗時的渲染,可以利用CacheBackground來緩存背景,當下次需要渲染背景時,可以快速進行渲染。它的原理就是,把整個視口先繪制到一個pixmap上。但是這個只適合較小的視口,也就是說,如果視圖窗口很大,而且有滾動條,那么就不再適合緩存背景。我們可以使用
setCacheMode(QGraphicsView::CacheBackground);
來設置背景緩存。默認設置是沒有緩存QGraphicsView::CacheNone
 
(六)OpenGL渲染
QGraphicsView默認使用一個QWidget作為視口部件,如果我們要使用OpenGL進行渲染,可以使用setViewport()函數來添加一個QGLWidget對象。看下面的例子。
我們先在項目文件graphicsView04.pro中加入
QT += opengl
說明要使用OpenGL模塊,然后在myview.cpp文件中添加頭文件:

#include <QtOpenGL>
最后在構造函數中加入代碼:
1 QGLWidget *widget =new QGLWidget(this); 2 setViewport(widget);
這樣便使用OpenGL進行渲染了。關於OpenGL,我們在后面的3D繪圖部分再講。
(七)打印
圖形視圖框架提供了兩個打印函數render(),一個是在QGraphicsScene中,一個是在QGraphicsView中,並且它們的函數原型是一模一樣的。不過它們實現的效果稍有不同。看一面的例子。
我們更改鼠標按下事件槽函數的內容如下:
1 void MyView::mousePressEvent(QMouseEvent*event) 2 { 3     rotate(90); //視圖旋轉順時針90度
4    QPixmap pixmap(400,400);  //必須指定大小
5    QPainter painter(&pixmap); 6    render(&painter,QRectF(0,0,400,400),QRect(0,0,400,400));  //打印視圖指定區域內容
7     pixmap.save("../graphicsView04/save.png"); 8    QGraphicsView::mousePressEvent(event); 9 }
這里我們使用了視圖的render()函數,其中的QRectF參數是指設備的區域,這里是指pixmap。而QRect參數是指視圖上要打印的區域。我們利用QPixmap類的save()函數,將pixmap圖片保存到我們項目源碼目錄中,文件名為“save.png”。下面是運行程序后,點擊鼠標,生成的圖片的效果:
我們每點擊一次鼠標,就會旋轉視圖,那么生成的圖片就是當前視口的截圖。下面我們使用場景的打印函數,將上面的打印一行的代碼改為:
scene()->render(&painter,QRectF(0,0,400,400),QRect(0,0,400,400));//打印場景內容
這時無論視圖怎樣變換,生成的圖片總是一樣的。而且它並沒有打印場景背景的圖片。就像我們看到的,視圖的打印函數是依據視圖的坐標系進行打印的,我們看到的就是打印出來后的效果,它可以看做是程序窗口的截屏。而場景的打印函數,是依據場景的坐標系的,無論視圖怎么轉換,只要場景坐標系沒有變換,它打印出來的圖片都是一樣的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM