PDA

View Full Version : QwtPlotPanner/QwtPlotZoomer issue when the plot loses the focus



Alekon
14th October 2020, 10:45
QwtPanner: while it is active (isVisible() == true), it must handle FocusOut event to ends up panning. Consider context menu invokation with ContextMenu key while panning - context menu grabs the focus and subsequent mouse release event (if user picks some item from the menu), so when returning back to the plot's canvas widget there will be inconsistent state due to the panner thinks it still active, while there is no longer mouse button held. We also may have Qt::ClosedHandCursor indicating panning (if we applied QwtPanner::setCursor(Qt::ClosedHandCursor)), but we no longer have actual panning (no MouseMove events occur due to no mouse button held).

I think the same is applicable to QwtPlotZoomer.

QwtPanner: while it activates at certain mouse button press and certain keyboard modifiers (QwtPanner::setMouseButton()), it ends at any mouse button release and any keyboard modifiers. IMHO, for sake of simmetry (and simplicity) ending combination must be the same as beginning.

Uwe
15th October 2020, 05:53
Focus events are for the keyboard - not the mouse. When setting a Qt::NoFocus policy to the canvas it will never have the focus - even when using the mouse on it.
Guess what you are thinking of is what is propagated as QEvent::UngrabMouse for QQuickItem/QGraphicsItem.

Uwe

Alekon
15th October 2020, 21:36
Hi, Uwe. Thanks for the responce.

Yes, Qt::NoFocus is an option for the plot canvas. But it has no meaning. Key moment - panner ends up at mouse release event in assumption that mouse was held all the time, but context menu steals mouse release event, mouse is no longer held (and panner will no longer receive MouseMove events as it expects), but the panner don't know about things happen. It's still active (isVisible()==true), but no longer works. A visible artefact - panner still shows override cursor (e.g. Qt::ClosedHandCursor).

My initial question was a narrow question in assumption that plot canvas is focus-aware. I was not about QEvent::UngrabMouse for QQuickItem/QGraphicsItem.

Uwe
18th October 2020, 11:42
I was not about QEvent::UngrabMouse for QQuickItem/QGraphicsItem.
I had a quick look at the implementation of QWidget::grabMouse + releaseMouse and it looks like the current mouse grabber is stored in global variable qt_mouseGrb. You have access to it using QWidget::mouseGrabber, but there seems to be no indication from Qt about when the value of the mouseGrabber is changing ( this is what the UngrabMouse event is about for the other systems ).

So maybe what can be done is to do "QWidget::mouseGrabber() == this" checks in the plot canvas to identify a loss of being the mouse grabber without having a proper mouse release event. Not 100% sure what type events would be those where this check has to be performed.

Uwe

Alekon
18th October 2020, 14:01
Thanks for advice.

Currently, I just handle FocusOut event in the eventFilter of plot's canvas widget. It's sufficient for my particular case right now. The code something like this (the code applies panning and ends up it correctly, and cancels zooming and ends up it correctly):

...
else if (QEvent::FocusOut == event->type()) {
// Apply and end up panning
if (qwtpltpnMain_->isVisible()) {
Qt::MouseButton pannerButton;
Qt::KeyboardModifiers pannerModifiers;
qwtpltpnMain_->getMouseButton(pannerButton, pannerModifiers);

// Force QwtPlotPanner ends up via sending the proper event. This event after
// processing by QwtPlotPanner appears here, due to QwtPlotPanner propagates it
// further, but that event does not trigger any action (e.g. cursor moving) due to
// pannerModifiers in the event.
QMouseEvent endPanning(QEvent::MouseButtonRelease,
qwtpltMain_->canvas()->mapFromGlobal(QCursor::pos()), pannerButton,
pannerButton, pannerModifiers);
QCoreApplication::sendEvent(qwtpltMain_->canvas(), &endPanning);

//##note: Aborting panning might be done with this event:
//QKeyEvent endPanning(QEvent::KeyPress, pannerKey, pannerModifiers);
//QCoreApplication::sendEvent(qwtpltMain_->canvas(), &endPanning);
}

// Cancel zooming
if (qwtpltzmMain_->isActive()) {
// Force QwtPlotZoomer cancels via sending the proper event. This event after
// processing by QwtPlotZoomer appears here, due to QwtPlotZoomer propagates it
// further.
//##assume: Qt::Key_Escape is QwtEventPattern::KeyAbort.
QKeyEvent endZooming(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier);
Q_ASSERT(qwtpltzmMain_->keyMatch(QwtEventPattern::KeyAbort, &endZooming));
QCoreApplication::sendEvent(qwtpltMain_->canvas(), &endZooming);

//##note: Applying zooming and ending might be done with this event:
//##assume: Qt::LeftButton is QwtEventPattern::MouseSelect1.
//QMouseEvent endZooming(QEvent::MouseButtonRelease,
// qwtpltMain_->canvas()->mapFromGlobal(QCursor::pos()), Qt::LeftButton,
// Qt::LeftButton, Qt::NoModifier);
// Q_ASSERT(qwtpltzmMain_->mouseMatch(QwtEventPattern::MouseSelect1, &endZooming));
}
...


Added after 17 minutes:

Probably, it might be sufficient to handle QEvent::Leave event, which occurs regardsless of whether a widget can get focus or cannot (NoFocus policy). I.e. QwtPlotPannel/QwtPlotZoomer might handle QEvent::Leave event and do there graceful ending up (applying for panning and cancelling for zooming, I beleive).