PDA

View Full Version : eventFilter anywhere in the program.



Oleg21
8th October 2017, 20:49
Hi everyone. The following situation arose: I have a project, it contains two classes (first.cpp and second.cpp).
first.cpp is a constant and compulsory project class, and the second.cpp is an optional class and is connected as needed.
So, in the second.cpp class, I have a desire to implement eventFilter at the program level. For this I write the following code:



second.cpp
#include "second.h"
#include <QtWidgets>

second::second(QWidget *parent) : QWidget(parent)
{
qApp->installEventFilter(parent);
}

second::~second(){

}

bool second::eventFilter(QObject *watched, QEvent *event){
//it's not working!
}


Unfortunately, eventFilter in this class does not work.
In order for everything to work, I need to implement eventFilter in class first.cpp:



#include "first.h"
#include "second.h"

first::first(QWidget *parent) : QMainWindow(parent)
{
second *tempSecond = new second(this);
}

first::~first()
{

}

bool first::eventFilter(QObject *watched, QEvent *event){
//it's working!
}


However, this approach contradicts my opinion about the possibility of disabling the class second.cpp.
Tell me, how to solve this problem?

Santosh Reddy
9th October 2017, 10:43
//qApp->installEventFilter(parent);
qApp->installEventFilter(this);

Oleg21
12th October 2017, 19:22
Hello. Truly, I had to do as you say:


qApp->installEventFilter(this);


And implementation of eventFilter:


bool second::eventFilter(QObject *watched, QEvent *event){
if (event->type() == QEvent::MouseButtonRelease) {

cc++;
qDebug() << cc;

return false;
}else{
return false;
}
}


However, there is a small problem here: eventFilter function gets MouseButtonRelease event twice.
Should this be so? If so, maybe you know why this happens?

Santosh Reddy
13th October 2017, 06:27
Hello. Truly, I had to do as you say:
However, there is a small problem here: eventFilter function gets MouseButtonRelease event twice.
Should this be so? If so, maybe you know why this happens?

You have installed the event filter on two objects, or may be installed event filer on same object twice.

Oleg21
13th October 2017, 18:34
Hmm, weird. I install event filter only in one place of code and only for one object.
Be kind, take a look at the code of my project, and tell me where I was mistaken?

Headers:
first.h


#ifndef FIRST_H
#define FIRST_H

#include <QMainWindow>

class first : public QMainWindow
{
Q_OBJECT

public:
first(QWidget *parent = 0);
~first();
};

#endif // FIRST_H

second.h


#ifndef SECOND_H
#define SECOND_H

#include <QWidget>

class second : public QWidget
{
Q_OBJECT
public:
explicit second(QWidget *parent = nullptr);
~second();

int cc = 0;

virtual bool eventFilter(QObject *watched, QEvent *event);
signals:

public slots:
};

#endif // SECOND_H

Sources:
main.cpp


#include "first.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
first w;
w.show();

return a.exec();
}

first.cpp


#include "first.h"
#include "second.h"
#include <QtWidgets>

first::first(QWidget *parent) : QMainWindow(parent)
{
second *second1 = new second(this);
}

first::~first()
{

}

second.cpp


#include "second.h"
#include <QtWidgets>

second::second(QWidget *parent) : QWidget(parent)
{
qApp->installEventFilter(this);
}

second::~second(){

}

bool second::eventFilter(QObject *watched, QEvent *event){
if (event->type() == QEvent::MouseButtonRelease) {

cc++;
qDebug() << cc;

return false;
}else{
return false;
}
}

high_flyer
15th October 2017, 01:47
print out the address of the 'watched' object, see if both events belong to the same object.

Oleg21
15th October 2017, 09:23
Hello. I already did it. But I have determined that events belong to different objects.
After this code:



cc++;
qDebug() << cc;

I added the following code:


qDebug() << watched->metaObject()->className();


Received a result:
12611

Therefore, in eventFilter, I added the following kind of verification:


QString tempStr = "QWidgetWindow";
if (watched->metaObject()->className() == tempStr){
// code here
}


In this case, the issue is resolved. But I do not know if it is correct to do so?

high_flyer
15th October 2017, 21:00
But I do not know if it is correct to do so?
Depends what your definition of "correct" is.
I would suggest however to not install the event filter on qApp, but on the actual object which of whom you want to filter events, in this case you 'first' object.
This will save you the need to "filter" your objects based on class name which is not optimal (what if you have several objects of the same class?)

Oleg21
19th October 2017, 20:33
(what if you have several objects of the same class?)
For each object, I can say a unique ObjectName which allows me to know from which object I get the events.
For example:


QApplication a(argc, argv);
first w;
w.setObjectName("Lollipop");
w.show();

In the eventFilter:


