本文参考博文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 的边界。
在对应操作的事件中,我们输出了一些调试信息,以便跟踪。
CustomItem.h
#ifndef CUSTOM_ITEM_H
#define CUSTOM_ITEM_H
#include
#include
//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
#include
#include
#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
#include
#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)点
// 创建 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);
参考点为item坐标系中的原点(0,0)