I've a really hard time understanding what the QML_INTERFACE and QML_IMPLEMENTS_INTERFACES macros are supposed to do. Naively I assumed that the macro does what it says it does, registering a non-instantiable ("uncreatable") type to the QML type system. A type I can then use as a custom property to bind to a specific implementation.
Let's get concrete (no pun intended) and elaborate with a simple example, an interface with a single QString property:
class QmlInterface : public QObject {
Q_OBJECT
QML_INTERFACE
Q_PROPERTY(QString name READ get WRITE set NOTIFY changed)
public:
virtual ~QmlInterface();
virtual QString get() const = 0;
virtual void set(QString const& name) = 0;
signals:
void changed();
};
Q_DECLARE_INTERFACE(QmlInterface, "QmlInterface")
And a class which implements that interface (declarations omitted for simplicity):
class QmlImplA : public QmlInterface {
Q_OBJECT
QML_ELEMENT
QML_IMPLEMENTS_INTERFACES(QmlInterface)
public:
QString get() const final;
void set(QString const& name) final;
private:
QString name_;
};
An instance of this implementation is then created in C+ and an interface pointer gets passed to the QML engine right before loading the .qml file
QQmlApplicationEngine engine;
// url to .qml file
std::unique_ptr<QmlInterface> qml_impl_a{std::make_unique<QmlImplA>()};
engine.setInitialProperties({{"qml_impl_a", QVariant::fromValue(qml_impl_a.get())}});
engine.load(// url);
I then assumed I could just import the interface in my .qml file and use it with the underlying implementation like this:
import QtQuick
import QtQuick.Controls
import QmlInterface
ApplicationWindow {
required property QmlInterface qml_impl_a
// ...
}
However this doesn't work. QML complains that QmlInterface is not a type
Upon googling for some QML_INTERFACE examples I found a passage in Cross-Platform Development with Qt 6 and Modern C++ which says that:
QML_INTERFACE registers an existing Qt interface type. The type is not instantiable from QML, and you cannot declare QML properties with it.
Wait... what? Is this true? The official documentation doesn't mention this anywhere. As this would practically render interfaces completely useless I assume the book is wrong here?
For now I got my example working by making the interface a normal QML_ELEMENT and adding QML_UNCREATABLE to prevent instantiation from inside QML. This works and does what I intended in the first place. I still wonder though did I do something wrong or are the macros supposed to do something else entirely and are just named badly?
Related
I would like to have a qt QML var accessible globally, and anywhere else in my qml files. Is this possible?
I know that upon creating a variable in a C++ object can be accessed in QML by exposing its getter function, but this only works if you know the type of the data type e.g. string, int, bool.
Is there a variable data type (or class) that can handle a QML var in C++, so that I can only call it in the other parts of the QML files?
AS Amfasis said, you can use the rootContext, so you can access it from anywhere in QML - as long as you do not shadow the name. Alternatively you can also register a Singleton to QML.
For both, you first need to create a QObject
public class MyContextObject: public QObject {
Q_OBJECT
Q_PROPERTY(QVariant myVar READ myVar NOTIFY myVarChanged)
QVariant m_myVar;
public:
MyContextObject(QObject* parent = nullptr) : QObject(parent) {}
QVariant myVar() { return m_myVar; }
void setMyVar(QVariant var) {
if (var == m_myVar) return;
m_myVar = var;
emit myVarChanged();
}
signals:
void myVarChanged();
}
This object you create in your main and set it as a contextProperty
MyContextObject* mctx = new MyContextObject();
view.rootContext()->setContextProperty("myCtx", mctx);
To set it from C++ use the setter. On the QML-side just bind to myCtx.myVar
Expose the setter also, if you want to modify it from QML also
This is not tested, I don't have a Qt development environment available right now.
To expose it as singleton, you can use this function:
https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterSingletonType-1
I have a class (e.g. MyEnumClass, Q_GADGET) in which I define an enum, e.g. MyEnum.
I call Q_ENUM(MyEnum) to register it to the metaobject, and register the whole class as uncreatable type to QML.
In my second class (MyObject : QObject with macro Q_OBJECT) I have a slot that consumes a MyEnum as parameter. This object is registered as regular type to QML (creatable).
I want to call the slot from QML with a value from MyEnum - this fails, as the Type MyEnumClass::MyEnum seems to be unknown.
When the enum is defined inside the class with the slot, it works fine.
MVCE
class MyEnumClass {
Q_GADGET
public:
enum MyEnum {
E1,
E2,
E3
};
Q_ENUM(MyEnum)
};
class MyObject : public QObject
{
Q_OBJECT
public:
MyObject(QObject* parent = nullptr) : QObject(parent) {}
enum TestEnum {
V1,
V2,
V3
};
Q_ENUM(TestEnum)
public slots:
void testFun1(MyEnumClass::MyEnum val) { qDebug() << val; }
void testFun2(TestEnum val) { qDebug() << val; }
};
in main.cpp:
qmlRegisterUncreatableType<MyEnumClass>("MyObject", 1, 0, "MyEnum", "Uncreatable");
qmlRegisterType<MyObject>("MyObject", 1, 0, "MyObject");
in main.qml:
import MyObject 1.0
ApplicationWindow {
id: window
visible: true
width: 600
height: 600
MyObject {
id: obj
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log(MyObject.V2)
console.log(MyEnum.E2)
obj.testFun2(MyObject.V2)
obj.testFun1(MyEnum.E1)
}
}
}
I tried to inherit MyEnumClass in MyObject to make the enum part of MyObject, I tried with different macros and functions to make the enum even more available in the MetaObjectSystem... to no avail.
I also tried to put an enum in a namespace as described here - it was also unusable for a slot.
The only way I found to have the slot called, was by removing the enum and using int as type for the parameter - which is not that nice...
How can I make this work?
Are there any tricks I am missing?
Register metatype:
qRegisterMetaType<MyEnumClass::MyEnum>();
Explanation:
From Q_ENUM( ...) documentation:
This macro registers an enum type with the meta-object system. It must
be placed after the enum declaration in a class that has the Q_OBJECT
or the Q_GADGET macro. For namespaces use Q_ENUM_NS() instead.
...
Registered enumerations are automatically registered also to the Qt
meta type system, making them known to QMetaType without the need to
use Q_DECLARE_METATYPE().
Using Q_ENUM automatically registers enum with meta-object system so you don't need to add
Q_DECLARE_METATYPE(MyEnum)
But to use enum in queued signal slot connections, properties... you need to register meta type.
As said in int qRegisterMetaType() documentation:
To use the type T in QVariant, using Q_DECLARE_METATYPE() is
sufficient. To use the type T in queued signal and slot connections,
qRegisterMetaType() must be called before the first connection is
established.
Also, to use type T with the QObject::property() API,
qRegisterMetaType() must be called before it is used, typically in
the constructor of the class that uses T, or in the main() function.
Extending #Eligijus Pupeikis' answer, In my case I got the following error:
Error: Unknown method parameter type: Parameter::Id
And solved it by registering the exact type name it was looking for:
qRegisterMetaType<Parameter::Id>("Parameter::Id");
where Parameter::Id is an enum class inside the Parameter class.
I have a class SingletonBaseClass that is defined in C++ and later extended in QML. In the qmldir file, it is marked as a Singleton:
// qmldir file
singleton SingletonClass 1.0 SingletonClass.qml
// SingletonClass.qml
import QtQml 2.0
pragma Singleton
SingletonBaseClass {
// ...
}
This is the base class:
class SingletonBaseClass : public QObject
{
Q_OBJECT
public:
SingletonBaseClass(QObject* parent = nullptr) {}
// Get the owner engine
Q_INVOKABLE void someMethodCalledFromQml()
{
QQmlEngine* ownerEngine = ?
}
// Get the single instance of the given engine
static SingletonBaseClass* instance(QQmlEngine* engine)
{
SingletonBaseClass* instance = ?
return instance;
}
};
How can I retrieve the QQmlEngine instance in SingletonBaseClass?
How can I get a pointer to my singleton instance from within a static function?
Note: I have multiple QML engine instances in my application.
[Update]: I wasn't satisfied with the suggested workarounds and so I finally contributed this patch for 1. and QQmlEngine::singletonInstance() for 2. Both changes will be available in Qt 5.12.
If you want to get the QQmlEngine you can use the contextForObject() method as I show below:
Q_INVOKABLE void someMethodCalledFromQml()
{
QQmlEngine *ownerEngine = QQmlEngine::contextForObject(this)->engine();
qDebug()<<ownerEngine;
}
singletons in qml do not have a parent so a way to access them is by creating a property, for that we create a .qml and create an object with that property as the Singleton:
qrc:/tmp.qml
import QtQuick 2.0
QtObject {
property var obj: SingletonClass
}
and then this element is accessed by the following code:
static SingletonBaseClass* instance(QQmlEngine* engine)
{
QQmlComponent component(engine, QUrl("qrc:/tmp.qml"));
QObject *item = component.create();
SingletonBaseClass *instance = qvariant_cast<SingletonBaseClass *>(item->property("obj"));
return instance;
}
In the following link we show an example.
I've a simple Qt Question. I want that automatically generated UIC files are derived from a custom interface class like in:
Intention
class MyUiInterface {
public:
virtual void setupUi(QWidget* w) = 0;
virtual void retranslateUi(QWidget*w) = 0;
};
Generated UIC file should look like:
class Ui_MyWidget {
public:
void setupUi(QWidget* w) {
...
}
void retranslateUi(QWidget* w) {
...
}
};
namespace Ui {
class MyWidget : public MyUiInterface , public Ui_MyWidget {};
}
Why?
Every Ui::Class would then implement MyUiInterface. In each class that derives from Ui::Class (see The Multiple Inheritance Approach) I would be able to call setupUi and retranslateUi which makes sense if the class that derives from UI::Class class is a base class either. I want every widget to be derived from my abstrcat base class MyWidgetBase. Consider following:
class MyWidgetBase abstract : public QWidget, protected MyUiInterface {
protected:
void changeEvent(QEvent *e) {
QWidget::changeEvent(e);
if (e->type() == QEvent::LanguageChange) {
retranslateUi(this); // Still abstract here
}
}
};
class MyWidget : public MyWidgetBase : public Ui::MyWidget {
};
The effect is, every time MyWidget::changeEvent() is callled, retranslateUi of that specific class is called. Otherwise changeEvent had to be reimplemented in each class. This would be a bit against "code reuse" concept.
I think Qt UIC is not able to handle this situation isn't it? Is there a similar way to solve this problem?
Unfortunately, reading XML Schema for ui files is telling us that this is not possible to automate using uic compiler.
However, it is unclear to me why you would want to implement that automatically - even if the Uic somehow manages to implement your interface, you will still need to add bodies of the functions by hand, editing generated .h file, as I am sure that there is no way to include custom code in xml file which will translate as C++ code.
Why you just don't reimplement setupUi and retranslateUi in your MyWidget class? Every Ui class will have one of these classes, so you can implement this on this level, instead of base class. It is possible that I am missing something, but I see this as an appropriate way to do this.
class MyWidget : public MyWidgetBase, public Ui::MyWidget {
public:
void setupUi(QWidget* w) {
...
}
void retranslateUi(QWidget* w) {
...
}
};
With this approach, you don't need to reimplement changeEvent() in any of your custom widgets, and changeEvent will still call the appropriate retranslateUi().
(C++/Qt) I have a smart pointer to a QObject. Let's say a QWeakPointer. For some external reason (something that might happen in another object or due to an event), it is possible that the pointed object gets destroyed. Since I have a smart pointer there will be no dangling reference, so there's no problem. But I always have to check if the pointer is null or not.
I'm thinking of using the null pattern in order to avoid checking this all the time but I'm not sure if this is possible or convenient with a QObject. The idea would be that the pointer points to the object and in case it gets destroyed, the smart pointer changes its pointed object to a null object. Is this a good idea or should I forget it and just check if the pointer is NULL all the time?
Let's show you an example. We have a worker who uses a tool to do its work:
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject *parent = 0);
void work()
{
if(m_tool)
m_tool->use();
emit workCompleted();
};
signals:
workCompleted();
public slots:
void setTool(QWeakPointer<Tool> tool);
private:
QWeakPointer<Tool> m_tool;
};
class Tool : public QObject
{
Q_OBJECT
public:
Tool();
public slots:
void use() =0;
};
class Screwdriver : public Tool
{
Q_OBJECT
public:
Screwdriver() : Tool();
public slots:
void use()
{
// do something
};
};
class Hammer : public Tool;
class Saw : public Tool;
...
In this case, the Tool is a public domain object of a library, which is used by the Worker. I'm developing such library. So the worker is using a screwdriver but it gets broken and gets destroyed. No problem:
if(m_tool)
m_tool->use();
emit workCompleted();
m_tool is 0 so it simply does nothing. But we have to check that it's not null everytime.
Now let's say we had a NullTool object:
class NullTool : public Tool
{
Q_OBJECT
public:
NullTool() : Tool();
public slots:
void use()
{
// does nothing
};
};
When the tool was destroyed, our pointer would be smart and would know it should point to a NullTool instance. So Worker::work() could be implemented like this:
void Worker::work()
{
m_tool->use();
emit workCompleted();
};
m_tool->use() would then get called on the NullTool which does nothing, so there would be no need to check the pointer is not null.
Is this a good idea? Is it possible with the smart pointer classes Qt provides or should I subclass QWeakPointer?
I think the null object pattern makes most sense for value-like classes. Examples are QString or QVariant, were you don't want to have code like if ( str && !str->isEmpty() ) but just do if ( !str.isEmpty() ). For QObjects, which are not values but have "an identity", I never found this useful.
I don't understand clearly your use case, but your program can be signaled when the object has been destroy by connecting the following signal from QObject:
void destroyed ( QObject * obj = 0 );
I don't see any problem in your idea. You just have to compare the work that it takes to implement it compared to the work for checking the pointer every time. Let's your checking the pointer 10.000 times it's a good idea to use your approach. Side note: Your null object pattern rely on the fact that Tool::use() has no side effects whatsoever.
Take care that possible side affects in Tool::use() don't get in the way when you replace it polymorphically with NullTool::use(). In other words: Be sure you don't break the Liskov Substitution Principle.