Unsolved invalidateScene() problem
-
Hi we have a Qt application in which there is a one to one mapping of scene to view.
The view contains widgets which can be highlighted by setting RubberBandDrag on the view's 'drag mode'. So pressing the 'Shift' key and performing a rubber band selection highlights our widgets.
The issue we are having is that if we let go of the 'Shift' key prior to letting go of the mouse, the rectangular rubber band selection does not go away.
We trying re-drawing the layers by doing view.invalidateScene() but this is not making the rectangle go away.
Any help would be great.
-
Hi and welcome to do devnet,
Can you share a minimal sample code that reproduce this behavior ?
-
Hi, thanks for the response. The code base is extremely large (6+ developers over 2 years) so, I'll try my best with the following. On key press event, we check to see if the 'Shift' key has been pressed, if so, we turn on RubberBandDrag on the view (self is a QGraphicsView object).
def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Shift: self.setDragMode(QGraphicsView.RubberBandDrag)
The above works, and we are able to highlight widgets in our view with a rectangular rubber band selection. However, if we let go of the shift key (but mouse left mouse is still pressed), the rectangular rubber band selection does not go away.
We tried a couple of things, but the one method we thought should 'refresh' the view (and make the rectangular box go away) was this:
def keyReleaseEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Shift: self.setDragMode(QGraphicsView.ScrollHandDrag) self.invalidateScene()
Thanks so much for your response and taking the time to look at this.
-
What if you change the drag mode again in the keyReleaseEvent method ?
-
@SGaist said:
What if you change the drag mode again in the keyReleaseEvent method ?
I believe SGaist is absolutely right about one thing - you need to clearly define both:
when specific interaction starts and when interaction ends.
InvalidateScene does not help cause RubberBandDrag mode is still set.But when depends on the what you want to achieve.
For example if you say that RubberBandDrag mode starts and ends with shift key pressed and released you may follow SGaist's suggestion.
But I would consider shift + Mouse click starting the mode, but once mode started it has to remain active even shift was released, and only released when user release mouse button (ends interaction).
In this case the logical would be:- on mouse click check if shift button is pressed start the mode;
- on mouse button release check that if you in a RubberBandDrag mode, change the mode
-
Sgaist/Alex_malyu,
Thank you both for your input. That's exactly what we tried (sorry I had a typo in my comment previously and had keyPressEvent pasted twice).
So this is the interaction:
On 'Shift' keyPressEvent, the interaction starts and we set the RubberBandDrag.
On 'Shift' keyReleaseEvent, the interaction ends and we change the drag mode (we even tried setting it to NoDrag) and do an invalidateScene().But the rectangle doesn't go away.
-
@Andonis said:
But the rectangle doesn't go away.
Technically I believe this is a bug caused by setDragMode not setting private d->rubberBanding = false;
This is making paint to draw last set d->rubberBandRect even technically in the current selection or dragging mode such rfectangle should not be displayed
Unfortunately since problem is in a private inaccessible variables fixing a source of the problem seems impossible.But I see 2 workarounds.
a) the way I like the most cause you do not force person to keep shift all the time while dragging.- do not change setDragMode(NoDrag); back in keyReleaseEvent
- override mouseReleaseEvent instead:
void MyGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
QGraphicsView::mouseReleaseEvent ( event );
setDragMode(NoDrag);
}
b) If you really want to end drag mode in keyReleaseEvent
you will also need to make keyReleaseEvent to create QMouseEvent
( i did not wrote complete code for 'b' and did not test it. when 'a' case was)void MyGraphicsView::keyReleaseEvent ( QKeyEvent * event )
{
// code checking if event is caused by shift release is skippedQGraphicsView::keyReleaseEvent ( event ); if( dragMode() == RubberBandDrag ) { // create mouse event with current target position QGraphicsView::mouseReleaseEvent ( mouse_event ); }
}
-
I do wish to pursue this further but have been side tracked with other tasks. Please accept my apologies. Will get back soon.
-
So it looks like I found the fix for this (I'm on the same project as OP). I created a toy example that reproduces the problem, even when mousePress/ReleaseEvent methods are used to transition drag mode (I agree these methods are a better place for this logic):
class MyView(QGraphicsView): def __init__(self, scene): super().__init__(scene) self.setDragMode(self.ScrollHandDrag) def mousePressEvent(self, event: QMouseEvent): self.setDragMode(self.RubberBandDrag) super().mousePressEvent(event) def mouseReleaseEvent(self, event: QMouseEvent): self.setDragMode(self.ScrollHandDrag) super().mouseReleaseEvent(event) # *** app = QApplication([]) scene = QGraphicsScene() scene.addEllipse(QRectF(0, 0, 10, 10)) view = MyView(scene) view.setScene(scene) view.show() app.exec()
The fix is simple: the line that has *** must come first so that the view can handle the release of rubber-band drag, and only once that is done then the view should switch drag modes. Our having used the key events instead of mouse events might have compounded the problem (which one comes first, the key release or mouse release?).
Although this is not unreasonable, I think Qt should not require this order, ie View should discard the rubber band when a change of drag mode occurs. I have made a recommendation for this in the ticket at https://bugreports.qt.io/browse/QTBUG-51105 (which I had opened following @Andonis post) but I can remove it if some think it would be a bad idea.