Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Change QML-property in C++



  • I've got a ToolButton (QML Desktop Widgets) and want to change its icon.

    @ ToolButton{
    objectName: "imgConnected"
    iconSource: "images/not-connected.png"
    anchors.verticalCenter: parent.verticalCenter
    }@

    Now when I try to change the iconSource I do this:

    @
    QDeclarativeView view(QUrl("qml/MyProject/main.qml"));
    view.show();

    // get root object
    QObject *rootObject = dynamic_cast<QObject *>(view.rootObject());
    // find element by name
    QObject *image = rootObject->findChild<QObject *>(QString("imgConnected"));
    
    image->setProperty("iconSource", QString("D:/Dropbox/Projekte/MyProject/release/images/connected.png"));
    

    @

    (I know that I have to remove the absolute path, its just there for testing).

    My problem is that whenever this code is executed another window looking like the Mainwindow pops up for a second and then hides. The iconSource never changes visibly though.



  • What you can do is assign a variable for that icon and change that variable from C++ side so
    that icon updates.. I was using that technique for states and it was working well..



  • Can you provide an example?



  • Say you have a onStateChange slot somewhere in you QDV implementation and you want to change state property of you QML ui. You could write the slot like that

    @
    void SignageView::onStateChange(const QString &newState)
    {
    QDeclarativeProperty state(rootObject(), QString("state"));

    if (state.type() == QDeclarativeProperty::Invalid) {
        WRT("state propery is invalid, unable to change qml state..");
        return;
    } else {
        state.write(newState); // should reflect immediately
    }
    

    }
    @

    AFAIK as long as you provide the notify() calls of your QProperty's you can use those properties in QML too.



  • I think that you should try to solve this by having the C++ side "announcing" when is has changed to connected state.

    One way could be to set a context property:
    @
    view->rootContext()->setContextProperty("connected", mgr->isConnected());
    @
    You have to set this property each time the connection changes.

    The QML file could look something like this:

    @
    ToolButton {
    ...
    iconSource: connected ? "images/connected.png" : "images/disconnected.png"
    }
    @

    By doing this, the view controls what image should be shown when, not the C++ side.



  • First of all: I think your (Hedge) approach should work. If it does not, then that is worth digging in to further.

    On the other hand, I think that this approach is not the most elegant, and if possible, should be avoided. Instead, you can insert a QObject in your QML environment and use the property binding methods. For that to work, you need to create a QObject class like this:

    @
    class QmlCppMediator: public QObject {
    Q_OBJECT

    Q_PROPERTY (bool connected READ isConnected WRITE setConnected NOTIFY connectedChanged);

    public:
    QmlCppMediator(QObject* parent) :
    QObject(parent),
    m_connected(false) {}

    void setConnected(bool value) {m_connected = value; emit connectedChanged();}
    bool isConnected() {return m_connected;}
    

    signals:
    connectedChanged();

    private:
    bool m_connected;
    }@

    Of course, your bridge could have many more methods to interface between your QML and your C++ parts, and the properties could also be implemented in other ways. The idea is that you provide a "mediator":http://en.wikipedia.org/wiki/Mediator_pattern between your C++ and your QML.

    You can insert an instance (probably the instance) of this class using the setContextProperty method mentioned by mario above:

    @
    view->rootContext()->setContextProperty("cppInterface", m_qmlCppMediator);
    @

    You can then connect to the exposed properties like this:
    @
    ToolButton {
    ...
    iconSource: cppInterface.connected ? "images/connected.png" : "images/disconnected.png"
    }
    @
    You can of course group functionality that logically belongs together in such a mediator object, and use more than one of these.



  • Thanks for all your help. I implemented it as Andre said.

    It finally works.

    One error keeps bugging me though.
    I've got bq. file:///D:/MyProject/main.qml:28: ReferenceError: Can't find variable: cppInterface
    For some reasons it still works.

    That is how my main-function looks like:

    @int main(int argc, char *argv[])
    {
    QApplication app(argc, argv);

    QmlApplicationViewer viewer;
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile&#40;QLatin1String("main.qml"&#41;&#41;;
    viewer.showExpanded();
    
    
    QmlCppMediator m_qmlCppMediator;
    QDeclarativeContext *context = viewer.rootContext();
    
    context->setContextProperty("cppInterface", &m_qmlCppMediator);
    m_qmlCppMediator.setConnected(true);
    
    IcecastServer icecastServer(&m_qmlCppMediator);
    
    return app.exec(&#41;;
    

    }@



  • Hi,

    You should be able to get rid of the error by setting the context property before calling setMainQmlFile().

    Regards,
    Michael



  • Indeed: first setup the context with the mediator object, and then set the QML.


Log in to reply