LINUX.ORG.RU

Что-то не могу достучаться до модели по идентификатору

 ,


0

1

У меня есть два файла:

PropertiesMenuView.ui.qml
PropertiesMenu.qml

В файле PropertiesMenuView.ui.qml нет логики, т. к. QtCreator не позволяет, да и предназначен он для дизайнера. И в этом файле есть ComboBox с пустой моделью (этих ComboBox там несколько штук):

ComboBox {
    id: interfaceLanguageComboBox
    anchors.left: parent.left
    anchors.right: parent.right
    model: ListModel {
        id: interfaceLanguageModel
    }
}

А файл PropertiesMenu.qml предназначен для того, чтобы в нем прописать код на объекты, существующие в PropertiesMenuView.ui.qml. Выглядит он так:
import QtQuick 2.6
import QtQuick.Window 2.3
import QtQuick.Controls 1.5

PropertiesMenuView {
    id: menu

    // Действия после создания меню
    Component.onCompleted: {
        interfaceLanguageModel.append("abcde");
        interfaceLanguageModel.append("fghij");
    }
}

Проблема в том, что при запуске проекта получаю ошибку:
PropertiesMenu.qml:10: ReferenceError: interfaceLanguageModel is not defined

И я понять не могу, почему нет доступа по идентификатору внутри типа, причем мы находимся в инстансе этого типа.

UPD. Я так понимаю, что в PropertiesMenuView.ui.qml можно сделать property alias на нужный ComboBox, и в PropertiesMenu.qml работать через него. Но черт возьми, мы же находимся в инстансе типа, зачем нам лишние костыли?

★★★★★

Последнее исправление: Xintrea (всего исправлений: 2)

Я бы вообще модель из ui в PropertiesMenu вынес

PropertiesMenuView {
    id: menu
    model: ListModel { 
        id: listModel 
        Component.onCompleted: {
            listModel.append("abcde");
            listModel.append("fghij");
        }
    }
}

fluorite ★★★★★
()
Ответ на: комментарий от fluorite

А listModel прописал бы как модель в PropertiesMenuView.ui.qml как

ComboBox {
    id: interfaceLanguageComboBox
    anchors.left: parent.left
    anchors.right: parent.right
    model: listModel
}


?

Но у меня сейчас появилась другая феерическая проблема:

Почему console.log() влияет на значение массива в QML?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Вообще id здесь не использовал ))

main.qml

import QtQuick 2.6
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    PropertiesMenu { anchors.fill: parent; anchors.margins: 20 }
}

PropertiesMenu.qml

import QtQuick 2.6

PropertiesMenuView {
    textRole:  "name"
    model: ListModel {
        ListElement { name: "eng"  }
        ListElement { name: "ru"  }
    }
}

PropertiesMenuView.ui.qml

import QtQuick 2.6
import QtQuick.Controls 1.5

ComboBox {
    anchors.left: parent.left
    anchors.right: parent.right
}

fluorite ★★★★★
()
Последнее исправление: fluorite (всего исправлений: 2)
Ответ на: комментарий от Xintrea

Всё как надо выводит:

qml: langList [0] = ABC
В меню ABC и ru.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include <FixedParameters.h>

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

    QQmlApplicationEngine engine;
    qmlRegisterType<FixedParameters>("Xintrea", 1, 0, "FixedParameters");
    auto fixedParameters = new FixedParameters;
    engine.rootContext()->setContextProperty("fixedParameters", fixedParameters);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

FixedParameters.h

#pragma once
#include <QObject>

class FixedParameters : public QObject
{
    Q_OBJECT
public:
    explicit FixedParameters(QObject *parent = nullptr) {}

    Q_INVOKABLE QStringList getInterfaceLanguageAvailable() const {
        static QStringList list { "eng", "ru"};
        return list;
    }
};

PropertiesMenu.qml

import QtQuick 2.6
import Xintrea 1.0

PropertiesMenuView {

    Component.onCompleted: {
        var langList=fixedParameters.getInterfaceLanguageAvailable()

        // Замена первого элемента
        langList[0]="ABC";
        console.log("langList [0] = "+langList[0]); // Добавил строчку
        model=langList;
    }
}

fluorite ★★★★★
()
Последнее исправление: fluorite (всего исправлений: 1)
Ответ на: комментарий от fluorite

И кстати, onCompleted не нужен.

import QtQuick 2.6
import Xintrea 1.0

PropertiesMenuView {
    model: {
        var langList = fixedParameters.getInterfaceLanguageAvailable()
        // Замена первого элемента
        langList[0]="ABC";
        console.log("langList [0] = "+langList[0]); // Добавил строчку
        return langList;
    }
}
fluorite ★★★★★
()
Ответ на: комментарий от fluorite

А если вместо:

static QStringList list { «eng», «ru»};
return list;

Написать:

return ( QStringList() << «Eng» << «Rus» );

то тоже проблем нет?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от fluorite

Стати, по теме этого топика: ты писал код из предположения, что ComboBox - корневой элемент PropertiesMenuView.ui.qml.

Но на самом деле у меня этих комбобоксов в PropertiesMenuView.ui.qml несколько штук, с разными моделями соответственно.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от fluorite

Да, без проблем. И onModelChanged срабатывает только один раз.

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

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Пфф.

PropertiesMenu.qml

import QtQuick 2.6
import Xintrea 1.0

PropertiesMenuView {
    comboBox1.model: {
        var langList = fixedParameters.getInterfaceLanguageAvailable()
        langList[0]="ABC";
        return langList;
    }
    comboBox2.model: fixedParameters.getInterfaceLanguageAvailable()
}

