QGraphicsScene not responding to synthetic events - c++

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.

Related

mouse tracking does not work In the widget in the main widget

I implemented showing button when mouse is under QTextEdit in the QWidget.
when I test code, I found it sometimes didn't work showing button
bug cause is that MouseMoveEvent doesn't work in Qwidget when I move in the QTextEdit.
I don't understand why MouseMoveEvent doesn't work...
here is my code
this->setMouseTracking(true); // this is QWidget
ui.textEdit_log->setMouseTracking(true); // this is textEdit
void QWidget::mouseMoveEvent(QMouseEvent* event) // reImplemented mouseMoveEvent
{
if (ui.textEdit_log->underMouse())
{
m_pButton->show();
}
else
{
m_pButton->hide();
}
}
Mouse tracking for QTextEdit will only work by subclassing QTextEdit and reimplementing it's mouseMoveEvent() from where you can pass the event up to the parent's mouseMoveEvent().
OR
Instead of using ui.textEdit_log->setMouseTracking(true) use ui.textEdit_log->setAttribute(Qt::WA_TransparentForMouseEvents) this will make your textedit_log invisible for mouse events. In this case you can use the childrenRect() function of your QWidget to determine if your mouse is over the textedit_log.
As far as software design goes, subclassing is a conceptually very heavy operation - it indicates a very deep coupling. It should be used with due concern for the maintainability impact it has. An almost no case should subclassing be used to merely observe the state of an object! - any API that forces you to do that is essentially broken.
Qt provides convenience "event listener" methods, but these methods are not meant to be used as external observers, i.e. you should never override them if all you want is to observe the objectfrom outside. The event filters should be used instead, since they introduce the weakest of possible couplings, and are very generic.
The mouse tracking events will be sent to the widget that tracks the mouse cursor - the editor widget. So no other widget will hear them. You have to implement QObject::filterEvent in a class of your choice (whatever class is convenient), then install that event filter on the editor widget. Your filter will intercept all events sent to the editor, including mouse move events. You should not subclass QTextEdit - use event filters instead!
In any case, the idea to do this via mouse movement tracking is overkill and has some overhead. Instead, look for QEnterEvent and QLeaveEvent - those should be sent to the editor widget without mouse tracking enabled. But still - do not intercept those via any subclassing!
Do note that QWidget is-a QObject, so the event filter can be a widget class, or a non-widget class.
I solved this problem like this
void Log::enterEvent(QEvent* event)
{
m_pButton->show();
}
void Log::leaveEvent(QEvent* event)
{
m_mousePos = this->mapFromGlobal(QCursor::pos());
QPoint logPos = mapToGlobal(this->normalGeometry().bottomLeft());
QPoint buttonPos = m_pButton->pos();
QPoint buttonRelatviePos = buttonPos - logPos;
// hide button when button is not in the button pos
if ((m_mousePos.x() < buttonRelatviePos.x()) or (m_mousePos.x() > buttonRelatviePos.x() + m_pButton->width()) or
(m_mousePos.y() < buttonRelatviePos.y()) or (m_mousePos.y() > buttonRelatviePos.y() + m_pButton->height()))
{
m_pButton->hide();
}
}

QWidget listening to other QWidgets events

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;
}

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.

How to select a position and add an item in a QGraphicsView by mouse click?

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

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.