Double triggering QEvent::KeyPress and QEvent::KeyRelease - c++

I have a problem: I'm trying to catch a keystroke.
I use eventFilter:
bool Inf_int_qt::eventFilter(QObject *obj, QEvent *event)
{
// ::SoundKeys(event);
this->SoundKeys(event);
return true;
}
Next I do the following checks:
void Inf_int_qt::SoundKeys(QEvent* event)
{
if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
{
bool p_press = event->type() == QEvent::KeyPress;
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if( keyEvent->isAutoRepeat() )
return;
switch (keyEvent->key())
{
case Qt::Key_Control:
//do something
break;
}
}
}
In the end, when I quickly press (and release) the ctrl button, I get as many as 4 QEvent triggers:
QEvent::KeyPress
QEvent::KeyPress
QEvent::KeyRelease
QEvent::KeyRelease
It turns out that each signal is generated twice. I can't understand anything. Why can it work like this? OC Win 10. Qt 5.8.0

Related

Should I check QEvent dynamic_cast result if I already checked event->type()?

QEvent has a type() member function which returns the event type as enum value. Should I check dynamic_cast result for QEvent* if I already checked event->type(). E.g.
bool ClassName::eventFilter(QObject* obj, QEvent* event)
{
if (event->type() == QEvent::KeyPress)
{
auto ke = dynamic_cast<QKeyEvent*>(event);
if (ke) // <----------- needed?
{
// ...
}
}
// ...
}
Is if (ke) condition needed here?
As a commenter suggested, checking out the Qt docs regarding this has the following example:
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if (object == target && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Tab) {
// Special tab handling
return true;
} else
return false;
}
return false;
}
Because specific events will always be guaranteed to return the same type, checking against QEvent::type() is sufficient to allow for a safe static_cast to the target type. This will avoids the RTTI cost that a dynamic_cast would entail.

Signals returnPressed() and editingFinished() does not work for QLineEdit

I am using editingFinished() signals of QLineEdit to perform an Operation. The documentation says that this signal will be emitted when return or enter key is pressed or when it will lose focus.
It works well with the enter key on the numlock (Windows keyboard), and also when it loses focus, but when i press "return key" on the keyboard, the signal is not emitted. i tried to use the returnPressed() signal, it behaves the same way.
Am i missing something ?
Thank you
Subclass QLineEdit
Reimplement keyPressEvent()
Catch Qt::Key_Enter pressing and do your job or emit signal yourself
From documentation:
Qt::Key_Return 0x01000004
Qt::Key_Enter 0x01000005 Typically located on the keypad.
Something like this:
void LineEdit::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_Enter)
{
//do something
}
}
If you do not want subclass, you can installEventFilter to your dialog window, catch your lineEdit and check is Qt::Key_Enter was pressed.
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == ui->lineEdit && event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(Qt::Key_Enter == keyEvent->key() )
{
qDebug() << "numpad Enter pressed";
}
}
}
Don't forget
protected:
bool eventFilter(QObject *obj, QEvent *event);//in header
and
qApp->installEventFilter(this);//in constructor
For example:
void MainWindow::on_lineEdit_returnPressed()
{
qDebug() << "numpad Enter pressed";
}

Make QSpinBox react to mouse wheel events when cursor is not over it

I am using Qt 5.3.2 with Qt Creator 3.2.1 with MinGW 4.8.2 on Windows 7. I have a QSpinBox and can change its value with the mouse wheel only if the mouse is over the QSpinBox. If the mouse is not over the QSpinBox, scrolling the mouse wheel has no effect, even though the QSpinBox still has focus. What do I need to do to be able to change values in the QSpinBox that has focus with the mouse wheel even if the mouse is not hovering over it? Setting mouseTracking to true does not have that effect.
Use eventFilter to do this. Install it on your mainWindow:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && event->type() == QEvent::Wheel)
{
QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
if(wheelEvent->delta() > 0)
ui->spinBox->setValue(ui->spinBox->value() + 1);
else
ui->spinBox->setValue(ui->spinBox->value() - 1);
}
}
It is just example, so you can improve it as you want.
Or use this:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && event->type() == QEvent::Wheel)
{
QApplication::sendEvent(ui->spinBox,event);
}
}
In this example, when you detect wheel event, you send it to your spinbox.
But don't forget
protected:
bool eventFilter(QObject *obj, QEvent *event);//in header
and
qApp->installEventFilter(this);//in constructor
As DmitrySazonov recommended. We will detect wheelEvents when our spinBox in focus, when spinBox losed focus, we don't react on wheel(other widgets react normal). We do this in one eventFilter. To do this provide new bool variable. For example:
private:
bool spin;//in header
Initialize it in constructor:
spin = false;
And your eventFilter should be.
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if(obj == ui->spinBox && event->type() == QEvent::FocusIn)
spin = true;
if(spin)
{
if (obj == this && event->type() == QEvent::Wheel)
{
QApplication::sendEvent(ui->spinBox,event);
}
}
if(obj == ui->spinBox && event->type() == QEvent::FocusOut)
spin = false;
}
Or do just this, without additional variable:
if (obj == this && event->type() == QEvent::Wheel)
{
if(ui->spinBox->hasFocus())
QApplication::sendEvent(ui->spinBox,event);
}
I did not mention it in the question but I have more that one QSpinBox and testing them all seems sub-optimal, so I need a generic message forwarder. Based on the Chernobyl's code I made my own version of the message filter:
bool MainWindow::eventFilter(QObject *obj, QEvent *event){
if (obj == this && event->type() == QEvent::Wheel)
{
auto focusWidget = QApplication::focusWidget();
if (focusWidget){
qApp->removeEventFilter(this);
QApplication::sendEvent(focusWidget, event);
qApp->installEventFilter(this);
return true;
}
}
return false;
}
This forwards all QWheelEvents to the QWidget with the focus. One could also add other events that need to be forwarded.
The qApp->removeEventFilter and qApp->installEventFilter inside the event filter is the only way I found that prevents the event filter calling itself when scrolling on the main window causing a stack overflow (condition focusWidget != this does not help). There isprobably a way to prevent the infinite recursion without reinstalling the event filter on every QWheelEvent.

