Dynamically created Custom QML object not visible - c++

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.

Related

Get user input from qml to c++

I'm trying to get user input from qml TextField to c++, but it only works if text property value is hardcoded (const value).
c++
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *rootObject = engine.rootObjects().first();
QObject *serverField1 = rootObject->findChild<QObject*>("serverField1");
qDebug() << serverField1->property("text"); //Here I expect to get user input
Qml
ApplicationWindow {
id: applicationWindow
visible: true
width: 300
height: 550
TextField {
id: serverField1
objectName: "serverField1"
width: 200
height: 110
// text: "hardcoded value" //If text is const value, qDebug will get data from this property
}
}
You are asking for the text of the TextField when the window is displayed so what you will get is the text you have initially set, what you should do is get it every time it changes. I think that you have some class that will handle some processing with that data, that class will be called Backend, it must inherit from QObject so that it can have a qproperty and embed an object of that class to QML through setContextProperty, so every time it changes the text in QML will also change the text in the Backend class object.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlProperty>
#include <QDebug>
class Backend: public QObject{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
QString text() const{
return mText;
}
void setText(const QString &text){
if(text == mText)
return;
mText = text;
emit textChanged(mText);
}
signals:
void textChanged(const QString & text);
private:
QString mText;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Backend backend;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("backend", &backend);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
// test
QObject::connect(&backend, &Backend::textChanged, [](const QString & text){
qDebug() << text;
});
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.4
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TextField {
id: serverField1
x: 15
y: 46
width: 120
height: 45
topPadding: 8
font.pointSize: 14
bottomPadding: 16
placeholderText: "Server Ip"
renderType: Text.QtRendering
onTextChanged: backend.text = text
}
}
You can find the complete code in the following link.

Interaction with qml objects from C++ code

I am trying to interact with an qml object from C++ file using QtQuick. But unfortunatelly unsuccessfully for now. Any idea what Im doing wrong? I tried 2 ways how to do it, result of the first was that findChild() returned nullptr, and in second try I am getting Qml comnponent is not ready error. What is the proper way to do it?
main:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
// 1-st attempt how to do it - Nothing Found
QObject *object = engine.rootObjects()[0];
QObject *mrect = object->findChild<QObject*>("mrect");
if (mrect)
qDebug("found");
else
qDebug("Nothing found");
//2-nd attempt - QQmlComponent: Component is not ready
QQmlComponent component(&engine, "Page1Form.ui.qml");
QObject *object2 = component.create();
qDebug() << "Property value:" << QQmlProperty::read(object, "mwidth").toInt();
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
Page1 {
}
Page {
}
}
}
Page1.qml:
import QtQuick 2.7
Page1Form {
...
}
Page1.Form.ui.qml
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
Item {
property alias mrect: mrect
property alias mwidth: mrect.width
Rectangle
{
id: mrect
x: 10
y: 20
height: 10
width: 10
}
}
findChild takes object name as first parameter. But not ID.
http://doc.qt.io/qt-5/qobject.html#findChild.
Here in your code, you are trying to query with id mrect. So it may not work.
Add objectName in your QML and then try accessing with findChild using object name.
Something like below (I did not try it. So chances of compile time errors):
Add objectName in QML
Rectangle
{
id: mrect
objectName: "mRectangle"
x: 10
y: 20
height: 10
width: 10
}
And then your findChild as shown below
QObject *mrect = object->findChild<QObject*>("mRectangle");

C++ and QML: Connect QML Signal to C++ Slot

I cannot get the signal connection in the following code to work. I specifically want to do this via connecting the signal to a cpp slot and not setting the context. I suppose the problem is that
item->findChild<QObject*>("signalItem");
does not find the right object?
Here the relevant code files:
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "include/myclass.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
QObject * item = engine.rootObjects().value(0);
QObject * myObject= item->findChild<QObject*>("signalItem");
MyClass myClass;
QObject::connect(item, SIGNAL(testSignal()),&myClass,SLOT(cppSlot()));
return app.exec();
}
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 800
height: 460
Page1 {
id: page1
visible: true
}
}
Page1.qml:
import QtQuick 2.7
import QtQuick.Window 2.2
Item {
width: 800
height: 460
id: signalItem
objectName: "signalItem"
signal testSignal()
CustomButton {
id: cppSignalButton
x: 14
y: 55
buttonText: "Test CPP Signal"
onButtonClicked: {
signalItem.testSignal();
}
}
}
Because you are connecting item(main.qml) instead of myObject
If you do so, it will work:
QObject::connect(myObject, SIGNAL(testSignal()),&myClass,SLOT(cppSlot()));
Actually you should also add checking if returned values from that functions aren't null:
QObject * item = engine.rootObjects().value(0);
QObject * myObject= item->findChild<QObject*>("signalItem");

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.

Set loader component by calling C++ function

I'm just wondering if anything like this is possible :
Loader {
id: loader
objectName: "loader"
anchors.centerIn: parent
sourceComponent: cppWrapper.getCurrentDisplay();
}
And in C++ :
QDeclarativeComponent currentDisplay;
and
Q_INVOKABLE QDeclarativeComponent getCurrentDisplay() const
{ return currentDisplay; }
I have trouble compiling it (it fails in the moc file compilation), but if it's possible, it can be a real shortcut for me
Sure, you can create Component in C++ part (as QQmlComponent) and return it to QML part.
Simple example (I use Qt 5.4):
First of all I create a class to use it as singleton (just for ease using)
common.h
#include <QObject>
#include <QQmlComponent>
class Common : public QObject
{
Q_OBJECT
public:
explicit Common(QObject *parent = 0);
~Common();
Q_INVOKABLE QQmlComponent *getComponent(QObject *parent);
};
common.cpp
QQmlComponent *Common::getComponent(QObject *parent) {
QQmlEngine *engine = qmlEngine(parent);
if(engine) {
QQmlComponent *component = new QQmlComponent(engine, QUrl("qrc:/Test.qml"));
return component;
}
return NULL;
}
Now I create and register my singleton:
main.cpp
#include "common.h"
static QObject *qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
static Common *common = new Common();
return common;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterSingletonType<Common>("Test", 1, 0, "Common", qobject_singletontype_provider);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Ok, now we have a singleton and it is very simple to use it in QML:
main.qml
import QtQuick 2.3
import Test 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: mainWindow
Loader {
id: loader
objectName: "loader"
anchors.centerIn: parent
sourceComponent: Common.getComponent(mainWindow)
}
}
Also our component we created in C++:
Test.qml
import QtQuick 2.3
Rectangle {
width: 100
height: 100
color: "green"
border.color: "yellow"
border.width: 3
radius: 10
}
Pay attention: all our QML files are in resource but it's just for example and you can put it in any place you want in your case