QGraphicsItem 的子类仅从边界矩形接收边框上的悬停事件
Posted
技术标签:
【中文标题】QGraphicsItem 的子类仅从边界矩形接收边框上的悬停事件【英文标题】:Subclass of QGraphicsItem only receives hover event on the border from bounding rectangle 【发布时间】:2021-09-20 08:49:06 【问题描述】:我正在使用 Qt-5.15.1 进行开发。我想自定义QGraphicsItem
,并在这个自定义项中添加了一个矩形和一些周围的圆圈。当鼠标悬停在该矩形上时,将显示小圆圈。所以我重新实现了hoverEnterEvent
和hoverLeaveEvent
函数来接收鼠标悬停事件,请参考minimal example
。
然后,在paint
事件中,我可以根据_mouseEnter
判断是否绘制圆圈。
问题来了,我发现hoverEnterEvent
会在鼠标进入该矩形的边框后立即触发,但是很快hoverLeaveEvent
也会在鼠标穿过边框时触发,靠近矩形的中心。似乎边框是鼠标悬停事件的实体,而不是填充的矩形。所以我只能在鼠标悬停在该矩形的边框上时显示圆圈。
我不知道我是否错过了什么?在我看来,shape()
和boundingRect()
会在事件发生时影响这些鼠标事件吗?我想让shape()
返回QPainterPath
的填充矩形,但不知道怎么做。
更新:最小示例
customizeitem.cpp
#include "customizeitem.h"
#include <QDebug>
CustomizeItem::CustomizeItem(QGraphicsItem *parent):
QGraphicsItem(parent),
_bbox(0,0,120, 120),_radius(7),
_mouseEnter(false)
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
setAcceptHoverEvents(true);
_rectRect = QRect(QPoint(_radius*4, _radius*4), QPoint(_bbox.width() - _radius*4, _bbox.height() - _radius*4));
QPointF upCenter(_bbox.width()/2, _radius);
QPointF rCenter(_bbox.width() - _radius, _bbox.height() / 2);
QPointF downCenter(_bbox.width()/2, _bbox.height() - _radius);
QPoint lCenter(_radius, _bbox.height() / 2);
_anchorRects.push_back(QRectF(upCenter, QSizeF(_radius, _radius)));
_anchorRects.push_back(QRectF(rCenter, QSizeF(_radius, _radius)));
_anchorRects.push_back(QRectF(downCenter, QSizeF(_radius, _radius)));
_anchorRects.push_back(QRectF(lCenter, QSizeF(_radius, _radius)));
void CustomizeItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
qInfo() << "mouse enter";
_mouseEnter = true;
update();
QGraphicsItem::hoverEnterEvent(event);
void CustomizeItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
qInfo() << "mouse leave";
_mouseEnter = false;
update();
QGraphicsItem::hoverLeaveEvent(event);
QRectF CustomizeItem::boundingRect() const
return shape().boundingRect();
void CustomizeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
painter->setRenderHint(QPainter::Antialiasing);
drawAnchors(painter);
painter->fillRect(_rectRect, QColor(255, 0, 0));
QPainterPath CustomizeItem::shape() const
QPainterPath path;
path.moveTo(_bbox.topLeft());
path.addRect(_bbox);
QPainterPathStroker stroker;
stroker.setWidth(10);
return stroker.createStroke(path);
void CustomizeItem::drawAnchors(QPainter *painter)
if(_mouseEnter)
for(int i = 0; i < _anchorRects.size(); i++)
QPainterPath path;
path.moveTo(_anchorRects[0].center());
path.addEllipse(_anchorRects[i].center(), _radius, _radius);
painter->drawPath(path);
customizeitem.h
#ifndef CUSTOMIZEITEM_H
#define CUSTOMIZEITEM_H
#include <QGraphicsItem>
#include <QObject>
#include <QPainter>
class CustomizeItem : public QObject, public QGraphicsItem
Q_OBJECT
public:
enum Type = UserType + 1 ;
explicit CustomizeItem(QGraphicsItem *parent = nullptr);
~CustomizeItem() = default;
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *);
QPainterPath shape() const;
private:
void drawAnchors(QPainter *painter);
QRect _bbox;
float _radius; // radius for circle anchor
QVector<QRectF> _anchorRects;
QRect _rectRect;
bool _mouseEnter;
;
#endif // CUSTOMIZEITEM_H
maiwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "customizeitem.h"
#include <QGraphicsView>
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
ui->setupUi(this);
QVBoxLayout *layout = new QVBoxLayout(centralWidget());
layout->setContentsMargins(0,0,0,0);
QGraphicsView *view = new QGraphicsView(this);
view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QGraphicsScene *scene = new QGraphicsScene;
CustomizeItem *item = new CustomizeItem;
scene->addItem(item);
view->setScene(scene);
layout->addWidget(view);
MainWindow::~MainWindow()
delete ui;
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui class MainWindow;
QT_END_NAMESPACE
class MainWindow : public QMainWindow
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
;
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
【问题讨论】:
请提供minimal reproducible example @eyllanesc 嗨~,我应该在哪里以及如何附加我的最小示例?现在,我只是把它们复制到这里,如果不合适,我会删除它们。 阅读链接,同时阅读How to Ask 并查看tour 谢谢,我添加了一个最小的例子。 【参考方案1】:QPainterPathStroker 在矩形周围生成一个空心区域,因此一旦该边框通过,它就已经超出了形状。解决方案是将该边缘与内部区域连接:
QPainterPath CustomizeItem::shape() const
QPainterPath path;
path.moveTo(_bbox.topLeft());
path.addRect(_bbox);
QPainterPathStroker stroker;
stroker.setWidth(10);
QPainterPath strokerPath = stroker.createStroke(path);
strokerPath.addPath(path); // join
return strokerPath;
【讨论】:
很抱歉,由于缺乏声誉而无法投票。 @taptiptop 请查看tour【参考方案2】:或者只是调整边界框矩形以适应额外的尺寸。
QPainterPath CustomizeItem::shape() const
QPainterPath path;
path.addRect(_bbox.adjusted(-5, -5, 5, 5));
return path;
由于您的“命中”形状是矩形,您可能只需重新实现boundingRect()
,因为默认的QGraphicsItem::shape()
实际上使用boundingRect()
结果(根据docs)。
QRectF CustomizeItem::boundingRect() const
return QRectF(_bbox.adjusted(-5, -5, 5, 5));
【讨论】:
试过了,还是不行。我错过了什么吗?以上是关于QGraphicsItem 的子类仅从边界矩形接收边框上的悬停事件的主要内容,如果未能解决你的问题,请参考以下文章
使用 mouseMoveEvent 约束 QGraphicsItem 移动