Enumerate all objects in QQmlEngine - c++

I want to enumerate all the objects (derived from QObject) existent and newly created in QQmlApplicationEngine. The main goal it to execute the following lines of code for each of them:
void denominate(QObject * const object)
{
if (object->objectName().isEmpty()) {
object->setObjectName(object->metaObject()->className());
}
}
It is needed to distinct some of the objects in QML runtime. Say, when StackView items are created from Components and I want to know which of them is really on the top.
Creation of some of components or C++ classes are delayed. And if I get a root objects and enumerate children() of them, it is a half of the solution. Also there is QQmlApplicationEngine::objectCreated signal, but I don't fully understand interrelation between contexts, engine and (root) objects.
How to name all the unnamed objects, which lives into QQmlEngine?

You can rename all of them using recursive enumeration
For example:
QML file:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea
{
id: r1
width: 200
height: 200
}
Rectangle
{
id: r2
color: "green"
width: 100
height: 100
Item
{
id: r3
width: 50
height: 50
}
}
}
You can set their names in the c++ part using the next mechanism:
void changeNames(QList<QObject*> ch)
{
for( QObject* ob: ch)
{
if (ob->objectName().isEmpty())
{
ob->setObjectName(ob->metaObject()->className());
}
changeNames( ob->children() );
}
}
void enumerate(QList<QObject*> ch)
{
for( QObject* ob: ch)
{
qInfo() << ob->objectName();
enumerate( ob->children() );
}
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *rootObject = engine.rootObjects().first();
changeNames(rootObject->children());
enumerate(rootObject->children());
return app.exec();
}
You will see the next names in the output:
"QQuickRectangle"
"QQuickMouseArea"
"QQuickItem"
If you have to add new component dynamically you can repeat this procedure, or find necessary component and rename it

Related

Parent is null in ListView delegate after upgrade to Qt 5.15

A ListView with a most simple delegate produces lots of warnings "21:35:31.911 warning T#16084047 unknown - qrc:/main.qml:15: TypeError: Cannot read property 'left' of null" if trying to set it's delegate anchors property and scrolling the list (which makes delegates to be destroyed/created). It was not the case in Qt 5.12 or 5.9.
File main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
ListView {
anchors.fill: parent
model: cppModel
delegate: Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: 50
Text { text: model.itemName }
}
}
}
File main.cpp:
#include <QAbstractListModel>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtGlobal>
#include <QQmlContext>
#include <iostream>
void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QString logLine = qFormatLogMessage(type, context, msg);
std::cout << logLine.toStdString() << std::endl;
}
class CppModel: public QAbstractListModel {
// QAbstractItemModel interface
public:
virtual int rowCount(const QModelIndex &parent) const override { return 100; }
virtual QVariant data(const QModelIndex &index, int role) const override {
if (role == (Qt::DisplayRole + 1)) {
return QString("Element %1").arg(index.row());
}
return QVariant();
}
virtual QHash<int, QByteArray> roleNames() const override {
return {{(Qt::DisplayRole+1), "itemName"}};
}
};
int main(int argc, char *argv[])
{
qSetMessagePattern("%{time hh:mm:ss.zzz} %{type} T#%{threadid} %{function} - %{message}");
qInstallMessageHandler(myMessageHandler);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
CppModel cppModel;
engine.rootContext()->setContextProperty("cppModel", &cppModel);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
Link to full source code
What I'm doing wrong and how to correctly set anchors for the delegate element?
This is the result of a behaviour change in Qt 5.15. The first issue was reported here, with a more detailed summary here. The new documentation says:
Delegates are instantiated as needed and may be destroyed at any time.
As such, state should never be stored in a delegate. Delegates are
usually parented to ListView's contentItem, but typically depending on
whether it's visible in the view or not, the parent can change, and
sometimes be null. Because of that, binding to the parent's properties
from within the delegate is not recommended. If you want the delegate
to fill out the width of the ListView, consider using one of the
following approaches instead:
ListView {
id: listView
// ...
delegate: Item {
// Incorrect.
width: parent.width
// Correct.
width: listView.width
width: ListView.view.width
// ...
}
}
So, you can either:
Give the ListView an id and use it in the binding instead of parent.
Use the attached property (ListView.view) to access the view.
Check for null (anchors.left: parent ? parent.left : undefined).
Options 1 and 2 will result in cleaner code.
Option 1 results in one less QObject being created (each attached object is a QObject) but ties the delegate to that particular view.
Option 2 is better for delegates that are standalone components that will be reused with other ListViews.

Dynamically created Custom QML object not visible

I have a custom QML item called "Cell.qml" which I'd like to insert dynamically in my root window and make it visible. I also tried changing the z property, but I couldn't fix it, the object is still invisible (even tough the output of root->dumpObjectTree() says the object exists as a child of the root window)
This is the result after executing the code
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *root = engine.rootObjects()[0];
QQmlComponent component(&engine, "qrc:/Cell.qml");
if (component.isReady()){
QObject *object = component.create();
object->setParent(root);
engine.setObjectOwnership(object, engine.CppOwnership);
}
root->dumpObjectTree();
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
}
Cell.qml
import QtQuick 2.0
import QtQuick.Controls 2.3
Item{
property string txt: ""
property color c: "#d4ccc4"
visible: true
z:20
Rectangle {
width: 75; height: 75
color: c
visible: true
radius : 3
scale : 1
z:10
Text{
anchors.centerIn: parent
text: txt
font.family: "Helvetica"
font.pointSize: 20
color: "white"
}
}
}
Output of root->dumpObjectTree();:
QQuickWindowQmlImpl::
QQuickRootItem::
Cell_QMLTYPE_0::
QQuickRectangle::
QQuickText::
setParent() sets a non-visual, QObject level parent member. QQuickItem has another property, parentItem which is the parent QQuickItem property in QML, it is that property that needs to be set so the object appears visually.

