《QT从基础到进阶·二十一》QGraphicsView、QGraphicsScene和QGraphicsItem坐标关系和应用

简介: 《QT从基础到进阶·二十一》QGraphicsView、QGraphicsScene和QGraphicsItem坐标关系和应用

前言:

我们需要先由一个 QGraphicsView,这个是UI显示的地方,也就是装满可见原色的Scene,然后需要一个QGraphicsScene 用来管理所有可见的界面元素,要实现UI功能,我们需要用各种从QGraphicsItem拼装成UI控件,并赋予他控件的逻辑。

简单的说,QGraphicsView代表视图,QGraphicsScene 代表场景,QGraphicsItem代表图形项,QGraphicsItem放在QGraphicsScene 中,QGraphicsScene 放在QGraphicsView中。

1、坐标关系

这是QGraphicsView,始终是以左上角为(0, 0)点,单位为像素

创建场景QGraphicsScene ,并放入视图中:

QGraphicsScene* _scene = new QGraphicsScene(); 
ui.graphicsView->setScene(_scene);

注意:如果设置scene的坐标和宽高则不能放在构造里,不然不起效果,设置图形项宽高和坐标也不能在构造设置。

scene的坐标怎么看?

先忽略坐标看scene宽高,如果scene宽高小于view宽高,scene默认放到view正中央,

如果scene宽高大于view宽高,view视窗自动出现滚轮,保证view宽高和scene宽高一致

如果scene宽高没有设置,scene中没放图形项前宽高为0,放入图形项后scene宽高会和图形项宽高保持一致且scene宽高和左上角坐标会随着图像同步变化,scene就等同于图像,scene宽高没有设置,scene默认左上角坐标为(0, 0),原点也在左上角。scene宽高没有设置,图像左上角坐标始终与scene左上角坐标重合。

然后看scene的(x, y)坐标,(x, y)坐标为scene左上角坐标,有点特别要注意,不管scene左上角坐标如何变化,scene整体始终处于view正中央。

如果scene的(x, y)为(0, 0),scene的原点也为(0, 0)

如果scene的(x, y)为(-scene->width() / 2, -scene->height() / 2),scene的原点在view正中央。

图像坐标系也是一个独立的坐标系,图像左上角默认为(0, 0)点,把图像放入scene,图像左上角坐标默认在scene的原点,设置图像左上角坐标

pixmapItem->setPos(-scene->width() / 2, -scene->height() / 2);

这样图像左上角和scene左上角重合。

下面草图中继续说明三者坐标关系:

2、view,scene和item三者坐标转换

上图中,图片和矩形都放在QGraphicsItem里,红点为图形项零点(0, 0),因为一旦有了QGraphicsItem,scene会和item重合,所以红点处也为scene零点坐标(0, 0),但区别在于红点往左边一点,sence依然有坐标比如(-1, 0),但item直接返回空,无法计算item坐标,view视图坐标一直都是view显示框左上角为零点。

通过鼠标事件可以拿到视图view的坐标,比如触发mousePressEvent事件:

视图view坐标:

QPoint nViewPoint = event->pos();

通过视图坐标转场景scene坐标:

QPointF fScenePoint = this->mapToScene(nViewPoint);

通过场景坐标转图形项item坐标:

QGraphicsItem* pItem = this->scene()->itemAt(fScenePoint, QTransform()); //图形项指针
if (NULL != pItem)
{
 QPointF fItemPoint = pItem->mapFromScene(fScenePoint);   //获取图形项坐标
}

注意:鼠标在不同的item上比如图片,画的多边形点击一下不仅可以拿到该图形项Item坐标,而且通过pItem拿到对应的item图形项指针!!!!!

获取场景scene中所有Item图形项指针:

QList<QGraphicsItem*> allItems = scene->items();

每个图形项都有一个type标识,可以根据这个标识判断该图形项是图片,矩形,圆等等:

if(allItems[0]->type() == QGraphicsLineItem::Type)
  //直线
else if(allItems[0]->type() == QGraphicsRectItem::Type)
  //  矩形
else if(allItems[0]->type() == QGraphicsSimpleTextItem::Type)
  //  文本
//......

3、在场景scene中绘制不同图形项

有时候我们会遇到这样一个问题:

当我们重新继承一个图形项的类比如QGraphicsRectItem,在自定义类中添加更多的关于矩形的功能,创建矩形对象时却一直画不出矩形?

需要把自定义类强转为QGraphicsRectItem类在设置绘制参数即可画出矩形,如下所示。

通过如上方式画矩形,QGraphicsRectItem会调用自己的paint事件画出矩形,当drawRect继承QGraphicsRectItem,同时又重写了paint方法,假如在drawRect的paint事件里直接return,用上面方法画矩形后会调用drawRect的paint方法然后直接return,当然无法画出矩形。如果重写paint方法又想在重写的paint方法中调用QGraphicsRectItem的paint方法,可以在paint方法中加上

QGraphicsRectItem::paint(painter, option, widget);

