初识Qt绘图QGraphicsItem

时间:2023-02-01 14:04:17

最近学习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