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

Вопрос чайника по Qt Quick



  • Извините за глупый вопрос, я только осваиваю Qt Quick. Доки великолепные, но ответов на глупые вопросы в них нет :-) Пока не взберешься хоть чуть-чуть по кривой обучения...

    В QML я могу написать:

    textEdit1.text: textEdit2.text
    textEdit2.text: textEdit1.text
    

    И вот она, особая уличная магия. Как движку Qt Quick это удалось? По логике, единственное, что нужно знать - соответствие имени свойства (text) и имени сигнала об изменении этого свойства (textChanged). Зная только это, движок как бы неявно добавляет:

    textEdit2.onTextChanged: textEdit1.text = textEdit2.text
    textEdit1.onTextChanged: textEdit2.text = textEdit1.text
    

    и вуаля.

    Теперь я хочу сделать такой же двусторонний байндинг, но чтобы вместо textEdit2 был объект C++.

    class TextObject: public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
        ...
    };
    ...
    TextObject textObject2;
    engine.rootContext()->setContextProperty("textObject2", &textObject2);
    

    По принципу наименьшего удивления руки сами пишут:

    textEdit1.text: textObject2.text
    textObject2.text: textEdit1.text
    

    А шо? Ведь и тут Qt известна связь между свойством text и сигналом textChanged объекта textObject2.

    Фигушки. Чтобы двусторонний байндинг заработал пришлось расписать аж так:

    textEdit1.text: textObject2.text
    textEdit1.onTextChanged: textObject2.text = textEdit1.text
    Connections
    {
        target: textObject2
        onTextChanged: mainForm.textEdit1.text = textObject2.text
    }
    

    Просто не верится, что в таком умном Qt Quick требуется такое непотребство.

    Вопрос: а как правильно?



  • у меня тоже вопрос чайника:
    а можно привести полные исходные тексты или хотя бы qml, ибо не могу однозначно понять каким образом приведенные строки кода тестируются?
    textEdit1.text: textObject2.text
    textObject2.text: textEdit1.text

    http://pastebin.com/fLqtaQiD - у меня вот так работает, правда через qmlRegisterType.



  • Спасибо за ответ!

    а можно привести полные исходные тексты или хотя бы qml, ибо не могу однозначно понять каким образом приведенные строки кода тестируются?

    А они никак и не тестируются, так как это не работает. Ошибка Cannot assign to non-existent property "textObject2". Приведенное – это то, что казалось логичным по аналогии с парой textEdit-ов.

    у меня вот так работает, правда через qmlRegisterType

    А у меня не работает =/ Добавил к вашему примеру кнопку, два слота и отладочного вывода: textobject.h, main.qml, main.cpp не менял.

    Во-первых, textObject2 в main.cpp и textObject2 в main.xml - это два разных объекта, второй создается движком. Можно вообразить ситуацию, когда это даже хорошо, но в моем случае нужно привязаться к уже созданному объекту.
    Во-вторых, плохо то, что сначала значение переносится из QML в C++, а не наоборот.
    И, наконец, байндинг все-таки односторонний: когда данные изменяются в C++, textEdit1 не обновляется (для этого слот changeValueViaCPP() и кнопка).

    Еще вариант подсказала эта статья.
    Двустороннюю связь можно создать и так:

    textEdit1.text: textObject2.text
    Binding {  
        target: textObject2
        property: "text"  
        value: mainForm.textEdit1.text 
    }  
    

    ИМХО, даже вариант с Connections лучше.
    А хочется чего-то вроде BindingMode.TwoWay из WPF.



  • textObject2 в main.cpp конечно не тот что qml, это я не стер его в первый раз пока эксперименты делал
    https://yadi.sk/d/IDzLcmdaiXJc6 - ссылка на архив с проектом со всякими экспериментами, главное не запутаться в похожих именах



  • Спасибо за участие. Ваше предложение насчет создания объекта QML-движком из всех зол кажется наименьшим. Пока замечены такие gotchas:

    1. Нужно следить, чтобы в QML TextObject {} текстуально шел ниже TextEdit {}, иначе первое движение значения будет неверным.
    2. Чтобы потом созданнй TextObject выцарапать, ему нужно задать objectName.
    3. Довольно кучерявый способ выцарапывания:
    TextObject *obj = engine.rootObjects().first()->findChild<TextObject*>("objectName from QML");
    
    1. В конструктоор TextObject не передашь параметров.
    2. Можно случайно насоздавать несколько TextObject-ов там где по замыслу он должен быть один (в случае setContextProperty() без qmlRegisterType() это было бы невозможно).
    3. TextObject насмерть привяан к GUI thread.

    Но все это мелочи, так как Binding и Connections совсем уже костыли.

    Да, разбаловали всякие там WPF, Backbone, да AngularJS.


Log in to reply