1. 如何解决QGraphicsItem内的图元无法被RubberBand选中(5.15.2)
在qgraphicsview.cpp的源码中 是这样实现的
void QGraphicsView::mousePressEvent(QMouseEvent *event)
{
Q_D(QGraphicsView);
// Store this event for replaying, finding deltas, and for
// scroll-dragging; even in non-interactive mode, scroll hand dragging is
// allowed, so we store the event at the very top of this function.
d->storeMouseEvent(event);
d->lastMouseEvent.setAccepted(false);
if (d->sceneInteractionAllowed) {
// Store some of the event's button-down data.
d->mousePressViewPoint = event->pos();
d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
d->mousePressScreenPoint = event->globalPos();
d->lastMouseMoveScenePoint = d->mousePressScenePoint;
d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
d->mousePressButton = event->button();
if (d->scene) {
// Convert and deliver the mouse event to the scene.
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);
mouseEvent.setWidget(viewport());
mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
mouseEvent.setScenePos(d->mousePressScenePoint);
mouseEvent.setScreenPos(d->mousePressScreenPoint);
mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
mouseEvent.setButtons(event->buttons());
mouseEvent.setButton(event->button());
mouseEvent.setModifiers(event->modifiers());
mouseEvent.setSource(event->source());
mouseEvent.setFlags(event->flags());
mouseEvent.setAccepted(false);
if (event->spontaneous())
qt_sendSpontaneousEvent(d->scene, &mouseEvent);
else
QCoreApplication::sendEvent(d->scene, &mouseEvent);
// Update the original mouse event accepted state.
bool isAccepted = mouseEvent.isAccepted();
event->setAccepted(isAccepted);
// Update the last mouse event accepted state.
d->lastMouseEvent.setAccepted(isAccepted);
if (isAccepted)
return;
}
}
#if QT_CONFIG(rubberband)
if (d->dragMode == QGraphicsView::RubberBandDrag && !d->rubberBanding) {
if (d->sceneInteractionAllowed) {
// Rubberbanding is only allowed in interactive mode.
event->accept();
d->rubberBanding = true;
d->rubberBandRect = QRect();
if (d->scene) {
bool extendSelection = (event->modifiers() & Qt::ControlModifier) != 0;
if (extendSelection) {
d->rubberBandSelectionOperation = Qt::AddToSelection;
} else {
d->rubberBandSelectionOperation = Qt::ReplaceSelection;
d->scene->clearSelection();
}
}
}
} else
#endif
if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
// Left-button press in scroll hand mode initiates hand scrolling.
event->accept();
d->handScrolling = true;
d->handScrollMotions = 0;
#ifndef QT_NO_CURSOR
viewport()->setCursor(Qt::ClosedHandCursor);
#endif
}
}
简单描述下流程
设置QGraphicsSceneMouseEvent 相关参数
事件通过qt_sendSpontaneousEvent(receiver: d->scene, event: &mouseEvent);从而被鼠标事件所在的图元item所处理
如果event 被 accept,则该事件被完成
事件被继续传递
事件识别QGraphicsView::RubberBandDrag 处理框选事件
因此我们需要在图元的item处理时将事件的状态改为ignore即可
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
QGraphicsItem::mousePressEvent(event);
event->ignore(); // 如果event 被 accept,那么QGraphicsView::mousePressEvent(QMouseEvent *event) 将会被退出,无法执行内部的框选事件
}
2. 对QGraphicsItem限制拖拽范围,且鼠标从区域外重新拖回到区域内时,仍然可以保存鼠标在限制区域中 相对于图元的位置
.h
#pragma once
#include <QGraphicsItem>
class MoveAbleItem : public QGraphicsItem {
public:
MoveAbleItem(QGraphicsItem *parent = nullptr);
~MoveAbleItem() override;
private:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
QRectF boundingRect() const override{
return QRectF(-10,-10,20,20);
}
QRectF limitRectF() const{ return {0,0,2000,2000};}
void refreshData();
private:
QPointF centerPos;
bool m_bLeftButtonPressed;
bool m_bMoving;
QPointF m_pressStartPos;
};
.cpp
#include "MoveAbleItem.h"
#include <QGraphicsSceneMouseEvent>
#include <QApplication>
#include <QPainter>
MoveAbleItem::MoveAbleItem(QGraphicsItem *parent) : QGraphicsItem(parent) {
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);
}
MoveAbleItem::~MoveAbleItem() {
}
void MoveAbleItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
m_bLeftButtonPressed = event->button() == Qt::LeftButton;
m_bMoving = false;
QGraphicsItem::mousePressEvent(event);
if(!m_bLeftButtonPressed)
return;
m_pressStartPos = event->scenePos();
}
void MoveAbleItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
if(!m_bLeftButtonPressed)
return;
if(!m_bMoving){
if((event->scenePos() - m_pressStartPos).manhattanLength() > QApplication::startDragDistance()){
m_bMoving = true;
}
if (!m_bMoving)
return;
}
QGraphicsItem::mouseMoveEvent(event);
refreshData();
}
void MoveAbleItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
if(!m_bLeftButtonPressed)
return QGraphicsItem::mouseReleaseEvent(event);
if (m_bMoving) {
QGraphicsItem::mouseReleaseEvent(event);
return refreshData();
}
return QGraphicsItem::mouseReleaseEvent(event);
}
void MoveAbleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
painter->save();
QPen pen = painter->pen();
pen.setColor(Qt::black);
painter->setPen(pen);
painter->drawRect(boundingRect());
painter->restore();
}
QVariant MoveAbleItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) {
if(change == QGraphicsItem::ItemPositionChange){
QPointF newPos = value.toPointF();
auto limit = limitRectF();
if(!limit.contains(newPos)){
QPointF newCenterPos = newPos;
if(newPos.x() < limit.left()) {
newCenterPos.setX(limit.left());
}
if(newPos.x() > limit.right()) {
newCenterPos.setX(limit.right());
}
if(newPos.y() < limit.top()) {
newCenterPos.setY(limit.top());
}
if(newPos.y() > limit.bottom()) {
newCenterPos.setY(limit.bottom());
}
return newCenterPos;
}
}
return QGraphicsItem::itemChange(change, value);
}
void MoveAbleItem::refreshData() {
centerPos = pos();
}