Accessing nested ListView with QQmlContext

I am trying to populate a QML ListView using a class that inherits QAbstractListModel. So far, I managed to create this using the QT Documentation here, under the "QAbstractItemModel subclass" section:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "gamemodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
GameModel model; //A class similar to AnimalModel in Qt Documentation.
//It contains a QList of Objects, each having 2 QString
//members (title and genre).
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
model.readFile("c:/somePath/XML_G.xml"); //Initializing GameModel QList member
//using an XML file
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("myModel", &model);
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
Window
{
id: win
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListView
{
id: myList
width: parent.width
height: 50
clip: true
spacing: 5
orientation: ListView.Horizontal
model: myModel
delegate:
Rectangle
{
width: 150
height: 20
color: "#2255ff"
Text
{
text: gameTitle + " " + genre
}
}
}
}
Up to this point, my code works. However, if I try to change my main.qml file like this:
import QtQuick 2.5
import QtQuick.Window 2.2
Window
{
id: win
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item //ListView is now nested in this Item
{
ListView
{
id: myList
width: parent.width
height: 50
clip: true
spacing: 5
orientation: ListView.Horizontal
model: myModel
delegate:
Rectangle
{
width: 150
height: 20
color: "#2255ff"
Text
{
text: gameTitle + " " + genre
}
}
}
}
}
I end up being unable to set my model using ctxt->setContextProperty("myModel", &model);. From what little I can gather from the Qt Documentation (although I am most likely wrong), QQmlContext acts like a scope for the QML file. Thinking that, I tried changing this:
QQmlContext *ctxt = engine.rootContext();
to this:
QQmlContext *ctxt = engine.rootContext()->findChild<QQmlContext*>("list");
As well as setting my Item's objectName property to "list". Obviously, that failed, and it also caused a crash. Since my experience with QML is limited to the Qt Docs, I have been unsuccessful at finding a workaround. Is a solution using QQmlContext possible, or do I have to use QObject? If so, what would the QObject equivalent of ctxt->setContextProperty("myModel", &model) be?
The first argument of the setContextProperty() call is basically the "identifier" of the object, like the id property on the QML side.
You need to set that before accessing it in QML, otherwise it is unknown at the time of usage.
So you don't need any other call, but you need to do it before you load QML that needs it.
Just move it before the engine.load(...) line in your main.cpp
OK, apparently my problem was in my QML file. In my code, I setup my ListView like this:
width: parent.width
However, when I added an Item as the parent of my ListView, I forgot to set an initial width for my Item, thus turning ListView's width to 0. After I set an initial width for my Item, everything works as expected again.

