初識Qt繪圖QGraphicsItem

最近學習Qt繪圖,花了很長的時間,也費了一些心思,好在有所收穫,也不枉這幾個月的各種苦熬,在這裏做一些總結。如題目所說,這也只是初識QGraphicsItem,我需要做的是繼續往後學習,只是希望能夠幫助一下那些初學Qt繪圖的同學,如有不足,還請多多指教。(當然,我覺得有必要說一下我的Qt版本:Qt 4.8.1 SDK).

首先,這裏顯示圖形的窗口爲QGraphicsView,我們可以直接從Qt可視化編程的窗口當中直接拖拽到我們需要繪製圖形的窗體當中,如果有必要處理一些特殊的事件,那就需要重寫QGraphicsView類,這裏我就不說這個。再有一個就是QGraphicsScene,我們可以把它理解成是一個畫布,我們的QGraphicsItem都會繪製到這個畫布上,然後再把這個畫布“掛”到QGraphicsView這垛“牆”上展示出來,當然如果你要處理一些比較特殊的事件(比如,我想在畫布上右鍵之後彈出一個菜單等等很多事件處理)就需要重寫這個類,這個以後再說。那麼接下來就是我們的主角QGraphicsItem登場了。

首先,值得一提的是,當我們重寫QGraphicsItem這個虛類時,一定要注意,假如我們自定義一個類MyItem繼承自QGraphicsItem,千萬不要讓MyItem這個類同時繼承自QObject,因爲QGraphicsItem根本就不是從QObject繼承來的,如果我們在用Qt新建MyItem類的時候把QObject當做是它的基類,就會出現很多問題,有時候可能是無法編譯成功,在有時候是運行時異常,還可能會被警告“會導致某些函數失去應有功能”,等等。所以一定不要試圖強制性的讓我們自己寫的MyItem類繼承自QGraphicsItem的同時設置其基類爲QObject,因此,也別想着在我們重寫的QGraphicsItem類當中使用“信號和槽”機制,因爲這個機制只針對QObject纔有用。

接下來,就是我們重寫QGraphicsItem類的幾個最基本的要點。第一個就是我們需要重寫的幾個函數:

1 QRectF boundingRect()const;
2 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
3 QPainterPath shape()const;

第一個函數,boundingRect(),官方的文檔已經把這個函數的作用說得比較清楚了,但是我覺得有必要說一下這個函數有多重要:首先,這個函數的返回值是一個QRectF(一個正方形的區域),當我們需要處理我們所寫的MyItem接受到的某些事件(比如鼠標按下、拖動等)時,這些事件就被規定只能發生在這個返回的矩形區域當中時纔會起被接收到,同時,我們下一個函數paint(……)所繪製的內容也只能在這個區域裏面畫,等等。第二個函數,paint(……),這裏面就是畫我們某一個具體的Item的全部內容。第三個函數,shape(),他所返回的就是我們所繪製的Item的大概形狀。下面我們來看一下一個具體的簡單的函數重寫的例子:

假設我們需要繪製的就是一個矩形,那麼矩形只需要定義它的寬和高就行了,所以頭文件的定義可以先下面這樣:/***MyItem.h***/

01 #ifndef MYITEM_H 
02 #define MYITEM_H 
03 #include <QGraphicsItem> 
04 #include <QPainter> 
05 #include <QRectF> 
06 #include <QPainterPath> 
07 class MyItem : public QGraphicsItem 
08
09 public:  
10     MyItem(qreal wid,qreal hgt);  
11     QRectF boundingRect()const;  
12     void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);  
13     QPainterPath shape()const;  
14 private:  
15     qreal m_width;  
16     qreal m_height; 
17 }; 
18 #endif // MYITEM_H

再來看CPP文件:/***MyItem.cpp***/

01 #include "MyItem.h" 
02 MyItem::MyItem(qreal wid,qreal hgt)  {  
03     m_width=wid;m_height=hgt; 
04
05 QRectF MyItem::boundingRect()const{  
06     return QRectF(-m_width/2-1,-m_height/2-1,m_width+2,m_height+2);//每個item都有自己的一個座標系, 
07 //這個設置相當於把item相對於自身的座標系的原點(0,0)放到自己的正中央。
08
09 QPainterPath MyItem::shape()const{  
10     QPainterPath path;  
11     path.addRect(QRectF(-m_width/2,-m_height/2,m_width,m_height));         return path; 
12
13 void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){  
14     Q_UNUSED(option);  
15     Q_UNUSED(widget);  
16     painter->drawRect(QRectF(-m_width/2,-m_height/2,m_width,m_height)); 
17 }


這個時候,我們就能開始在QGraphicsScene實例當中畫了,假如在我們的MainWindow中繪製一個MyItem,繪製代碼如下: 

01 scene=new QGraphicsScene; 
02 ui->graphicsView->setScene(scene);
03 //把“畫布” “掛”到展示的窗體QGraphicsView當中 
04 scene->setSceneRect(-500,-500,1000,1000);
05 //設置“畫布”矩形區域,座標(-500,-500)爲左上角,這就意味着scene的中心位置爲(0,0).這個座標系就是scene的座標系 
06 ui->graphicsView->centerOn(0,0);//把畫布scene的中心位置放到QGraphicsView正中央 
07 item=new MyItem(180,260); 
08 scene->addItem(item);//往“畫布”上繪製MyItem 
09 item->setPos(0,0); 
10 //item->setPos(0,0)設置MyItem在“畫布”scene的位置,此時,就是把item相對於自己的座標系的原點放到scene的(0,0)位置

運行程序,我們就可以看到我們繪製的矩形框了。

當然,MyItem還有更多的功能,比如設置被鼠標左鍵選中時出現虛線的矩形框(默認的是沒有矩形框的),我們可以在MyItem的paint(……)函數中實現,舉一個例子:

1 void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){  
2     Q_UNUSED(widget);  
3     if(option->state & QStyle::State_Selected){   
4         painter->setPen(QPen(Qt::red,2,Qt::DotLine));  
5         painter->drawRect(shapeRectF.x(),shapeRectF.y(),
6         shapeRectF.width(),shapeRectF.height()); 
7     }  
8     painter->drawRect(QRectF(-m_width/2,-m_height/2,m_width,m_height));
9  }

當然,這個還需要有一些相應的設置(可以在構造函數當中設置):

1 setZValue(0);
2 setAcceptDrops(true);
3 setFlags(ItemIsSelectable);

同時,還可以設置MyItem在scene中被鼠標左鍵按下選中之後進行拖動: 

1 setAcceptDrops(true);
2 setFlags(ItemIsSelectable | ItemIsMovable);

上面幾個函數的具體功能看看文檔就很明瞭了。 另外,在實際的應用當中,我們還有可能需要實現另外一個比較使用的功能,利用鼠標拖動MyItem的邊緣對MyItem進行大小縮放,這個無法通過Qt中QGraphicsItem已有的set一類的函數簡單實現,那麼我們就需要自己來實現,這裏我可以提供一個思路:利用hoverEvent系列事件捕捉鼠標是否到達MyItem的邊緣,然後利用mouseEvent系列事件進行縮放處理病重繪MyItem。具體的實現,有需要的同學可以參考這個例子:http://download.csdn.net/detail/rubone/5430191

以上內容來自開源中國社區:http://www.oschina.net/question/658193_111904

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