Why does my QGraphicsWidget receive events it shouldn't after QDrag? - qt

I have a simple QGraphicsWidget, MyGraphicsWidget. Here's my mouseMoveEvent(), which seems to work fine :
void MyGraphicsWidget::mouseMoveEvent (QGraphicsSceneMouseEvent *event)
{
QPointF p = event->scenePos() - m_StartPos;
if(p.manhattanLength() < 20)
return;
//omitted drawing a rounded rect on the drag
QDrag *drag = new QDrag(event->widget());
drag->start(Qt::MoveAction);
}
The scene's dropEvent() just moves this widget to its new position, and I don't have a press/move event for the scene itself, so those should get passed on correctly to the widgets within.
However, once the drag completes, the next mouse press will be on this widget. So if I try to click and drag another widget, I'll be stuck dragging this one on accident, despite the fact that my cursor is not on this widget. I've printed out the event->pos() and event->scenePos(), and both reported that the cursor is where it appears to be (not on the widget at all). If I click once before trying to click and drag, everything works normally. Is there maybe something I need to implement within mouseReleaseEvent() or my mouseMoveEvent() ?
Thanks.

It's working now. I'm pretty sure this was the issue:
MyGraphicsWidget had another custom GraphicsWidget sitting on top of it, and in its mousePressEvent, I was calling QGraphicsWidget::mousePressEvent(event); at the start of the event. The rest of the event was never getting triggered, which was messing it all up. I moved that line to the end of the method, and everything seems okay now.

Related

Implementing mouseMoveEvent for qgraphicsObject to follow cursor

I have a Qt application with objects derived from QGraphicsObjcet that need to be movable in a scene. I know that I can use the flags for movement to achieve this.
myObject->setFlag(QGraphicsItem::ItemIsMovable);
myObject->setFlag(QGraphicsItem::ItemIsSelectable);
myObject->setFlag(QGraphicsItem::ItemSendsGeometryChanges);
but I am having an issue with the objects popping out of position when i use it. The only time when the objects move into an incorrect position is when an object I've moved is deleted and removed from the scene, the next time I try to move another object in the scene, its position relative to mouse Cursor is distorted until I release it and press it again. I realise that my problem could be occuring somewhere completely elsewhere in my code but from my own debugging I would at least want to try and implement the move functionality myself to solve this issue.
So my question is: How can you implement movable objects (derived from QGraphicsObject) that act as if the flags above are active?
I've been trying to use mouseMoveEvent but can't figure out how I get the objects to move with the cursor. Maybe I should look into DragMoveEvent instead? If possible, I would however really appreciate to see what the code for mouseMoveEvent below would look:
void myObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
// How do I get myObject to follow
// the event pos in the scene from here?
QGraphicsObject::mouseMoveEvent(event);
update();
}
I think something like this should do the trick (not tested):
void myObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
// Set the item position as the mouse position.
this->setPos(event->scenePos());
// If your object (this) has no parent, then setPos() place it using scene coordinates, which is what you need here.
QGraphicsObject::mouseMoveEvent(event);
update();
}
With the code above, your object is going to follow the mouse untill the end of the world, so you probably want to combine it with a start/stop flag.
And if your item has a parent, you are probably going to need to translate the coordinates.

How to stop a middle mouse click from deselecting items in a QGraphicsScene without blocking it from items in the scene?