Accesing QML element from C++ code in QT

I am trying to use QML with C++ in QT, but for now unsuccessfully. I cannot access my QML element from the C++ code using rootObjects() function. What am I doing wrong?
qml part:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: window
visible: true
width: 640
height: 520
title: qsTr("My app")
Item {
anchors.fill: parent
Rectangle {
id: rectangle1
x: 0
y: 0
width: 640
height: 370
color: "#ffffff"
}
Button {
id: startButton
x: 325
y: 425
text: qsTr("Start")
}
}
}
C++ Part:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
QObject *rootObject = engine.rootObjects().first();
qDebug() << rootObject->objectName();//prints ""
QObject *qmlObject = rootObject->findChild<QObject*>("window");// or "startButton"
//qDebug() << qmlObject->objectName(); //app fails, because window was not found
QList<QObject *> allQObjects = rootObject->findChildren<QObject *>();
for(int i=0;i< allQObjects.length();++i)
{
qDebug() << allQObjects[i]->objectName(); //prints everytime ""
}
qDebug() << "len: " << allPQObjects.length(); //prints 132
return app.exec();
}
At first: If you do not set a object name there will be no!
QML:
Rectangle { id : frame; objectName : "objFrame" color : "blue" }
Qt:
QObject *pRootObject = m_pQmlView->rootObject();
QObject *pobjFrame = m_pRootObject->findChild<QObject *>("objFrame");
The other way arround:
Qt:
m_pQmlView->rootContext()->setContextProperty( "_view", this );
QML:
Component.onCompleted: {
/********************** Connections ***************************/
// connect signal MyView::retranslate() with slot retranslate
_view.retranslate.connect(retranslate)
}
Need to add objectname to QML
ApplicationWindow {
id: window
objectName: "window"
...
}

Accessing QML's LayoutMirroring attached property from C++

The LayoutMirroring QML attached property allows switching the directionality of an item from left-to-right to right-to-left, or vice versa. I would like to access an item's current LayoutMirroring values from C++ code, extending a QML application. I understand that the qmlAttachedPropertiesObject function can be used to access attached properties from C++, as explained in the documentation, but I don't know where I can find the class that represent this specific property in C++.
Is it possible to do that?
It's possible to read and write attached properties easily with QQmlProperty class since Qt5.
Simple main.qml file:
import QtQuick 2.0
Rectangle {
LayoutMirroring.enabled: true
LayoutMirroring.childrenInherit: true
}
Simple C++ code to read above LayoutMirroring.enabled attached property:
QQuickView view;
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.show();
QQuickItem* rootItem = view.rootObject();
qDebug() << QQmlProperty::read(rootItem, "LayoutMirroring.enabled", qmlContext(rootItem));
Console output:
=> QVariant(bool, true)
import QtQuick 1.1
Rectangle {
id: parentRect
property alias layoutValue : LayoutMirroring.enabled// ? true : false;// Read Only
LayoutMirroring.enabled: false
LayoutMirroring.childrenInherit: true
width: 300; height: 50
color: "yellow"
border.width: 1
Row {
anchors { left: parent.left; margins: 5 }
y: 5; spacing: 5
Repeater {
model: 5
Rectangle {
color: "red"
opacity: (5 - index) / 5
width: 40; height: 40
Text {
text: index + 1
anchors.centerIn: parent
}
}
}
}
}
#include
#include
#include
#include
#include "qmlapplicationviewer.h"
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer app(createApplication(argc, argv));
QScopedPointer viewer(QmlApplicationViewer::create());
viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer->setMainQmlFile(QLatin1String("qml/Test/main.qml"));
QDeclarativeProperty propLayout(viewer->rootObject(),"layoutValue");
QDeclarativeProperty propLayoutMargin(viewer->rootObject(),"anchors.leftMargin");
qDebug() showExpanded();
return app->exec();
}