I'm implementing a QDialog which shows up when another QWidget (a QFrame) is hovered and destroyed when the mouse cursor leaves the QFrame. I do the construction and destruction via the enterEvent and leaveEvent (inside the QFrame):
void Frame::enterEvent(QEvent *) {
m_dialog = new Dialog(this);
}
void Frame::leaveEvent(QEvent *) {
m_dialog->deleteLater();
}
The dialog moves itself right below the frame when it's constructed. So far so good, but the mouse cursor also has to be able to go to the dialog. I thought about using underMouse() like so:
void Frame::leaveEvent(QEvent *) {
if (! m_dialog->underMouse()) {
m_dialog->deleteLater();
}
}
but the problem is that inside Frame::leaveEvent, m_dialog->underMouse() is always false, even if the mouse is moved right onto it.
How can I know, inside Frame::leaveEvent, if the mouse is now on the dialog or somewhere else?
I could implement it in the following way:
The dialog's leaveEvent triggers a zero timer calling a function in the "target" widget (the widget where the cursor can go without the dialog being destroyed). This function checks if this widget is currently under the mouse cursor. If so, nothing happens. If not, the dialog is destroyed by that widget.
Same for the leaveEvent of the "target" widget, which calls a function implemented in the same way inside the dialog by a zero timer.
Related
I'm using a QWindow (Not a QMainWindow) with OpenGL. I need to use a QWindow to correctly control the OGL context.
I'm trying to follow the Scribble example to implement something similar to panning, but I can't find a paradigmatic way to trigger the mouseMoveEvent().
How can I get a "tooltip" effect where mouseMoveEvent() is constantly triggered, similar to setMouseTracking()?
It works fine for me. I created a test program with a MainWindow that inherits QWindow instead of QMainWindow, and handles the mouse move event to print the cursor position:
void MainWindow::mouseMoveEvent(QMouseEvent *e)
{
qDebug("%d, %d", e->pos().x(), e->pos().y());
}
It works, as I move the mouse I get events even without pressing any mouse buttons.
If mouse tracking is disabled (the default), the widget only receives mouse move events when at least one mouse button is pressed while the mouse is being moved.
you can call hasMouseTracking() or setMouseTracking() to control mouse's tracing state.
When mouse is traced, mouseMoveEvent() will be called, and you can reimpletment mouseMoveEven() to acquire mouse position, just as #sashoalm did.
BTW, mouse event is always transfered to your app, but filtered by it's parent or itself. You can reimpletment eventFilter() to code your own filter.
I'm trying to create a detachable type style widget, like in the way Chrome tabs are detachable (class is called Tab). I have everything working, except for a bug where sometimes (maybe 50% of the time), the Tab object never gets the mouse release event, and stops getting mouse move events.
Essentially, the detaching system works by allowing drags in the mouse press/move/release functions, just like normal. The mouseMoveEvent checks the total distance moved from the start, and if over a certain amount, will start the "detaching" process. The detaching process involves setting the parent widget to 0 (top level widget, undecorated window), so the Tab object is pretty much floating above everything, under the mouse, and continues to be dragged along with it until released.
I ran through all the QEvent items being delivered, and I found that when this issue occurs, the QEvent::MouseMove items (and all mouse events after this) are being sent to the TabBar (the Tab object's original parent). This occurs directly after calling setParent(0) on the Tab.
Basic mouse handling overview:
void Tab::mousePressEvent(*) {
[set up some boolean, start positions, etc]
}
void Tab::mouseMoveEvent(*) {
[track the updated position]
if (positionChange > STATIC_AMOUNT)
detachTab();
}
void Tab::mouseReleaseEvent(*) {
[return the Tab to its original position, and set the parent back to the TabBar]
}
void Tab::detachTab() {
QPoint mappedPos = mapToGlobal(0, 0);
setParent(0); //The loss of MouseMove events occurs when this returns.
move(mappedPos);
show();
raise();
}
Here are the events that the Tab object receives (first row is QEvent type, second is the name)
[Tab::detachTab() started]
[setParent(0) started]
QEvent::Hide
QEvent::Leave
qApp QEvent::MouseMove [ TabBar ] <-- now the TabBar is soaking up the mouse events
QEvent::HideToParent
QEvent::ParentAboutToChange
QEvent::ParentChange
[setParent(0) returned]
....
Summed up: my draggable QWidget loses QEvent::MouseMove and QEvent::MouseButtonRelease events after having its parent set to 0.
Any advice would be really appreciated!
A bit tricky workaround. I didn't test it, it's just an idea.
When your mouse hovers draggable part of a widget you may create topmost widget (let's call it Shade) with Qt::FramelessWindowHint (and possible with Qt::WA_TranslucentBackground). You may manipulate with Shade apperance via reimplementing paintEvent. For example - draw content of original widget, or draw some transparent preview, etc.
Then you may resize a Shade during dragging, to show user that widget will be detached. You will not loose mouse capture.
When user release mouse - you remember position of Shade, destroy it and detach+move original widget.
Feel free to ask, if you want more details.
Here is similar question.
So you suppose to use QDocWidget and enforce stacking of this widgets using tabifyDockWidget.
The situation is:
In side a QGraphicsView, I used its doubleClickEvent function to create a QGraphicsItem.
I rewrite the QGraphicsItem's mousePressEvent function to simply print a qDebug information.
However, what I found is that: even if I just double click to create that QGraphicsItem, the newly created QGraphicsItem's mousePressEvent is also called.
How would this happen? What can I do to prevent this?
A QGraphicsView is a widget, so it has a doubleClickEvent. In contrast, a QGraphicsItem is not a widget, but has a mousePressEvent.
When you override events, if you don't want them to be propagated to other objects, you need to accept the event to tell the system that you've handled it.
void MyGraphicsView::mouseDoubleClickEvent(QMouseEvent * event)
{
// Create a graphics item..
...
event->accept(); // tell the system that the event has been handled
}
Without accepting the event, the double click is passed on to the QGraphicsScene and then QGraphicsItem.
If you want to be able to double-click to create an item when clicking on an empty area of a scene, but have an item react if you double-click on an item, you can override the scene's mouseDoubleClickEvent instead of the view and check if there's an item at the cursor position before deciding upon whether or not to create an item, or pass the event on.
void QGraphicsScene::mouseDoubleClickEvent( QGraphicsSceneMouseEvent * mouseEvent )
{
if(items(mouseEvent->scenePos()).isEmpty()) // if no items are at this position
{
// create new graphics item
...
mouseEvent->accept(); // don't propagate the event
}
// no items, so allow the event to propagate by doing nothing
}
All,
I have a QGraphicsEllipseItem with setFlags(Qt::ItemIsSelectable | Qt::ItemIsMovable). This allows me drag and move ellipses quickly in a QGraphicsView.
I decided to be fancy, and setCursor(Qt::OpenHandCursor) to let the user know they can move it by clicking. However, now it won't let go when I let go of the left mouse button? What am I doing wrong?
Example code: Custom QGraphicItem and Repaint Issues
Note: I removed the update() calls, and added prepareGeometryChange() calls.
Now modify the MakeNewPoint function:
QGraphicsEllipseItem * InteractivePolygon::MakeNewPoint(QPointF & new_point)
{
QGraphicsEllipseItem * result = 0;
result = new QGraphicsEllipseItem();
result->setPos(new_point);
result->setRect(-4, -4, 8, 8);
result->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable)
result->setCursor(Qt::OpenHandCursor); //Setting this removes my ability to let go of an item. NOTE: result is parented by this.
return result;
}
later:
QGraphicsEllipseItem * new_item = MakeNewPoint(bla);
new_item->setParent(this);
//add it to my QList<QGraphicsEllipseItem *> m_points;
I would like to note that my QGraphicsEllipseItem is parented by a custom QGraphicsItem. I don't change the parents/Custom Item cursor, only the ellipse's. I do not experience this problem with non parented ellipses...
Interesting result: So my class custom QGraphicsItem class (the parent of the ellipses) is a QObject so I can filter incoming mouse events from the scene. I did a setCursor(Qt::ArrowCursor) in my custom class's constructor... and here's where it gets interesting:
The eventFilter now catches (event->type() == QEvent::GraphicsSceneMouseMove) even if a mouse button isn't pressed down. If I don't have the setCursor call, that event only fires while a mouse button is pressed... thoughts?
Okay got it, here's what's happening, it's intuitive once you realize it:
When you set that a QGraphicsItem to a unique cursor, the QGraphicsView has to setMouseTracking(true) otherwise the QGraphicsScene will never know when to change the cursor (ie when it's over the graphics item with the unique cursor.) The mouse move events were affecting my ellipse.
Normally, the QGraphicsScene only gets mouse move events when a button is held down.
I have a class derived from QGraphicsView, which contains QGraphicsItem-derived elements. I want these elements to change color whenever the mouse cursor hovers over them, so I implemented hoverEnterEvent (and hoverLeaveEvent):
void MyGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
update (boundingRect());
}
However, this event handler code is never executed. I've explicitly enabled mouse tracking:
MyGraphicsView::MyGraphicsView(MainView *parent) :
QGraphicsView(parent)
{
setMouseTracking(true);
viewport()->setMouseTracking(true);
...
}
Still, no luck. What am I doing wrong?
Fixed it. I need to use setAcceptHoverEvents(true) in the constructor of my QGraphicsItem-derived class.
In my case, hover events wouldn't work if I overrode mouseMoveEvent in my implementation of the QGraphicsView class. I fixed this by adding a call to
QGraphicsView::mouseMoveEvent(event);
which propagated the event to the parent, which in turn sent it out to all the scene items.