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

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

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.

Control Qt QML SwipeView with Signals/Slots

I'm very new to Qt, and right now struggling to figure out how to change the index of the SwipeView through a different C++ class. I'd like to make some kind of a signal that emits from the C++ class saying "Swipe Right" or something like that, and having the SwipeView respond. I'm also new to signals and slots, so I might be misunderstanding how they work.
When you want to control a QML element from C ++ the best strategy is to create a QObject that has Q_PROPERTY, slot and signals and export it to QML through setContextProperty(), and perform the direct binding or use Connections to connect the signals, in this case it is only necessary to emit a signal and connect it in QML.
In the following example I show how to change to the right or left using QPushButton:
main.cpp
#include <QApplication>
#include <QPushButton>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QHBoxLayout>
class SwipeManager: public QObject{
Q_OBJECT
public:
using QObject::QObject;
public slots:
void moveToRight(){
emit toRight();
}
void moveToLeft(){
emit toLeft();
}
signals:
void toRight();
void toLeft();
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
SwipeManager manager;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("manager", &manager);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QWidget w;
QPushButton left("to left");
QPushButton right("to right");
QHBoxLayout *lay = new QHBoxLayout(&w);
lay->addWidget(&left);
lay->addWidget(&right);
QObject::connect(&left, &QPushButton::clicked, &manager, &SwipeManager::moveToLeft);
QObject::connect(&right, &QPushButton::clicked, &manager, &SwipeManager::moveToRight);
w.show();
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("SwipeView Test")
SwipeView {
id: view
anchors.fill: parent
currentIndex: 4
Repeater {
model: 10
Loader {
active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem
sourceComponent: Item{
Text {
text: "Page: "+index
anchors.centerIn: parent
}
}
}
}
}
Connections{
target: manager
onToRight: if(view.currentIndex + 1 != view.count) view.currentIndex += 1
onToLeft: if(view.currentIndex != 0) view.currentIndex -= 1
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
The complete example can be found in the following link.

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.

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

How to access properties of another file from main.qml after they are modified by C++ code first

I’m having problems with QML and C++. If someone could take a look at this piece of code and tell me what I am doing wrong. I ‘m just trying to print “msg.author” on a window (main window) but everytime I try to access it from main.qml there’s an error saying msg is not defined. Thank you for your time.
main.qml
import QtQuick 2.2
import QtQuick.Window 2.1
Window {
property alias name: value
visible: true
id: main_window
width: 500; height: width
color:"black"
}
MyItem.qml
import QtQuick 2.0
import QtQuick.Window 2.0
Text {
id: text1
visible: true
width: 100; height: 100
text: msg.author // invokes Message::author() to get this value
color: "green"
font.pixelSize: 20
Component.onCompleted: {
msg.author = "Jonah" // invokes Message::setAuthor()
}
message.h
#ifndef MESSAGE
#define MESSAGE
#include <QObject>
#include <QString>
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
void setAuthor(const QString &a) {
if (a != m_author) {
m_author = a;
emit authorChanged();
}
}
QString author() const {
return m_author;
}
signals:
void authorChanged();
private:
QString m_author;
};
#endif // MESSAGE
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QDebug>
#include "message.h"
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine_e;
engine_e.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQmlEngine engine;
Message msg;
engine.rootContext()->setContextProperty("msg", &msg);
QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));
if(component.status()!=component.Ready){
if(component.Error){
qDebug()<<"Error: "<<component.errorString();
}
}
return app.exec();
}
The code is not working, and the main has two engines. The second is not properly set-up and it should also be noted, from QQmlApplicationEngine documentation, that:
This class combines a QQmlEngine and QQmlComponent to provide a convenient way to load a single QML file. It also exposes some central application functionality to QML, which a C++/QML hybrid application would normally control from C++.
Hence, QQmlApplicationEngine can perfectly suit your needs and load the QML file. Now, it should also be noted that context properties should always be set before loading a QML file. The final correct code is the following:
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine_e;
Message msg;
engine_e.rootContext()->setContextProperty("msg", &msg);
engine_e.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
EDIT
For what concerns QML files, they should be placed inside the resources (as I hope you have done). My test setting is the following:
main.qml:
import QtQuick 2.3
import QtQuick.Window 2.1
Window {
// property alias name: value
visible: true
id: main_window
width: 500; height: width
// color:"black"
MyItem {
anchors.centerIn: parent // <--- added to the main window to see the result
}
}
MyItem.qml
import QtQuick 2.0
Text {
id: text1
visible: true
width: 100; height: 100
text: msg.author // invokes Message::author() to get this value
color: "green"
font.pixelSize: 20
Component.onCompleted: {
msg.author = "Jonah" // invokes Message::setAuthor()
}
}