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.
Related
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();
}
}
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;
}
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
is there away to set global mouseReleaseEvent?
what i mean is i have QMainWindow and on it QFrame and init QListView and In it
i have Widgets that constarct the QListView and inside the widget i have al sort of lables and text fields.
so i want to detect mouseRelease any where in my app i have to implement in all widgets the mouseReleaseEvent?
void ItemWidget::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
;
}
event->accept();
}
According to the QMouseEvent documentation, the widget that receives the mouse press will also get the mouse release. So, you shouldn't have to look any further for your mouse release than the widget that received the original press. This is usually referred to as a mouse "grab."
You may also want to check that Qt::WA_NoMousePropagation is not set on one of your children. If it is set, it will ensure that your mouse event does not bubble up.
If you really want to catch all mouse release events, you could try installing an event filter on the QApplication itself. That is a bit of a heavy solution, but you should get every mouse release event.
Looking at the (somewhat older) documentation here indicates that events are by default ignored by a widget and are propagated to their parents. So, if you make all your widgets children of your main application widget (or 'grandchildren', etc.) then you should only need to install the event handler on the application widget.
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.