c++qt5qmouseeventrubber-band

How to constrain a QRubberBand within a QLabel?


I am building a Qt5 application that allows a user to draw a rubberband with his mouse over an image, to select certain area of the image for further processing.

I got my code to only allow the user to start drawing the rubberband, by subclassing a QLabel to a custom class (frame_displayer) which mousePressEvent() is overridden and thus be only invoked when the mouse press happens within the custom classed widget.

The problem is that when the initial click is inside the frame_displayer, mouseMoveEvent(), the function that I use to change the rubberband size accordingly, keeps getting called even when the mouse cursor has been dragged outside of the frame_displayer.

I have tried using leaveEvent() and enterEvent() to control a class boolean flag that codes inside mouseMoveEvent could rely on to know whether the cursor is still within the widget. However, both leaveEvent() and enterEvent() are only called while a mouse button is not being held, thus rendering them no use for constraining the rubberband.

Also, the underMouse() always return true, for a reason unknown to me.

Segment of frame_displayer.cpp

frame_displayer::frame_displayer(QWidget * parent) : QLabel(parent)
{
    _rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
}

void frame_displayer::mousePressEvent(QMouseEvent *event)
{
    _lastClickedBtn = event->button();
    if (_lastClickedBtn == Qt::LeftButton)
    {
        _mouseOriginClickPoint = event->pos();
        _rubberBand->setGeometry(QRect(_mouseOriginClickPoint, _mouseClickPoint));
        _rubberBand->show();
    }
}

void frame_displayer::mouseMoveEvent(QMouseEvent *event)
{
    if(_rubberBand != nullptr)
    {
        if (this->underMouse())
        {
            if (_lastClickedBtn == Qt::LeftButton)
            {
                QPoint mouseCurrentPoint = event->pos();
                _rubberBand->setGeometry(QRect(_mouseOriginClickPoint, mouseCurrentPoint).normalized());
            }
        }
    }
}

void frame_displayer::mouseReleaseEvent(QMouseEvent *event)
{
    _mouseOriginClickPoint = QPoint();
    _lastClickedBtn = Qt::MidButton;
    if(_rubberBand != nullptr)
    {
        _rubberBand->hide();
        _rubberBand->clearMask();
    }
}

void frame_displayer::leaveEvent(QEvent *event)
{
    qDebug() << "Leaving";
}

void frame_displayer::enterEvent(QEvent *event)
{
    qDebug() << "Entering";
}

Thanks in advance!


Solution

  • I think that's the expected behaviour. If you want to limit the extents of the rubber band then simply clamp them in the mouseMoveEvent override...

    void frame_displayer::mouseMoveEvent(QMouseEvent *event)
    {
        if(_rubberBand != nullptr)
        {
            if (this->underMouse())
            {
                if (_lastClickedBtn == Qt::LeftButton)
                {
                    QPoint mouseCurrentPoint = event->pos();
    
                    /*
                     * Clamp mouseCurrentPoint to the QRect of this widget.
                     */
                    auto clamp_rect = rect();
                    mouseCurrentPoint.rx() = std::min(clamp_rect.right(), std::max(clamp_rect.left(), mouseCurrentPoint.x()));
                    mouseCurrentPoint.ry() = std::min(clamp_rect.bottom(), std::max(clamp_rect.top(), mouseCurrentPoint.y()));
                    _rubberBand->setGeometry(QRect(_mouseOriginClickPoint, mouseCurrentPoint).normalized());
                }
            }
        }
    }