Is there an equivalent to Gtk's GObject.idle_add()
method in Qt?
The
gobject.idle_add()
function adds a function to be called whenever there are no higher priority events pending to the default main loop.
My research led me to some QTimer
trickery:
QtCore.QTimer().singleShot(0, self.my_method)
but it looks kind of hackish, not as straightforward, which makes me think it might not be the best option.
Is there a "Qter" solution? Or shouldn't I be doing this in the first place?
The context
My GUI has widgets that act upon plot parameters. I use signals/slots to refresh the plot when widgets are clicked/edited/whatever. A widget may impact several other widgets, thus triggering in cascade several refresh actions on a single user click, so I'm looking for a way to avoid refreshing multiple times if not needed: only the last refresh makes sense.
I tried disconnecting signals while processing such events, then connecting them back when done and calling refresh "manually". I also tried using a flag to inhibit refresh method which is more or less equivalent. Both ways seemed uselessly complicated.
I'm thinking instead of connecting each widget to refresh()
, I could connect them to a refresh_request()
method that would raise a "refresh request" flag and set a 0 second timer to execute refresh_if_needed()
when idle to refresh if flag is raised. Only one refresh would occur since it would execute only when the event processing is done. And it would refresh only if at least one widget's data was really modified. Besides, the GUI might feel a little more fluid to the user, since the event would be processed faster.
Edit
Rather than having refresh_request()
raise the "refresh request" flag and add a timer to deal with the flag in idle, I could have refresh_if_needed()
permanently watching the flag from main loop, in which case refresh_request()
would only have to deal with the flag.
Would this be better design? Not sure, but in any case, my question could be "how can I add some task to the main loop in Qt?". Would this eat all CPU? Would I need to add a "manual", 500msec or so, pause in refresh_if_needed()
? In this case I guess I could launch it using a QTimer
in normal mode (not singleShot), but is this the "normal" way?
Regardless, I'm still interested in an equivalent to idle_add
. A use case I have in mind is a Gtk GUI I coded a few years ago. The buttons start or stop sounds in threads. To avoid race conditions, I started and stopped sounds in the main loop (code on GitHub (1000 LoC file, not a trimmed-down example)).
The equivalent to g_idle_add()
in Qt can be achieved by implementing a custom event for one of your widgets, which is handled by overriding the receiver's event()
method as shown in the documentation for Qt 5 and was already pointed out in the comments.
A Qt 5 example (Qt 4 is similar for all of this) would be:
// Initialize event type
int customType = QEvent::registerEventType();
// Handle
bool MyWidget::event(QEvent* event) {
if (event->type() == (QEvent::Type) customType) {
// TODO: Do whatever your idle callback would do
// Optional: Post event again to self if this should be repeated
// implementing behaviour of returning TRUE from g_idle_add callback
return true;
}
// Else default handler, may want to replace QWidget with actual base of "MyWidget"
return QWidget::event(event);
}
// Emit (given this is of type MyWidget)
QApplication::postEvent(this, new QEvent((QEvent::Type) customType));
However, if the goal is just to always defer handling of a signal to the main loop, as is the case with OP's question, there is an easier alternative. When connecting slot and signal, specify Qt::QueuedConnection
for the type
parameter of QObject::connect()
. Different connection types are documented here.