bool second::eventFilter(QObject *watched, QEvent *event){
QString tempStr = "LollipopWindow";
if(watched->objectName() == tempStr){
//Code here
}
}

However, it should be remembered that to the ObjectName is added string "Window". Why this happens, I do not know.



I would suggest however to not install the event filter on qApp, but on the actual object which of whom you want to filter events, in this case you 'first' object.

The following code:


qDebug() << watched->metaObject()->className();

returns the following string: "QWidgetWindow", after him "first", after - "second". Why on the "first" should I set the event filter?
Perhaps "QWidgetWindow" is the very first class? And that it should be set to filter events?
But, I do not find a class with that name.
12618
What is this class, "QWidgetWindow"? How to find it? Where can I find out about it?

high_flyer
19th October 2017, 23:06
For each object, I can say a unique ObjectName which allows me to know from which object I get the events.
Sure you *can* do it, but this is hardly good practice, it makes the code very rigid and you are ignoring the fact that when you have the pointer 'watched' it already points to a known (to you) object, so why bother with setting and querying strings when you can simply compare addresses (when you insist to filter on the application level)?
It would more generic, and less rigid and robust to changes you might have later in your code.

I could not understand the text below the qDebug() snippet, sorry.

Oleg21
20th October 2017, 20:27
I could not understand the text below the qDebug() snippet, sorry.
I apologize for the wrong text. I wanted to say that qDebug () displays the following values:
12622
I am interested in the QWidgetWindow value, which is marked in orange.
To which class is this name? Can I install event filter for an object of this class?

Again I apologize for the incomprehensible text.

high_flyer
22nd October 2017, 00:18
It seems QWidgetWindow is some sort of a non documented helper class, and it derives from QWindow.
Here is a grep output on it (searched only headers):


./qtbase/src/widgets/kernel/qapplication.h:217: friend class QWidgetWindow;
./qtbase/src/widgets/kernel/qwidget.h:714: friend class QWidgetWindow;
./qtbase/src/widgets/kernel/qwidgetwindow_p.h:40:#ifndef QWIDGETWINDOW_P_H
./qtbase/src/widgets/kernel/qwidgetwindow_p.h:41:#define QWIDGETWINDOW_P_H
./qtbase/src/widgets/kernel/qwidgetwindow_p.h:67:class QWidgetWindow : public QWindow
./qtbase/src/widgets/kernel/qwidgetwindow_p.h:71: QWidgetWindow(QWidget *widget);
./qtbase/src/widgets/kernel/qwidgetwindow_p.h:72: ~QWidgetWindow();
./qtbase/src/widgets/kernel/qwidgetwindow_p.h:139:#endif // QWIDGETWINDOW_P_H
./qtbase/src/corelib/kernel/qcoreapplication.h:220: friend class QWidgetWindow;


Without looking into the code itself, from the grep it seems that this class is a bit like a private implementation class which Qt is heavily using (PIMPL design pattern), probably to inject some special behavior for specific events in specific cases.
I think its safe to assume its your top level application window.

And again, the problem with the QWidgetWindow is a good example why using the string based polling of classes or object names is bad practice and very fragile code.
And because its part of the private Qt area also means it can change, be renames or removed in any future Qt release breaking your code.

Oleg21
22nd October 2017, 19:55
Hello. Thank you very much for helping me find "QWidgetWindow".



And again, the problem with the QWidgetWindow is a good example why using the string based polling of classes or object names is bad practice and very fragile code.
And because its part of the private Qt area also means it can change, be renames or removed in any future Qt release breaking your code.
I absolutely agree with you. Performing a check on the name of the class is not correct.

To better reflect my actions, I threw out unnecessary code from the project.
Consequently, the project contains two classes: first.cpp and second.cpp.
In the first.cpp class, is created an object of the QPushButton class "Button1".
In the second.cpp class, is created an object of the QPushButton class "Button2".
In the second.cpp class is installed events filter for qApp.
In the first.cpp class is created the object of second.cpp "second1".
Graphically I depicted it in the picture below:
12625

main.cpp


#include "first.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
first first1;
first1.show();

return a.exec();
}


first.cpp


#include "first.h"
#include "second.h"
#include <QtWidgets>

first::first(QWidget *parent) : QMainWindow(parent)
{
this->resize(230, 70);

second *second1 = new second(this);

QPushButton *button1 = new QPushButton;
button1->setParent(this);
button1->setText("Button1");
button1->setObjectName("Button1");
button1->setGeometry(120, 10, 100, 25);
}

first::~first()
{

}


second.cpp


#include "second.h"
#include <QtWidgets>

second::second(QWidget *parent) : QWidget(parent)
{
resize(115, 45);

qApp->installEventFilter(this);

QPushButton *button2 = new QPushButton;
button2->setParent(this);
button2->setText("Button2");
button2->setObjectName("Button2");
button2->setGeometry(10, 10, 100, 25);
}

