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. How to discern between user and programming generated events in QtQuick controls?
QtWS25 Last Chance

How to discern between user and programming generated events in QtQuick controls?

Scheduled Pinned Locked Moved QML and Qt Quick
5 Posts 2 Posters 1.9k Views
  • 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
    ariacorrente
    wrote on last edited by
    #1

    I'm doing some tests with QtQuick controls to find out how to use it in a bigger project.
    I'm controlling a webcam and i want to connect a QML spinbox to a C++ module so i can change the exposure time of the webcam. I successfully connected the spinbox so i can change the exposure and i can check on a QML label the state of the C++ property.
    Now i want to update the state of the spinbox if the exposure property changes in the C++ module. If i bind the two propertyes together the update happens only one way so i tried to use "onValueChanged".
    The problem is that the event "valueChanged" is emitted not only when the user interacts but also if the software changes the value. If i connect both events like:
    @
    //C++ module connected to the camera
    CppCore {
    id: cppCore
    onValueChanged: spinBoxExposure.value = value;
    }
    //QML control connected to the user
    SpinBox {
    id: spinboxExposure
    onValueChanged: cppCore.value = value;
    }
    @

    It all loops and go crazy :-) .

    In QtWidget i remember i avoided the problem using signals emitted only by user interaction or disconnecting the events when needed.

    How can i do the same in QML? I think this problem can surface in a other environments, how are you dealing with it?

    1 Reply Last reply
    0
    • F Offline
      F Offline
      fonzi337
      wrote on last edited by
      #2

      You probably want to try to avoid the property binding being updated from both C++ and QML. Not just from a practical standpoint to avoid the infinite loop, but also for debugging sanity. :)

      One way I accomplished this for one of my projects was to consolidate the property updates in C++. I did this by exposing a read-only property from my C++ object to QML (done by omitting the WRITE attribute in the Q_PROPERTY declaration). QML would use this property to read the current value.

      I allowed QML to set this value by exposing a separate function from C++ marked as Q_INVOKABLE. This function, being in C++, could perform more complex error handling and other operations and would update the read-only property value as appropriate.

      Hopefully this approach could be applied to your use case as well.

      1 Reply Last reply
      0
      • A Offline
        A Offline
        ariacorrente
        wrote on last edited by
        #3

        Thanks for the reply.

        Currently (Qt5.1.1) binding a property between QML and C++ and change the value both side is safe because only the QML side is updated. At leas this is what i'm seeing in my tests with SpinBox and property binding:

        @
        webcam initialization => c++ => SpinBox => User
        User action => SpinBox | STOP
        @

        About your solution. I don't understand how your approach can avoid the loop if i connect the read-only property and the Q_INVOKABLE to the same QML control. Are you using two separate controls?

        For now i'm using a SpinBox as input and a Label as output but it's a quite bad solution. The spinbox is out of sync every time the camera rejects a value. Be able to stay up to date it's definitely an important feature for a graphical interface. I'ts like i'm missing some important detail here, i can't see a way to avoid the problem without adding a signal like "valueChangedByUser".

        1 Reply Last reply
        0
        • F Offline
          F Offline
          fonzi337
          wrote on last edited by
          #4

          Hi ariacorrente,

          Here is what I was thinking (pseudo code, not compiled/tested):

          [code]
          // cppcore.h
          #include <QObject>

          class CppCore : public QObject
          {
          Q_OBJECT
          // Note: a read-only property (no WRITE attribute). This is the only exposure-related property shown in the QML GUI.
          Q_PROPERTY(qreal exposure READ exposure NOTIFY exposureChanged)

          public:
          CppCore(QObject* parent = nullptr) :
          QObject(parent),
          m_exposure(0.0)
          {

          }
          
          qreal exposure() const
          {
              return m_exposure;
          }
          

          public slots:
          // An separate exposure setter
          void commitExposure(const qreal exposure)
          {
          // if check needed to avoid infinite loop with the SpinBox's onValueChanged slot.
          if(m_exposure != exposure)
          {
          // do some error handling, other adjustments here.
          m_exposure = ;// some new value
          emit exposureChanged(m_exposure);
          }
          }

          private:
          qreal m_exposure;

          };
          [/code]

          QML:

          [code]
          import QtQuick 2.1
          import QtQuick.Controls 1.1

          Item {
          // ...

          SpinBox {
              // _cppCore is the C++ CppCore object exposed as a context property with setContextProperty().
              value: _cppCore.exposure
          
              onValueChanged: _cppCore.commitExposure(value)
          }
          

          }
          [/code]

          I'm thinking the above provides the primary benefit of allowing the property binding on the SpinBox's value property is be maintained throughout program execution (because no assignment (=) is used to modify its value).

          The if check in commitExposure() also ensures you avoid an infinite loop when the user modifies the exposure value using the SpinBox.

          Also, the C++ side is free to modify the exposure value it gets from the camera. All it has to do is ensure the exposureChanged signal is emitted to update it in QML.

          Would this work for your use case?

          1 Reply Last reply
          0
          • A Offline
            A Offline
            ariacorrente
            wrote on last edited by
            #5

            Thanks fonzi337, yours is a nice solution but unfortunately doesn't solve all my problems.
            [quote author="fonzi337" date="1386355198"]
            The if check in commitExposure() also ensures you avoid an infinite loop when the user modifies the exposure value using the SpinBox.
            [/quote]
            In commitExposure i can't check the current value because the camera code is running on another thread connected through signals.
            Maybe i can keep a copy of the state of the camera on the GUI thread and/or expose a camera object directly to QML.
            It sound like a lot of unneeded complications. It's the spinbox that knows the cause of the valueChange event, it's so strange this information is discarded and now i must guess it studying the variation of the values.

            Update:
            I'm continuing testing possible solutions so i update the post just in case someone else have a similar problem. I was trying to connect the camera object directly to QML to avoid too many signal/slot connections. This is the result:
            @QQmlEngine: Illegal attempt to connect to Camera(0x22fd88) that is in a different thread than the QML engine QQmlApplicationEngine(0x22fe70).@
            So I think this option is not viable.

            1 Reply Last reply
            0

            • Login

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