Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Delegate creation and multithreaded property updating
Forum Updated to NodeBB v4.3 + New Features

Delegate creation and multithreaded property updating

Scheduled Pinned Locked Moved Solved QML and Qt Quick
47 Posts 3 Posters 10.8k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    Asperamanca
    wrote on last edited by
    #8

    Could you post your current code, like you did in the OP?

    F 1 Reply Last reply
    0
    • A Asperamanca

      Could you post your current code, like you did in the OP?

      F Offline
      F Offline
      felsi
      wrote on last edited by
      #9

      @Asperamanca
      The code hasn't changed, i only changed the way i call it and added the lines to verify.
      Aren't connections between C++ and QML blocking queued?
      Therefore i don't think, that the mixed-up creation and updating on the qml side is caused on the C++ side.
      I think my multithreaded approach only increased the likelyhood of happening and is apart from that coincidental.

      1 Reply Last reply
      0
      • A Offline
        A Offline
        Asperamanca
        wrote on last edited by
        #10

        Does the qDebug() for setTrack appear with track 3? And in the main thread?

        1 Reply Last reply
        0
        • A Offline
          A Offline
          Asperamanca
          wrote on last edited by Asperamanca
          #11

          Side note:
          I just noticed that this was pointless anyway:

              Track *PlaylistTrack::getTrackRef()
              {
                  QReadLocker locker(&this->lock);
                  return this->track.data();
              }
          

          You hand out a pointer to the internal data of the track. The locker goes out of scope, and the pointer keeps being used in QML.
          When setTrack assigns a new pointer to this->track, the old shared_ptr goes out of scope, gets destroyed, but might still be used by QML (use-after-free)
          So your locking mechanism wouldn't have worked anyway.
          Reference semantics are fraught with danger...

          F 1 Reply Last reply
          1
          • A Asperamanca

            Side note:
            I just noticed that this was pointless anyway:

                Track *PlaylistTrack::getTrackRef()
                {
                    QReadLocker locker(&this->lock);
                    return this->track.data();
                }
            

            You hand out a pointer to the internal data of the track. The locker goes out of scope, and the pointer keeps being used in QML.
            When setTrack assigns a new pointer to this->track, the old shared_ptr goes out of scope, gets destroyed, but might still be used by QML (use-after-free)
            So your locking mechanism wouldn't have worked anyway.
            Reference semantics are fraught with danger...

            F Offline
            F Offline
            felsi
            wrote on last edited by felsi
            #12

            Yes, i just checked. (it appears more often, due to unrelated changes)
            The lock is needed to safely access the member on the C++ side, not to protect between C++ and QML.
            Yes, that's a bit ugly, but i am aware of that and there is always one shared pointer left holding the reference.
            And what alternatives do i have? I think this is a general problem with shared pointers and qml.

            A 1 Reply Last reply
            0
            • F Offline
              F Offline
              felsi
              wrote on last edited by
              #13

              Oh, sorry, i misunderstood, exactly, you're absolutely wright.
              No problem with gui thread...

              1 Reply Last reply
              0
              • F felsi

                Yes, i just checked. (it appears more often, due to unrelated changes)
                The lock is needed to safely access the member on the C++ side, not to protect between C++ and QML.
                Yes, that's a bit ugly, but i am aware of that and there is always one shared pointer left holding the reference.
                And what alternatives do i have? I think this is a general problem with shared pointers and qml.

                A Offline
                A Offline
                Asperamanca
                wrote on last edited by
                #14

                @felsi The alternative is to provide a stable pointer (the Q_PROPERTY is then a CONSTANT and cannot change), and change the content of the object.
                The object itself has Q_PROPERTY entries in turn for all the single properties that can change (and some of them could - again - be stable pointers)

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Asperamanca
                  wrote on last edited by
                  #15

                  But that was a side note.
                  Is your problem with Track 3 still open, or does it work now?

                  1 Reply Last reply
                  0
                  • F Offline
                    F Offline
                    felsi
                    wrote on last edited by
                    #16

                    Yes, still open, nothing changed about that problem.
                    And pretty sure not related to threading anymore.

                    Uh, thank you very much, i will check out these topics!

                    1 Reply Last reply
                    0
                    • A Offline
                      A Offline
                      Asperamanca
                      wrote on last edited by
                      #17

                      I didn't see whether your setter for Track 3 was ever called. Your logging does not contain that information.

                      F 1 Reply Last reply
                      0
                      • A Asperamanca

                        I didn't see whether your setter for Track 3 was ever called. Your logging does not contain that information.

                        F Offline
                        F Offline
                        felsi
                        wrote on last edited by
                        #18

                        It looks like this:

                        onPlaylistTrackChanged 1
                        updateTrack 1 : NULL
                        onCompleted 1
                        onPlaylistTrackChanged 2
                        updateTrack 2 : NULL
                        onCompleted 2
                        onPlaylistTrackChanged 3
                        updateTrack 3 : NULL
                        setTrack 1
                        onTrackChanged 1
                        updateTrack 1 : Track 1
                        setTrack 2
                        onTrackChanged 2
                        updateTrack 2 : Track 2
                        setTrack 3
                        setTrack 4
                        setTrack 5
                        onCompleted 3
                        onPlaylistTrackChanged 4
                        updateTrack 4 : Track 4
                        onCompleted 4
                        onPlaylistTrackChanged 5
                        updateTrack 5 : Track 5
                        onCompleted 5

                        1 Reply Last reply
                        0
                        • A Offline
                          A Offline
                          Asperamanca
                          wrote on last edited by
                          #19

                          I would really try this:

                          class Track : public QObject
                          {
                              Q_OBJECT
                              Q_PROPERTY(QString labeling READ getLabeling NOTIFY notifyLabelingChanged) // I would prefer BINDABLE, it's much simpler to code, but not relevant to the issue at hand
                              Q_PROPERTY(QString duration_string READ getDurationString NOTIFY notifyDurationStringChanged)
                          public:
                          // Add the obvious getters and setters
                          
                          private:
                              QString m_Labeling{"loading..."};
                              QString m_DurationString{"00:00"};
                          };
                          
                          class PlaylistTrack : public QObject
                          {
                              Q_OBJECT
                              Q_PROPERTY(Track* track READ getTrackPtr CONSTANT) // I would add FINAL, but not really relevant
                          public:
                          
                              Track* getTrack() {return &m_Track;}
                          
                          private:
                              Track m_Track; // If you want PIMPL, I'd use std::unique_ptr<Track> and create it in the constrcutor
                          };
                          

                          The track will be created with PlaylistTrack , and will live as long as PlaylistTrack does.
                          Initially, it holds the default strings you previously specified in your QML.
                          Once you have data, you simply change the properties, and QML should update.
                          There are some variants to this pattern, but the important thing is that object points stay where they are for as long as the QML scene exists. This saves you from a lot of tricky edge cases and extra checks on the QML side.

                          F 1 Reply Last reply
                          0
                          • A Asperamanca

                            I would really try this:

                            class Track : public QObject
                            {
                                Q_OBJECT
                                Q_PROPERTY(QString labeling READ getLabeling NOTIFY notifyLabelingChanged) // I would prefer BINDABLE, it's much simpler to code, but not relevant to the issue at hand
                                Q_PROPERTY(QString duration_string READ getDurationString NOTIFY notifyDurationStringChanged)
                            public:
                            // Add the obvious getters and setters
                            
                            private:
                                QString m_Labeling{"loading..."};
                                QString m_DurationString{"00:00"};
                            };
                            
                            class PlaylistTrack : public QObject
                            {
                                Q_OBJECT
                                Q_PROPERTY(Track* track READ getTrackPtr CONSTANT) // I would add FINAL, but not really relevant
                            public:
                            
                                Track* getTrack() {return &m_Track;}
                            
                            private:
                                Track m_Track; // If you want PIMPL, I'd use std::unique_ptr<Track> and create it in the constrcutor
                            };
                            

                            The track will be created with PlaylistTrack , and will live as long as PlaylistTrack does.
                            Initially, it holds the default strings you previously specified in your QML.
                            Once you have data, you simply change the properties, and QML should update.
                            There are some variants to this pattern, but the important thing is that object points stay where they are for as long as the QML scene exists. This saves you from a lot of tricky edge cases and extra checks on the QML side.

                            F Offline
                            F Offline
                            felsi
                            wrote on last edited by felsi
                            #20

                            Tracks are widely spread across my whole program. My collection points to them, sorted trees of my collection point to them and different PlaylistTracks in different playlists can point to the same Track. They can even get replaced program-wide.
                            They are created by the collection and are only looked up in a hash table. All for low memory, fast loading, etc.
                            I can live with my little qml workaround, i encountered no other problems.
                            I posted more, because maybe there is a bug, when using connections in delegates.

                            1 Reply Last reply
                            0
                            • A Offline
                              A Offline
                              Asperamanca
                              wrote on last edited by
                              #21

                              I admit I don't fully understand the issue you have myself. However, eliminating the state change from "null" to "something" in QML can't hurt. If you want to manage the tracks in a more centralized way, pass the shared_ptr as part of the constructor of PlaylistTrack and never change it during the lifetime of PlaylistTrack. Make the Q_PROPERTY CONSTANT. From the perspective of QML, that should be the same as my suggestion.

                              F 1 Reply Last reply
                              0
                              • A Asperamanca

                                I admit I don't fully understand the issue you have myself. However, eliminating the state change from "null" to "something" in QML can't hurt. If you want to manage the tracks in a more centralized way, pass the shared_ptr as part of the constructor of PlaylistTrack and never change it during the lifetime of PlaylistTrack. Make the Q_PROPERTY CONSTANT. From the perspective of QML, that should be the same as my suggestion.

                                F Offline
                                F Offline
                                felsi
                                wrote on last edited by
                                #22

                                Thank you very much, i also prefer the safe way, if possible. :)
                                And your suggestion is clean and safe. But i really need to be able to set and change the track for a variety of reasons.
                                In fact, it also appears when i change the track.

                                And for the missed signal problem, it should not matter whether it's an address or a value, it's not even a signal-parameter.
                                To me, it looks like, that the delegate gets created in a separate thread, otherwise "updateTrack 2" couldn't appear during creation of track 3. But i don't have a clue about the Qt source code, so i can't look that up easily...
                                And my guess would further be, that signals emitted after the creation has started and before the connection is established, are simply lost.

                                1 Reply Last reply
                                0
                                • A Offline
                                  A Offline
                                  Asperamanca
                                  wrote on last edited by
                                  #23

                                  To the best of my knowledge, all the scene setup and JavaScript runs in the main thread. Only parts of rendering may run in a different thread, but they would not change your properties.
                                  Is the "updateTrack" in the Button's onCompleted still commented out? Because your logging shows that track 3 gets the onCompleted after some properties have already been set, so I would not expect any signals to arrive anymore.

                                  F 1 Reply Last reply
                                  0
                                  • A Asperamanca

                                    To the best of my knowledge, all the scene setup and JavaScript runs in the main thread. Only parts of rendering may run in a different thread, but they would not change your properties.
                                    Is the "updateTrack" in the Button's onCompleted still commented out? Because your logging shows that track 3 gets the onCompleted after some properties have already been set, so I would not expect any signals to arrive anymore.

                                    F Offline
                                    F Offline
                                    felsi
                                    wrote on last edited by
                                    #24

                                    Fortunately, the problem appears every few runs...
                                    By the way, there is of course no problem, when all are set before or after the delegate creations.
                                    And also not everytime when in between.
                                    After a few checks, i would say, the workarounds behave like expected.
                                    For completeness:

                                    With connectTrack() hackmack:
                                    onPlaylistTrackChanged 1
                                    connectTrack 1
                                    updateTrack 1 : NULL
                                    onCompleted 1
                                    onPlaylistTrackChanged 2
                                    connectTrack 2
                                    updateTrack 2 : NULL
                                    onCompleted 2
                                    onPlaylistTrackChanged 3
                                    connectTrack 3
                                    updateTrack 3 : NULL
                                    setTrack 1
                                    onTrackChanged 1
                                    updateTrack 1 : Track 1
                                    setTrack 2
                                    onTrackChanged 2
                                    updateTrack 2 : Track 2
                                    setTrack 3
                                    onTrackChanged 3
                                    updateTrack 3 : Track 3
                                    setTrack 4
                                    setTrack 5
                                    onCompleted 3
                                    onPlaylistTrackChanged 4
                                    connectTrack 4
                                    updateTrack 4 : Track 4
                                    onCompleted 4
                                    onPlaylistTrackChanged 5
                                    connectTrack 5
                                    updateTrack 5 : Track 5
                                    onCompleted 5

                                    With updateTrack() in onCompleted:
                                    onPlaylistTrackChanged 1
                                    updateTrack 1 : NULL
                                    onCompleted 1
                                    updateTrack 1 : NULL
                                    onPlaylistTrackChanged 2
                                    updateTrack 2 : NULL
                                    onCompleted 2
                                    updateTrack 2 : NULL
                                    onPlaylistTrackChanged 3
                                    updateTrack 3 : NULL
                                    setTrack 1
                                    onTrackChanged 1
                                    updateTrack 1 : Track 1
                                    setTrack 2
                                    onTrackChanged 2
                                    updateTrack 2 : Track 2
                                    setTrack 3
                                    setTrack 4
                                    setTrack 5
                                    onCompleted 3
                                    updateTrack 3 : Track 3
                                    onPlaylistTrackChanged 4
                                    updateTrack 4 : Track 4
                                    onCompleted 4
                                    updateTrack 4 : Track 4
                                    onPlaylistTrackChanged 5
                                    updateTrack 5 : Track 5
                                    onCompleted 5
                                    updateTrack 5 : Track 5

                                    1 Reply Last reply
                                    0
                                    • A Offline
                                      A Offline
                                      Asperamanca
                                      wrote on last edited by
                                      #25

                                      I wouldn't call it a workaround. The order of creation and update is not deterministic, so you have to take into account that components are created before or after you have already set some properties. For example, are some items visible and some not? This could trigger a kind of "priority of initialization" heuristic.

                                      F 1 Reply Last reply
                                      0
                                      • A Asperamanca

                                        I wouldn't call it a workaround. The order of creation and update is not deterministic, so you have to take into account that components are created before or after you have already set some properties. For example, are some items visible and some not? This could trigger a kind of "priority of initialization" heuristic.

                                        F Offline
                                        F Offline
                                        felsi
                                        wrote on last edited by felsi
                                        #26

                                        Yeah, but i wouldn't call it a patch either... ;)
                                        Yes, i know, that's the reason, why i force the connection to be established before i update.
                                        As far as i can see, that ensures, that there is at least one correct update.
                                        And i have no other problems, everything's updating fine, if it does.
                                        Without an explanation of the root cause i have no better solution...

                                        I am very thankful for your time and effort to help me.
                                        But honestly, if it's not a Qt bug, i will move on and live with the obscurity...

                                        1 Reply Last reply
                                        0
                                        • A Offline
                                          A Offline
                                          Asperamanca
                                          wrote on last edited by
                                          #27

                                          QML scene are often loaded asynchronously. That's just necessary to prevent everything from blocking as you load a large scene.
                                          When do you start calling the setters for Track? I don't believe I have seen your code that controls it.
                                          Basically, if you don't wait for your QQuickView / QQuickWidget to signal it's status "Ready", you cannot depend that all components exist. Even after that, if you are using Loader or dynamic instantiation, you could be surprised. So the most robust code will not depend on the order of events. Component created first? Properties changed first? You should not (need to) care.

                                          F 1 Reply Last reply
                                          1

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved