I have to make a virtual keyboard.
So I created few QPushButtons, but if I click on one, and move to release on another, then the other button doesn't receive any mouse event.
I to override enterEvent(QEvent *) and leaveEvent(QEvent *), but without success when I have clicked on another button.
Anyone got an idea?
In QAbstractButton code:
void QAbstractButton::mousePressEvent(QMouseEvent *e)
{
Q_D(QAbstractButton);
if (e->button() != Qt::LeftButton) {
e->ignore();
return;
}
if (hitButton(e->pos())) {
setDown(true);
d->pressed = true;
repaint(); //flush paint event before invoking potentially expensive operation
QApplication::flush();
d->emitPressed();
e->accept();
} else {
e->ignore();
}
}
When mousePressEvent triggers, QAbstractButton calls setDown(true) and QAbstractButtonPrivate ( Q_D(QAbstractButton) ) calls d->emitPressed(). After that, others buttons will not receive any events, they are already handled in the active button.
You can see in mouseReleaseEvent
void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
{
Q_D(QAbstractButton);
d->pressed = false;
if (e->button() != Qt::LeftButton) {
e->ignore();
return;
}
if (!d->down) {
// refresh is required by QMacStyle to resume the default button animation
d->refresh();
e->ignore();
return;
}
if (hitButton(e->pos())) {
d->repeatTimer.stop();
d->click();
e->accept();
} else {
setDown(false);
e->ignore();
}
}
It also checks state d->down. And if mouse hits that button, it will call d->click(); emit click event. If mouse does not hit button, it call setDown(false); to reset state.
I suggest to inherit virtual button from QWidget for easier handle mouse events.
Related
I write desktop application that works with map,
and I want to react on pan and long press events.
It is possible to use QGestureEvent on Qt/Linux/X11 with ordinary mouse?
I took Qt gesture example, it works on tablet,
but not reaction on press left mouse button and move (I expect that application recognizes it as tap or swipe event).
Then I added to Qt gesture example app.setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true); at main and such code to imagewidget.cpp:
void ImageWidget::mousePressEvent(QMouseEvent *e)
{
e->ignore();
}
void ImageWidget::mouseReleaseEvent(QMouseEvent *e)
{
e->ignore();
}
void ImageWidget::mouseMoveEvent(QMouseEvent *e)
{
e->ignore();
}
this code still works on tablet, but again no reaction on mouse on Linux/X11.
Any way to enable qgesture on linux/x11, should I write my own gesture recognition
for mouse?
The official way to make gestures out of mouse events in Qt is deriving from the QGestureRecognizer class, which allows to listen to relevant mouse events, set gesture properties accordingly, then trigger the gesture (or cancel it).
Here follows an example for pan gestures only, just to give an idea of what has to be done.
Have a QGestureRecognizer subclass like this:
#include <QGestureRecognizer>
#include <QPointF>
class PanGestureRecognizer : public QGestureRecognizer
{
QPointF startpoint;
bool panning;
public:
PanGestureRecognizer() : panning(false){}
QGesture *create(QObject *target);
Result recognize(QGesture *state, QObject *watched, QEvent *event);
};
The create method has been overridden to return a new instance of our gesture of interest:
QGesture *PanGestureRecognizer::create(QObject *target)
{
return new QPanGesture();
}
The recognize method override is the core of our recognizer class, where events are passed in, gesture properties set, gesture events triggered:
QGestureRecognizer::Result PanGestureRecognizer::recognize(QGesture *state, QObject *, QEvent *event)
{
QMouseEvent * mouse = dynamic_cast<QMouseEvent*>(event);
if(mouse != 0)
{
if(mouse->type() == QMouseEvent::MouseButtonPress)
{
QPanGesture * gesture = dynamic_cast<QPanGesture*>(state);
if(gesture != 0)
{
panning = true;
startpoint = mouse->pos();
gesture->setLastOffset(QPointF());
gesture->setOffset(QPointF());
return TriggerGesture;
}
}
if(panning && (mouse->type() == QMouseEvent::MouseMove))
{
QPanGesture * gesture = dynamic_cast<QPanGesture*>(state);
if(gesture != 0)
{
gesture->setLastOffset(gesture->offset());
gesture->setOffset(mouse->pos() - startpoint);
return TriggerGesture;
}
}
if(mouse->type() == QMouseEvent::MouseButtonRelease)
{
QPanGesture * gesture = dynamic_cast<QPanGesture*>(state);
if(gesture != 0)
{
QPointF endpoint = mouse->pos();
if(startpoint == endpoint)
{
return CancelGesture;
}
panning = false;
gesture->setLastOffset(gesture->offset());
gesture->setOffset(mouse->pos() - startpoint);
return FinishGesture;
}
}
if(mouse->type() == QMouseEvent::MouseButtonDblClick)
{
panning = false;
return CancelGesture;
}
return Ignore;
}
}
Basically, we track mouse events, updating a couple of properties of our own (panning and startpoint) and the passed in gesture properties as well. For each mouse event type, we also return a QGestureRecognizer::Result . All other events are discarded (the method returns Ignore).
This code can be tested with the Image Gestures Example, though: just add the class to the project and this line in the ImageWidget constructor:
QGestureRecognizer::registerRecognizer(new PanGestureRecognizer());
This should let the user grab the picture and move it around, using a mouse.
Look into this image widget gestures example. (search for mouseDoubleClickEvent)
http://doc.qt.io/qt-5/qtwidgets-gestures-imagegestures-imagewidget-cpp.html
Based on that you need to reimplement the required mouse events.
MyWidget::MyWidget() {
---
---
}
bool MyWidget::event(QEvent *ev)
{
---
---
}
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{
}
void MyWidget::mouseMoveEvent(QMouseEvent *event)
{
}
And declare those two functions in header
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
Using the mouse events of QWidget it is possible to catch the user's interaction with the widget. However, Qt does not deliver only the resulting action (a click or a double click), but provides the whole sequence of events as it is. Thus, the action should be recognized manually and my question is: How? QMouseEvent::flags() doesn't help much, since the only flag Qt::MouseEventCreatedDoubleClick is never set, as reported here.
In other words, how to properly emit those two signals, defined in the header file of MyWidget (derived from QWidget):
void clicked();
void doubleClicked();
having those slots:
void MyWidget::mousePressEvent(QMouseEvent *event)
{
...
}
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{
...
}
void MyWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
...
}
By properly I mean that if the user double clicks MyWidget only MyWidget::doubleClicked should be emited instead of MyWidget::clicked followed by MyWidget::doubleClicked;
I met the same problem before. I think it is by design of Qt. This may not be a best solution, but what you can do is to create an event filter and 'wait' for a while:
bool m_bClicked = false;
bool eventFilter(QObject* object, QEvent* event)
{
if(event->type() == QEvent::MouseButtonPress)
{
// Wait for 400 milliseconds
m_bClicked = true;
QTimer::singleShot(400, this, SLOT(eventMousePressTimer()));
return true;
}
else if(event->type() == QEvent::MouseButtonDblClick)
{
// Double-clicked
m_bClicked = false;
return true;
}
return false;
}
void eventMousePressTimer()
{
if(m_bClicked)
{
// Single-clicked
}
}
I want to get rid of the my application's title and border, but to do that I need to be able to move the window by dragging on the menuBar. The two method's I've found to do this is:
void TopMenuBar::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
dragPosition = event->globalPos() - frameGeometry().topLeft();
event->accept();
}
}
void TopMenuBar::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
move(event->globalPos() - dragPosition);
event->accept();
}
}
However if I put this in the MainWindow, it will move around no matter what you click on, and if I put it in a custom QMenuBar, it only moves the menubar within the window. I've also attempted to do some signals and slots trickery between the objects (Like keeping mousePressEvent in menuBar and mouseMoveEvent in MainWindow), but the tendency is that the window will "jump" to where the mouse pointer is instead of moving it smoothly.
Anyone else have a solution for this?
Environment is Windows
This will work for certain - just checked it. Call ui->menuBar->installEventFilter(this); in the MainWindow constructor.
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (watched == ui->menuBar)
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouse_event = dynamic_cast<QMouseEvent*>(event);
if (mouse_event->button() == Qt::LeftButton)
{
dragPosition = mouse_event->globalPos() - frameGeometry().topLeft();
return false;
}
}
else if (event->type() == QEvent::MouseMove)
{
QMouseEvent* mouse_event = dynamic_cast<QMouseEvent*>(event);
if (mouse_event->buttons() & Qt::LeftButton)
{
move(mouse_event->globalPos() - dragPosition);
return false;
}
}
}
return false;
}
I have a subclass of QGraphicsView that should accept two kinds of mouse events: drag and drop for scrolling and simple clicks for item selection/highlight.
So I use
setDragMode(QGraphicsView::ScrollHandDrag);
to enable scrolling the view with the "Hand". And I have a function like this:
void GraphView::mouseReleaseEvent(QMouseEvent* e)
{
if (e->button() == Qt::LeftButton)
emit leftClicked(mapToScene(e->pos()));
else
emit rightClicked(mapToScene(e->pos()));
QGraphicsView::mouseReleaseEvent(e);
}
which creates signal whenever the user clicks on the scene.
However, the problem is: when I stop dragging and release the mouse button, the mouseReleaseEvent function is called, and if the cursor happens to be over some element of the scene, it will get highlighted.
How can I changed the mouseReleaseEvent function so that the signals are created only if there was no previous drag of the mouse?
If you use mousePress and mouseMove in combination with mouseRelease, then you can determine what mouse action the user just performed.
If you have mousePress then mouseRelease, then it must be a simple click.
If you have mousePress, mouseMove, and then mouseRelease, then it must be a drag.
The Qt documentation contains an example of interpreting combinations of mouse events in action in a scribbling program.
http://doc.qt.io/qt-4.8/qt-widgets-scribble-example.html
You can extend the principle to something like this:
private bool MyWidget::dragging = false;
private bool MyWidget::pressed = false;
void MyWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
pressed=true;
}
QGraphicsView::mousePressEvent(event)
}
void MyWidget::mouseMoveEvent(QMouseEvent *event)
{
if ((event->buttons() & Qt::LeftButton) && pressed)
{
dragging=true;
}
QGraphicsView::mouseMoveEvent(event)
}
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && pressed) {
pressed = false;
if (dragging)
{
dragging = false;
}
else
{
// plain mouse click
// do something here
}
}
QGraphicsView::mouseReleaseEvent(event)
}
Note that this does not address edge cases where a user's mouse action is performed only partially inside the widget. I must also admit that I am relatively new to Qt and have not yet used ScrollHandDrag, but this is how one would go about identifying a certain combination of mouse events.
I have problems in PySide while trying to determine which mouse button is pressed in event function. I need it in particular for ignoring mouse move event, because it's doing job on both mouse buttons, left and right.
I want to ignore mouse move event if the right button on scene is pressed. Any help?
All of mouse events have two methods (button and buttons) to determine which of buttons are pressed. But for only move event the documentation says:
Note that the returned value is always Qt::NoButton for mouse move events.
for mouseMoveEvent you should use buttons method.
void mouseMoveEvent(QMouseEvent *e)
{
if(e->buttons() == Qt::RightButton)
qDebug() << "Only right button";
}
In order to ignore move events you need to do this work in eventFilter of course.
QApplication::mouseButtons() will return the status of mouseButton,
so, you can get the status of mouse in KeyPressEvent.
you can check, which mouse button is pressed via Qt::RightButton. Sorry for c++ code, but i hope, you would understand idea anyway:
void mousePressEvent(QMouseEvent *event)
{
if (event->button()==Qt::RightButton){
qDebug() << "right button is pressed
}
}
You could use a boolean:
void mousePressEvent(QMouseEvent *event)
{
if (event->button()==Qt::RightButton){
qDebug() << "right button is pressed
pressed=true; //<-----
}
}
and on mouseMoveEvent
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
float dx = event->x() - lastPos.x(); // where lastpos is a QPoint member
float dy = event->y() - lastPos.y();
if (dx<0) dx=-dx;
if (dy<0) dy=-dy;
if (event->buttons() & Qt::LeftButton) { //if you have MOVEd
...do something
}
if (event->buttons() & Qt::RightButton) {
if (pressed==true) return;
else{
...do
}
}
}
On mouserelease you have to set pressed=false; ( "pressed" must be a member of the class)
Hope it helps,let me know