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
        }
}

简单描述下流程

  1. 设置QGraphicsSceneMouseEvent 相关参数

  2. 事件通过qt_sendSpontaneousEvent(receiver: d->scene, event: &mouseEvent);从而被鼠标事件所在的图元item所处理

    1. 如果event 被 accept,则该事件被完成

    2. 事件被继续传递

  3. 事件识别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();
}


本站由 困困鱼 使用 Stellar 创建。