I have a small class that is not working properly, and I can't get what is wrong with it. The compiler gives the message:
main.cpp: error: undefined reference to 'CDetails::CDetails()'
This is the snapshot from the code:
//main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <QQmlContext>
#include <QDebug>
class CDetails : public QObject
{ Q_OBJECT
public:
CDetails() {}
~CDetails(void) {}
public slots:
void cppSlot(const QString &msg)
{ qDebug() << "Called the C++ slot with message:" << msg;
}
};
int main(int argc, char *argv[])
{ QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/testqml/main.qml"));
viewer.showExpanded();
CDetails *test = new CDetails();
QObject::connect((QObject*)viewer.rootObject(),
SIGNAL(qmlSignal(QString)),test,
SLOT(cppSlot(QString)));
return app.exec();
}
And in main.qml:
import QtQuick 2.0
Rectangle {
id: guide
width: 360
height: 360
signal qmlSignal(string msg)
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
property double scaleFactor: 1.0
property string iconUrl: "image.png"
MouseArea {
anchors.fill: parent
onClicked: {
guide.qmlSignal("Hello from QML")
}
}
}
Update: Thanks for the suggestion on constructor. Now the error is:
error: undefined reference to 'vtable for CDetails'
What is missed here? All suggestions are welcome.
error: undefined reference to 'vtable for CDetails'
What is missed here? All suggestions are welcome.
Seems you are missing the moc include before the main function.
main.cpp
#include <QtGui/QGuiApplication>
#include <QQmlContext>
#include <QDebug>
class CDetails : public QObject
{ Q_OBJECT
public:
CDetails() {}
~CDetails(void) {}
public slots:
void cppSlot(const QString &msg)
{ qDebug() << "Called the C++ slot with message:" << msg;
}
};
#include "main.moc"
int main(int argc, char *argv[])
{ QGuiApplication app(argc, argv);
QQuickView view;
viewer.setMainQmlFile(QStringLiteral("qml/testqml/main.qml"));
viewer.showExpanded();
CDetails *test = new CDetails();
QObject::connect((QObject*)viewer.rootObject(),
SIGNAL(qmlSignal(QString)),test,
SLOT(cppSlot(QString)));
return app.exec();
}
main.pro
...
TEMPLATE = app
TARGET = main
QT += quick
SOURCES += main.cpp
...
Note that, you will also need to add your custom lines that were there before, like dealing with the application viewer, et al.
Alternatively, you could also decouple the class and the main.cpp which means you would put the declaration of the class into a separate header, and then the defintition into a separate source file.
The main.cpp would include the freshly established header, and you would need to make sure that the new header and source file are added to the HEADERS an SOURCES variables in the qmake project file, respectively to get through the moc processing.
You're missing implementations of your constructor and destructor. Quick fix:
class CDetails : public QObject
{ Q_OBJECT
public:
CDetails() {}
~CDetails(void) {}
...
};
Related
I am having a problem understanding how to use a c++ singleton object from qml.
I know that I have to inherit my classes from the QObject class and that I have to expose the properties.
And that in order for them to be usable in the qml I have to do setContextProperty("ClassName", &class name).
However, if we admit that this class contains another object and that I want to be able to use it from the qml, I get errors like "cannot call method name" from undefined object.
Example:
APi.h
class API : public QObject {
Q_OBJECT
Q_PROPERTY(User *user READ getUser WRITE setUser NOTIFY userChanged)
public:
Q_INVOKABLE inline User *getUser() const {
qDebug() << "get called";
return user;
};
inline void setUser(User *new_user) { this->user = new_user; };
signals:
void userChanged();
private:
User *user;
};
User.h
#ifndef USER_H
#define USER_H
#include <QObject>
#include <QString>
class User: public QObject{
Q_OBJECT
public:
Q_INVOKABLE inline QString &getName(){return name;}; // qrc:/main.qml:63: TypeError: Cannot call method 'getName' of undefined
private:
QString name;
};
#endif // USER_H
main.cpp
#include <QQmlContext>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQuick3D/qquick3d.h>
#include "API.h"
#include "User.h"
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
API api;
User user;
api.setUser(&user);
engine.rootContext()->setContextProperty("API", &api);
qmlRegisterType<User>("User", 1, 0, "User");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick
import QtQuick3D
import QtQuick.Controls
import QtQuick.Layouts
Window {
Item{
id: test
Component.onCompleted: {
console.log("Completed")
API.getUser() // no error, getUser is called without error
console.log(API.getUser().getName())
}
}
}
What possibilities do I have to access the User object from the qml through API?
You could do any of the following:
Add a public slot in API class:
API.cpp:
QString API::getUserName()
{
return user.getName();
}
main.qml:
Component.onCompleted: console.log( API.getUserName() )
Make User a Q_PROPERTY in API class:
API.h:
Q_PROPERTY( User* user READ user NOTIFY userChanged)
main.qml:
Component.onCompleted: console.log( API.user.getName() )
Register User with QML:
main.cpp:
qmlRegisterType<User>("MyUser", 1, 0, "MyUser");
main.qml:
Component.onCompleted: console.log( API.getUser().getName() )
Here's what my main.cpp looks like:
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QCoreApplication::addLibraryPath("./");
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:/myqml.qml"));
view.show();
return app.exec();
}
As you can see, it creates things from myqml. Well, myqml instantiates a C++ class MyClass.
How do I access this C++ methods from the object QQuickView view? For example, I'd like to do something of the type view.MyClassInstance.myMethod1()
You want to obtain an object created in QML by C++, here it does not matter if the target has a prototype created in C ++ or not. If you want this you must obtain the object through findChild since all the objects created in QML have a kinship relationship with the window.
main.cpp
#include <QtQuick>
#include <QtGui>
class MyClass: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
Q_INVOKABLE void invokable(){
qDebug()<< "invokable";
}
Q_SLOT void slot(){
qDebug()<< "slot";
}
void function(){
qDebug()<< "function";
}
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<MyClass>("foo", 1, 0, "MyClass");
QGuiApplication app(argc, argv);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.show();
if(MyClass* myclass_instance = view.findChild<MyClass *>("myclass_instance")){
myclass_instance->invokable();
myclass_instance->slot();
myclass_instance->function();
}
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import foo 1.0
Rectangle {
color: "salmon"
width: 640
height: 480
MyClass{
objectName: "myclass_instance"
}
}
But this method has several drawbacks such as who manages the life cycle of the object is QML, not C ++ so the pointer could at some point point to an unreserved address. Another disadvantage is that there is a dependency of C++ to QML since if the objectName is changed in QML the code in C ++ would have to be changed.
Another approach is to create a helper class that is exported to QML with setContextProperty() and that interacts with the MyClass object.
main.cpp
#include <QtQuick>
#include <QtGui>
class MyClass: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
Q_INVOKABLE void invokable(){
qDebug()<< "invokable";
}
Q_SLOT void slot(){
qDebug()<< "slot";
}
void function(){
qDebug()<< "function";
}
};
class Helper: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
void call_function(){
emit called();
}
Q_SIGNAL void called();
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<MyClass>("foo", 1, 0, "MyClass");
QGuiApplication app(argc, argv);
Helper helper;
QQuickView view;
view.rootContext()->setContextProperty("helper", &helper);
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.show();
helper.call_function();
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import foo 1.0
Rectangle {
color: "salmon"
width: 640
height: 480
MyClass{
id: myclass
}
Connections{
target: helper
onCalled:{
myclass.invokable()
myclass.slot()
}
}
}
The advantage of this method is that there is no dependence between the logic of C++ and QML, besides the life cycle does not generate problems since the myclass objects are not handled directly in QML. The disadvantage is that you write a little more code and you can only call the Q_INVOKABLE or Q_SLOT.
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.
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"
I am aware that to use the signals and slots mechanism of Qt inside a class, the class must include the Q_OBJECT macro, but I am attempting to use signals and slots in main(), without using any class.
Here is my code so far:
#include <QApplication>
#include <QWidget>
#include <QTextEdit>
#include <QtGui>
void saveText();
int main(int argv, char **args)
{
QApplication app(argv, args);
QTextEdit textEdit;
QPushButton saveButton("Save!");
QPushButton exitButton("Exit!");
QObject::connect(&exitButton,SIGNAL(clicked()),qApp,SLOT(quit()));
QObject::connect(&saveButton,SIGNAL(clicked()),qApp,SLOT(saveText()));
QVBoxLayout vlyt;
vlyt.addWidget(&textEdit);
vlyt.addWidget(&exitButton);
vlyt.addWidget(&saveButton);
QWidget mainWindow;
mainWindow.setLayout(&vlyt);
mainWindow.show();
return app.exec();
}
void saveText()
{
exit(0);
}
Here is the GUI window generated:
From the above code, the exit button is connected to quit(), which is a Qt function, when clicked it works. The save button assigned to the function saveText(), is configured to exit, but does not do so.
Please tell me where I have gone wrong in understanding signals and slots in Qt.
Qt4...
All classes that inherit from QObject or one of its subclasses (e.g.,
QWidget) can contain signals and slots.1
So, you can not use slots where placed outside of QObject children.
You can connect signals to the slots which are in classes where derived from QObject. Put your slot in a class which is in a separated .h/.cpp file:
class MyClass : public QObject
{
Q_OBJECT
...
public slots:
void saveText();
};
According to Qt5: New Signal Slot Syntax in Qt 5. You can connect to those type of global functions. (Thanks to #thuga's comments)
I'll just put example here.
main.cpp:
#include <QCoreApplication>
#include <iostream>
#include <QObject>
#include "siggen.h"
void handler(int val){
std::cout << "got signal: " << val << std::endl;
}
int main(int argc, char *argv[])
{
SigGen siggen;
QObject::connect(&siggen, &SigGen::sgAction, handler);
siggen.action();
QCoreApplication a(argc, argv);
std::cout << "main prog start" << std::endl;
return a.exec();
}
siggen.h:
#ifndef SIGGEN_H
#define SIGGEN_H
#include <QObject>
class SigGen : public QObject
{
Q_OBJECT
public:
explicit SigGen(QObject *parent = 0);
void action(void);
signals:
void sgAction(int value);
};
#endif // SIGGEN_H
siggen.cpp:
#include "siggen.h"
SigGen::SigGen(QObject *parent) : QObject(parent)
{}
void SigGen::action()
{
emit sgAction(42);
}
QObject::connect(&saveButton, &QPushButton::clicked, [](){saveText();}); // qt5.9.6
or as mentioned in the masoud's answer
QObject::connect(&saveButton, &QPushButton::clicked, saveText); // qt5.9.6
It is possible to connect a signal to function inside main function.
This has been tested in Qt5.15. Here is the simple example where the QPushButton 'Clicked' signal is used to trigger a function (here I used lamda's, but regular functions can also be used).
int main(int argc, char *argv[])
{
// Created QApplication
QApplication a(argc, argv);
// Created the splashscreen(which is QObject)
QPixmap pixmap(":/images/Sample.png");
QSplashScreen splash(pixmap);
// Created the pushbutton and added to splashscreen
QPushButton b(&splash);
b.setGeometry(50,50, 100, 50);
b.setText("FPS");
// variable to be modified inside a lamda function
int i = 0;
// Connection for button clicked signal executes lamda function
QObject::connect(&b, &QPushButton::clicked,
[i = static_cast<const int&>(i), &splash = static_cast<QSplashScreen&>(splash)]()mutable
{i = i+1; splash.showMessage("clicked: "+ QString::number(i));});
// Adding properties and displaying the splashscreen
splash.setGeometry(0,0, 1920, 1080);
splash.setEnabled(true);
splash.show();
a.processEvents();
return a.exec();
}