QWidget listening to other QWidgets events - c++

To explain the situation, I have a QMainWindow with plenty of stuff inside.
My goal is to make some kind of a notification that appears at the bottom right corner of this window.
The notification widget must be over every other widgets.
So far I've created that Notification widget that inherits from QWidget.
By setting the QMainWindow as its parent I've got it 'floating' on top of every other widgets. It doesn't belong to any layout.
Now the big issue is to make it stick to the bottom right corner. I managed to place it manually but what I want now is to move it upon a resize event.
Unfortunately since it doesn't belong to any layout it doesn't receive any resizeEvent, so I can't overload the resizeEvent() function.
An attempt was to make the QMainWindow emit a signal in its resizeEvent method but I am not really happy with this method. Especially because in the Notification widget constructor I have this connect(static_cast<MainWindow*>(parent), &MainWindow::resized, this, &Notification::update_position);, and it kind of breaks the genericity of the widget since its parent must be a MainWindow widget.
So the question is how can one widget react to another widget event ?
In my case here how can the notification widget be notified when its parent is getting resized ?
And I forgot to mention that I don't want the MainWindow widget to know anything about this notification widget. The notification widget can be managed on its own. You just call something like new Notification(parent) and it will do its stuff on its own
Any ideas are welcome,
Thanks in advance :)

You can install the notification widget as an event filter for its parent, e.g.:
NotificationWidget::NotificationWidget(QWidget *parent) : QWidget(parent) {
parent->installEventFilter(this);
}
bool NotificationWidget::eventFilter(QObject *obj, QEvent *event) {
// normal event handling
bool ret = QObject::eventFilter(obj, event);
if (event->type() == QEvent::Resize) {
// Parent resized, adjust position
}
return ret;
}

Related

How to receive scrollbar mouse events for QGraphicsView

In qt widget QGraphicsView I want to detect mouse-press, mouse-release and mouse-move events.
I derived a class from QGraphicsView and I have overridden the following functions:
mousePressEvent(QMouseEvent *event)
mouseReleaseEvent(QMouseEvent *event)
mouseMoveEvent(QMouseEvent *event)
Now I can detect these mouse events almost everywhere, except for the area where are the scroll bars, which are part of QGraphicsView.
I want to be able to catch these events and move the scroll bar manually.
Edit:
I am trying to simulate second mouse in windows environment sending WM_LBUTTONDOWN,... events. I would like to be able to detect this events also for scroll bars in QGraphicsView.
Besides detecting the events I would like to know the event->x() and event->()y position.
QGraphicsView inherits the QAbstractScrollArea class. Thus the scrollbar widgets can be accessed with the QAbstractScrollArea::verticalScrollbar and QAbstractScrollArea::horizontalScrollbar methods.
You don't even have to use event filters once you can access the scrollbar object as the QScrollBar inherits QAbstractSlider and thus offers the signals:
QAbstractSlider::sliderPressed()
QAbstractSlider::sliderReleased()
So you can just connect them to your slots like:
connect(horizontalScrollBar(), SIGNAL(sliderPressed()), this, SLOT(doSomething()));
connect(horizontalScrollBar(), SIGNAL(sliderReleased()), this, SLOT(doSomething()));
If you need the position of the event, you will have to use event filter though. You can install an event filter on the widget object.

Implementing mousePressEvent and setDragMode in Qt

I am doing a project in which I should implement mousePressEvent and setDragMode on QGraphicsScene. I am able to implement individually but I should implement both at the same time. I included both into the code but only mousePressEvent is working and not able to drag the scene . Could you please anyone tell me that how to implement both.Please find the attached code. Thankyou.
MyGraphicsView.cpp
MyGraphicsView::MyGraphicsView(QWidget* parent) : QGraphicsView(parent) {
Scene = new QGraphicsScene(this);
setScene(Scene);
setDragMode(ScrollHandDrag);
}
void MyGraphicsView::mousePressEvent(QMouseEvent *eve)
{
if (eve->button() == Qt::LeftButton) {
// handle Left mouse button here
QPointF pt = mapToScene(eve->pos());
}
}
MyGraphicsView.h
class MyGraphicsView : public QGraphicsView
{
Q_OBJECT
protected:
virtual void mousePressEvent(QMouseEvent *eve);
}
From QGraphicsView Qt docs on DragMode property:
(...) The default value, NoDrag, does nothing.
This behavior only affects mouse clicks that are not handled by any item. You can define a custom behavior by creating a subclass of QGraphicsView and reimplementing mouseMoveEvent().
This is probably why you can't make a DragMode to get to work when you implement a mousePressEvent: The event is processed, so the Qt framework thinks the "job is done", and no need to trigger a drag.
The doc suggest to re-implement the mouseMoveEvent. It still provides a QMouseEvent, so you can get the coordinates, and the information that what mouse button is currently pressed. From that on you only have to implement a state machine for Idle; StartDrag; Drag; FinishDrag
according to what buttons are pressed during a move, and implement your desired functionality in whichever states you need them.

