Создать объект QML из C++ с указанными свойствами

Динамическое создание объекта QML из C++ — это хорошо задокументировано, но я не могу найти, как создать экземпляр с предварительно заданными значениями для его свойств.

Например, я создаю слегка модифицированный SplitView из C++ следующим образом:

QQmlEngine* engine = QtQml::qmlEngine( this );
QQmlComponent splitComp( engine, QUrl( "qrc:/qml/Sy_splitView.qml" ) );
QObject* splitter = splitComp.create();

splitter->setProperty( "orientation", QVariant::fromValue( orientation ) );

Проблема, с которой я столкнулся, заключается в том, что указание orientation для SplitView после его создания приводит к нарушению его внутреннего макета. Итак, есть ли способ создать SplitView с уже указанным orientation?

В качестве альтернативы я могу создать как горизонтальную, так и вертикальную версию SplitView в отдельных файлах и создать соответствующий экземпляр во время выполнения, но это менее элегантно.

Обновить

Я нашел QQmlComponent::beginCreate(QQmlContext* publicContext):

QQmlEngine* engine = QtQml::qmlEngine( this );
QQmlComponent splitComp( engine, QUrl( "qrc:/qml/Sy_splitView.qml" ) );
QObject* splitter = splitComp.beginCreate( engine->contextForObject( this ) );

splitter->setProperty( "orientation", QVariant::fromValue( orientation ) );
splitter->setParent( parent() );
splitter->setProperty( "parent", QVariant::fromValue( parent() ) );
splitComp.completeCreate();

Но на удивление не подействовало.


person cmannett85    schedule 30.10.2013    source источник
comment
Бьюсь об заклад, дело в том, как вы пытаетесь назначить перечисление через QVariant (перечисления несколько глючат в QML). Я бы попытался сначала зарегистрировать простой тип на основе QObject и собственное перечисление и проверить, работает ли все это вообще. [также обратите внимание, что вы, по-видимому, дважды пытаетесь установить родителя, но это незначительно]   -  person mlvljr    schedule 31.10.2013
comment
Я не знал этого о перечислениях, так что спасибо, я попробую. И я не устанавливаю родителя дважды, сначала я устанавливаю родителя QObject, а затем визуальный родитель QML (если бы я мог установить родителя QObject через QML, я бы вообще не стал возиться с C++ для этого).   -  person cmannett85    schedule 31.10.2013
comment
Правильно, действительно; вместо того, чтобы устанавливать родительский объект QObject, вы можете установить владение памятью (или, как бы оно ни называлось) на QmlOwnership, я полагаю (так что только что созданный объект будет собираться мусором/ссылаться во время выполнения QML по мере необходимости). Кстати, вам специально нужно установить родителя QObject по каким-то причинам, кроме управления памятью?   -  person mlvljr    schedule 31.10.2013
comment
Это для управления памятью. Каждый SplitView содержит два настраиваемых окна просмотра OSG, каждое из которых, в свою очередь, может быть разделено до бесконечности (как панели текстового редактора Qt Creator), образуя дерево. Поэтому, когда я удаляю определенный сплиттер, он соответствующим образом удаляет все дочерние элементы. Я посмотрю на право собственности на стороне QML, это может упростить ситуацию - спасибо!   -  person cmannett85    schedule 31.10.2013


Ответы (2)


Я думаю, вы должны иметь возможность использовать пользовательский метод QQmlIncubator и QQmlComponent::create(QQmlIncubator & incubator, QQmlContext * context = 0, QQmlContext * forContext = 0) factory.

В частности, цитата из документации QQmlIncubator:

void QQmlIncubator::setInitialState(QObject * объект) [виртуальная защита]

Вызывается после первого создания объекта, но до оценки привязок свойств и, если применимо, вызывается QQmlParserStatus::componentComplete(). Это эквивалентно точке между QQmlComponent::beginCreate() и QQmlComponent::endCreate() и может использоваться для присвоения начальных значений свойствам объекта.

Реализация по умолчанию ничего не делает.

person bks    schedule 31.12.2013

У меня была аналогичная ситуация для моего собственного компонента QML. Просто хотел инициализировать некоторые реквизиты перед запуском некоторых привязок. В чистом QML я сделал так:

var some = component.createObject(this, {'modelClass': my_model});

Из С++ я пробовал так:

// create ui object
auto uiObject = qobject_cast<QQuickItem*>(component.beginCreate(ctx));
// place on ui
uiObject->setParentItem(cont);

// set model properties
classInstance->setView(QVariant::fromValue(uiObject));
classInstance->setPosition(QPointF(x, y));

// set ui object properties
uiObject->setProperty("modelClass", QVariant::fromValue(classInstance.get()));

// finish
component.completeCreate();

но у меня были ошибки привязки, потому что modelClass остается нулевым! Покопавшись некоторое время нашел причину. И это выглядит разумным для меня. Я изменил свой класс QML!

Item {
    id: root
    // property var modelClass: null
    property var modelClass // just remove : null!
}

Свойства с начальными значениями сразу после вызова beginCreate не видны из C++, пока я не вызову completeCreate(). Но если я удалю свойство начального значения, оно станет видимым, и я смогу инициализировать его в коде C++.

person Viktor    schedule 02.03.2016