Why we usually give Q_OBJECT in private section only?I tried a sample program by giving it in public section ,I found no difference.If anybody knows can you tell me the reason.
By default the Q_OBJECT macro expands to:
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
static const QMetaObject staticMetaObject; \
Q_OBJECT_GETSTATICMETAOBJECT \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
QT_TR_FUNCTIONS \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
at least on mine Qt 4.8, so you can see that it switches to public visibility level to declare the needed functions and the back to private in order to ensure that nothing is broken. So if you declare your Q_OBJECT in a public section you might have the funny consequence that things after are not public anymore :)
If you put Q_OBJECT under public identifier, all your public declaration will become private. For example:
class Dialog : public QDialog
{
public:
Q_OBJECT
Dialog(QWidget *parent = 0); // <- now declared as private
Related
I have a class that returns a list of custom objescts, to view in qml
#include <QObject>
#include "networkinterface.h"
class Network : public QObject {
Q_OBJECT
public:
explicit Network(QObject *parent = 0);
Q_INVOKABLE QList<NetworkInterface> getNetworkAdaptors();
private:
QList<NetworkInterface> networkAdaptors; };
At main.qml i call this method as
model: network.getNetworkAdaptors()
It all was working when NetworkInterface was a struct, but when i converted it to a class, can't make it work.
Class NetworkInterface is inherited from QObject and got properties
class NetworkInterface : public QObject
{
Q_OBJECT
public:
NetworkInterface();
QString name;
QString description;
const QString &getName() const;
void setName(const QString &newName);
const QString &getDescription() const;
void setDescription(const QString &newDescription);
...
private:
Q_PROPERTY(QString name READ getName CONSTANT)
Q_PROPERTY(QString description READ getDescription CONSTANT)
};
So the error i got is :
main.cpp.o:-1: error: Undefined symbols for architecture x86_64:
"NetworkInterface::NetworkInterface()", referenced from:
QtPrivate::QMetaTypeForType<NetworkInterface>::getDefaultCtr()::'lambda'(QtPrivate::QMetaTypeInterface const*, void*)::operator()(QtPrivate::QMetaTypeInterface const*, void*) const in mocs_compilation.cpp.o
Network::getNetworkAdaptors() in network.cpp.o
_main in main.cpp.o
I suspect it is wrong type expose, as with sctruct it was working fine, how to do that correctly?
UPD: in NetworkInterface i have following constructors:
NetworkInterface();
NetworkInterface(const NetworkInterface &obj);
NetworkInterface & operator=( const NetworkInterface & obj);
Without them i can't push_back(networkInterface) to the list of interfaces, as it requires copy constructor. Also list of pointers won't work for qml as a model, it must be exactly objects list.
When i copy all code in NetworkInterface and leave only above constructors, it is minimal code that gives a error.
Default constructor was missing, adding
NetworkInterface::NetworkInterface(QObject *parent) : QObject(parent)
{
}
Resoled a problem
new bee here on QT.
I am trying to build an online example that used to work on QT5, but the compilation keeps failing on QT6. I keep getting this error and not sure why.
footballteam.cpp:55:12: No matching constructor for initialization of 'QQmlListProperty<Player>'
qqmllist.h:76:5: candidate constructor not viable:
no known conversion from 'int (*)(QQmlListProperty<Player> *)'
to 'QQmlListProperty<Player>::CountFunction'
(aka 'long long (*)(QQmlListProperty<Player> *)') for 4th argument
Code:
QQmlListProperty<Player> FootBallTeam::players()
{
return QQmlListProperty<Player>(this,this,&FootBallTeam::appendPlayer,
&FootBallTeam::playerCount,
&FootBallTeam::player,
&FootBallTeam::clearPlayers);
}
Classes:
class FootBallTeam : public QObject
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QString coatch READ coatch WRITE setCoatch NOTIFY coatchChanged)
Q_PROPERTY(Player * captain READ captain WRITE setCaptain NOTIFY captainChanged)
Q_PROPERTY(QQmlListProperty<Player> players READ players NOTIFY playersChanged)
... bunch of stuff
private:
//Callback Methods
static void appendPlayer(QQmlListProperty<Player>*, Player*);
static int playerCount(QQmlListProperty<Player>*);
static Player* player(QQmlListProperty<Player>*, int);
static void clearPlayers(QQmlListProperty<Player>*);
....
class Player : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(bool playing READ playing WRITE setPlaying NOTIFY playingChanged)
Q_PROPERTY(QString position READ position WRITE setPosition NOTIFY positionChanged)
public:
explicit Player(QObject *parent = nullptr);
QString name() const;
bool playing() const;
QString position() const;
void setName(QString name);
void setPlaying(bool playing);
void setPosition(QString position);
signals:
void nameChanged(QString name);
void playingChanged(bool playing);
void positionChanged(QString position);
private :
QString m_name;
bool m_playing;
QString m_position;
};
I looked into this thread, but did not help much.
Pass QQmlListProperty from QML to C++ as parameter
Any ideas why it does not compile on QT6?
thank you.
As pointed out by absolute.madnes, the fix was to change int to qsizetype
qsizetype FootBallTeam::playerCount(QQmlListProperty<Player> * list)
{
return reinterpret_cast<FootBallTeam*>(list->data)->playerCountCustom();
}
Player *FootBallTeam::player(QQmlListProperty<Player> * list, qsizetype index)
{
return reinterpret_cast<FootBallTeam*>(list->data)->playerCustom(index);
}
How can I add a new "blank" entry to a ListView that is populated using a QObjectList-based model?
The documentation outlines that we can use QList<QObject*> to populate a model (https://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html), but I don't see anyway to add a new entry using this method...
I've made a sample program to show what I mean, the problem lies in the Main.qml's button onClick event.
Main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
Window {
width: 640
height: 480
visible: true
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
ListView {
id: listView
Layout.fillWidth: true
Layout.preferredHeight: 100
model: sampleManager.sampleObjList
delegate: RowLayout {
anchors.fill: listView
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: modelData.id
}
}
}
Button {
text: "Add New Entry"
Layout.fillWidth: true
Layout.fillHeight: true
onClicked: function(mouse) {
// How do I add a new entry to listView.model...
// This says append does not exist...
listView.model.append({});
}
}
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "SampleManager.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
SampleManager* sample_manager = new SampleManager();
engine.rootContext()->setContextProperty("sampleManager", sample_manager);
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();
}
SampleManager.h
#ifndef SAMPLEMANAGER_H
#define SAMPLEMANAGER_H
#include <QObject>
#include "QMLMacros.h"
#include "SampleObj.h"
class SampleManager :
public QObject
{
Q_OBJECT
QML_WRITABLE_PROPERTY(QList<SampleObj*>, sampleObjList)
public:
explicit SampleManager(
QObject *parent = nullptr);
signals:
public slots:
private:
};
#endif // SAMPLEMANAGER_H
SampleManager.cpp
#include "SampleManager.h"
#include "SampleObj.h"
SampleManager::SampleManager(
QObject *parent) :
QObject(parent)
{
m_sampleObjList.append(new SampleObj(this));
m_sampleObjList.append(new SampleObj(this));
m_sampleObjList[0]->set_id(0);
m_sampleObjList[1]->set_id(1);
}
SampleObj.h
#ifndef SAMPLEOBJ_H
#define SAMPLEOBJ_H
#include <QObject>
#include "QMLMacros.h"
class SampleObj : public QObject
{
Q_OBJECT
QML_WRITABLE_PROPERTY(int, id);
public:
explicit SampleObj(
QObject *parent = nullptr);
signals:
private:
};
#endif // SAMPLEOBJ_H
SampleObj.cpp
#include "SampleObj.h"
SampleObj::SampleObj(
QObject *parent) :
QObject(parent),
m_id(0)
{
}
QMLMacros.h (Just some macros I found online for easily declaring a Q_PROPERTY)
#ifndef QMLMACROS_H
#define QMLMACROS_H
#include <QObject>
/*!
\defgroup QT_QML_HELPERS Qt helper macros
Brings a couple of macros that can help saving development time,
by avoiding manual code duplication, often leading to heavy copy-and-paste,
which is largely error-prone and not productive at all.
*/
/*!
\def QML_WRITABLE_PROPERTY(type, name)
\ingroup QT_QML_HELPERS
\hideinitializer
\details Creates a \c Q_PROPERTY that will be readable / writable from QML.
\param type The C++ type of the property
\param name The name for the property
It generates for this goal :
\code
{type} m_{name}; // private member variable
{type} get_{name} () const; // public getter method
void set_{name} ({type}); // public setter slot
void {name}Changed ({type}); // notifier signal
\endcode
\b Note : Any change from either C++ or QML side will trigger the
notification.
*/
#define QML_WRITABLE_PROPERTY(type, name) \
protected: \
Q_PROPERTY(type name READ get_##name WRITE set_##name NOTIFY name##Changed) \
private: \
type m_##name{}; \
\
public: \
type get_##name() const { return m_##name; } \
Q_SIGNALS: \
void name##Changed(type name); \
public Q_SLOTS: \
void set_##name(type name) { \
if (m_##name != name) { \
m_##name = name; \
emit name##Changed(m_##name); \
} \
} \
\
private:
/*!
\def QML_READONLY_PROPERTY(type, name)
\ingroup QT_QML_HELPERS
\hideinitializer
\details Creates a \c Q_PROPERTY that will be readable from QML and writable
from C++.
\param type The C++ type of the property
\param name The name for the property
It generates for this goal :
\code
{type} m_{name}; // private member variable
{type} get_{name} () const; // public getter method
void update_{name} ({type}); // public setter method
void {name}Changed ({type}); // notifier signal
\endcode
\b Note : Any change from C++ side will trigger the notification to QML.
*/
#define QML_READONLY_PROPERTY(type, name) \
protected: \
Q_PROPERTY(type name READ get_##name NOTIFY name##Changed) \
private: \
type m_##name{}; \
\
public: \
type get_##name() const { return m_##name; } \
bool update_##name(type name) { \
bool ret = false; \
if ((ret = m_##name != name)) { \
m_##name = name; \
emit name##Changed(m_##name); \
} \
return ret; \
} \
Q_SIGNALS: \
void name##Changed(type name); \
\
private:
/*!
\def QML_CONSTANT_PROPERTY(type, name)
\ingroup QT_QML_HELPERS
\hideinitializer
\details Creates a \c Q_PROPERTY for a constant value exposed from C++ to
QML.
\param type The C++ type of the property
\param name The name for the property
It generates for this goal :
\code
{type} m_{name}; // private member variable
{type} get_{name} () const; // public getter method
\endcode
\b Note : There is no change notifier because value is constant.
*/
#define QML_CONSTANT_PROPERTY(type, name) \
protected: \
Q_PROPERTY(type name READ get_##name CONSTANT) \
private: \
type m_##name; \
\
public: \
type get_##name() const { return m_##name; } \
\
private:
#define QML_LIST_PROPERTY(CLASS, NAME, TYPE) \
public: \
static int NAME##_count(QQmlListProperty<TYPE> *prop) { \
CLASS *instance = qobject_cast<CLASS *>(prop->object); \
return (instance != NULL ? instance->m_##NAME.count() : 0); \
} \
static void NAME##_clear(QQmlListProperty<TYPE> *prop) { \
CLASS *instance = qobject_cast<CLASS *>(prop->object); \
if (instance != NULL) { \
instance->m_##NAME.clear(); \
} \
} \
static void NAME##_append(QQmlListProperty<TYPE> *prop, TYPE *obj) { \
CLASS *instance = qobject_cast<CLASS *>(prop->object); \
if (instance != NULL && obj != NULL) { \
instance->m_##NAME.append(obj); \
} \
} \
static TYPE *NAME##_at(QQmlListProperty<TYPE> *prop, int idx) { \
CLASS *instance = qobject_cast<CLASS *>(prop->object); \
return (instance != NULL ? instance->m_##NAME.at(idx) : NULL); \
} \
QList<TYPE *> get_##NAME##s(void) const { return m_##NAME; } \
\
private: \
QList<TYPE *> m_##NAME;
/*!
\def QML_ENUM_CLASS(name, ...)
\ingroup QT_QML_HELPERS
\hideinitializer
\details Creates a class that contains a C++ enum that can be exposed to
QML.
\param name The name for the class
\param ... The variadic list of values for the enum (comma-separated)
It generates for this goal :
\li The \c {name} C++ QObject-derived class
\li The \c {name}::Type enumeration containing the values list
\li The \c Q_ENUMS macro call to allow QML usage
Example in use :
\code
QML_ENUM_CLASS (DaysOfWeek, Monday = 1, Tuesday, Wednesday, Thursday,
Friday, Saturday, Sunday) \endcode
\b Note : The QML registration using \c qmlRegisterUncreatableType() will
still be needed.
*/
#define QML_ENU_CLASS(name, ...) \
class name : public QObject { \
Q_GADGET \
public: \
enum Type { __VA_ARGS__ }; \
Q_ENUMS(Type) \
};
class QmlProperty : public QObject {
Q_OBJECT
}; // NOTE : to avoid "no suitable class found" MOC note
#endif // QMLMACROS_H
Not sure if anyone will ever run into this issue, but I could not find a solution. Unfortunately, I was forced to use create my own version of QAbstractListModel in order to effectively manage a list consisting of 1 int and 1 string per row within QML :(. An overly complicated solution for a minor minor problem, not elegant at all unfortunately.
I've two ViewModels, MainVM and AddVM. In main.cpp, MainVM is declared this way:
MainVM *mvm;
int main(int argc, char *argv[])
{
...
mvm = new MainVM();
...
engine.rootContext()->setContextProperty("mainContext", mvm);
engine.rootContext()->setContextProperty("addContext", new AddVM());
...
}
and in MainVM, I've this Q_PROPERTY:
class MainVM : public QObject
{
Q_OBJECT
...
PROPERTY(QVector<Plot*>, plots)
...
public:
...
QSqlDatabase db;
int maxPlotId, maxSpaceId, maxTenantId, maxHeadId, maxLeaseId;
...
};
the PROPERTY macro does this:
#define PROPERTY(QType, name) \
Q_PROPERTY(QType name READ name WRITE set##name NOTIFY name##Changed) \
public: \
QType name(){return m_##name;} \
void set##name(QType value){if(m_##name != value){m_##name = value; emit name##Changed();}} \
Q_SIGNAL void name##Changed(); \
private: \
QType m_##name;
In my AddVM I've another Q_PROPERTY newPlot and a Q_INVOKABLE addNewPlot:
class AddVM : public QObject
{
Q_OBJECT
PROPERTY(Plot*, newPlot)
public:
explicit AddVM(QObject *parent = nullptr);
Q_INVOKABLE void addNewPlot();
};
on top of the AddVM.cpp, I've these:
#include "MainVM.h"
extern MainVM *mvm;
and addNewPlot function has these instructions:
void AddVM::addNewPlot()
{
mvm->db.open();
QSqlQuery query;
query.prepare("INSERT INTO Plots (Name, Description) VALUES(:Name, :Description)");
query.bindValue(":Name", newPlot()->name());
query.bindValue(":Description", newPlot()->description());
query.exec();
mvm->db.close();
mvm->plots().push_back(newPlot());
setnewPlot(new Plot());
newPlot()->setid(++mvm->maxPlotId);
}
everything in this function works as expected except the mvm->plots().push_back(newPlot()); line! This doesn't add the newPlot in the QVector of MainVM!
EDIT
Probably the best way is to redefine the getter in the macro like this:
QType& name(){return m_##name;} \
and with that my existing code works without any modification.
When you call mvm->plots(), it returns a copy of the real plots vector (That's my guess because you didn't show what plots() does). Thus, a new plot is added to the copy not the original vector. So, I think the best way is to add a function in MainVM that is called 'addPlot()` for example where you can add to the plots vector internally and directly.
In particular, I am implementing a QWizardPage ("MyWizardPage") for a QWizard, and I want to emit a signal ("sigLog") from my override of the QWizardPage::nextId virtual method.
Like so:
class MyWizardPage
: public QWizardPage
{
Q_OBJECT
public:
MyWizardPage();
virtual int nextId() const;
Q_SIGNALS:
void sigLog(QString text);
};
int MyWizardPage::nextId() const
{
Q_EMIT sigLog("Something interesting happened");
}
But when I try this, I get the following compile error on the Q_EMIT line:
Error 1 error C2662: 'MyWizardPage::sigLog' : cannot convert 'this' pointer from 'const MyWizardPage' to 'MyWizardPage &'
It is possible to emit a signal from a const method by adding "const" to the signal declaration, like so:
void sigLog(QString text) const;
I tested this and it does compile and run, even though you don't actually implement the signal as a normal method yourself (i.e. Qt is okay with it).
You may try to create another class , declare it as friend for your wizard page and add to wizard as a mutable member. after that you may emit it's signal instead of wizard's.
class ConstEmitter: public QObject
{
Q_OBJECT
...
friend class MyWizardPage;
Q_SIGNALS:
void sigLog(QString text);
};
class MyWizardPage
: public QWizardPage
{
Q_OBJECT
public:
MyWizardPage();
protected:
mutable CostEmitter m_emitter;
Q_SIGNALS:
void sigLog(QString text);
};
int MyWizardPage::nextId() const
{
Q_EMIT m_emitter.sigLog("Something interesting happened");
}
MyWizardPage::MyWizardPage()
{
connect(&m_emitter,SIGNAL(sigLog(QString)),this,SIGNAL(sigLog(QString)));
}
or you may just use
int MyWizardPage::nextId() const
{
Q_EMIT const_cast<MyWizardPage*>(this)->sigLog("Something interesting happened");
}
that is not recommended way, because const_cast is a hack, but it's much shorter :)