Qt keyPressEvent, "Hold", and keyReleaseEvent Handling with Buttons/Mouse Clicks - qt

I am writing a Qt program to simulate a piece of hardware and I would like to simulate button press, hold, and release events. In my application, I'd like to handle input from both the keyboard and mouse clicks to make things convenient to the user (i.e. me). I've noticed some odd behavior and I don't understand it.
The application uses QPushButton with autoRepeat enabled and a 100 ms autoRepeatDelay and autoRepeatInterval. If I mouse click on a button, I receive alternating "pressed" and "released" events. I would have expected to see 1 to N-1 "pressed" events followed by a "released" event. Why is Qt behaving that way?
I've also implemented the following code to handle button presses from the keyboard:
void MyApp::keyPressEvent(QKeyEvent *event)
{
QString s = QString("My PRESS key is %1. The counter is %2").arg(event->text(), QString::number(keyCounter));
qDebug() << s;
keyCounter++;
}
void MyApp::keyReleaseEvent(QKeyEvent *event)
{
QString s = QString("My RELEASE key is %1. The counter is %2").arg(event->text(), QString::number(keyCounter));
qDebug() << s;
keyCounter = 0;
}
bool MyApp::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
this->keyPressEvent(dynamic_cast<QKeyEvent*>(event));
return true;
}
else if (event->type() == QEvent::KeyRelease)
{
this->keyReleaseEvent(dynamic_cast<QKeyEvent*>(event));
return true;
}
else
{
return QObject::eventFilter(obj, event);
}
}
Here I see two types of behavior. For alphanumeric keys, I see the alternating "pressed" and "released" events. For arrow keys, I only see the "released" events. Again, I would have expected to see 1 to N-1 "pressed" events followed by a "released" event. Why do the arrow keys behave differently than the alphanumeric keys?
Is what I'm trying to do possible in Qt?

Here was my solution: First I disabled autoRepeat and stopped handling keyPressEvents because I found that the arrow keys weren't generating them. Instead I registered shortcuts for the keyboard buttons I wanted to use:
QShortcut *shortcutUp = new QShortcut(QKeySequence("Up"), this);
QObject::connect(shortcutUp, SIGNAL(activated()), this, SLOT(on_upButton_pressed()));
shortcutUp->setAutoRepeat(false);
Then in the on_upButton_pressed() function (for example) I set a flag indicating the button was pressed. This flag is cleared in the on_upButton_released() function. That flag is checked on a 100 ms interval (using a QTimer). If the flag is true, I call on_upButton_pressed() again. This means:
Each mouse click or keyboard button press generates one "Press" event
The "Press" event sets a flag that is checked by a QTimer
If the flag is true, another "Press" event is generated
When the mouse or keyboard button is released, the "Release" event is generated and the flag is cleared.
It's working now.

This is caused by some keys in QT generate auto-repeat event:
Here I see two types of behavior. For alphanumeric keys, I see the
alternating "pressed" and "released" events. For arrow keys, I only
see the "released" events. Again, I would have expected to see 1 to
N-1 "pressed" events followed by a "released" event. Why do the arrow
keys behave differently than the alphanumeric keys?
In QT5 you can detect key held down by function isAutoRepeat() of QKeyEvent object. If the key is held down then isAutoRepeat() will return true. for example:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
{
return;
}
if (!event->isAutoRepeat())
{
qDebug() << "[MainWindow::keyPressEvent()] " << event->key() << "; " << event->isAutoRepeat();
}
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
{
return;
}
qDebug() << "[MainWindow::keyReleaseEvent()] " << event->key() << "; " << event->isAutoRepeat();
}

Related

Qt: Issuing a message before emitting a signal

