Accessing QML's LayoutMirroring attached property from C++ - 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();
}

Related

How can I customize the child items in a TreeView of QML?

QML:
import QtQuick 2.2
import QtQuick.Controls 1.5
import QtQml.Models 2.2
import filesystem_browser 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
ItemSelectionModel
{
id: sel
// This model is comming from C++' class DisplayFileSystemModel.
model: treeViewModel
}
TreeView {
id: view
anchors.fill: parent
anchors.margins: 2 * 12
model: treeViewModel
rootIndex: root
selection: sel
TableViewColumn
{
title: "Name"
role: "display"
resizable: true
}
itemDelegate:
Rectangle
{
id: dd
color: "pink"
height: 20
Rectangle
{
height: 20; width: 40; color: "green"; anchors.right: parent.right
border.width: 1
}
border.width: 1
Text
{
anchors.verticalCenter: parent.verticalCenter
text: styleData.value
}
}
}
}
C++
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
QStandardItemModel model;
QStandardItem *item1 = new QStandardItem("File");
item1->appendRows({new QStandardItem("New"),
new QStandardItem("Open"),
new QStandardItem("Open Recent"),
new QStandardItem("Close"),
new QStandardItem("Save..."),
new QStandardItem("Save As..."),
new QStandardItem("Import Audio File..."),
new QStandardItem("Print")
});
QStandardItem *item3 = new QStandardItem("Edit");
item3->appendRows({new QStandardItem("Undo"),
new QStandardItem("Redo"),
new QStandardItem("Cut"),
new QStandardItem("Copy"),
new QStandardItem("Paste"),
new QStandardItem("Delete"),
new QStandardItem("Select All")
});
model.appendRow(item1);
model.appendRow(item3);
qmlRegisterUncreatableType<DisplayFileSystemModel>("filesystem_browser", 1, 0,
"FileSystemModel", "Cannot create");
engine.rootContext()->setContextProperty("treeViewModel", &model);
engine.rootContext()->setContextProperty("root", model.indexFromItem(model.invisibleRootItem()));
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
This results in:
I want to customize the child items and the parent item individually.
For example:
File //Parent item
New.. // Child item
Save.. // Child item
This current code puts the same customisation on the parent as well as the children.
My previous comment about using the row or column value was close, but incorrect. A quick look at the docs shows us that there is another property that gives us the depth of an item. So I think you can achieve what you want by simply doing something like this:
color: styleData.depth ? "blue" : "pink"

StackPanel Equivalent In Qt Quick 2 / QML - Problem With Width

I'm trying to make an equivalent to wpf stackpanel, I already had a logic and implemented it but something is wrong about width, I don't know how to create new components without getting into width loop binding, here is my stackpanel:
StackPanel.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import KiMa.Models 1.0
Item {
id:root
property var orientation : UOrientation.Horizontal
property int itemSpacing : 10
default property list<Item> pageData
Loader{
property var childs
anchors.fill: parent
id:loader
onChildsChanged: {
if(root.pageData != null){
for(var z = 0;z<root.pageData.length;++z){
root.pageData[z].parent = loader.childs
}
}
}
}
state: orientation == UOrientation.Horizontal ? "row": "col"
states: [
State {
name: "row"
PropertyChanges {
target: loader
sourceComponent : row
}
},
State{
name: "col"
PropertyChanges {
target: loader
sourceComponent : col
}
}
]
Component{
id:col
Column{
Component.onCompleted: {
childs = _col;
}
id:_col
width: parent.width
spacing: root.itemSpacing
}
}
Component{
id:row
Row{
Component.onCompleted: {
childs = _row
}
id:_row
width: parent.width
layoutDirection: Qt.RightToLeft
spacing: root.itemSpacing
}
}
}
and my orientation enum is like this:
#ifndef UORIENTATION_H
#define UORIENTATION_H
#include<QObject>
class UOrientation
{
Q_GADGET
public:
explicit UOrientation();
enum Orientation{
Horizontal,
Vertical
};
Q_ENUM(Orientation)
};
#endif // UORIENTATION_H
and usage example should be like this:
StackPanel{
x: 320
height: 50
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 25
Button{
}
Button{
}
}
you need to add this into main.cpp:
qmlRegisterUncreatableType<UOrientation>("KiMa.Models",1,0,"UOrientation","its not creatable type!");
This code is working , if you have anything to suggest me to change or you think I made a mistake let me know, the only problem I can see here is width binding.
I already tried using childrenRect but it is not working:
width: childrenRect.width
height: childrenRect.height
Note : stackpanel allowing you to stack item after item on top of each other you can set orientation to horizontal or vertical so in qt its a column and row together which i made it already.
vertical one :
horizontal one :
You can do this easily with a Grid by setting the number of columns.
If you want a separate component, you can create your StackPanel.qml with the following:
import QtQuick 2.0
Grid {
property int orientation: Qt.Horizontal
columns: orientation === Qt.Horizontal ? -1 : 1
}
If you want a scrollable object, you could also use a ListView with an ObjectModel model. ListView has an orientation property.

Enumerate all objects in QQmlEngine

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

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.

Getting a Box2D body created in C++ to collide with Box2D bodies in QML

I am using the qml-box2d library. I would like to create a Box2D body with fixtures in C++ and use it in QML. Unfortunately, any time I create a b2Body object and create fixtures for the object my app crashes. If I use the Box2DBody object, that comes with the qml-box2d library, it doesn't show in QML.
I need the Box2D body with fixtures in C++ to collide with all Box2D bodies in QML. What is the proper way to do this?
Pay attention - Box2DBody is not visual item since it derived from QObject. It only adds physical properties to its target. Anyway, I think the easiest way to create physical bodies is to do that in QML. But if you still want to do that in C++ you can use the code below. Suppose, we have a scene in main.qml with a QML rect item:
Window {
width: 800
height: 600
visible: true
World {}
Rectangle {
objectName: "rect"
width: 100
height: 100
x: 350
y: 50
color: "green"
}
Rectangle {
id: floor
height: 50
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
color: "brown"
Body {
target: floor
fixtures: Box {
width: floor.width
height: floor.height
density: 0.5
friction: 0.5
restitution: 0.5
}
}
}
DebugDraw {
visible: true
opacity: 0.7
}
}
And now we want to "revive" it:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Box2DPlugin plugin;
plugin.registerTypes("Box2D");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *root = engine.rootObjects()[0];
QQuickItem *rect = root->findChild<QQuickItem *>("rect");
Box2DBody * body = new Box2DBody(root);
body->setBodyType(Box2DBody::Dynamic);
body->setTarget(rect);
Box2DBox * fixture = new Box2DBox(rect);
fixture->setWidth(rect->width());
fixture->setHeight(rect->height());
fixture->setDensity(0.8);
fixture->setFriction(0.5);
fixture->setRestitution(0.5);
body->addFixture(fixture);
body->componentComplete();
return app.exec();
}