QT Quick Application Window embed C++ object - c++

I have a QT Quick 2.2 ApplicationWindow and I want to use inside this ApplicationWindow a C++ object.
I know QQuickView view, but this work only for objects which are derived from QQuickItem (not for ApplicationWindow).
I also know qmlRegisterType, but this adds only a general C++ class in QML. I want only to have one C++ object (instantiated in C++ code) in the ApplicationWindow.
Is there a possibility to use a C++ Object in a QT Quick 2.2 ApplicationWindow?
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QQmlContext>
#include "myclass.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyClass myClass;
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
myclass.h
#include <QObject>
#include <QString>
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0) {};
Q_INVOKABLE QString getValue();
};
myclass.cpp
#include "myclass.h"
#include <QString>
QString MyClass::getValue() {
return "42";
}
qrc:///main.qml
import QtQuick 2.2
import QtQuick.Controls 1.1
ApplicationWindow {
visible: true
Text {
text: myClass.getValue()
anchors.centerIn: parent
}
}
Thanks

It depends or your purpose. If you want to define QML type from C++, you should do this in your main.cpp for example :
qmlRegisterType< MyClass >("com.example.myclass", 1, 0, "MyClass");
now in your .qml file, first you need to import your newly created data type using import statement :
import com.example.myclass 1.0
and then you can create an item from your own data type:
import QtQuick 2.2
import QtQuick.Controls 1.1
import com.example.myclass 1.0
ApplicationWindow {
visible: true
Text {
text: myClass.getValue()
anchors.centerIn: parent
}
MyClass {
}
}
But you have another solution. You can pass a QObject object from c++ to QML.
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("myClass", new MyClass);
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
Now inside of your qml file you can access myClass object.

Related

Error using Connections in c++ defined QML Type

when using Connections on a c++ defined QML Type, I get the following error:
Cannot assign to non-existent default property.
Following code produces the error:
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQml 2.15
import myextension 1.0
Window {
id:root
width: 640
height: 480
visible: true
Custom
{
Connections{
//target:root
//function onWidthChanged(){console.debug("received")}
}
}
}
customcppclass.h
#ifndef CUSTOMCPPCL_H
#define CUSTOMCPPCLASS_H
#include <QObject>
class Message : public QObject
{
Q_OBJECT
public:
};
#endif // CUSTOMCPPCLASS_H
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "customcppclass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<Message>("myextension", 1, 0, "Custom");
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();
}
Is there a different way to receive the changed signals of properties in a qml extension?
Your error has nothing to do with Connections objects, or your custom C++ class. You will get the same error if you do this:
QtObject {
Item { }
}
Everything in a QML object has to be stored in a property, including child objects. But it doesn't usually look like a property because they use a default property that automatically moves objects into the list of children.
Item {
QtObject { }
}
is effectively the same as:
Item {
data: QtObject {}
}
Where data is the name of the default property that Item uses to hold its child objects.
The problem with your code is that your object is a simple QObject and has no default property and no built-in method for managing children. The easy way to solve your problem is by creating your Connections object as its own property.
Custom {
property Connections myConnections: Connections {
target: root
function onWidthChanged() { console.debug("received") }
}
}
If you wanted to make it more like the way Item manages its children, then you would have to modify your C++ class to maintain a list and add a default property. That's most likely overkill for your needs though.
UPDATE:
The "overkill" solution would look something like this (This is just extracted from QQuickItem source code):
class Message: public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QObject> data READ data)
Q_CLASSINFO("DefaultProperty", "data")
public:
QQmlListProperty<QObject> data();
static void data_append(QQmlListProperty<QObject> *, QObject *);
static int data_count(QQmlListProperty<QObject> *);
static QObject *data_at(QQmlListProperty<QObject> *, int);
static void data_clear(QQmlListProperty<QObject> *);
};
You can read up on QQmlListProperty or read the QQuickItem source to figure out the implementation of those functions.

Q_INVOKABLE does not expose method to QML

