QGraphicsScene管理QGraphicsItem(单击/选择/移动/缩放/删除)

前言

本文参考博文https://blog.csdn.net/liang19890820/article/details/53504323

简述

在图形视图框架中,QGraphicsScene 提供一个快速的接口,用于管理大量 item,QGraphicsItem 是场景中 item 的基类。

图形视图提供了一些典型形状的标准 item,当然,我们也可以自定义 item。除此之外,QGraphicsItem 还支持以下特性:

​ 鼠标按下、移动、释放和双击事件,以及鼠标悬浮事件、滚轮事件和上下文菜单事件
​ 键盘输入焦点和键盘事件
​ 拖放
​ 分组:通过父子关系,或 QGraphicsItemGroup
​ 碰撞检测
下面,一起来看看 QGraphicsScene 对 QGraphicsItem 的管理,主要包括:单击、选择、移动、缩放、删除等。

操作细节

为了实现以上功能,我们主要实现了 QGraphicsScene 和 QGraphicsItem 对应的事件,通过鼠标和键盘来操作。

操作细节主要包括:

​ 选择:点击左键、按 Shift 键可以单选,按下 Ctrl 可进行多选。
​ 添加:点击左键
​ 删除:点击右键,删除鼠标下的 item;当按下 Ctrl 选择多个 items 时,按下 Backspace 键,将选中的全部删除。
​ 移动:点击左键,选择 item,然后移动鼠标;当按下 Ctrl 选择多个 items 时,可以移动选中的 items。
​ 缩放:按 Alt 键,然后鼠标拖拽 item 的边界。
在对应操作的事件中,我们输出了一些调试信息,以便跟踪。

示例

效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HT9LyJRa-1592192168113)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1592191916471.png)]

源码

CustomItem.h

#ifndef CUSTOM_ITEM_H
#define CUSTOM_ITEM_H

#include <QGraphicsRectItem>
#include <QGraphicsScene>

//QGraphicsScene管理QGraphicsItem(单击/选择/移动/缩放/删除)
// 自定义 Item
class CustomItem : public QGraphicsRectItem
{
public:
	explicit CustomItem(QGraphicsItem *parent = 0);
protected:
	// Shift+左键:进行选择  Alt:准备缩放
	void mousePressEvent(QGraphicsSceneMouseEvent *event);
	// Alt+拖拽:进行缩放  移动
	void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
	void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
	// 使item可使用qgraphicsitem_cast
	int type() const;
private:
	QPointF m_centerPointF;
	bool m_bResizing;
};

// 自定义 Scene
class CustomScene : public QGraphicsScene
{
protected:
	// 左键:添加item  右键:移除item
	void mousePressEvent(QGraphicsSceneMouseEvent *event);
	void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
	// Backspace键移除item
	void keyPressEvent(QKeyEvent *event);
};

#endif // CUSTOM_ITEM_H

CustomItem.cpp

#include <QKeyEvent>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include "customitem.h"

// 自定义 Item
CustomItem::CustomItem(QGraphicsItem *parent)
	: QGraphicsRectItem(parent)
{
	// 画笔 - 边框色
	QPen p = pen();
	p.setWidth(2);
	p.setColor(QColor(0, 160, 230));

	setPen(p);
	// 画刷 - 背景色
	setBrush(QColor(247, 160, 57));

	// 可选择、可移动
	setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
}

void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
	if (event->button() == Qt::LeftButton) {
		if (event->modifiers() == Qt::ShiftModifier) {
			qDebug() << "Custom item left clicked with shift key.";
			// 选中 item
			setSelected(true);
		}
		else if (event->modifiers() == Qt::AltModifier) {
			qDebug() << "Custom item left clicked with alt key.";
			// 重置 item 大小
			double radius = boundingRect().width() / 2.0;
			QPointF topLeft = boundingRect().topLeft();
			qDebug() << "WWW" << pos() << boundingRect();
			m_centerPointF = QPointF(topLeft.x() + pos().x() + radius, topLeft.y() + pos().y() + radius);
			QPointF pos = event->scenePos();
			qDebug() << boundingRect() << radius << this->pos() << pos << event->pos();
			double dist = sqrt(pow(m_centerPointF.x() - pos.x(), 2) + pow(m_centerPointF.y() - pos.y(), 2));
			if (dist / radius > 0.8) { //判断 鼠标拖拽item的边界 是否为边界
				qDebug() << dist << radius << dist / radius;
				m_bResizing = true;
			}
			else {
				m_bResizing = false;
			}
		}
		else {
			qDebug() << "Custom item left clicked.";
			QGraphicsItem::mousePressEvent(event);
			event->accept();
		}
	}
	else if (event->button() == Qt::RightButton) {
		qDebug() << "Custom item right clicked.";
		event->ignore();
	}
}