How to disable control+A shortcut for QTableView and prevent select all option?

I want to prevent control+A shortcut from selecting all the files in QTableView, I want to make it as such that it can only select with mouse but not with a keyboard shortcut. Right now my eventFilter code is as below. Could you please suggest me a way to do that?
bool MainWindow::eventFilter(QObject* obj, QEvent *ev)
{
if(ev->type() == QEvent::MouseButtonPress)
{
if(obj == ui->listOfImages->viewport())
{
QMouseEvent * mouseEv = static_cast<QMouseEvent*>(ev);
if((mouseEv->buttons() & Qt::LeftButton) && (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) == true))
{
controlButtonCounter++;
fetch = true;
return QObject::eventFilter(obj,ev);
}
else if((mouseEv->buttons() & Qt::LeftButton) && (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) == false))
{
if(selectedImages.size()>0)
{
ui->listOfImages->clearSelection();
selectedImages.clear();
selectedList.clear();
ui->selectedFiles->clear();
ui->selectedFiles->show();
}
fetch = false;
controlButtonCounter = 0;
}
}
}
return QObject::eventFilter(obj,ev);
}
I would try to extend your event filter's code with the following block:
[..]
if (event->type() == QEvent::KeyPress && obj == ui->listOfImages->viewport()) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
int key = keyEvent->key();
Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
if(modifiers & Qt::ShiftModifier)
key += Qt::SHIFT;
if(modifiers & Qt::ControlModifier)
key += Qt::CTRL;
if(modifiers & Qt::AltModifier)
key += Qt::ALT;
if(modifiers & Qt::MetaModifier)
key += Qt::META;
if (QKeySequence(key) == QKeySequence(QKeySequence::SelectAll)) {
// Filter the event.
return true;
}
}
[..]
This code is supposed to return true (filter the event) if standard "select all" key combination pressed. It is usually Ctrl+A keys.

Preventing key events

I have a simple application with just one QPlainTextEdit, basically the same as Qt's example here:
http://qt-project.org/doc/qt-5.1/qtwidgets/mainwindows-application.html
When I press Ctrl+Z, it calls undo. When I press Ctrl+A, it selects all text. This is ok.
But when I press Ctrl+E or Ctrl+R, which are not defined in the menu, the characters "e" and "r" will appear in the QSimpleTextEdit.
How do I prevent this? How to "filter" keypresses which have been defined as menu shortcuts and keep them working, and "prevent" those keypresses not defined as menu shortcuts from appearing in the edit?
There are 2 options:
1) Create a subclass and reimplement keyPressEvent()
2) Create an eventFilter and use installEventFilter() (see http://qt-project.org/doc/qt-5.0/qtcore/qobject.html#installEventFilter)
You can use the following code:
CSimpleEdit.h
#include <QPlainTextEdit>
class CSimpleEdit: public QPlainTextEdit
{ Q_OBJECT
public:
explicit CSimpleEdit(QWidget* parent = 0);
~ CSimpleEdit();
protected:
bool eventFilter(QObject* obj, QEvent* event);
};
CSimpleEdit.cpp
CSimpleEdit::CSimpleEdit(QWidget* parent)
: QPlainTextEdit(parent)
{ installEventFilter(this); }
CSimpleEdit::~CSimpleEdit()
{ removeEventFilter(this); }
bool CSimpleEdit::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->modifiers() != Qt::NoModifier &&
!keyEvent->modifiers().testFlag(Qt::ShiftModifier))
{ bool bMatch = false;
for (int i = QKeySequence::HelpContents; i < QKeySequence::Deselect; i++)
{ bMatch = keyEvent->matches((QKeySequence::StandardKey) i);
if (bMatch) break;
}
/*You can also set bMatch to true by checking you application
*actions list -> QWidget::actions(). */
if (!bMatch) return true;
}
}
else if (event->type() == QEvent::KeyRelease)
{ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->modifiers() != Qt::NoModifier &&
!keyEvent->modifiers().testFlag(Qt::ShiftModifier))
{ bool bMatch = false;
for (int i = QKeySequence::HelpContents; i < QKeySequence::Deselect; i++)
{ bMatch = keyEvent->matches((QKeySequence::StandardKey) i);
if (bMatch) break;
}
if (!bMatch) return true;
}
}
return QObject::eventFilter(obj, event);
}