How to expose enum in class to QML
-
I have a class like this to change the language of the application at runtime:
class LanguageSelector : public QObject { Q_OBJECT public: enum class Language { german, english, spanish }; Q_ENUM(Language) explicit LanguageSelector(QObject *parent = nullptr); Q_INVOKABLE void changeLanguage(Language newLanguage); QTranslator *getTranslator() const; private: QTranslator *mTranslator; void loadGerman(); void loadEnglish(); void loadSpanish(); void loadLanguage(const QLocale::Language &newLanguage); signals: void languageChanged(); }; Q_DECLARE_METATYPE(LanguageSelector::Language);
This class gets exposed to qml like this:
QQmlApplicationEngine engine; auto context = engine.rootContext(); context->setContextProperty("languageSelector", &languageSelector);
In QML then I thought I could change the language like this (refering to the enum):
ToolButton { //... onClicked: languageSelector.changeLanguage(languageSelector.spanish) }
No matter what I pass:
onClicked: languageSelector.changeLanguage(languageSelector.german)
onClicked: languageSelector.changeLanguage(languageSelector.english)
onClicked: languageSelector.changeLanguage(languageSelector.spanish)
In C++ always german gets selected.
I know the method works correct because if i pass the numerical values:
onClicked: languageSelector.changeLanguage(0) // german
onClicked: languageSelector.changeLanguage(1) // english
onClicked: languageSelector.changeLanguage(2) // spanish
Then it works.
So 2 questions here:
Is the enum exposed correctly? If not how to do it correctly.
Is it the correct way to add this class as a context or should it be made a singleton or something like that? -
Register the type:
qmlRegisterType<LanguageSelector>("LanguageSelectors",1,0,"LanguageSelector");
// or qmlRegisterTypeNotAvailableqmlRegisterUncreatableType if you don't want to instantiate in QMLThen in qml:
import LanguageSelectors 1.0 ... languageSelector.changeLanguage(LanguageSelector.german)
-
@fcarney I think thats the wrong register call. Enums aren't types!
qmlRegisterUncreatableType< LanguageSelector >("LanguageSelectors",1,0,"LanguageSelector","Enum is not a type");
it says so in the documentation 😉
https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterUncreatableType -
@J-Hilk That's the correct register call but the error message you put is misleading.
This would appear while trying to do
LanguageSelector {}
in QML, so the link with Enum is not really apparent. Just mention that LanguageSelector is not instantiable in QML code. -
@GrecKo I actually have that from here:
https://doc.qt.io/archives/qt-5.10/qtbluetooth-heartrate-game-main-cpp.htmland it's still in the newest documentation
https://code.qt.io/cgit/qt/qtconnectivity.git/tree/examples/bluetooth/heartrate-game/main.cpp?h=5.15
-
Thank you all for all the answers. I tried to go with the uncreateable type but I get an Error.
Here is what I did:
Change the enum names to uppercase (German, English, Spanish) because qml complains they have to be uppercase.
In Cpp:
qmlRegisterUncreatableType<LanguageSelector>( "LanguageSelectors", 1, 0, "LanguageSelector", "LanguageSelector is not instantiatable in QML");
In QML:
import LanguageSelectors 1.0
Component.onCompleted: { LanguageSelector.changeLanguage(LanguageSelector.German) }
This line gives me an error:
TypeError: Property 'changeLanguage' of object [object Object] is not a function
I don't get it why. In the class it is invokeable like this:
class LanguageSelector : public QObject { Q_OBJECT public: enum class Language { German, English, Spanish }; Q_ENUM(Language) explicit LanguageSelector(QObject *parent = nullptr); Q_INVOKABLE void changeLanguage(Language newLanguage); //.... private: //.... signals: void languageChanged(); }
A second concern. When I call changeLanguge in QML the signal "languageChanged". When that happends we need to call
QQmlApplicationEngine::retranslate
to retranslate the ui.Is that possible to connect to the uncreateable type?
Before when i exposed it with the context I simply did:
QObject::connect(&languageSelector, &LanguageSelector::languageChanged, &engine, &QQmlApplicationEngine::retranslate);
Maybe for that I still have to create an object? Or is there annother way to call retranslate from QML when languageChanged is called in QML?
Since I have the signal isn't it then a canidate for
qmlRegisterSingletonInstance
? -
I could get it to work with the singleton approach:
QScopedPointer<LanguageSelector> languageSelector(new LanguageSelector); //.. qmlRegisterSingletonInstance<LanguageSelector>( "LanguageSelectors", 1, 0, "LanguageSelector", languageSelector.get()); //.. QObject::connect(languageSelector.get(), &LanguageSelector::languageChanged, &engine, &QQmlApplicationEngine::retranslate); engine.load(url);
In QML:
import LanguageSelectors 1.0 //.... Component.onCompleted: { showButtonsIfConditionsAreMet() LanguageSelector.changeLanguage(LanguageSelector.German) }
Now the only strange thing. I get a complain from QT Creator that it does not know the import:
Any way to get rid of that or is it like a bug in creator?
-
@sandro4912 said in How to expose enum in class to QML:
This class gets exposed to qml like this:
QQmlApplicationEngine engine;auto context = engine.rootContext();
context->setContextProperty("languageSelector", &languageSelector);In QML then I thought I could change the language like this (refering to the enum):
ToolButton {
//...
onClicked: languageSelector.changeLanguage(languageSelector.spanish)
}Backup, I built upon the code above. I was using the languageSelector context property you set. Do not capitalize this object.
This code is not an error. languageSelector.changeLanguage should appear like this because your context property cannot be capitalized.
LanguageSelector.german is capitalized because you are using it as a QML type. Don't mix this up.import LanguageSelectors 1.0 ... languageSelector.changeLanguage(LanguageSelector.german)
-
Note that qmlRegisterSingletonInstance should be preferred over setContextProperty for new code.
@J-Hilk said in How to expose enum in class to QML:
@GrecKo I actually have that from here:
https://doc.qt.io/archives/qt-5.10/qtbluetooth-heartrate-game-main-cpp.htmland it's still in the newest documentation
https://code.qt.io/cgit/qt/qtconnectivity.git/tree/examples/bluetooth/heartrate-game/main.cpp?h=5.15
What a weird place to copy code from :) Still, it doesn't really make sense even there.
-
Maybe I was not clear in the last post. I got rid of context property complete and only registered the type with
qmlRegisterSingletonInstance
.Now just wonder why creator complains about the import even if all the code works fine.