这样可以在return之前调用父类的绘画方法画出矩形。

如果在drawRect类中重写paint方法,当我实现了用鼠标画矩形的功能,画出来的矩形用鼠标在矩形上点一下却拿不到矩形的item指针(如下)

原因在于drawRect继承了QGraphicsRectItem,想要显示矩形并能拿到item指针,需要在paint中把drawRect强转为QGraphicsRectItem指针,再把更新的矩形数据QRect设置到QGraphicsRectItem中,那么paint中最后面应该加上如下代码:

另一种原因看不到绘制的图形项:

这里先创建一个矩形设置到sence中,然后再把图片设置到sence,运行后只看到图片看不到画的矩形原因在于sence中的items会根据additem的顺序展示出来,运行程序后先展示矩形,然后展示图片,但图片会覆盖矩形,所以只能看到图片,并不是矩形没有了,如果先additem图片在additem矩形,这样先展示图片,然后会在图片上画出矩形,这样可以正确显示。

4、其他一些补充:

(1)界面变化触发resizeEvent事件,想要图像实时在scene正中央需要在resizeEvent中设置:

m_Scene->setSceneRect(m_PixmapItem->boundingRect());

(2)如果在构造中创建矩形或图片并显示(图片显示m_PixmapItem->setPixmap(pixmap);),运行程序后会显示在view的左上角,这时sence和item的(0,0)点在view的左上角。

如果先创建一个矩形不设置坐标和宽高先创建出来additem到sence,然后等界面运行后通过点击按钮item->setRect(0,0,100,100),这样矩形会显示在view正中间。

(3)对一个item图元可设置焦点,设置可选中,设置可移动

item->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsSelectable |
                   QGraphicsItem::ItemIsMovable );

(4)如果图片很大直接用QPixmap加载有可能在界面上显示不出来,可以先把图片加载成QImage类型,然后再把QImage转为QPixmap即可显示。或者设置图片缓存为0,QPixmapCache::setCacheLimit(0)这个方法有待验证。

(5)图形项的各种事件

void mousePressEvent(QGraphicsSceneMouseEvent* event) 
void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
void hoverEnterEvent(QGraphicsSceneHoverEvent* event);         //鼠标移入图形项
void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);         //鼠标移出图形项
void contextMenuEvent(QGraphicsSceneContextMenuEvent* event);  //右击菜单事件

需要注意的是图形项中的鼠标移动事件mouseMoveEvent必须鼠标在图形项上按下移动才能触发,这里没有setMouseTrack设置。使用鼠标在图形项上移入移出事件,需要在构造中加入setAcceptHoverEvents(true);

还有一点需要注意:想要图形项中的事件有相应,必须在继承GraphicsView类中写好对应的事件并在事件中调用父事件使事件透传到item,比如在界面画了一个矩形,想要在矩形上右键进入contextMenuEvent事件,必须在继承的GraphicsView类中重写contextMenuEvent事件,并在事件中调用父类事件QGraphicsView::contextMenuEvent(event);


相关文章
|
6月前
|
Web App开发 编解码 安全
视频会议技术 入门探究:WebRTC、Qt与FFmpeg在视频编解码中的应用
视频会议技术 入门探究:WebRTC、Qt与FFmpeg在视频编解码中的应用
626 4
|
6月前
|
开发框架 Linux API
Qt:构建高效且用户友好的跨平台应用
Qt:构建高效且用户友好的跨平台应用
|
6月前
|
开发框架 网络协议 数据库
Qt:构建跨平台应用的强大框架
Qt:构建跨平台应用的强大框架
|
6月前
|
安全 前端开发 C++
C++视角下的Qt按钮:从基础应用到高级定制(二)
C++视角下的Qt按钮:从基础应用到高级定制
130 2
|
6月前
|
XML JSON 算法
C++视角下的Qt按钮:从基础应用到高级定制(一)
C++视角下的Qt按钮:从基础应用到高级定制
434 2
|
6月前
|
算法 数据可视化 程序员
【Qt UI】调色板QPalette类在Qt编程中的应用
【Qt UI】调色板QPalette类在Qt编程中的应用
190 0
|
6月前
|
存储 算法 API
【Qt 基本类】QDateTime类在C++中的应用与深度解析
【Qt 基本类】QDateTime类在C++中的应用与深度解析
225 0
|
6月前
|
算法 Unix 调度
【Qt 线程】深入探究QThread线程优先级:原理、应用与最佳实践
【Qt 线程】深入探究QThread线程优先级:原理、应用与最佳实践
507 0
|
6月前
|
Web App开发 编解码 安全
【WebRTC 入门教程】全面解析WebRTC:从底层原理到Qt和FFmpeg的集成应用
【WebRTC 入门教程】全面解析WebRTC:从底层原理到Qt和FFmpeg的集成应用
2891 1
|
3月前
|
API 开发工具 C语言
C语言与图形界面:利用GTK+、Qt等库创建GUI应用。
C语言与图形界面:利用GTK+、Qt等库创建GUI应用。
177 0