LINUX.ORG.RU

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

 , ,


0

2

У меня есть такой код:

Component.onCompleted: {
    
    // Получение списка
    var langList=fixedParameters.interfaceLanguageAvailable;

    // Замена первого элемента
    langList[0]="ABC";

    interfaceLanguageComboBox.model=langList;
}

В результате ComboBox показывает первым элементом строку «ABC». Это хорошо, это правильно.

Затем я просто добавляю команду отладочного вывода, вот так:
Component.onCompleted: {
    
    // Получение списка
    var langList=fixedParameters.interfaceLanguageAvailable;

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

    interfaceLanguageComboBox.model=langList;
}

И в консоли я вижу:
langList [0] = Eng

Вот это да! Строка «Eng» - это совсем не «ABC». То есть, по какой-то причине, если в коде есть вывод на консоль, то первоначальное значение не меняется. В графическом интерфесе тоже «ABC» пропадает, и выводится «Eng».

Почему так?

Пояснение. fixedParameters.interfaceLanguageAvailable - это метод C++ - объекта, который возвращает QStringList из двух значний «Eng» и «Rus».

★★★★★

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

Что такое `fixedParameters.interfaceLanguageAvailable`?

Попробуй просто подёргать несколько раз langList[0] и посмотри что он возвращает. Мб там странный геттер у тебя.

Я не думаю, что дело в самом console.log

ChALkeR ★★★★★
()

Как тебе и сказали, прочитай несколько раз, сделай

    console.log("до: "+langList);
    langList[0] = "ABC";
    console.log("после: "+langList);

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

И глянь какой тип выводит console.log
Есть у меня подозрение что это нифига не массив, а объект.

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

Да, это объект. Его C++ - шный тип QStringList, я это в топике написал.

Вот я сделал две строки вывода:

console.log("1. langList [0] = "+langList[0]);
langList[0]="ABC";
console.log("2. langList [0] = "+langList[0]);

Результат:
qml: 1. langList [0] = Eng
qml: 2. langList [0] = Eng

И в графическом интерфейсе никакого ABC нет.

Но как только я убираю эти два console.log, в графическом интерфейсе получаю ABC.

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

Геттер у меня примитивный:

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

Он все время возвращает одно и то же (поэтому класс и называется FixedParameters).

Xintrea ★★★★★
() автор топика

Проверь сколько раз дергается onModelChanged. И попробуй вместо вывода console.log отдебажить это место, у биндингов и console.log немного разное время срабатывания.

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

Добавил вывод в консоль «Model changed» в onModelChanged.

Результаты запусков:

1.

Код:

var langList=fixedParameters.interfaceLanguageAvailable;
interfaceLanguageComboBox.model=langList;

Консоль:

qml: Model changed
qml: Model changed

На экране:

Eng
Rus


2.
Код:

var langList=fixedParameters.interfaceLanguageAvailable;
langList[0]="ABC";
interfaceLanguageComboBox.model=langList;

Консоль:

qml: Model changed
qml: Model changed

На экране:

ABC
Rus


3.
Код:

var langList=fixedParameters.interfaceLanguageAvailable;
langList[0]="ABC";
console.log("langList [0] = "+langList[0]); // Строчка вывода
interfaceLanguageComboBox.model=langList;

Консоль:

qml: Model changed
qml: langList [0] = Eng
qml: Model changed

На экране:

Eng
Rus

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

Лол, ну так он пересоздаётся каждый раз, нет?

Хочешь сказать, что проблема возникает из-за того, что в яваскрипте объекты присваиваются по ссылке?

Но тогда как в QML склонировать объект? Я попробовал так:

var objLangList=fixedParameters.interfaceLanguageAvailable;
var langList = Object.asign({}, objLangList);


В результате ошибка:
TypeError: Property 'asign' of object function() { [ code ] } is not a function

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

Хотя, проблемы быть не должно. Ну генерируется при каждом обращении QStringList, ну и что? Этот QStringList попадает в JavaScript код. Там он меняется (нуливой элемент становится ABC) и потом вставляется к CheckBox. Хоть сто раз вызови, в CheckBox должен вставиться модифицированный список с ABC в нуливом элементе.

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

Брал вот отсюда:

https://toster.ru/q/262135

Я думал что asign - это что-то типа access signature, даже не придал значения, мало ли каких методов в JavaScript понагородили.

Но даже если написать assign, ошибка не меняется:

TypeError: Property 'assign' of object function() { [ code ] } is not a function

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

Так получается, что ты где-то дважды устанавливаешь модель. onModelChanged должен сработать только раз. См. мой код в предыдущем своём топике.

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

Не знаю почему у тебя установка модели срабатыват один раз. Я сейчас проверил, у меня ComboBox прописан без модели, и JavaScript с установкой модели я удалил.

Оставил только:

    Connections {
        target: interfaceLanguageComboBox
        onModelChanged: {
            console.log("Model is changed");
        }
    }


И в результате один раз установка модели срабатывает, на экране выпадающий список со значением 0. Видимо это и есть первая инициализация.

