I have this class that do not derive from QObject because I need it to be copyable:
#ifndef ITEM_HPP
#define ITEM_HPP
#include <QObject>
class Item
{
Q_GADGET
Q_PROPERTY(int intValue READ intValue WRITE setIntValue)
Q_PROPERTY(QString stringValue READ stringValue WRITE setStringValue)
public:
Item();
int intValue() const { return m_intValue; }
void setIntValue(int intValue) { m_intValue = intValue; }
const QString& stringValue() const { return m_stringValue; }
void setStringValue(const QString& stringValue) { m_stringValue = stringValue; }
private:
int m_intValue;
QString m_stringValue;
};
Q_DECLARE_METATYPE(Item)
#endif // ITEM_HPP
How can I notify the QML when intValue or stringValue changes?
If the class derived from QObject I could add the NOTIFY property, but how can I do this without deriving from QObject?
Related
I have a class MyWindow. This class call
MyWindow.h
class MyWindow : public QObject
{
Q_OBJECT
Q_PROPERTY(int nbMatch READ GetNbMatch NOTIFY matchChangedQMLL)
public:
explicit MyWindow(QObject *parent = nullptr);
explicit MyWindow(AsyncCalendarGetter& calendar, QObject *parent = nullptr);
~MyWindow();
Q_INVOKABLE QString getFirstMatch() {
return QString::fromUtf8(calendar->GetCalendar().front().GetDate().toString().c_str());
}
Q_INVOKABLE Date getFirstDate() {
return calendar->GetCalendar().front().GetDate();
}
// ...
}
Date.h
#pragma once
#include <string>
#include <sstream>
#include <QObject>
#include <iostream>
class Date
{
Q_GADGET
Q_PROPERTY(std::string dateStr READ toString)
public:
Date(std::string&& str);
Date();
Date(int day, int month, int year, int h, int m);
friend std::ostream& operator<<(std::ostream& os, const Date& obj);
Q_INVOKABLE std::string toString() const {
std::stringstream ss;
ss << *this;
return ss.str();
}
private:
int day = 1;
int month = 1;
int year = 1970;
int h = 0;
int m = 0;
};
When I call the first function getFirstMatch in my QML, it works.
But, the second function gtFirstDate does not work, I have an error message :
qrc:/main.qml:27: Error: Unknown method return type: Date
My QML
Connections {
target: mainmywindow
onMatchChangedQMLL: {
lbl0.text = "" + Number(mainmywindow.nbMatch) + " -> " + qsTr(mainmywindow.getFirstMatch()) // WORKS
lbl1.text = "" + Number(mainmywindow.nbMatch) + " -> " + qsTr(mainmywindow.getFirstDate().toString()) // DOES NOT WORK
}
}
Someone have an idea ?
Thanks
You can find information about Q_DECLARE_METATYPE here:
https://doc.qt.io/qt-5/qmetatype.html#Q_DECLARE_METATYPE
According to it, you should do these steps to resolve your problem:
Add Q_DECLARE_METATYPE(Date) after declaration of Date
Add qRegisterMetaType<Date>(); somewhere before engine.load(url);
(I assume you have QQmlApplicationEngine engine; in main() to load and run QML)
Update: Also std::string not supported directly by QML, you should use QString
I have a class which I am exposing to QML as follows:
#ifndef MYTYPE_H
#define MYTYPE_H
#include <QString>
#include <QObject>
class MyType : public QObject
{
Q_OBJECT
Q_ENUMS(TestEnum)
Q_PROPERTY(TestEnum foo READ foo WRITE setFoo NOTIFY fooChanged)
public:
enum class TestEnum
{
State1 = 1,
State2 = 2
};
MyType(QObject *parent = nullptr) :
QObject(parent),
mFoo(TestEnum::State1)
{
}
TestEnum foo() const
{
return mFoo;
}
void setFoo(TestEnum foo)
{
if (foo == mFoo)
return;
mFoo = foo;
emit fooChanged(mFoo);
}
signals:
void fooChanged(MyType::TestEnum blah);
private:
TestEnum mFoo;
};
Q_DECLARE_METATYPE(MyType::TestEnum)
#endif // MYTYPE_H
Here I have an enumeration type which I would like to expose to QML.
I register the type n my main function call as:
qmlRegisterType<MyType>("App", 1, 0, "MyType");
Now I have a signal in my qml file which I takes this enumeration parameter:
signal submitTextField(MyType::TestEnum state) // Compiler complains here
The signal is connected as:
QObject *topLevel = engine.rootObjects().value(0);
//QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
// This is just a class that implements the slot
HandleTextField handleTextField;
// connect our QML signal to our C++ slot
QObject::connect(topLevel, SIGNAL(submitTextField(MyType::TestEnum)),
&handleTextField, SLOT(handleSubmitTextField(MyType::TestEnum)));
The class that implements the slot is defined as:
class HandleTextField : public QObject
{
Q_OBJECT
public:
explicit HandleTextField(QObject *parent = 0);
public slots:
void handleSubmitTextField(MyType::TestEnum in);
void handleFooChanged(MyType::TestEnum in);
public:
MyType myType;
};
The syntax MyType::TestEnum is not valid. I am tempted to convert everything to ints and do away with the enumeration but wondering if there is a way to use this enumeration type in a QML signal. I can do MyType.State1 and MyType.State2 but unable to refer to the enumeration type.
Ok, it does not seem like this can be done with enums i.e. you have to use ints. I made the following change and it works:
class MyType : public QObject
{
Q_OBJECT
Q_ENUMS(TestEnum)
Q_PROPERTY(TestEnum foo READ foo WRITE setFoo NOTIFY fooChanged)
public:
enum TestEnum
{
State1 = 1,
State2 = 2
};
MyType(QObject *parent = nullptr) :
QObject(parent),
mFoo(TestEnum::State1)
{
}
TestEnum & foo()
{
return mFoo;
}
void setFoo(TestEnum foo)
{
if (foo == mFoo)
return;
mFoo = foo;
emit fooChanged(static_cast<int>(mFoo));
}
signals:
void fooChanged(int blah);
private:
TestEnum mFoo;
};
Q_DECLARE_METATYPE(MyType::TestEnum)
The slots need to be changed as well:
class HandleTextField : public QObject
{
Q_OBJECT
public:
explicit HandleTextField(QObject *parent = 0);
public slots:
void handleSubmitTextField(int in);
void handleFooChanged(int in);
public:
MyType myType;
};
We need to change the connections for int type:
QObject::connect(topLevel, SIGNAL(submitTextField(int)),
&handleTextField, SLOT(handleSubmitTextField(int)));
and finally the signal is declared as:
signal submitTextField(int text)
How can the cut, copy paste slots be invoked for QgraphicsTextItem. Though the the text editor has default slots for cut, copy and paste, but I don't know how to invoke them.
I have class that is inherited from QgraphicsTextItem. The use of this class is to add a text in GraphicsView. Now I want the cut, paste and copy functality for it. How can I go for that?
drawText.h
#include <QGraphicsTextItem>
#include <QPen>
QT_BEGIN_NAMESPACE
class QFocusEvent;
class QGraphicsItem;
class QGraphicsScene;
class QGraphicsSceneMouseEvent;
QT_END_NAMESPACE
class mText : public QGraphicsTextItem
{
Q_OBJECT
public:
mText( int, QGraphicsItem *parent=0 );
enum { Type = UserType + 5 };
int type() const;
int id;
signals:
void lostFocus(mText *item);
void selectedChange(QGraphicsItem *item);
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
void focusOutEvent(QFocusEvent *event);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
};
.cpp
#include "mtext.h"
mText::mText( int i, QGraphicsItem *parent)
: QGraphicsTextItem(parent )
{
//assigns id
id = i;
}
int mText::type() const
{
// Enable the use of qgraphicsitem_cast with text item.
return Type;
}
QVariant mText::itemChange(GraphicsItemChange change,
const QVariant &value)
{
if (change == QGraphicsItem::ItemSelectedHasChanged)
emit selectedChange(this);
return value;
}
void mText::focusOutEvent(QFocusEvent *event)
{
setTextInteractionFlags(Qt::NoTextInteraction);
emit lostFocus(this);
QGraphicsTextItem::focusOutEvent(event);
}
void mText::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if (textInteractionFlags() == Qt::NoTextInteraction)
setTextInteractionFlags(Qt::TextEditorInteraction);
QGraphicsTextItem::mouseDoubleClickEvent(event);
}
My class has enum property, i wish to access this property using QObject*. When calling QVariant QObject::property ( const char * name ) const return value is empty QVariant of enum type.
Consider the following code:
/* Interface class */
class IFoo
{
Q_GADGET
public:
Q_ENUMS(ColorType)
typedef enum
{
COLOR_RED = 0,
COLOR_BLUE
} ColorType;
virtual QString Name(void) const = 0;
};
Q_DECLARE_METATYPE(IFoo::ColorType)
class Foo
: public IFoo
{
Q_OBJECT
public:
Foo(void)
{
qint32 typeId = qRegisterMetaType<IFoo::ColorType>("ColorType");
qRegisterMetaTypeStreamOperators<int>(IFoo::ColorType);
}
virtual QString Name(void) const { return _name; }
void SetColor(ColorType color) { _color = color; }
ColorType Color(void) const { return _color; }
QString ColorString(void) const { return _color == IFoo::COLOR_RED ? "Red" : "Blue"; }
Q_PROPERTY(IFoo::ColorType Color READ Color WRITE SetColor)
Q_PROPERTY(QString ColorString READ ColorString)
private:
ColorType _color;
QString _name;
};
int main (int argc, char **argv) {
QCoreApplication app(argc, argv);
Foo f;
f.SetColor(IFoo::COLOR_RED);
qDebug() << f.property("Color"); // Returns QVariant(IFoo::ColorType, )
qDebug() << f.property("ColorString"); // Returns QString(Red)
}
Why does property return empty QVariant value? String wrapper property works as it should.
There were a few mistakes on the code that prevent it from compiling:
Using Q_OBJECT without inheriting from QObject.
qRegisterMetaType is not needed if you use Q_ENUM (which I used to replace Q_ENUMS, which is the old version, deprecated in 5.5).
qRegisterMetaTypeStreamOperators was being passed an int as template argument instead of the type to register, and the argument to the function (not template argument) should be a string, which is optional anyway; but not a type.
Full source:
#include <QtCore> // Just for the test. Use more fine grained includes.
/* Interface class */
class IFoo
{
Q_GADGET
public:
enum ColorType
{
COLOR_RED = 0,
COLOR_BLUE
};
Q_ENUM(ColorType)
virtual QString Name(void) const = 0;
};
class Foo : public QObject, public IFoo
{
Q_OBJECT
public:
Foo(void)
{
qRegisterMetaTypeStreamOperators<IFoo::ColorType>();
}
virtual QString Name(void) const { return _name; }
void SetColor(ColorType color) { _color = color; }
ColorType Color(void) const { return _color; }
QString ColorString(void) const { return _color == IFoo::COLOR_RED ? "Red" : "Blue"; }
Q_PROPERTY(IFoo::ColorType Color READ Color WRITE SetColor)
Q_PROPERTY(QString ColorString READ ColorString)
private:
ColorType _color;
QString _name;
};
int main (int argc, char **argv) {
QCoreApplication app(argc, argv);
Foo f;
f.SetColor(IFoo::COLOR_RED);
qDebug() << f.property("Color"); // Returns QVariant(IFoo::ColorType, )
qDebug() << f.property("ColorString"); // Returns QString(Red)
// Now returns:
// QVariant(IFoo::ColorType, "COLOR_RED")
// QVariant(QString, "Red")
}
#include "main.moc"
It looks like that moc tool is unable to generate strings for respective values. IMO problem is typedef.
Try simple enum inside of class:
enum ColorType {
COLOR_RED = 0,
COLOR_BLUE
};
Or typedef with enum keyword:
typedef enum {
COLOR_RED = 0,
COLOR_BLUE
} ColorType;
I'm pretty sure that missing enum keyword confuses the moc tool.
I'm trying to use a QDeclarativeListProperty in order to manage a list of parameters, mostly for the purposes of displaying them in a ListView. However, I would also like to be able to directly access the parameters in QML from the QDeclarativeListProperty so that I can display/modify individual parameters on different screens.
My class is called ParameterClass, for which I've created a QList:
class SystemData : public QObject
{
Q_OBJECT
Q_PROPERTY(QDeclarativeListProperty<ParameterClass> parameters READ parameters CONSTANT)
QDeclarativeListProperty<ParameterClass> parameters();
...
QList<ParameterClass *> m_parameterList;
}
I've also registered the ParameterClass class and set up an instance of my SystemData as a property, which I know is necessary.
m_context->setContextProperty("SystemData", m_pSystemData);
qmlRegisterType<ParameterClass>();
Now, what I want to do within QML is something like this:
Rectangle {
id: frame
property variant parameter: SystemData.parameters[5]
...
}
I'm just not getting it to work: I keep getting back [undefined]. Am I wasting my time, or am I missing something?
Edit:
I've changed things to use the suggestion from ... . Here are some selections from my updated code.
main.cpp:
#include <QApplication>
#include <QSplashScreen>
#include <QLocale>
#include <QLibraryInfo>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QDeclarativeEngine>
#include <QObject>
#include <QDeclarativeListProperty>
#include "systemdata.h"
#include "parameterclass.h"
static const QString contentPath = "qrc:/qml/qml/pk_ui/";
static const QString filename(contentPath + "main.qml");
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QDeclarativeView mainView;
SystemData* systemData = SystemData::getInstance();
QThread thread;
UpdateWorker updateWorker;
QObject::connect((const QObject*)systemData, SIGNAL(startWork()),
(const QObject*)&updateWorker, SLOT(doWork()));
updateWorker.moveToThread(&thread);
thread.start();
systemData->startUpdates();
QFont defaultFont;
defaultFont.setFamily("Sans Serif");
QApplication::setFont(defaultFont);
// Register types to be available in QML
qmlRegisterType<ParameterClass>();
qmlRegisterUncreatableType<SystemEnum>("SystemEnums", 1, 0, "SystemEnum", QString());
mainView.engine()->rootContext()->setContextProperty("SystemData", systemData);
// Set view optimizations not already done for QDeclarativeView
mainView.setResizeMode(QDeclarativeView::SizeRootObjectToView);
mainView.setAttribute(Qt::WA_OpaquePaintEvent);
mainView.setAttribute(Qt::WA_NoSystemBackground);
mainView.setSource(QUrl(filename));
mainView.show();
return app.exec();
}
The ParameterClass looks like this:
class ParameterClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int type READ get_type NOTIFY typeChanged)
Q_PROPERTY(bool enabled READ get_ParameterEnabled WRITE set_ParameterEnabled NOTIFY enabledChanged)
Q_PROPERTY(int groupID READ get_GroupID WRITE set_GroupID NOTIFY groupIDChanged)
Q_PROPERTY(int unitID READ get_UnitID WRITE set_UnitID NOTIFY unitIDChanged)
Q_PROPERTY(int securityLevel READ get_SecurityLevel WRITE set_SecurityLevel NOTIFY securityLevelChanged)
Q_PROPERTY(QString parameterName READ get_ParameterName NOTIFY parameterNameChanged)
Q_PROPERTY(QString shortDescription READ get_ShortDescription NOTIFY shortDescriptionChanged)
Q_PROPERTY(int currentValue READ get_CV WRITE set_valueptrvalue NOTIFY currentValueChanged)
Q_PROPERTY(int lowerBound READ get_LB NOTIFY lowerBoundChanged)
Q_PROPERTY(int upperBound READ get_UB NOTIFY upperBoundChanged)
public:
struct ValueTypes
{
enum
{
IntegerType,
StringType,
StringListType
};
};
ParameterClass(QObject *parent = 0);
int get_type();
bool get_ParameterEnabled();
int get_GroupID();
int get_UnitID();
int get_SecurityLevel();
QString get_ParameterName();
QString get_ShortDescription();
int get_CV() { return *CurrentValuePtr; }
int get_LB() { return *LowerBoundPtr; }
int get_UB() { return *UpperBoundPtr; }
void set_ParameterEnabled(bool InParameterEnabled);
void set_GroupID(int InGroupID);
void set_UnitID(int InUnitID);
void set_SecurityLevel(int InSecurityLevel);
signals:
void typeChanged();
void enabledChanged();
void groupIDChanged();
void unitIDChanged();
void securityLevelChanged();
void parameterNameChanged();
void shortDescriptionChanged();
private:
int type;
bool ParameterEnabled;
int GroupID;
int UnitID;
int SecruityLevel;
QString ParameterName;
QString ShortDescription;
int * CurrentValuePtr;
int * LowerBoundPtr;
int * UpperBoundPtr;
};
And my QML file:
Rectangle {
id: frame
property int val: SystemData.parameters[4].currentValue
...
}
It looks like I'm still getting an undefined value in this case. I'm trying to debug now, so that I can provide more information.
It's very much possible. The key is to make sure you register the QML type and set the context property before setting the source on your QDeclarativeView.
Here's a working example -
main.cpp:
#include <QApplication>
#include <QtDeclarative>
class MyPropertyObject : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value CONSTANT)
public:
MyPropertyObject(int value = -1) : m_value(value) { }
int value() const {
return m_value;
}
private:
int m_value;
};
class MyObject : public QObject {
Q_OBJECT
Q_PROPERTY(QDeclarativeListProperty<MyPropertyObject> props READ props CONSTANT)
public:
MyObject() {
m_props.append(new MyPropertyObject(55));
m_props.append(new MyPropertyObject(44));
m_props.append(new MyPropertyObject(33));
}
QDeclarativeListProperty<MyPropertyObject> props() {
return QDeclarativeListProperty<MyPropertyObject>(this, m_props);
}
private:
QList<MyPropertyObject *> m_props;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDeclarativeView view;
view.engine()->rootContext()->setContextProperty(QLatin1String("tester"), new MyObject);
qmlRegisterType<MyPropertyObject>();
view.setSource(QUrl("qrc:///qml/main.qml"));
view.setResizeMode(QDeclarativeView::SizeRootObjectToView);
view.resize(300, 300);
view.show();
return a.exec();
}
#include "main.moc"
main.qml:
import QtQuick 1.1
Rectangle {
property variant foo: tester.props[2].value
Text {
anchors.centerIn: parent
text: parent.foo
}
}
Note: read the docs for the QDeclarativeListProperty constructors. The one I'm using for this example is not the preferred one, but is good for quick prototypes.