Brief description of my application and my question:
In a QTabWidget I have several groupBoxes containing each 2 QRadioButtons.
E.g.
Select Menu (groupBox): (RadioButton:) A (RadioButton:) B
At one point of time, only one Menu can be active.
Both radioButtons are connected to each othe ->
When I click radioButton A and set it true, radioButton B is automatically set false - and the other way round.
When trying to change the menu setting, before a click signal is emitted, I would like to issue a QMessageBox Warning "Are you sure you want to change the menu? This can cause severe damage to your device." -Yes/No.
When clicking Yes, I would like to change the menue setting. When clicking No, I would like everything to remain as before.
The only problem I have is: When issuing the QMessageBox in the on_radio_button_toggled slot, the radioButton state has already changed to true.
Even if I change the state in the slot again and correct them, it looks like the state has already changed when the pop up message shows up. I don't want that because that implies that the state of the menue has already changed.
Where or How can I let a QMessageBox pop up before emitting the actual signal slot - when clicking the radio Button?
Thank you very much for your help.
Update:
I have now implemented an eventFilter as recommended. Here is my source code:
ui->radioButtonMenu1->installEventFilter(this);
ui->radioButtonMenu2->installEventFilter(this);
SubmenuOne is a QWidget. It is integrated into the MainWindow by a QTabWidget (via placeholder).
bool SubmenuOne::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonPress)
{
QMessageBox::StandardButton reply;
reply= QMessageBox::question(this,tr("WARNING"),tr("Changing the settings may cause severe damage to your device! Please confirm your decision."),QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
//QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
//keyEvent->accept();
//event->accept();
qDebug("Yes.");
return false;
}
else
{
qDebug("No.");
return true;
}
}
}
You have to use bool eventFilter(QObject *obj, QEvent *event); Declare event filter in your window, then instal event filter to every radiobutton like this: radioButton1->installEventFilter(this);. Then at eventFilter check event type: if (event->type() == QEvent::MouseButtonPress) and show your QMessageBox. Then you can accept event and return true or false depending on user choise.
bool SubmenuOne::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonPress)
{
QMessageBox::StandardButton reply;
reply= QMessageBox::question(this,tr("WARNING"),tr("Changing the settings may cause severe damage to your device! Please confirm your decision."),QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
static_cast<QRadioButton*>(obj)->setChecked(True);
}
event->accepted();
return true;
}
return QMainWindow::eventFilter(obj, event); //you forget this. QMainWindow is just base class of your SubmenuOne.
}

QPlainTextEdit double click event

I need to capture the double click event on a QPlainTextEdit that is inside a QDockWidget.
In my actual code I have installed an event filter in the QDockWidget, to handle resize operations, and in the QPlainTextEdit, to handle the double click events:
// Resize eventfilter
this->installEventFilter(this);
ui->myPlainTextEdit->installEventFilter(this);
But, although it works for the QDockWidget I am unable to catch the double click event for the QPlainTextEdit:
bool MyDockWidget::eventFilter(QObject *obj, QEvent *event) {
if (event->type() == QEvent::Resize && obj == this) {
QResizeEvent *resizeEvent = static_cast<QResizeEvent*>(event);
qDebug("Dock Resized (New Size) - Width: %d Height: %d",
resizeEvent->size().width(),
resizeEvent->size().height());
} else if (obj == ui->myPlainTextEdit && event->type() == QMouseEvent::MouseButtonDblClick) {
qDebug() << "Double click";
}
return QWidget::eventFilter(obj, event);
}
With this code the message "Double click" is never shown. Any idea what is wrong with the code?
QTextEdit inherits a QScrollView and when you double click on the viewport of the QTextEdit, the viewport receives the double click event. You can cross check your current code by double clicking on the edges of the text edit. It will capture the event.
To solve this, add the event filter to the view port in addition to the current event filters you have installed as shown below:
ui->myPlainTextEdit->viewport()->installEventFilter(this);
Next, capture the event using this if statement:
if ((obj == ui->myPlainTextEdit||obj==ui->myPlainTextEdit->viewport()) &&
event->type() == QEvent::MouseButtonDblClick)
{
qDebug() << "Double click"<<obj->objectName();
}
You can capture the click position using QMouseEvent:
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug()<<QString("Click location: (%1,%2)").arg(mouseEvent->x()).arg(mouseEvent->y());

How to listen global mouse and keyboard events on linux platform using Qt?

