I've inherited a legacy qt gui application. One of first thing that I need to do is to create a log file where every user interaction is logged, like the click of a button, the edit of a QLineEdit and so on. This due to the fact that users apparently are not be able to tell how to reproduce specific bugs, so they want, when a bug is opened, to read this log file in order to check the steps that the user performed when he found it.
I've seen that it's possible to override the notify
method in QCoreApplication
in order to get all events, and it works, but it's not what I want, since it seems to manage only events of the operating system, like focusing etc, and it does not handle the emitting of signals.
Of course I can change the code and create a connection between, for example, a textChanged
signals and a lambda that reports it, but in this case I will need to change all che code. Is there a way to achieve the same result in another way, without rewriting the most part of the application?
You can list all widgets in your application by using QApplication::allWidgets()
.
Then, you could list all signals of each widget (with QObject::metaObject()
) and connect them to a logger.
A quick example:
class Spy: public QObject {
Q_OBJECT
public:
Spy();
public slots:
void log();
};
void Spy::log()
{
auto i = senderSignalIndex(); // The signal that fired this slot
auto signal = sender()->metaObject()->method(i);
qDebug() << sender() << signal.methodSignature();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
Spy* spy = new Spy();
QMetaMethod log = spy->metaObject()->method(spy->metaObject()->indexOfSlot("log()")); // in order to use the right QObject::connect signature
for (auto widget: a.allWidgets())
{
auto metaObject = widget->metaObject();
for (int i = 0; i != metaObject->methodCount(); ++i)
{
auto method = metaObject->method(i);
if (method.methodType() != QMetaMethod::Signal) // We want to connect all the signals. We don't care about the other methods
continue;
widget->connect(widget, method, spy, log);
}
}
return a.exec();
}
You'll have to improve the code if you want to display the parameters.
You can also filter the objects to log only the significant signals (for ex, logging only the clicked
signal from buttons)