second::~second(){

}

bool second::eventFilter(QObject *watched, QEvent *event){
if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *pe = static_cast<QMouseEvent*>(event);

cc++;
qDebug() << cc;
qDebug() << watched->metaObject()->className() << endl;

return false;
}else{
return false;
}
}


Now when I'm clicking a mouse on any objects, i have result:

12630

Now I installed event filter for "first1" (first.cpp).



second::second(QWidget *parent) : QWidget(parent)
{
resize(115, 45);

parent->installEventFilter(this);

QPushButton *button2 = new QPushButton;
button2->setParent(this);
button2->setText("Button2");
button2->setObjectName("Button2");
button2->setGeometry(10, 10, 100, 25);
}

"parent" is pointer to "first1" object.

When I'm clicking a mouse on "first1" or "second1" i have result:

12631

When I'm clicking a mouse on any buttons (button1 or button2) i dont have result. This is probably due to the fact that eventFilter does not work for them.
This is why I want to leave eventFilter for qApp and not for other objects. I want so that eventFilter work globally for all objects.
How to achieve this by setting eventFilter to the first.cpp class object ("first1")?

high_flyer
22nd October 2017, 22:26
What happens when you do this:


bool second::eventFilter(QObject *watched, QEvent *event){
if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *pe = static_cast<QMouseEvent*>(event);

cc++;
qDebug() << cc;
qDebug() << watched->metaObject()->className() << endl;

return false;
}

return QWidget::eventFileter(watched, event);
}

Oleg21
23rd October 2017, 18:22
What happens when you do this:

When I made the changes as you suggested, nothing has changed:


bool second::eventFilter(QObject *watched, QEvent *event){
if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *pe = static_cast<QMouseEvent*>(event);

cc++;
qDebug() << cc;
qDebug() << watched->metaObject()->className() << endl;

return false;
}

/*else{
return false;
}*/

return QWidget::eventFilter(watched, event);
}


when I'm clicking on "first1" or "second1" i have result:
12638
when I'm clicking on any buttons (button1 or button2) i do not have result.

When I made the next code:


bool second::eventFilter(QObject *watched, QEvent *event){
if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *pe = static_cast<QMouseEvent*>(event);

cc++;
qDebug() << cc;
qDebug() << watched->metaObject()->className() << endl;

return false;
}
return second::eventFilter(watched, event);
}

then the program is crashed after run.

high_flyer
23rd October 2017, 22:28
I still don't understand what it is you want to do.
What is the goal you are after? which events do you need form which objects?
If you want to filter the button events, you can install an event filter on the buttons.
This all seems very over engineered and complex for no reason.
Also, do you really need to fileter event by another object?
Are you aware you can process events in the objects themselves by overloading event() or specialized event handler?

In short: please explain what is your goal - not what you want to do (like filtering some event) - depending on the goal we can see what is the best method to achieve it.

Oleg21
24th October 2017, 19:26
I still don't understand what it is you want to do.
What is the goal you are after?

Okay, I want to record actions with the program (with the interface). And save this data in log file.


which events do you need form which objects?
If you want to filter the button events, you can install an event filter on the buttons.


I want to install global eventFilter, so that work with everyone object at once. I don't want to install unique eventFiletr for all objects in my app. Quantity and type of objects are unknown in advance.



Also, do you really need to fileter event by another object?


The idea itself is to put this code into a separate class and include it as needed (a utility class).



Are you aware you can process events in the objects themselves by overloading event() or specialized event handler?

Overloading event() - yes i know about it. But this will force me to overload the event() in all objects. It's complicated.

Therefore, in my opinion, the best option of all is to use:


qApp->installEventFilter(this);

Oleg21
26th October 2017, 18:51
Hello.
You wrote above:

It seems QWidgetWindow is some sort of a non documented helper class, and it derives from QWindow.
And really it is so! I read the Qt source code. Namely the header file qwidgetwindow_p.h. Here is a warning I saw in this file:

qwidgetwindow_p.h


//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

You were right!

So, on this basis, I firmly decided to refuse to use any parts of the code that mention the "QWidgetWindow" class.
Therefore, I reworked my evenFilter:


bool second::eventFilter(QObject *watched, QEvent *event){
QPoint tempPoint(0, 0);

if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *pe = static_cast<QMouseEvent*>(event);

tempPoint = pe->globalPos();
QWidget *w = QApplication::widgetAt( tempPoint );

if (w == watched) {
iTempCount = iTempCount + 1;
qDebug() << iTempCount;
qDebug() << watched->metaObject()->className();
}

return false;
}else{
return false;
}
}

Indeed, this code is also not perfect. But he suits me.

Thank you for the help provided to me. I apologize if I was rude, obtrusive or incomprehensible.