How to find out the QQmlEngine instance of a singleton object? - qt

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.

Related

Understanding QML_INTERFACE

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?

How to make a Qt QML variable accessible anywhere

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

Using ENUMS from different classes (or namespaces) for slots called from QML

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.

Calling qmlRegisterType() in the registered class on debug crashes application

I want to use qmlRegiterType() in registered class itself. I tried to use method from this topic but whenever I try to run application in debug mode it crashes the application with error
read access violation at 0x0
Crashes on macro QML_GETTYPENAMES inside qqml.h (235 line).
TestClass.h:
class RegisterQmlTest : public QObject
{
Q_OBJECT
public:
explicit RegisterQmlTest(QObject *parent = 0);
};
TestClass.cpp:
QML_REGISTER(RegisterQmlTest);
RegisterQmlTest::RegisterQmlTest(QObject *parent) : QObject(parent)
{ }
void RegisterQmlTest::foo()
{
qDebug() << "Foo test";
}
I tried to compile application on MSVC2013x64 on Qt 5.6.2, on Windows.
You are not the only one that it is happening to : https://github.com/benlau/quickflux/issues/7, and I believe it is likely due to the
static initialization order fiasco.
One solution could be to use Q_COREAPP_STARTUP_FUNCTION to ensure the call to qmlRegisterType is not done too early.
You can use this macro in a .cpp file like so :
static void registerMyQmlTypes() {
qmlRegisterType<MyType>("MyImortUri", 1, 0, "MyType");
}
Q_COREAPP_STARTUP_FUNCTION(registerMyQmlTypes)
I had the exact same issue. The problem is that the staticMetaObject associated to your class is not initialized at the moment the macro invokes the call to qmlRegisterType. As already stated in this answer (from the same topic), you're more flexible without macros. I solved this by introducing one static type per custom class.
appQmlRegister.hpp
#include <functional>
#include <QtQml>
#include <QList>
namespace app {
namespace Qml {
namespace Register {
auto Init() -> void;
static auto GetList()->QList<std::function<void(void)>>&;
template <class T>
struct Type {
Type() {
auto initializer = []() {
qmlRegisterType<T>();
};
GetList().append(initializer);
}
};
}
}
}
appQmlRegister.cpp
#include "appQmlRegister.hpp"
namespace app {
namespace Qml {
namespace Register {
auto Init() -> void {
for (auto registerFunc : GetList()) {
registerFunc();
}
}
auto GetList()->QList<std::function<void(void)>>& {
static QList<std::function<void(void)>> List;
return List;
}
}
}
}
The type app::Qml::Register::Type takes a template argument (the type of your custom class) and wraps the call to qmlRegisterType in a lambda. And that's the basic concept. Instead of an immediate call you now have full control of when to register all your custom types via app::Qml::Register::Init(). By calling that function at runtime but before starting the QML engine you can ensure that the staticMetaObjects are initialized properly and you're safe to register them.
This requires a bit of typing on a per-custom-class level though. You'd have to declare a static member in the header of the class you want to register in QML:
MyCustomClass.hpp
#include "appQmlRegister.hpp"
namespace app {
class MyCustomClass : public QObject {
Q_OBJECT
private:
static Qml::Register::Type<MyCustomClass> Register;
// stuff...
}
}
and then define it in the .cpp file like this:
MyCustomClass.cpp
#include "MyCustomClass.hpp"
namespace app {
Qml::Register::Type<MyCustomClass> MyCustomClass::Register;
}
This can of course be extended to support other sorts of type registration like registering uncreatable types, custom versions/names etc. I implemented this in a QML showcase/template project on GitHub
Hope this helps!

Derive UIC generated Qt UI class from custom interface

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().

Resources