自定义QGraphicsItem
来源:互联网 发布:阿里云固定电话 编辑:程序博客网 时间:2024/05/21 21:59
简述
QGraphicsItem 是场景中 item 的基类。图形视图提供了一些典型形状的标准 item,例如:矩形 ( QGraphicsRectItem )、椭圆 ( QGraphicsEllipseItem ) 、文本项 ( QGraphicsTextItem )。当这些不满足需求时(例如:在一些复杂的工作流场景中),往往需要自定义,通常的做法就是继承 QGraphicsItem。
- 简述
- 自定义 QGraphicsItem
- Bounding Rect 和 Shape
- 参照模型
- 使用示例
- 效果
- 源码
自定义 QGraphicsItem
要实现自定义 item,需要覆盖 QGraphicsItem 的两个纯虚函数:
- void paint()
- 以本地坐标绘制 item 的内容
- QRectF boundingRect()
- 将 item 的外边界作为矩形返回
- 由 QGraphicsView 调用以确定什么区域需要重绘
除此之外,可能还需要附加其他需求,例如:
- QPainterPath shape() - item 的形状
- 由 contains() 和 collidesWithPath() 用于碰撞检测
- 如果未实现,则默认为 boundingRect()
- 使用信号/槽、属性机制:继承 QObject 和 QGraphicsItem(或直接继承 QGraphicsObject)
- 处理鼠标事件:重新实现 mouse***Event()
- 处理键盘事件:重新实现 key***Event()
- 处理拖放事件:重新实现 drag***Event()、dropEvent()
……
关于信号/槽、事件、算法相关的内容,本节暂时不做讲解,放到后面章节。
Bounding Rect 和 Shape
先来一张效果图,解释 Bounding Rect 和 Shape 的联系与区别:
Bounding Rect
将 item 的外边界定义为矩形,所有绘制必须限制在此区域内,QGraphicsView 使用它来确定 item 是否需要重绘。
虽然 item 的形状可以是任意的(例如:直线、椭圆、矩形 ),但是 bounding rect 总是矩形,并且不受 item 变换的影响。
Shape
以本地坐标中的 QPainterPath 形式返回 item 的形状。形状可用于许多事情,包括:碰撞检测,命中测试以及 QGraphicsScene::items() 函数。
shape() 默认实现调用 boundingRect() 返回一个简单的矩形形状,但子类可以重新实现该函数,以返回非矩形 item 更准确的形状。例如,一个圆形 item 可以选择返回椭圆形状,以便更好地进行碰撞检测。
shape() 由 contains() 和 collidesWithPath() 的默认实现调用。
参照模型
来一个笑脸,瞬间萌萌哒……笑一笑,十年少!
要实现这个效果很简单,可以逐步分解:
- 整体(最外侧的圆)
- 眼睛(左眼/右眼/眼球)
- 嘴(笑容)
分别计算出各部分的区域坐标、大小,然后根据形状进行绘制。
上述图案标识的是绝对位置,为了适应各种大小, 可以进行比例及相对位置换算,将各部分进行逐一转换。
使用示例
效果
下图显示了 3 个不同大小的笑脸:
源码
根据以上思路,我们可以很快的实现一个自定义的笑脸 - SmileItem。
SmileItem.h:
#ifndef SMILE_ITEM_H#define SMILE_ITEM_H#include <QGraphicsItem>class SmileItem : public QGraphicsItem{public: explicit SmileItem(QGraphicsItem *parent = Q_NULLPTR); explicit SmileItem(const QRectF &rect, QGraphicsItem *parent = Q_NULLPTR); explicit SmileItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = Q_NULLPTR); ~SmileItem(); QRectF rect() const; void setRect(const QRectF &rect); inline void setRect(qreal x, qreal y, qreal w, qreal h); QRectF boundingRect() const Q_DECL_OVERRIDE; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) Q_DECL_OVERRIDE;private: void updateRect();private: QRectF m_rect; mutable QRectF m_boundingRect; // 缩放比例 double m_dScale; // 左眼、右眼、嘴的中点 QPointF m_leftEyeCenter; QPointF m_rightEyecenter; QPointF m_smileCenter; // 眼睛的宽度、高度 double m_dEyeWidth; double m_dEyeHeight; // 眼球宽度(高和宽相同) double m_dEyeBallWidth; // 嘴的高度、宽度 double m_dSmileWidth; double m_dSmileHeight;};inline void SmileItem::setRect(qreal ax, qreal ay, qreal w, qreal h){ setRect(QRectF(ax, ay, w, h));}#endif //SMILE_ITEM_H
除了上述必须实现的两个函数之外,我们还提供一些额外的接口,例如:setRect() 来更改 item 的大小,在更新大小之后,则会调用 updateRect() 来重新计算笑脸中各个部位的坐标、大小。
SmileItem.cpp:
#include <QPainter>#include "SmileItem.h"SmileItem::SmileItem(QGraphicsItem *parent) : QGraphicsItem(parent){ setRect(QRect(-50, -50, 100, 100));}SmileItem::SmileItem(const QRectF &rect, QGraphicsItem *parent) : QGraphicsItem(parent){ setRect(rect);}SmileItem::SmileItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent) : QGraphicsItem(parent){ setRect(x, y, w, h);}SmileItem::~SmileItem(){}QRectF SmileItem::rect() const{ return m_rect;}void SmileItem::setRect(const QRectF &rect){ if (m_rect == rect) return; prepareGeometryChange(); m_rect = rect; m_boundingRect = QRectF(); updateRect(); update();}QRectF SmileItem::boundingRect() const{ if (m_boundingRect.isNull()) m_boundingRect = m_rect; return m_boundingRect;}void SmileItem::updateRect(){ // 缩放比例 m_dScale = m_rect.width() / 100.0; // 左眼的中点 m_leftEyeCenter.setX(-15 * m_dScale); m_leftEyeCenter.setY(- 25 * m_dScale); // 右眼的中点 m_rightEyecenter.setX(15 * m_dScale); m_rightEyecenter.setY(- 25 * m_dScale); // 嘴的中点 m_smileCenter.setX(0); m_smileCenter.setY(10 * m_dScale); // 眼睛的宽度、高度(宽度的 2 倍) m_dEyeWidth = m_rect.width() / (100.0 / 12); m_dEyeHeight = m_dEyeWidth * 2; // 眼球为眼睛大小的 1/4 m_dEyeBallWidth = m_dEyeWidth / 4; // 嘴的高度、宽度 m_dSmileWidth = m_rect.width() / (100.0 / 66); m_dSmileHeight = m_rect.height() / (100.0 / 50);}void SmileItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){ Q_UNUSED(option); Q_UNUSED(widget); // 反走样 painter->setRenderHint(QPainter::Antialiasing, true); // 脸 painter->setPen(Qt::NoPen); painter->setBrush(Qt::yellow); painter->drawEllipse(m_rect); // 左眼 painter->setPen(QPen(Qt::black)); painter->setBrush(Qt::white); painter->drawEllipse(QRectF(m_leftEyeCenter.x() - m_dEyeWidth / 2, m_leftEyeCenter.y() - m_dEyeHeight / 2, m_dEyeWidth, m_dEyeHeight)); // 左眼球 painter->setPen(QPen(Qt::black)); painter->setBrush(Qt::black); painter->drawEllipse(QRectF(m_leftEyeCenter.x() - m_dEyeBallWidth / 2, m_leftEyeCenter.y() - m_dEyeBallWidth / 2, m_dEyeBallWidth / 2, m_dEyeBallWidth / 2)); // 右眼 painter->setPen(QPen(Qt::black)); painter->setBrush(Qt::white); painter->drawEllipse(QRectF(m_rightEyecenter.x() - m_dEyeWidth / 2, m_rightEyecenter.y() - m_dEyeHeight / 2, m_dEyeWidth, m_dEyeHeight)); // 右眼球 painter->setPen(QPen(Qt::black)); painter->setBrush(Qt::black); painter->drawEllipse(QRectF(m_rightEyecenter.x() - m_dEyeBallWidth / 2, m_rightEyecenter.y() - m_dEyeBallWidth / 2, m_dEyeBallWidth / 2, m_dEyeBallWidth / 2)); // 嘴 - 笑容 painter->setPen(QPen(Qt::red)); painter->setBrush(Qt::NoBrush); QPainterPath path; path.arcMoveTo(QRectF(- m_dSmileWidth / 2, - (m_dSmileHeight / 2 - m_smileCenter.y()), m_dSmileWidth, m_dSmileHeight), 0); path.arcTo(QRectF(- m_dSmileWidth / 2, - (m_dSmileHeight / 2 - m_smileCenter.y()), m_dSmileWidth, m_dSmileHeight), 0, -180); painter->drawPath(path);}
正如 setRect(),无论以任何方式更改 item 的几何形状,必须首先调用prepareGeometryChange(),以保证 QGraphicsScene 中的索引是最新的。
为了实现大小的自适应,在 updateRect() 中实现了坐标、大小的换算。并通过调用 update() 重新对 item 进行绘制。
然后就可以使用了:
// 定义笑脸SmileItem *pItem = new SmileItem();pItem->setRect(QRect(-25, -25, 50, 50));pItem->setPos(10, 50);SmileItem *pItem2 = new SmileItem();pItem2->setRect(QRect(-50, -50, 100, 100));pItem2->setPos(100, 50);SmileItem *pItem3 = new SmileItem();pItem3->setRect(QRect(-75, -75, 150, 150));pItem3->setPos(250, 50);// 将笑脸添加至场景中QGraphicsScene *pScene = new QGraphicsScene();pScene->addItem(pItem);pScene->addItem(pItem2);pScene->addItem(pItem3);// 为视图设置场景QGraphicsView *pView = new QGraphicsView();pView->setScene(pScene);pView->setStyleSheet("border:none; background:transparent;");pView->show();
首先,构造三个不同大小的笑脸,并调用 setPos() 设置它们的位置。然后通过 QGraphicsScene::addItem() 将笑脸添加至场景中。最后,调用 QGraphicsView::setScene() 为视图设置场景,并显示视图。
- 自定义QGraphicsItem
- 自定义QGraphicsItem
- 自定义QGraphicsItem选中样式
- 自定义QGraphicsItem选中样式
- 自定义QGraphicsItem的继承顺序
- QGraphicsItem
- QGraphicsItem
- QGraphicsItem
- QGraphicsItem
- QGraphicsItem
- 图形视图框架中自定义QGraphicsItem
- Qt修炼手册7_图形:用户自定义QGraphicsItem
- Qt图形视图框架(三) 自定义QGraphicsItem
- QGraphicsItem类
- QGraphicsItem分组
- QGraphicsItem分组
- Qt实现自定义QGraphicsItem,实现了一个闪烁的圆,和闪烁的文字
- QGraphicsItem::setZValue函数功能
- 错误 编码GBK的不可映射字符
- android studio 下 ffmepg读取视频文件信息
- 双十一感想
- Linux启动或禁止SSH用户及IP的登录,只允许密钥验证登录模式
- 仿今日头条视频播放JieCaoVideoPlayer
- 自定义QGraphicsItem
- 编程必了解的概念HTML、XML、XHTML、DHTML,WEB前端工程师须知
- php操作数据库的类
- 2016最新微信公众号运营必备网址大全
- 第三阶段-JavaScript模块化
- Linux下实现 MASM汇编
- sqlite3 编写简易通讯录
- Head first HTML&CSS ---[学习笔记第四章]
- 嵌入式系统(摘抄)