Когда срабатывает установка модели через JavaScript - это вторая инициализация.

Как так получается, что у тебя только одна инициализация, я не догоняю.

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

Нет. В твоем архиве с примером я добавляю одну строчку лога, и топиковая проблема появляется. Давай последний раз проверим, походу это баг в Qt.

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

Xintrea ★★★★★
() автор топика

Кинь ссыль на код где эта хрень проявляется.
Дело было вечером, делать было нечего, хоть потыкаю.

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

Блин, странно, а ты можешь вставить в interfaceLanguageAvailable добавление в хвост таймстампа и залогировать сгенеренные значения из плюсов. Такое подозрение что там где-то то ли дважды дергается значение, то ли сериализация срабатывает (без лога в ней типа нужды нет) и делает клон массива.

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 1)

Глянул, нормальное это поведение :)
Либо в FixedParameters для interfaceLanguageAvailable допиши сеттер нормальный, да и геттер перепиши.

Либо, как-то так(если что я js не дружен :) )

var langList_ = [];
for (var i in langList) {
    langList_.push(langList[i]);
}
langList = langList_;

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

В реализации js в Qt нету Object.assign

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

т.е. в FixedParameters.h можно вообще геттер убрать, пусть будет по умолчанию и сделать примерно так:

    Q_PROPERTY(QStringList  interfaceLanguageAvailable MEMBER _interfaceLanguageAvailable)
    QStringList _interfaceLanguageAvailable;
    explicit FixedParameters(QObject *parent = nullptr) {
        this->_interfaceLanguageAvailable << "Eng" << "Rus";
    }

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

Желательно минимальный пример :)

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

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

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

в FixedParameters для interfaceLanguageAvailable допиши сеттер нормальный, да и геттер перепиши.

Это все общие слова. Как должен выглядеть нормальный геттер? Почему он должен выглядеть именно так. Нужен код.

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

Вот пример FixedParameters.h с которым работает:

#pragma once
#include <QObject>
#include <QDebug>

class FixedParameters : public QObject
{
    Q_OBJECT
public:
    Q_PROPERTY(QStringList  interfaceLanguageAvailable 
               MEMBER _interfaceLanguageAvailable)
    QStringList _interfaceLanguageAvailable;
    explicit FixedParameters(QObject *parent = nullptr) {
        this->_interfaceLanguageAvailable << "Eng" << "Rus";
    }
};
если хочешь могу объяснить почему у тебя не работает.

anonymous
()
Ответ на: комментарий от Kakadu

Если коротко:
В FixedParameters.h Q_PROPERTY(QStringList interfaceLanguageAvailable WRITE setInterfaceLanguageAvailable) объявляется свойство только для чтения.
В qml же var langList = fixedParameters.interfaceLanguageAvailable; langList нифига не данные из этого свойства, а объект свойства.
Почему langList[0] = "some" отрабатывает хз.
Но при любом чтении из langList происходит вызов геттера свойства.
С соответствующей заменой внутреннего состояния js объекта.

anonymous
()
Ответ на: комментарий от Kakadu

Анонимус всех запутал. Показал сеттер с WRITE и сказал что свойство только для чтения:

В FixedParameters.h Q_PROPERTY(QStringList interfaceLanguageAvailable WRITE setInterfaceLanguageAvailable) > объявляется свойство только для чтения.

Из всего что, он написал правильно только вот это:

Но при любом чтении из langList происходит вызов геттера свойства.
С соответствующей заменой внутреннего состояния js объекта.

Но он не объяснил почему это правильно.

Q_PROPERTY предполагает, что у объекта есть поле со свойством. Да, синтаксис Q_PROPERTY позволяет делать реализацию и без поля со свойством. Но такой подход будет некорректно работать в случае с пробросом в JavaScript, где присвоение объектов идет по ссылке, без копирования.

В топикстартовом случае геттер объекта fixedParameters возвращает одно и то же значение QStringList. Оно преобразуется в JavaScript-объект. В JavaScript коде этот объект присваивается по ссылке некоторой переменной. И модифицируется. Но модификация сохраняется ровно до того момента, пока не произойдет обращение на чтение к этой переменной. По ссылке определится, что обращение идет к JavaScript-объекту, но на этом не остановится, и система определит через метаобъектную систему что обращение идет к полю объекта fixedParameters. И запрос будет идти к метаобъектному полю объекта fixedParameters. Объект fixedParameters вернет то же самое значение QStringList, и оно перепишет JavaScript-объект.

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

Анонимус всех запутал.

Ты сам запутался, у тебя getter всегда создаёт новый обьект. А в qml видимо идёт работа со свойством а не с его значением. Т.е. как работает js код в list[0] = «42» в со стороны c++:

auto list = get_property();
list[0] = "42";
set_property(list);

Убедись в отладчике просто в этом.

В общем, либо переделай свой getter как то в таком духе:

...
QStringList property = {"1","2","3");

const QstringList& get_property() const {
  return property;
}
...

Либо просто скопипасть код анонимуса.

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