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
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.