How to create a subclass of QWidget when using qml - c++

I am trying to create a class in QT C++ that is a subclass of QWidget. However when I add it to my QML the program crashes. Before it chrashes "Constructed" is printed by QDebug. Any suggestions on how to fix this is wellcome.
MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QWidget>
class MyClass : public QWidget
{
private:
Q_OBJECT
public:
MyClass(QWidget *parent = nullptr);
~MyClass();
};
#endif // MYCLASS_H
MyClass.cpp
#include "myclass.h"
#include <QDebug>
MyClass::MyClass(QWidget *parent): QWidget(parent)
{
qDebug() << "Constructed";
}
MyClass::~MyClass()
{
qDebug() << "Deconstructed";
}
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include "myclass.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType <MyClass> ("MyImport", 1, 0, "MyObject");
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/RectTest/main.qml"_qs);
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();
}
main.qml
import QtQuick
import MyImport 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
MyObject {
id: obj
}
}
When I try the exact same code but instead of subclassing QWidget I subclass QObject it works fine.

Related

Method in class inheriting QObject is identified as property when called, doesn't run method

Basically, I have a project that I'm working on that's supposed to be a graphical game of Tic-Tac-Toe, and method calls to a class called "Game" (and any method calls at all) from the main qml file don't work. This error is returned:
TypeError: Property 'takeTurn' of object [object Object] is not a function
I've inherited QObject in this class, as well as included the Q_OBJECT macro and labelled the method as a Q_INVOKABLE.
The code compiles and links perfectly fine, this is a run-time error.
Here's the relevant code to help:
Game.hpp:
#define GAME_HPP
#include "Board.hpp"
#include <ostream>
#include <QObject>
class Game : public QObject
{
Q_OBJECT;
public:
//...
Q_INVOKABLE void takeTurn(int x, int y);
Q_INVOKABLE bool checkWin();
friend std::ostream& operator<<(std::ostream&, const Game&);
private:
char player_;
int turns_;
Board board_;
};
std::ostream& operator<<(std::ostream&, const Game&);
#endif // GAME_HPP
Game.cpp:
#include <iostream>
#include <QObject>
#include <QApplication>
using std::cout;
using std::endl;
//...
void Game::takeTurn(int x, int y)
{
QWindow* app = QApplication::topLevelWindows()[0];
cout << app << endl;
board_.setTile(x, y, player_);
player_ == 'X' ? player_ = 'O' : player_ = 'X';
turns_++;
}
//...
main.cpp:
#include "Game.hpp"
#include <iostream>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
using std::cout;
using std::endl;
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Game>("com.myself", 1, 0, "Game");
qmlRegisterType<Board>("com.myself", 1, 0, "Board");
QQmlApplicationEngine engine;
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();
}
main.qml:
import QtQuick.Window 2.12
import com.myself 1.0
Window {
visible: true
width: 600
height: 600
title: qsTr("TicTacToe")
Item {
//...
MouseArea {
id: mouseArea1
anchors.fill: parent
onClicked: {
Game.takeTurn(0,0)
}
}
}
//...
}
//...
You have registered Game as a type, so what you need to do is create an object of that type, the error points to that problem:
// ...
Window {
visible: true
width: 600
height: 600
title: qsTr("TicTacToe")
Game {
id: game
}
// ...
Item {
// ...
MouseArea {
id: mouseArea1
anchors.fill: parent
onClicked: {
game.takeTurn(0,0)
}
}
}
// ...

How is a QList of QString assigned to a list view?

I wrote this database front-end program with Qt and I used a C++ function to return the result of each query. However, I'm not able to assign the results to a list view.
MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QString> val READ val WRITE setVal NOTIFY valChanged)
public:
void setVal(QList<QString>);
QList<QString> val() const;
explicit MyObject(QObject *parent = nullptr);
Q_INVOKABLE int registernew(int Sno=0, QString Name="NULL",long long PhoneNo=0, QString Country="NULL",QString State="NULL", QString District="NULL", int PhoneLine=0,long long Whatsapp = 0);
Q_INVOKABLE int querydistrict(QString);
signals:
void valChanged();
private:
QList<QString> m_val;
};
#endif // MYOBJECT_H
MyObject.cpp
.............. void MyObject::setVal(QList<QString> list)
{
if(list != m_val)
{
m_val = list;
emit valChanged();
}
}
QList<QString> MyObject::val() const
{
return m_val;
}....................
main.cpp
#include <QGuiApplication>
#include<QQmlApplicationEngine>
#include "myobject.h"
#include<QQuickView>
#include<QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
///delete later!!
qmlRegisterType<MyObject>("io.qt.examples.MyObject", 1, 0, "MyObject");
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();
}
page2.ui.qml
ScrollView {
id: scrollView
anchors.rightMargin: 20
anchors.leftMargin: 20
anchors.bottomMargin: 40
anchors.topMargin: 200
anchors.fill: parent
ListView {
id: listView
model: myobject.val
delegate: ItemDelegate {
text: modelData
}
}
}
Where am i going wrong? The list is never updated with values when i run the program. It is always blank. But the variable m_val, when I return it in MyObject.cpp and use qDebug to output it, outputs relevant strings.
You should not use QList<QString> but QStringList as it is registered. On the other hand if you are going to establish the data from C ++ since you indicate that it is the result of a query then that Q_PROPERTY is not of being written in QML. Finally, although it is not an error, it is better to register the classes before creating the QXApplication.
myobject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList val READ val NOTIFY valChanged)
public:
explicit MyObject(QObject *parent = nullptr);
QStringList val() const;
void setVal(QStringList val);
// other methods
signals:
void valChanged(QStringList val);
private:
QStringList m_val;
};
#endif // MYOBJECT_H
myobject.cpp
#include "myobject.h"
// other methods
QStringList MyObject::val() const{
return m_val;
}
void MyObject::setVal(QStringList val)
{
if (m_val == val)
return;
m_val = val;
emit valChanged(m_val);
}
main.cpp
// ...
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<MyObject>("io.qt.examples.MyObject", 1, 0, "MyObject");
QGuiApplication app(argc, argv);
// ...
So when you do your query you must pass the result to setVal:
setVal(your_QStringList);