prevent focus out for a QWidget

I'd like to write a QDialog lookalike class. I've managed to filter out mouse events to non-dialog widgets pretty well, but I still have a problem with focus. As the QDialog lookalike class is just a usual widget it can lose focus by way of key presses (tabs). Hence widgets not related to the QDialog lookalive, that I cannot click, but are focus-able, may get the focus. Is there a neat way to prevent the user from focusing away from my dialog lookalike's child widgets?
Assuming that your QDialog-like widget is an individual window, I think you are looking for QWdiget::setModal( true ). It prevents widgets in other windows of your application to receive any input events.
Here is a solution:
// somewhere in your code
connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),
SLOT(focusChanged(QWidget*,QWidget*)));
void MyDialog::focusChanged(QWidget*, QWidget* to)
{
if (!isAncestorOf(to))
{
QWidget* widget(qobject_cast<QWidget*>(children().front()));
widget->setFocus(Qt::OtherFocusReason);
QKeyEvent event(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
qApp->sendEvent(widget, &event);
}
// else do nothing
}
Assuming the child is an instance of QFrame or QWidget.

QGraphicsScene not responding to synthetic events

I have a very simple QGraphicsScene dervied class of my own:
class SceneComponent : public QGraphicsScene
{
Q_OBJECT
public:
explicit SceneComponent(QObject* parent = 0);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
};
And mousePressEvent(QGraphicsSceneMouseEvent*) is defined as:
void SceneComponent::mousePressEvent(QGraphicsSceneMouseEvent* event) {
Q_UNUSED(event);
std::cout<<"[Processing] MouseEvent"<<std::endl;
}
To display this QGraphicsView, this is as simple as:
QGraphicsView view(sceneComp);
view.show();
Now, when I click on the Window (for the QGraphicsView) that is displayed, I get the following output:
[Processing] MouseEvent
[Processing] MouseEvent
However, when I send a synthetic event using:
QMouseEvent* mevent = new QMouseEvent(
QMouseEvent::MouseButtonPress, QPoint(50, 50),
Qt::LeftButton, Qt::NoButton, Qt::NoModifier
);
QApplication::sendEvent(&view, mevent);
I get absolutely no output. Why is this?
On a related note, installing a eventFilter on the QGraphicsScene yields no results at all. This is probably (in my opinion) because of the fact that a QGraphicsScene expects a QGraphicsSceneMouseEvent instead of a QMouseEvent. This props up two questions:
Why is it so that a QGraphicsScene not accept standard QEvent and only QGraphicsSceneEvent?
Why is any QGraphicsSceneEvent derived class not instantiable?
Why is that I can send events to a QGraphicsScene using the sceneEvent method, but I need to specify a specific QGraphicsItem to send the event to?
From the docs for QGraphicsSceneEvent:
When a QGraphicsView receives Qt
mouse, keyboard, and drag and drop
events (QMouseEvent, QKeyEvent,
QDragEvent, etc.), it translates them
into instances of QGraphicsSceneEvent
subclasses and forwards them to the
QGraphicsScene it displays. The scene
then forwards the events to the
relevant items.
For example, when a QGraphicsView
receives a QMouseEvent of type
MousePress as a response to a user
click, the view sends a
QGraphicsSceneMouseEvent of type
GraphicsSceneMousePress to the
underlying QGraphicsScene through its
mousePressEvent() function. The
default
QGraphicsScene::mousePressEvent()
implementation determines which item
was clicked and forwards the event to
QGraphicsItem::mousePressEvent().
QGraphicsView uses a viewport widget to display the contents of the scene and to receive user events. Try sending your event to the QGraphicsView::viewport() widget - it should work.
By design - see the quote, these events are probably not meant to be sent manually. QMouseEvent is associated with a physical action - ex. mouse click, while QGraphicsSceneMouseEvent is something abstract...
The question is not clear, but that quote might be helpful too. Which event in particular are you talking about? You should also keep in mind that QGraphicsScene has QObject level events, sent with QApplication::sendEvent(), and QGraphicsItem events, that are sent with QGraphicsScene::sendEvent(). These two kinds work in different planes and have different purposes. Refer to the docs for more info.

QGraphicsItem doesn't receive mouse hover events

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.