I use Qt to develop my application, and I want to listen the global mouse and keyboard events, so I can do something after detect these events. On windows platform, I use SetWindowsHookEx API. But I don't know how to do the similar thing on Linux.
My code on Windows as below:
/*********************listen mouse event*****************************/
mouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, NULL, 0);
if (mouseHook == NULL) {
qDebug() << "Mouse Hook Failed";
}
/*********************listen keyboard event*****************************/
keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, keyBoardProc, NULL, 0);
if (keyboardHook == NULL) {
qDebug() << "Keyboard Hook Failed";
}
Thank you for your answer sincerely!
Use the vast and helpful Qt Event System. That way you can use the same code on Windows as well as Linux.
I would recommend adding a eventFilter to your application. If the event caught is a QEvent::KeyPress or QEvent::MouseButtonPress( or QEvent::MouseButtonDblClick based on your requirement), take the necessary action.
If the event filter is to be applied for a particular widget in the main window, in the constructor of the main window, add
ui->myWidget->installEventFilter(this);
Now you need to implement the protected method eventFilter for the main window.
In the header file.
protected:
bool eventFilter(QObject* obj, QEvent* event);
Implementation
bool MainWindow::eventFilter(QObject* obj, QEvent* event)
{
if(event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent *>(event);
qDebug() << "Ate key press " << keyEvent->text();
return true;
}
else if(event->type() == QEvent::MouseButtonPress)
{
qDebug() << "Mouse press detected";
return true;
}
// standard event processing
return QObject::eventFilter(obj, event);
}
It is also possible to filter all events for the entire application, by installing an event filter on the QApplication or QCoreApplication object. You can read more on that from the documentation link I provided in the first line.
If the event filter has to be reused, I would recommend adding a dummy class which inherits from QObject. In this class you can just implement the function eventFilter. You can now pass an instance of this class to the function installEventFilter and communicate with the other objects using SIGNALS.
EDIT:
If you are looking to capture events even outside the application, Qt itself does not support that (yet). But you can use the Qxt library that adds support for it using the QxtGlobalShortcut class.

Handle mouse events in QListView

I've Dialog that shows folders (in treeView) and files (in listView) respectively. In listView doubleClick signal is handled by a slot that Qt created while I used Designer with aproppriate slot to be implemented. The problem is that I'm not able to handle RIGHT MOUSE click. Is there a solution?
P.S.
I've googled for a while to solve this problem, it seems that inheriting QListView and overriding solve the problem. But in my case I've already populated Qt's standart QListView using Designer.
In this case you can use event filter:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == ui->listView->viewport() && event->type() == QEvent::MouseButtonDblClick)
{
QMouseEvent *ev = static_cast<QMouseEvent *>(event);
if (ev->buttons() & Qt::RightButton)
{
qDebug()<< "double clicked" << ev->pos();
qDebug()<< ui->listView->indexAt(ev->pos()).data();
}
}
return QObject::eventFilter(obj, event);
}
To use eventFilter you should also:
protected:
bool eventFilter(QObject *obj, QEvent *event);//in header
and
qApp->installEventFilter(this);//in constructor
Possible addition to your problem. If you want do different things when user clicks left or right mouse buttons you should handle lest and right clicks in filter, without doubleClick signal (because it emits signal in both cases) and your code can be something like:
QMouseEvent *ev = static_cast<QMouseEvent *>(event);
if (ev->buttons() & Qt::RightButton)
{
qDebug()<< "RightButton double clicked";
//do something
}
if (ev->buttons() & Qt::LeftButton)
{
qDebug()<< "LeftButton double clicked";
//do something
}
In my case, I started trying to catch mouse events when a user right-clicked on a line in the QListView, but they never came through. However, all I really wanted to do was popup a context menu, and it turns out the contextMenuEvent did get through! So I didn't have to subclass QListView, just added a contextMenuEvent() to my widget that contained the QListView.
This was Qt3, so your mileage will most definitely differ.

Detect if a QPushButton is being clicked

I'm trying to find out whether a button is being pressed or not from within the paintEvent(), so that I can draw the "down" state. However, I don't know where to find this information. I tried QStyleOptionButton::state but it doesn't tell whether the button is being clicked or not.
The output of the debug statement is always something like "QStyle::State( "Active | Enabled | HasFocus | MouseOver" )" so nothing about a MouseDown state.
void XQPushButton::mousePressEvent(QMouseEvent* event) {
QPushButton::mousePressEvent(event);
QStyleOptionButton options;
options.initFrom(this);
qDebug() << (options.state);
}
void XQPushButton::paintEvent(QPaintEvent* event) {
QPushButton::paintEvent(event);
QStyleOptionButton options;
options.initFrom(this);
qDebug() << (options.state);
}
So any idea how I can detect if the button is being clicked?
QPushButton inherits QAbstractButton, which provides the down property:
This property holds whether the button is pressed down.
The documentation of the QStyleOption parent class contains an example that uses this property:
void MyPushButton::paintEvent(QPaintEvent *)
{
QStyleOptionButton option;
option.initFrom(this);
option.state = isDown() ? QStyle::State_Sunken : QStyle::State_Raised;
//...
}
In other words, the sunken/raised state is not initialized by initFrom(). This makes some sense, since initFrom is inherited from QStyleOption and takes a QWidget:
void initFrom ( const QWidget * widget )
– and a generic QWidget has no notion of "raised" or "sunken".
At least this is how I read the docs.

Resources