How does setContextProperty() fail in this situation?

I have got three classes created in C++ as Window, PropertyList and MyParams and I want to pass PropertyList and MyParams these two classes to qml.
class Window
{
public:
PropertyList* getPropertyList();
private:
PropertyList* propertyList;
};
class PropertyList : public QObject
{
Q_OBJECT
public:
MyParams* getMyParams();
Q_INVOKABLE void test();
private:
MyParams* myParams;
};
class MyParams : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void test();
};
int main(int argc, char *argv[])
{
Window* window = new Window();
PropertyList* propertyList = window->getPropertyList();
MyParams* myParam = propertyList->getMyParams();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("myParams"), myParam);
engine.rootContext()->setContextProperty(QStringLiteral("propertyList"), propertyList);
}
Why does this work:
engine.rootContext()->setContextProperty(QStringLiteral("propertyList"), propertyList);
And this fail:
engine.rootContext()->setContextProperty(QStringLiteral("myParams"), myParam);
Is it because I declare PropertyList as Q_OBJECT? And how can I fix this? Thanks a lot.
propertyList.test() can be called succesfully while myParams.test() cannot be called and crashed the qml.
I cannot see any problems in your code example, because the sample is not complete (Consider: https://stackoverflow.com/help/reprex).
Nevertheless I implemented a solution for you. I hope it will help. (Warning: there will be memory leaks around class Window, but it should helpt to understand C++ and Qml binding)
Here is your PropertyList.h:
#ifndef PROPERTYLIST_H
#define PROPERTYLIST_H
#include "myparams.h"
#include <QObject>
class PropertyList : public QObject
{
Q_OBJECT
public:
explicit PropertyList(QObject *parent = nullptr) : QObject (parent) { }
MyParams* getMyParams()
{
return myParams;
}
Q_INVOKABLE void test()
{
qDebug() << "PropertyList test";
}
private:
MyParams* myParams = new MyParams();
};
#endif // PROPERTYLIST_H
Here is your MyParams.h:
#ifndef MYPARAMS_H
#define MYPARAMS_H
#include <QObject>
#include <QDebug>
class MyParams : public QObject
{
Q_OBJECT
public:
explicit MyParams(QObject *parent = nullptr) : QObject (parent) { }
Q_INVOKABLE void test()
{
qDebug() << "MyParams test";
}
};
#endif // MYPARAMS_H
Here is your main.cpp:
#include "propertylist.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQmlContext>
#include <QDebug>
class Window
{
public:
PropertyList* getPropertyList()
{
return propertyList;
}
private:
PropertyList* propertyList = new PropertyList(nullptr);
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Window* window = new Window();
PropertyList* propertyList = window->getPropertyList();
MyParams* myParam = propertyList->getMyParams();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("myParams"), myParam);
engine.rootContext()->setContextProperty(QStringLiteral("propertyList"), propertyList);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
{
return -1;
}
return app.exec();
}
Here is your main.qml:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ColumnLayout {
anchors.centerIn: parent
Button {
text: "myParams"
onClicked: {
myParams.test()
}
}
Button {
text: "propertyList"
onClicked: {
propertyList.test()
}
}
}
}