void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
	if ((event->modifiers() == Qt::AltModifier) && m_bResizing) {
		QPointF pos = event->scenePos();
		double dist = sqrt(pow(m_centerPointF.x() - pos.x(), 2) + pow(m_centerPointF.y() - pos.y(), 2));
		setRect(m_centerPointF.x() - this->pos().x() - dist, //位置和大小
			m_centerPointF.y() - this->pos().y() - dist, 
			dist * 2, dist * 2);
	}
	else if (event->modifiers() != Qt::AltModifier) {
		qDebug() << "Custom item moved.";
		QGraphicsItem::mouseMoveEvent(event);
		qDebug() << "moved" << pos();
	}
}

void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
	if ((event->modifiers() == Qt::AltModifier) && m_bResizing) {
		m_bResizing = false;
	}
	else {
		QGraphicsItem::mouseReleaseEvent(event);
	}
}

int CustomItem::type() const
{
	return UserType + 1;
}

// 自定义 Scene
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
	qDebug() << "Custom scene clicked.";
	QGraphicsScene::mousePressEvent(event);
	if (!event->isAccepted()) {
		if (event->button() == Qt::LeftButton) {
			// 在 Scene 上添加一个自定义 item
			QPointF point = event->scenePos();
			CustomItem *item = new CustomItem();
			item->setRect(point.x() - 25, point.y() - 25, 60, 60);
			addItem(item);
		}
		else if (event->button() == Qt::RightButton) {
			// 检测光标下是否有 item
			QGraphicsItem *itemToRemove = NULL;
			foreach(QGraphicsItem *item, items(event->scenePos())) {
				if (item->type() == QGraphicsItem::UserType + 1) {
					itemToRemove = item;
					break;
				}
			}
			// 从 Scene 上移除 item
			if (itemToRemove != NULL)
				removeItem(itemToRemove);
		}
	}
}

void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
	qDebug() << "Custom scene moved.";
	QGraphicsScene::mouseMoveEvent(event);
}

void CustomScene::keyPressEvent(QKeyEvent *event) {
	if (event->key() == Qt::Key_Backspace) {
		// 移除所有选中的 items
		qDebug() << "selected items " << selectedItems().size();
		while (!selectedItems().isEmpty()) {
			removeItem(selectedItems().front());
		}
	}
	else {
		QGraphicsScene::keyPressEvent(event);
	}
}

main.cpp

#include <QApplication>
#include <QGraphicsView>
#include "customitem.h"

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);

	// 创建 item
	CustomItem *pItem = new CustomItem();
	pItem->setRect(20, 20, 60, 60);

	// 将 item 添加至场景中
	CustomScene scene;
	scene.setSceneRect(0, 0, 400, 300);
	scene.addItem(pItem);

	// 为视图设置场景
	QGraphicsView view;
	view.setScene(&scene);
	view.show();
	view.resize(400, 300);

	return a.exec();
}

疑问自解自答:

在main.cpp中

// 创建 item
	CustomItem *pItem = new CustomItem();
	pItem->setRect(20, 20, 60, 60);

我们没有给pItem->setpos(),这样默认item在场景中的位置为(0,0),

即item坐标的原点(0,0点) 对应于 场景中的(0,0)点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kDQdtTiO-1592217793016)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1592217132959.png)]

那么正方形旋转参照的是哪个点那?

// 创建 item
	CustomItem *pItem = new CustomItem();
	pItem->setRect(100, 20, 60, 60);	

	//测试
	CustomItem *pItem1 = new CustomItem();
	pItem1->setRect(100, 20, 60, 60);
	pItem1->setRotation(45); //旋转45°

	// 将 item 添加至场景中
	CustomScene scene;
	scene.setSceneRect(0, 0, 400, 300);
	scene.addItem(pItem);
	scene.addItem(pItem1);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hBmT1r4a-1592217793026)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1592217664378.png)]
请添加图片描述

参考点为item坐标系中的原点(0,0)

标题CustomItem::mouseMoveEvent()中的逻辑:

请添加图片描述

  • 9
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值