Interaction with qml objects from C++ code - c++

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");

Related

How to use QThread from QML while having QML function async too

I'm looking for the way to use QThread in QML.
I want to pass parameters to the QThread function and return a bool value from it.
Another thing I want from the QML side is to not block the app when it's executing a script that will happen before calling/executing the QThread.
Below is an example code:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "testasync.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<testAsync>("testAsync",1,0,"thread");//not working on main.qml
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
//import testAsync 1.0
ApplicationWindow {
id: window
title: "Stack"
visible: true
width: 1400
Page {
id: page
anchors.fill: parent
property int responsiveWidth: 1000
property int maximumWidth: 900
ScrollView {
id:configScroll
anchors.fill: parent
GridLayout {
columns: 2
width: page.width > page.responsiveWidth ? page.maximumWidth : page.width
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: page.width > page.responsiveWidth ? (page.width - childrenRect.width)/2 : 10
anchors.rightMargin: page.width > page.responsiveWidth ? 0 : 10
//this function needs to be processed and will return the values we need for the testasync. this can't block UI thread too
function teste() {
for(var i=0; i<10000000; i++)
{
console.log(i)
}
return "teste"
}
Button {
property bool test: true
text: "async"
onClicked: {
var val = parent.teste()
// if(test)
// val=thread.start()
// else
// val=thread.quit()
console.log(val)
test=!test
}
}
}
}
}
}
testasync.h
#ifndef TESTASYNC_H
#define TESTASYNC_H
#include <QThread>
#include <QObject>
class testAsync : public QThread
{
Q_OBJECT
public:
testAsync();
void run();
private:
QString name;
};
#endif // TESTASYNC_H
testasync.cpp
#include "testAsync.h"
#include <QDebug>
#include <QtCore>
testAsync::testAsync(){}
void testAsync::run() {
for(int i = 0; i <= 100; i++)
{
qDebug() << this->name << " " << i;
}
//return true
}
How can these be done?
Register the type correctly:
qmlRegisterType<testAsync>("TestAsync", 1, 0, "TestAsync");
Make a instance of your type in the qml file and call the methods of it.
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import TestAsync 1.0
ApplicationWindow {
id: window
title: "Stack"
visible: true
width: 1400
TestAsync {
id: threadAsync
}
Page {
....
Button {
property bool test : true
text: "async"
onClicked: {
if(test) {
val=threadAsync.start()
} else {
val=threadAsync.quit()
}
console.log(val)
test=!test
}
}
....
}
You've done several errors that drives you away from desired.
As it was already mentioned by #folibis and by #Hubi -- you've used C++ class names which starts from small letter. QML has problems with it.
Regarding multi-threading, there are a lots of ways to do it. It really depends on your particular tasks.
I do really recommend you to read next articles (from official Qt documentation):
Threading Basics
Multithreading Technologies in Qt
Since you have signals in Qt and QML, you may implement all what you want in C++ and then just drop it to QML.
You may refer to this simple project on GitHub I've prepared for you. There is moveToThread approach implemented.

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.

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.

Access to existing QML component from C++

I am a newbie in QML, and cannot resolve a simple issue. I want to get access to the QML components from C++, but I cannot.
The pointer test is always 0. What can be the reason?
The code is the following:
main.cpp
int main(int argc, char *argv[])
{
QGuiApplication &app=reg6::Bonder::BonderGuiApplication::instance();
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject* test=engine.rootObjects().first()->findChild<QObject*> ("cameraArea");
test->setProperty("color","black");
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 1800
height: 900
SplitView
{
anchors.fill: parent
orientation: Qt.Vertical
SplitView {
Layout.fillHeight: true
SplitView {
orientation: Qt.Vertical
width:400
Layout.minimumWidth: 400
Layout.maximumWidth: 500
Camera {
id: cameraArea
height: 400
Layout.maximumHeight: 400
Layout.minimumHeight: 300
}
List {
id: listArea
}
}
Bonder {
id: mainArea
Layout.fillWidth: true
}
Properties {
id: propertiesArea
Layout.minimumWidth: 300
Layout.maximumWidth: 400
}
}
Error {
id: errorArea
Layout.minimumHeight: 100
height: 200
}
}
}
Camera.qml
import QtQuick 2.5
Rectangle {
color: "lightblue"
}
You have to set the objectName property also of the QML component to get a valid pointer to your QObject because T QObject::findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const needs the objectName not the ID