I'm developing with Qt-5.15.1. I want to customize QGraphicsItem
, and in this customized item one rectangle and some surrounding circles added. The little circles will show when the mouse is hovering on that rectangle. So I reimplement the hoverEnterEvent
and hoverLeaveEvent
function to receive mouse hover event, please refer to minimal example
.
Then, in paint
event I can determine whether draw circles or not based on _mouseEnter
.
Here comes the problem, I found the hoverEnterEvent
will triggered as soon as mouse enter the border of that rectangle, however quickly hoverLeaveEvent
is also triggered as mouse go through the border, being near the center of rectangle. Seems the border is the entity of mouse hover event, not the filled rectangle. So I can only show circles on when mouse is hovering on the border of that rectangle.
I don't know if I miss something? In my opinion, shape()
and boundingRect()
will affect these mouse events on when event happens? I want to make shape()
to return a filled rectangle of QPainterPath
, but don't know how to.
Update: minimal example
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();
}
CodePudding user response:
QPainterPathStroker generates a hollow region around the rectangle so as soon as that border passes it is already out of the shape. The solution is to join that edge with the inner region:
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;
}
CodePudding user response:
Or just adjust the bounding box rectangle to accommodate the extra size.
QPainterPath CustomizeItem::shape() const
{
QPainterPath path;
path.addRect(_bbox.adjusted(-5, -5, 5, 5));
return path;
}
Since your "hit" shape is rectangular, you can probably just re-implement boundingRect()
, since the default QGraphicsItem::shape()
actually uses boundingRect()
result (according to docs).
QRectF CustomizeItem::boundingRect() const
{
return QRectF(_bbox.adjusted(-5, -5, 5, 5));
}