Problems with Open-Source Downloads read https://www.qt.io/blog/problem-with-open-source-downloads and https://forum.qt.io/post/638946

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 qmlRegisterTypeNotAvailable qmlRegisterUncreatableType if you don't want to instantiate in QML

    Then in qml:

    import LanguageSelectors 1.0
    
    ...
    
    languageSelector.changeLanguage(LanguageSelector.german)
    

  • Moderators

    @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


  • Qt Champions 2018

    @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.


  • Moderators



  • @J-Hilk said in How to expose enum in class to QML:

    Enums aren't types!

    Edit: I think I am slow this morning.

    Your right, it should be qmlRegisterUncreatableType



  • 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:

    159964ab-1b2e-45da-9ab3-21d5176abda6-image.png

    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)
    

  • Qt Champions 2018

    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.html

    and 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.


Log in to reply