I want to call C++ method from QML, passing parameters to it. As i understood, i should mark class with macro Q_OBJECT and desired public functions with Q_INVOKABLE.
Although i did it, i still get in runtime error
qrc:/main.qml:42: TypeError: Property 'addFile' of object QObject(0xf20e90) is not a function
Here is my .hpp and .cpp files of the class:
lib_controller.hpp
#include <QObject>
#include <QString>
...
class LibController : public QObject{
Q_OBJECT
Q_PROPERTY(decltype(getProgress) progress READ getProgress NOTIFY ProgressChanged)
public:
...
Q_INVOKABLE
void addFile(QString from_name, QString to_name);
...
};
lib_controller.cpp
#include "lib_controller.hpp"
...
void LibController::addFile(QString from_name, QString to_name){
file_manager = new FileManager(from_name.toUtf8().constData(),
to_name.toUtf8().constData());
}
...
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include "lib_controller.hpp"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// Registration of custom type
qmlRegisterType<LibController>("com.sort.controller", 0, 1, "LibController");
...
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2
import com.sort.controller 0.1
...
FileDialog {
id: fileDialog_for_load
title: "Load file"
onAccepted: {
fileDialog_for_save.open()
}
}
FileDialog {
id: fileDialog_for_save
title: "Save file"
onAccepted: {
var loadPath = fileDialog_for_load.fileUrl.toString();
loadPath = loadPath.replace(/^(file:\/{2})/,"");
var savePath = fileDialog_for_save.fileUrl.toString();
savePath = savePath.replace(/^(file:\/{2})/,"");
console.log("Save Path:" + savePath)
libController.addFile(loadPath, savePath)
}
}
LibController { id: libController }
What i want, is to call function addFile() to construct file_manager member then it needs to sort new file.
Why is this error happenning? What am I doing wrong?
According to the docs, fileUrl property of FileDialog returns a url type which is equivalent to C++ type QUrl not QString. So you can either:
Edit your C++ method to take two QUrls.
Or in your QML pass .fileUrl.toString().
in your libcontroller constructor you delete your private member instead of initializing it.
I think you are missing a component instancing LibController { id:libController } in your main.qml.

Fail To Connect Qml signal to C++ Slot

I have been trying to connect signal between Qml file and c++, but public slot in c++ doesn't seem to receive the signal.
What might be wrong with my program?
main.qml
Item{
id:item
signal qml_signal
Button{
onClicked: {
item.qml_signal();
}
}
}
main.cpp
QQuickView view(QUrl("qrc:/main.qml"));
QObject *item = view.rootObject();
Myclass myclass;
QObject::connect(item, SIGNAL(qml_signal()), &myclass,SLOT(cppSlot()));
myclass.h
void cppSlot() ;
myclass.cpp
void Myclass::cppSlot(){
qDebug() << "Called the C++ slot with message:";
}
When you want objects to interact between C++ and QML, you must do it on the QML side, since obtaining a QML object from C++ can cause you many problems, as in this case, the signal created in QML can not be handled in C++.
The solution is to export your object myclass to QML and make the connection there:
main.cpp
#include "myclass.h"
#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQuickView view(QUrl("qrc:/main.qml"));
Myclass myclass;
view.rootContext()->setContextProperty("myclass", &myclass);
view.show();
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Controls 1.4
Item{
id:item
signal qml_signal
Button{
onClicked: item.qml_signal()
}
onQml_signal: myclass.cppSlot()
}

Calling method on C++ singleton object in JS yields "TypeError: Property 'foo' of object [object Object] is not a function"