How to delete QML window on closing signal (how to do it right)?

I have some problems with qt + qml: Sometimes I get segmentation fault trying to clear QQmlengine. And even if program doesn't crashes it has memory leaks. (sorry for my english). I think so because the larger qml file (many nested classes) the larger application process even after QQmlengine was deleted. So my question is what am I doing wrong in the code below?
//main.cpp
#include "qmlwindow.h"
#include <QApplication>
int QMLwindow::someValue = 0;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(false);
QSharedPointer<QMLwindow> w0(new QMLwindow);
QSharedPointer<QMLwindow> w1(new QMLwindow);
QSharedPointer<QMLwindow> w2(new QMLwindow);
w0.data()->createWindow();
w0.data()->createWindow();
w1.data()->createWindow();
w2.data()->createWindow();
return a.exec();
}
 
//qmlwindow.h
#ifndef QMLWINDOW_H
#define QMLWINDOW_H
#include <QObject>
#include <QQmlComponent>
#include <QQuickWindow>
#include <QSharedPointer>
#include <QDebug>
#include <QQmlEngine>
#include <QQmlContext>
class QMLwindow : public QObject
{
Q_OBJECT
private:
QSharedPointer<QQmlEngine> engine;
QQmlComponent *component;
QObject *windowObj;
QQuickWindow *loginWindow;
QVariant count;
public:
static int someValue;
explicit QMLwindow(QObject *parent = 0);
~QMLwindow();
void createWindow();
signals:
public slots:
void deleteWindow(QQuickCloseEvent *event);
};
#endif // QMLWINDOW_H
 
//qmlwindow.cpp
#include "qmlwindow.h"
QMLwindow::QMLwindow(QObject *parent) : QObject(parent)
{
someValue++;
count = someValue;
qDebug() << "construction of QMLwindow " << count.toInt();
}
QMLwindow::~QMLwindow()
{
qDebug() << "destruction of QMLwindow " << count.toInt();
}
void QMLwindow::createWindow(){
if(engine.isNull()){
engine = QSharedPointer<QQmlEngine>(new QQmlEngine);
component = new QQmlComponent(engine.data());
component->loadUrl(QUrl(QStringLiteral("qrc:/SomeWindow.qml")));
Q_ASSERT(component->isReady());
windowObj = component->create();
loginWindow = qobject_cast<QQuickWindow *> (windowObj);
engine.data()->rootContext()->setContextProperty("someValue", count) ;
connect(loginWindow, SIGNAL(closing(QQuickCloseEvent*)), SLOT(deleteWindow(QQuickCloseEvent*)));
loginWindow->show();
}
else loginWindow->show();
}
void QMLwindow::deleteWindow(QQuickCloseEvent*event){
Q_UNUSED(event)
if(!engine.isNull()) {
qDebug() << "loginWindow destruction";
engine.clear();
}
this->deleteLater();
}
 
