Solved How to get handle to QQmlApplicationEngine from inside c++ to call retranslate for QML
-
I have a QML app that has a Combobox I populate in c++, and what to retranslate the QML, currently I have to use setContextProperty, but I want to convert to qmlRegisterType or qmlRegisterUncreatableType after reading they might depreciate setContextProperty due to issues, but I cannot figure out a way to get a handle for QQmlApplicationEngine in main, and I cannot figure out how to initialize it in main so I can pass in the handle, making it work similar to setContextProperty, ideally I would initialize the class in main passing in the handle to QQmlApplicationEngine if I cannot get that handle internally, qApp seems to be the only gateway but I cannot find a function to call, and then pass in the initialize function, it seems that qmlRegisterUncreatableType should work for this, but I could not get it to work, nor can I find an example of doing this, and I have been searching the internet for a week now, and seem to be stuck here, I use the qApp->installTranslator which works for Widgets, but does not retranslate QML, I have to call retranslate() on the handle for QQmlApplicationEngine, it seems that you cannot ininalize a function and use either qmlRegisterType or qmlRegisterUncreatableType like you do setContextProperty, unless I am missing something, I have even search GitHub trying to find anyone doing this, but no luck so far.
Thanks, Flesh
-
@Flesh Your question is confusing. Could you show a demo of what you have?
-
The full source code is here:
https://github.com/Light-Wizzard/QmlWidgetTranslatorI will show you the main parts, this is how I do it now using setContextProperty, note that there has been talk about deprecating it if they have not already done so, but most people agree it is not the best way to do it do to performance issues
This is my current way of doing this with setContextProperty
class MyLanguageModel : public QAbstractListModel { Q_OBJECT ... void setQmlEngine(QQmlEngine &thisQmlEngine) { myEngine = thisQmlEngine; } Q_INVOKABLE bool setLanguage(const QString &thisLanguageName) { ... qApp->installTranslator(myTranslator); myEngine.retranslate(); } QQmlEngine &myEngine; };
The QML does not matter in this case since there is no way of knowing QQmlEngine, so I am at a loss unless I missed something, how can I get assess to QQmlEngine inside a class if I do not use setContextProperty since I have to use it to call retranslate when I set the Language, I hope this made it clearer, unless one of the 3 ways of registering a class allows you to do this, I could not find a way to do that, it seems all these methods do not allow you to do this, unless I am missing something.
int main(int argc, char *argv[]) { ... QQmlApplicationEngine theEngine; MyLanguageModel *theLanguageModel = new MyLanguageModel(theEngine, qApp); theContext->setContextProperty("myLanguageModel", theLanguageModel); // setContextProperty allow me to set theEngine, but this method does not // Instantiable which does not allow me to pass in theEngine qmlRegisterType<MyLanguageModel>("Qt.LanguageModel", 1, 0, "MyLanguageModel"); // Uncreatable which also does not allow me to pass theEngine qmlRegisterUncreatableType<Qt.LanguageModel>("myLanguageModel", 1, 0, "MyLanguageModel"); // Anonymous which also does not allow me to pass theEngine qmlRegisterAnonymousType<Qt.LanguageModel>("myLanguageModel", 1, 0, "MyLanguageModel"); // Singleton which has possiblities but I have not found a way to pass in theEngine qmlRegisterSingletonType("Qt.LanguageModel", 1, 0, "MyLanguageModel", myLanguageModelSingletontype_provider); ... } static QJSValue myLanguageModelSingletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { MyLanguageModel *theLanguageModel = new MyLanguageModel(engine, qApp); // This is where I get lost, this has to return a property, // and I have many types of data and models, // this is just an example I found online, // I could not figure out how to use this method other than for properties QJSValue example = scriptEngine->newObject(); example.setProperty("propertyInt", 80); example.setProperty("propertyString","testString"); example.setProperty("propertyDate",QDateTime::currentDateTime().toString()); return example; }
The Singleton is the closes I can come to figuring out how to do this, but I am lost with how to set a data model using a property.
I have a few things going on in this class that I can move to another class, I have a data model, and other data that I want to be translated in this class, so it needs to handle a List for a Combobox, and it does string translations in the c++ class, so I need a way for it to work like setContextProperty, so I need to find a way to get theEngine in main set in the class I register or find a way to pass it in, I hope that was clear, this is hard for me to explain for some reason.
Thanks, Flesh
-
MyLanguageModel *theLanguageModel = new MyLanguageModel(theEngine, qApp);
with
QQmlEngine &myEngine;
is prone to dangling references. Instantiate the model with the QML engine as a parent. Don't pass the
qApp
instance, it's superfluous - you can get it from anywhere.This isn't valid C++, you can't assign references, they can only be initialized:
void setQmlEngine(QQmlEngine &thisQmlEngine) { myEngine = thisQmlEngine; }
Beside the obvious I still don't get what "to pass in theEngine" means exactly. Please explain that phrase, specifically. "Pass in" to what and for what purpose? On that note, there's the
Qt.uiLanguage
from QML (wheneverQQmlApplicationEngine
is the instantiated engine, why can't you use this one? -
@kshegunov In the Language model, I initialize the Engine, I made it a separate function here to keep it short, but when you change languages, you have to have a reference to the Engine to trigger retranslate, by passing in the variable reference I can access that function in the Engine, the arguments to a function or a constructor initialization in this case.
When the QML combobox changes, its the Language Model that gets notified of the change, the only problem I have is getting the reference to the QML Engine, and all of this is a hack as I said, this is what I am trying to get away from, this is the problem with using setContextProperty instead of registering it, but as I said, without the reference to the Engine in main, I cannot trigger the retranslate function, and all the examples I can find do not show another method of getting it, besides the qmlRegisterSingletonType, which might work, I will work with it today to see how to get a Property to take a QStringList.
To clarify, I need a reference to the Qml Engine in main, "Pass in" means by reference, meaning & as a constructor, currently, I do this as such:
MyLanguageModel::MyLanguageModel(QQmlEngine &thisQmlEngine, QObject *parent) : QAbstractListModel(parent), myEngine(thisQmlEngine)
Singletons seem to be my answer, but I have to find a way to make it work, maybe the QObject overload instead of QJSValue.
-
For objects managed by the engine, there's the C++ function qmlEngine().
Eg:
class LanguageModel : public MyQmlInstantiatedListModel { Q_INVOKABLE void changeLanguage() { if (QQmlEngine *engine = qmlEngine(this)) { engine->retranslate(); } } };
-
@jeremy_k That is what I need, I will test it out as soon as possible, thanks.
-
@jeremy_k said in How to get handle to QQmlApplicationEngine from inside c++ to call retranslate for QML:
if (QQmlEngine *engine = qmlEngine(this)) {
engine->retranslate();
}Just need to refactor now, thanks