I am creating a node graph and I want to be able to click the empty space in a scene and middle-mouse-drag to navigate without deselecting the currently selected items in the scene. Any suggestions?
I can block the middle click in the view's mousePressEvent and get the right behavior but then I no longer have middle-mouse click events working on items in the scene. I don't mind a middle-click resulting in a single selection when clicking on an item in the scene, but if I middle-click the empty space in the scene I don't want the selection altered.
This didn't cover the more complex behavior I am looking for: PyQt. How to block clear selection on mouse right click?
I didn't try using an eventFilter as I assume the issue would be the same
I am using PyQt/PySide, FWIW.
Before I roll my own workaround I thought I'd post here for the correct way or at least other workaround ideas.
Some workaround ideas:
Block the mousePressEvent to the scene but iterate over child items to deliver it directly
Restore selection while still in the mousePressEvent in the scene. Probably bad for performance at scale but simple I suppose.
Any feedback would be great!
[Edit:]
Here is my python version of the answer. Code tested. In my QGraphicsScene derived class:
def mousePressEvent(self, event):
# Prevent the QGraphicsScene default behavior to deselect-all when clicking on
# empty space by blocking the event in this circumstance.
item_under_the_mouse = self.itemAt(event.scenePos())
if event.button() == QtCore.Qt.MidButton and not item_under_the_mouse:
event.accept()
else:
super(GraphScene, self).mousePressEvent(event)
In your QGraphicsScene::mousePressEvent derived implementation, if it's a middle mouse click, check for items under the mouse click. If there aren't any, then accept the event and don't call the base class implementation. If something is under the click, then just call the base implementation; you don't have to try to reimplement that yourself. I think this is the general idea:
void MyScene::mousePressEvent (QGraphicsSceneMouseEvent *evt)
{
if ((evt->buttons () & Qt::MidButton) && items (evt->scenePos ().count ())
{
QGraphicsScene::mousePressEvent (evt);
}
else
{
evt->accept ();
}
}
I'm not sure if the accept is required in this case or not. I haven't compiled or tested this, but hopefully, it's helpful to get you going in the right direction.

Qt press left click twice for context menu to disappear

I'm trying to use a context menu from Qt when I press right click.
Here is what I've tried:
connect(mtreeView, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(showContextMenu(const QPoint&)));
Then
void MainTreeViewController::showContextMenu(const QPoint& pos)
{
QPoint globalPos = mtreeView->mapToGlobal(pos);
QMenu rightClickMenu;
rightClickMenu.addAction(QString("Option"));
rightClickMenu.exec(globalPos);
}
When I press right click, the menu appears. Then if I press left click outside of it nothing happens. I must press left click twice in order to make the menu disappear.
Why does that happen? Thanks!
This can happen if showContextMenu is called twice for a single right click. You can verify by setting a breakpoint in showContextMenu and checking whether it is called twice.
Probably your signal slot connection is created twice, which can be the reason behind this. You can verify by setting a breakpoint to the line where signal slot connection is made.

Qt Main window loosing focus and going to background unexpectedly

Going up the learning curve and everything has been making sense so far except for this one:
I have a main window from where I call one or more dialog like this:
d->setWindowFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint);
d->setGeometry(etc...
d->show();
The dialogs are not bound to the main window, and they are actually set to be displayed right beside the main window looking like an extension.
To close the dialogs I use a mouse 'enterEvent' on the mainwindow to send a close signal to the dialog(s) which will use hide().
void MainWidget::enterEvent(QEvent *event)
{
emit signal_related_close();
}
This works but randomly this causes a very annoying issue. When the signal is sent to close the dialogs, the main window would loose focus and go in the background (behind whatever application that is on the screen). Note that it does not minimize but only goes in the background and like I said it is random (about one out of 3 times I do it). I can't seem to get a pattern.
I initially thought the signal was messed up and instead used a dialog::leaveEvent() but that didn't help. I also tried using dialog::mouseMoveEvent and using that to hide the dialog but that would still randomly loose focus on the application.
The only method that doesn't loose focus is using a push button on each dialog that will initiate the same hide commands but that's not great for my UI. So it would seem that using the mouse enterEvent/leaveEvent,mouseMoveEvent will cause that random issue.
Anyone experienced this issue before? Any idea what could be wrong? Thx.
I can't explain why but this problem occurred when using QEvent or any mouse related event together with recursive children widgets. For example take this code in the parent widget constructor:
ChildWidget c1 = new ChildWidget (this);
ChildWidget c2 = new ChildWidget (c1);
Childwidget c3 = new ChildWidget (c2);
Using QEvent in the parent widget will randomly make the window go to the background and loose focus.
Setting all the ChildWidget to the same parent stopped the problem.

QPainter redraw on window gaining/losing focus

I am learning Qt and am trying to paint a simple display for my program with QPainter.
I draw static elements (frames etc.) once and only update dynamic elements afterwards.
Everything works fine, except for when the window loses focus. As soon as that happens, the whole area is cleared (dynamic elements keeps being painted as before).
Is it possible to prevent this behaviour? If not, how do I determine if the window have lost focus?
When your widget is uncovered, the paintEvent member will be called. The event passed in has a region() member that tells you what part of the widget should be redrawn. You can use that to redraw the static parts if/when necessary.
While I did not find why the screen was repainted, the focus can be triggered by using
eventFilter(QObject *, QEvent *event) {
if (event->type() == QEvent::ActivationChange){}
}
and paint function can be called from here. Although a slight delay must be added, as the trigger usually fires before the window looses focus (hence still clearing the repaint).

Resources