[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 14:23:36 CET 2020


On 17 March 2020 at 12:03:59, Mitch Curtis (mitch.curtis at qt.io) wrote:

Hi.

> -----Original Message-----
> From: Interest <interest-bounces at qt-project.org> On Behalf Of Federico
> Ferri
> Sent: Tuesday, 17 March 2020 11:30 AM
> To: interestqt-project.org <interest at qt-project.org>
> Subject: [Interest] QML engine, C++ model in secondary thread, signal lag,

> and infinite change loop
>
> Hi,
>
> 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
> problem:
>
> 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.

I don't know if this is actually what's causing your problem, but you
should use the signals that are emitted upon user interaction, not the
change signals for properties, as that will cause these kind of headaches
with binding loops and stuff. For Slider, that is moved():

https://doc.qt.io/qt-5/qml-qtquick-controls2-slider.html#moved-signal


Thanks!

Using a signal exclusively emitted upon user input breaks the loop. This is
the solution I was looking for.


> 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”? :-)

I don't have an answer to this, and I don't know if this is helpful, but
here's how I did some stuff in a separate thread and then displayed the
results in a model:

https://github.com/mitchcurtis/slate/blob/master/lib/autoswatchmodel.h


That is basically the Worker/Controller pattern described in
https://doc.qt.io/qt-5/qthread.html#details no?

I’m doing the same.


Cheers,

Federico Ferri
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20200317/673988f5/attachment.html>


More information about the Interest mailing list