[Interest] QML engine, C++ model in secondary thread, signal lag, and infinite change loop

Federico Ferri federico.ferri.it at gmail.com
Tue Mar 17 11:30:06 CET 2020


consider this scenario:

I have a Qt Quick application, and a C++ model which I need to have in a
separate thread. Since it is not possible* to connect a QObject from
another thread with the QML engine, I have also a proxy QObject living in
the main thread, forwarding the signals and slots via other signal/slot
connections. This can be sumarized in this diagram:

QML Engine <—> Proxy QObject <—> QObject model [worker QThread]

Obviously the UI should react to changes in the model, and the model should
be updated by UI.

For simplicity, assume the model is a real number, and the UI is a slider
to change this value.

>From what I can see, the mechanism for avoiding an infinite change loop
adopted by QtQuick is to generate a valueChanged event (signal) if the
value being set is different from the old value. So I implement this
mechanism also on the C++ model side.

So, for instance, if the slider’s value is 42, and i (programmatically) set
it to 42, no change event happens; if I set it to 99, a valueChange will be
emitted, which will also reach the model, set the model state to 99 as
well; the latter will cause another valueChange to be emitted from C++ back
to QML, but it will stop there, as the value is already 99.
Similar thing would happen if directly changing the value on the model.

So far so good… well, that’s what I naïvely thought before hitting this

Suppose we start again from 42. Dragging the slider will generate
valueChanged(43), valueChanged(44), etc.. in a burst. Due to the queued
connection across the QThread boundary, these changes will lag a bit. As
soon as the model sees the valueChanged(43), will emit a similar signal
back to the UI. But when that valueChanged(43) signal will reach the UI,
due to the lag, the UI will be already in the 44 state, i.e. at the point
where the dragging had stopped. So it will generate another
valueChanged(43) towards the model, while a similar thing is happening also
for the next signal in the queue (44), thus generating an infinite sequence
of change signals.

Where is the flaw in my design?
How can this problem be solved in a way that every Qt engineer could say
“yes, that’s how it should be done”? :-)

Note: I would have preferred a lot if the proxy QObject was not needed,
i.e. if the QML engine could make queued connections to a QObject living in
another QThread. But that is not the cause of the problem, as with a queued
connection (between QML engine and the C++ model in the worker thread),
there still would be lag in signal/slot transport.

[*]: see "QQmlEngine: Illegal attempt to connect to ... that is in a
different thread than the QML engine” and suggested solutions to it.

Best regards,
Federico Ferri
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20200317/17b9523c/attachment.html>

More information about the Interest mailing list