//SomeWindow.qml
import QtQuick 2.3
import QtQuick.Window 2.0
Window {
width: 200
height: 200
color: "white"
Text{
text: someValue
anchors.centerIn: parent
}
}

Qt Signals and Slots - nothing happens

I'm currently trying to connect a QML Signal to a C++ Slot unsuccessfully.
I just had a look on several other examples but I think i didn't got it with how to get the root object of a qml document...
My problem is, it seems like the signal will be sent from the qml file, but not received in the cpp file. There are no errors when I execute this code.
//Counter.h
#ifndef COUNTER_H
#define COUNTER_H
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
private:
int counter;
public:
explicit Counter(QObject *parent = 0);
signals:
void counterHasChanged(int);
public slots:
void click();
};
#endif // COUNTER_H
//counter.cpp
#include "counter.h"
#include <QDebug>
Counter::Counter(QObject *parent) :
QObject(parent)
{
this->counter = 0;
qDebug() << "Class Counter created";
}
void Counter::click() {
this->counter++;
qDebug() << "clickRegistered() - emit counterHasChanged()";
emit counterHasChanged(counter);
}
//main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "counter.h"
#include <QObject>
#include <QtQuick>
#include <QDebug>
#include <QQuickItem>
#include <QQmlContext>
#include <QtCore>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/StatesTest2/main.qml"));
viewer.showExpanded();
QQuickView view;
view.setSource(QUrl::fromLocalFile("qml/StatesTest2/main.qml"));
QObject *item = view.rootObject();
Counter counter;
QObject::connect(item, SIGNAL(click()), &counter, SLOT(click()));
return app.exec();
}
//main.qml
import QtQuick 2.0
Rectangle {
id: root
width: 360
height: 360
signal click
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
root.click
console.log("click() qml")
}
}
Text {
text: "clicks: "
x: 30
y: 250
}
Text {
id: counter
text: "0"
x: 75
y: 250
}
}
I know there are tons of topics like this..
For some reason, no other solution worked for me.. maybe I should change my IDE :D
I suggest you to add counter as a context property.. This requires following changes.
//Counter.h
#ifndef COUNTER_H
#define COUNTER_H
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
private:
int counter;
public:
explicit Counter(QObject *parent = 0);
signals:
void counterHasChanged(int Value);
public slots:
void click();
};
#endif // COUNTER_H
The Counter.cpp file
//counter.cpp
#include "counter.h"
#include <QDebug>
Counter::Counter(QObject *parent) :
QObject(parent)
{
this->counter = 0;
qDebug() << "Class Counter created";
}
void Counter::click() {
this->counter++;
qDebug() << "clickRegistered() - emit counterHasChanged()";
emit counterHasChanged(counter);
}
The main.cpp file
//main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "counter.h"
#include <QObject>
#include <QtQuick>
#include <QDebug>
#include <QQuickItem>
#include <QQmlContext>
#include <QtCore>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Counter counter;
QtQuick2ApplicationViewer viewer;
viewer.rootContext()->setContextProperty("counterObj",&counter);
viewer.setMainQmlFile(QStringLiteral("qml/SO_QMLCPPCommunication/main.qml"));
viewer.showExpanded();
return app.exec();
}
And in the qml file, you can access the slot of the Counter object by referring to counterObj.
//main.qml
import QtQuick 2.0
Rectangle {
id: root
width: 360
height: 360
signal click
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
counterObj.click()
console.log("click() qml")
}
}
Text {
text: "clicks: "
x: 30
y: 250
}
Text {
id: counter
text: "0"
x: 75
y: 250
}
Connections
{
id:cppConnection
target:counterObj
ignoreUnknownSignals : true
onCounterHasChanged:{
//To access signal parameter,please name the parameter.
console.debug("Counter value changed")
counter.text = Value
}
}
}
QQuickView::rootObject() returns you a QQuickItem which apparently on Qt5.0 hasn't any click signal (http://qt-project.org/doc/qt-5.0/qtquick/qquickitem.html)
Make sure you're linking the right (existing) signal to the right slot and that the signal gets actually called.