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.
Related
I would like to select positions and items in a QGraphicsView by mouse click and add items to the connected view at this position/item. Do I need to implement my own subclass of QGraphicsView or is there a shorter solution, e.g. with signal/slot?
There are few ways to do it:
Reimplement mousePressEvent(QMouseEvent*) (so, you need to implement subclass of QGraphicsView),
Call installEventFilter(QObject *) for QGraphicsView and implement bool eventFilter(QObject *, QEvent *) to catch all events (and process only QEvent::MouseButtonPress inside this function). In this case you do not need to implement subclass of QGraphicsView.
See also: Click event for QGraphicsView Qt and How to draw a point (on mouseclick) on a QGraphicsScene
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
}
I would like to be able to move a QSlider with arrows of the keyboard at any time.
I want to be able to click anywhere on the QWindow and keep QSlider activated to move the cursor with the arrows.
My problem is that move the cursor with arrows is only allowed if we click on the QSlider before.
I hope my question is clear enough.
Does anyone know how to move the QSlider with arrows of the keyboard without clicking on the QSlider before please?
There are two approaches:
In Qt terms, you'd like to give slider the focus. Widgets have the setFocus method, so you need to call slider->setFocus(Qt::OtherFocusReason).
Since you want the slider to get focus whenever the underlying window has focus, you need to put the setFocus call in your implementation of focusInEvent for the parent widget.
You can forward the key events from the underlying widget to the slider. In the parent widget, reimplement keyPressEvent and keyReleaseEvent. When the desired keys are detected, forward them to the slider:
// same for keyReleaseEvent!
void MyWindow::keyPressEvent(QKeyEvent * ev) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
slider->event(ev);
}
}
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.