I'm registering a C++ class as a QML singleton, and attempting to call a method on this singleton from JS. I'm getting this error message:
TypeError: Property 'readAll' of object [object Object] is not a function
My code:
main.cpp (just registers a singleton, done by the books):
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "fileio.h"
static QObject* fileIOSingletonTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
FileIO* example = new FileIO();
return example;
}
static void registerQmlTypes()
{
qmlRegisterSingletonType<FileIO>("FileIO", 1, 0, "FileIO", fileIOSingletonTypeProvider);
}
int main(int argc, char *argv[])
{
registerQmlTypes();
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
fileio.cpp:
#include "fileio.h"
FileIO::FileIO(QObject *parent) :
QObject(parent)
{
}
void FileIO::readAll()
{
}
fileio.h:
#ifndef FILEIO_H
#define FILEIO_H
#include <QObject>
class FileIO : public QObject
{
Q_OBJECT
public:
explicit FileIO(QObject *parent = 0);
Q_INVOKABLE void readAll();
};
#endif // FILEIO_H
main.qml:
import QtQuick 2.5
import QtQuick.Window 2.2
import "test.js" as Code
Window {
visible: true
Component.onCompleted: {
Code.func();
}
}
test.js:
.import FileIO 1.0 as FileIO
function func() {
FileIO.readAll();
}
I have found the answer here. Although they refer to importing from QML and I refer to importing from JS, the solution is the same, namely:
Either call the method as FileIO.FileIO.readAll() or remove the as FileIO from the .import statement.
This is not at all apparent from the docs which neither mention the need to double the FileIO qualifier, nor mention the possibility to remove the as FileIO. I've reported a documentation bug to Qt.

setMainQmlFile`, rootObject and showExpanded are not members of QQmlApplicationEngine

I've written this piece of code following some guys tutorial but I can't get it to run. The error says:
setMainQmlFile`, rootObject and showExpanded are not members of
QQmlApplicationEngine
What it's supposed to do is get a signal from QML and print out a message (in console). Basically I'm trying to integrate C++ and QML.
EDIT
I've tried to replace some of the functions with some others that seemed appropriate (at least to me). I've also tried to find what to include so that these functions would work but with no luck.
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "qtquickglobal.h"
#include <QQmlContext>
#include "myclass.h"
#include <QtCore>
#include <QtDebug>
#include <QQuickWindow>
int main(int argc, char *argv[]){
//Q_OBJECT;
QGuiApplication app(argc, argv);
QQmlApplicationEngine viewer;
viewer.load(QUrl(QStringLiteral("Qt/Versuch2/main.qml")));
myclass data;
viewer.rootContext() ->setContextProperty("myclassData", &data);
viewer.setMainQmlFile(QStringLiteral("qml/Versuch2/main.qml"));
QObject *viewerobject = viewer.rootObject();
QObject::connect(viewerobject, SIGNAL(qmlSignal(QString)), &data, SLOT(cppSlot(QString)));
viewer.showExpanded();
return app.exec();
}
void myclass::cppSlot(QString msg) {
qDebug() <<QString ("Called the cpp slot with message: %1").arg(msg);
}
Thank You.
I don't know where did you find you tutorial but, regarding Qt documentation, there is no such methods as setMainQmlFile nor showExpanded for QQmlApplicationEngine.
For setMainQmlFile(...), try to use instead setSource(...).
For showExpanded(), it's a QWidget function, and QQmlApplicationEngine do not inherit QWidget.
Regarding rootObject(), it might be a typo, you can use rootObjects() which return a QList<QObject*>.
Edit: Looks like you'll have to use the Qt Quick 2 Application wizard of Qt Creator in order to re-create that QtQuick2ApplicationViewer class used in the tutorial you found.
Using Qt 5.4.0 and Qt Creator 3.3.0, create New Project:
Click New Project
Qt Quick Application
Click Choose...
Name the project and select where to place it
Click Next
Select Qt Quick 2.4 from the drop-down menu
Click Next
Select desired Kit(s)
Click Next
Click Finish
Now You should see open main.qml file with following code:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
Qt.quit();
}
}
}
Make changes to the file so that it looks like the following:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
//### New Code ###
signal myQmlSignal(string msg)
//################
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
//### New Code ###
//Replace "Qt.quit();" with
console.log("Sending myQmlSignal from QML...");
myQmlSignal("Hello from QML")
//################
}
}
}
Add new class to Your project:
Right Mouse Click project name in Projects viewer
Click Add New...
Select C++ Class if not already selected
Click Choose...
In Class name filed enter "MyCppClass"
Set Base class to QObject
Click Next
Click Finish
Open mycppclass.h file, it should look like the following:
#ifndef MYCPPCLASS_H
#define MYCPPCLASS_H
#include <QObject>
class MyCppClass : public QObject
{
Q_OBJECT
public:
explicit MyCppClass(QObject *parent = 0);
~MyCppClass();
signals:
public slots:
};
#endif // MYCPPCLASS_H
Make changes to mycppclass.h so it looks like this:
#ifndef MYCPPCLASS_H
#define MYCPPCLASS_H
#include <QObject>
//### New Code ###
#include <QDebug>
//################
class MyCppClass : public QObject
{
Q_OBJECT
public:
explicit MyCppClass(QObject *parent = 0);
~MyCppClass();
signals:
public slots:
//### New Code ###
void myCppSlot(const QString &msg);
//################
};
#endif // MYCPPCLASS_H
Open mycppclass.cpp, which should look like this:
#include "mycppclass.h"
MyCppClass::MyCppClass(QObject *parent) : QObject(parent)
{
}
MyCppClass::~MyCppClass()
{
}
Change it to this:
#include "mycppclass.h"
MyCppClass::MyCppClass(QObject *parent) : QObject(parent)
{
}
MyCppClass::~MyCppClass()
{
}
void MyCppClass::myCppSlot(const QString &msg)
{
//### New Code ###
qDebug() << "Signal was received by C++. It contains follwoing message: " << msg;
//################
}
Open main.cpp, which looks like this:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
And make following changes:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
//### New Code ###
#include "mycppclass.h"
//################
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
//### New Code ###
MyCppClass myCppClass;
QObject::connect(engine.rootObjects().takeFirst(), SIGNAL(myQmlSignal(QString)), &myCppClass, SLOT(myCppSlot(QString)));
//################
return app.exec();
}
Click big green triangle to compile and run Your application. Watch Application Output area, click Hello World, You should see following message being printed out:
qml: Sending myQmlSignal from QML...
Signal was received by C++. It contains follwoing message: "Hello from QML"