PropertiesMenuView.ui.qml

import QtQuick 2.6
import QtQuick.Controls 1.5

Rectangle {
    property alias comboBox1: cb1
    property alias comboBox2: cb2

    anchors.left: parent.left
    anchors.right: parent.right

    Column {
        ComboBox {
            id: cb1
        }
        ComboBox {
            id: cb2
        }
    }
}

fluorite ★★★★★
()
Ответ на: комментарий от fluorite

Ты сделал реализацию через Q_INVOKABLE, а я делаю через Q_PROPERTY, может в этом проблема?

class FixedParameters : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QStringList  interfaceLanguageAvailable
               READ      getInterfaceLanguageAvailable)

public:
    FixedParameters(QObject *parent=0);
    virtual ~FixedParameters();

    // Допустимые языки интерфейса пользователя
    QStringList getInterfaceLanguageAvailable() const;
};


QStringList FixedParameters::getInterfaceLanguageAvailable() const
{
    return ( QStringList() << "Eng" << "Rus" );
}

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Вот я взял твой проект и добавил одну строчку:

langList[0]="ABC";
console.log("langList [0] = "+langList[0]); // Добавил строчку

И у меня ABC пропало. Выводится Eng.

Qt 5.9.2, QtCreator 4.4.1, Linux Debian 9 64 bit

Пожалуйста, проверь у себя еще раз: твой архив в отдельный каталог, добавляешь одну строку, запускаешь, что получается?

Xintrea ★★★★★
() автор топика
Последнее исправление: Xintrea (всего исправлений: 1)
Ответ на: комментарий от Xintrea

Да, любое обращение (не только вывод в консоль, можешь просто написать

langList[0]="ABC"
langList[0]
) приводит к перетиранию значения. Анонимус в другой ветке верно пишет почему, хоть и сумбурно. QStringList на самом деле конвертится нифига не в яваскриптовый Array, а в объект
{"0":"Eng", "1":"Rus"}
. Можешь проверить выводом console.log(JSON.stringify(langList)). И любое чтение заново запросит данные из c++.

Как исправить?

- Можешь заменить QStringList на QVariantList, тогда поведение будет верным.

- Про отсутствие разницы между Q_PROPERTY и Q_INVOKABLE я тебя обманул, с Q_INVOKABLE тоже всё будет ок.

Хотя и выглядит как баг, такое поведение (видимо?) сделали намеренно и задокументировали.

These sequence types are implemented directly in terms of the underlying C++ sequence. There are two ways in which such sequences can be exposed to QML: as a Q_PROPERTY of the given sequence type; or as the return type of a Q_INVOKABLE method. There are some differences in the way these are implemented, which are important to note.

If the sequence is exposed as a Q_PROPERTY, accessing any value in the sequence by index will cause the sequence data to be read from the QObject's property, then a read to occur. Similarly, modifying any value in the sequence will cause the sequence data to be read, and then the modification will be performed and the modified sequence will be written back to the QObject's property.

- Можешь добавить переменную-член класса и сеттер.

fluorite ★★★★★
()
Ответ на: комментарий от fluorite

Ну вот я перевожу:

Эти типы последовательностей реализуются непосредственно в терминах базовых C++ - последовательностей. Существует два способа, которыми такие последовательности могут быть проброшены в QML: как Q_PROPERTY с заданным типом последовательности, или как возвращаемый тип метода, помеченного макросом Q_INVOKABLE. Есть некоторые различия в том, как они реализованы, и это важно отметить.

Если последовательность пробрасывается как Q_PROPERTY, доступ к любому значению в последовательности по индексу будет приводить к чтению данных последовательности из свойства QObject, и такое чтение будет произведено. Аналогично, изменение любого значения в последовательности приведет к чтению данных последовательности, а затем будет выполнена модификация, и измененная последовательность будет записана обратно в свойство QObject.



Можешь добавить переменную-член класса и сеттер.

Но у меня свойство неизменяемое... Его копия должна изменяться в JavaScript части.

Если последовательность возвращается из функции Q_INVOKABLE , доступ и изменение намного дешевле, так как никакое свойство объекта QObject не читается и не записывается, вместо этого доступ к данным, содержащихся в C++ - последовательности, получается напрямую и изменения тоже производятся напрямую.



Не читается и не записывается? Что-то какая-то магия. Хорошо, может быть потому что данные получаются напрямую (видимо по указателю)? Но тогда чем это отличается от Q_PROPERTY? А как происходит преобразование в JavaScript-объект? Ведь в нем все равно данные дублируются.

Дальше-больше:

Как в Q_PROPERTY, так и в случае возврата в виде Q_INVOKABLE, копируются элементы std::vector. Это копирование может быть дорогостоящей операцией, поэтому std::vector следует использовать разумно.



Ага, значит копирование все-таки происходит. Причем, копирование и в случае Q_PROPERTY, хотя парой абзацев выше говорилось, что просто происходит чтение/запись прямо в свойство QObject.

Я вообще не понимаю как все устроено. Вроде перевожу максимально корректно, но смысла в этом наборе слов нет.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от fluorite

В общем, я Q_PROPERTY применил неправильно. Семантически эта конструкция предполагает, что у объекта есть свойство, в котором хранится какое-то значение. А синтаксически Q_PROPERTY позволяет создавать интерфейс без наличия свойства у класса. Отсюда все эти проблемы с JavaScript, в котором объекты передается по ссылке.

Xintrea ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.