translation doesn't work when executing application - qt

I am using Qt linguist to translate my application (ui file and the rest of the code).
Everything goes alright , the problem is all translations (in ui) work fine except an element added not from designer, here is my code to further explain :
tableWidget = new MyDropTableWidget(ui->verticalLayoutWidget_2);
if (tableWidget->columnCount() < 1)
tableWidget->setColumnCount(1);
tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("My non translated text"))); if (tableWidget->rowCount() < 21)
tableWidget->setRowCount(21);
...
ui->verticalLayout_2->addWidget(tableWidget);
...
all texts in ui are translated fine , only "My non translated text" was not.
*.ts file are ok ("My non translated text" was detected and checked) , .qm file are well placed, I rebuild re lupdate re lrelease my application but in vain: when executing my application, all texts are translated expect "My non translated text" still in the source language.
Any help will be appreciated.
Edit:
in main file:
QTranslator Translator ;
qDebug()<<"Translator->load( lang)"<< Translator.load(":/"+language);
app.installTranslator( &Translator );
in mainwindow constructor:
ui->retranslateUi(this);

A non-translated text in Qt GUI is often caused by a bad order in your code.
It is impossible to answer precisely without reading your whole code. I suggest you to read the content of Ui::retranslateUi() method. You will see many code like:
button->setText(QApplication::translate("MainWindow", "Import", 0));
The retranslateUi metod is generated for you when compiling a *.ui file, but it is only a list of calls to setText(), setToolTip() or another setXXX() methods on widgets you defined in the ui file. When a setText() method is called (for example), the parameter must be tr("my text"), this tr() is replaced by the corresponding translation at the moment you call it. So you should check if when you initialize your table widget item, your Translator has correctly been installed.
For example, if you create your item in the MainWindow constructor and if you have a main function like:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
QTranslator Translator ;
qDebug()<<"Translator->load( lang)"<< Translator.load(":/"+language);
app.installTranslator( &Translator );
w.show();
return app.exec();
}
it will not work, because the translator is installed after MainWindow constructor call, so when the item is initialized, the translator is not set.
To find the issue in your case, I suggest you to put qDebug() calls at some point to check the order of the calls.

Are your .qm located in the same folder as your binary ?
As you are using tr() function, yout text should be translated. I am using this syntax in my program and the translation works fine :
QTranslator translator;
if (QLocale::system().language() == QLocale::French)
translator.load("fr_lang", QCoreApplication::applicationDirPath());
else
translator.load("en_lang", QCoreApplication::applicationDirPath());
app.installTranslator(&translator);
My translation files are fr_lang.qm and en_lang.qm

Related

Why does my Qt app ignore the setting of applicationDisplayName?

I am running a small app on KDE Plasma 5 created with Qt and the KDE framework. Almost everything works like a charm, just one part doesn't work. I just cannot set the application display name. I have the following code:
int main(int argc, char **argv) {
QApplication application(argc, argv);
KLocalizedString::setApplicationDomain("blender-render-control");
KCrash::initialize();
KAboutData aboutData(QStringLiteral("blender-render-control-center"),
i18n("Blender Render Control Center"),
QStringLiteral("1.0"),
i18n("A simple application to control the blender render control server"),
KAboutLicense::Custom,
i18n("Copyright 2019, Knerd "));
aboutData.addAuthor(i18n("Knerd"), i18n("Author"), QStringLiteral("knerd#knerd.knerd"));
aboutData.setOrganizationDomain("knerd.knerd");
aboutData.setDesktopFileName(QStringLiteral("knerd.knerd.blender-render-control"));
KAboutData::setApplicationData(aboutData);
QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("knerd.knerd.blender-render-control")));
application.setApplicationDisplayName(i18n("Blender Render Control Center"));
application.setApplicationName(i18n("Blender Render Control Center"));
QCommandLineParser parser;
aboutData.setupCommandLine(&parser);
parser.process(application);
aboutData.processCommandLine(&parser);
auto *window = new MainWindow();
window->show();
return QApplication::exec();
}
From reading the docs and checking some examples, this should set the application title in my KDE environment. But it doesn't, the application name is the name of the executable.
Is this a bug in KDE or am I doing something wrong?
The docs are a bit confusing on what the applicationName and displayApplicationName are actually used for, there has been some bug reports about it, and behavior has changed between versions if I remember correcly.
If you want a window-title, I think you can do.
window->setWindowTitle( QCoreApplication::applicationName() );

Using QSettings in a global static class

My task is to create a QSettings wrapper class (wrapping is mostly needed by QML) which I can reach everywhere from the model, too.
The obvious choice is a static global instance of this class. However the problem is with this approach is that it's destroyed after main, after QApplication is destroyed, thus giving me the following warning:
QApplication::qAppName: Please instantiate the QApplication object first
Here is a simplified, sample code showing a really simple wrapper, and the destruction phases:
#include <QCoreApplication>
#include <QDebug>
#include <QGlobalStatic>
#include <QSettings>
#include <QTimer>
class Settings: public QObject
{
public:
~Settings() { qDebug() << "~Settings"; }
QSettings settings;
};
Q_GLOBAL_STATIC(Settings, globalSettings)
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [](){qDebug() << "~QCoreApplication aboutToQuit";});
QObject::connect(&app, &QCoreApplication::aboutToQuit, [](){globalSettings->settings.setValue("Hi", 2);});
QObject::connect(&app, &QCoreApplication::destroyed, [](){qDebug() << "~QCoreApplication destroyed";});
QTimer::singleShot(0, &app, SLOT(quit()));
return app.exec();
}
It prints out the following:
~QCoreApplication aboutToQuit
~QCoreApplication destroyed
~Settings
QApplication::qAppName: Please instantiate the QApplication object first
My question is: providing, in my program QSettings is used after QCoreApplication::aboutToQuit is emitted but before QCoreApplication::destroyed, how can I avoid this warning?
Using QSettings
I've used QSettings in pretty much every project I've ever made. Here is the pattern that I tend to use it:
in main.cpp
#include <QSettings>
//...
// Then in main(), after QApplication is instantiated, but before any settings are accessed
QSettings::setDefaultFormat(QSettings::IniFormat);
QApplication::setOrganizationName("MyOrg");
QApplication::setApplicationName("MyApp"):
Then anytime you are about to access QSettings, you just do this:
QSettings s;
s.value(// reading a value
s.setValue(// writing a value
Everything gets saved in the User Scope in an INI text file. It will be located in Windows under C:/Users/<username>/AppData/Roaming/MyOrg/MyApp.ini.
This usage of QSettings (IMHO) is very clean, doesn't require global variables or static references and is very fast and efficient. And it is very readable.
Now to be able to have things load settings at the right times, and not get the errors you mentioned in your question, see the initial example in the links below:
http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application
http://doc.qt.io/qt-5/qtwidgets-mainwindows-application-example.html
It works much better in the timeline of a Qt application and works great. I tend to make a readSettings and writeSettings for most of my GUI classes and for a number of my backend classes. readSettings happens when the widget has its showEvent or constructor happen and the writeSettings happens in the closeEvent. Also if I have a dedicated settings dialog, I emit a signal to have any affected classes to readSettings, right after the settings dialog writes those specific settings.
If you use the QML port of QSettings, it also uses the Organization name and Application name and the default format of QSettings to pick its filename and location.
http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html
I believe the default functionality of that QML component is just to read the setting when the component is created, and to write the setting whenever QML changes it. So to change it from C++ and have it recognized by QML, you should probably use the standard QML/C++ methods out there such as:
http://doc.qt.io/qt-5/qtqml-cppintegration-topic.html
And lastly if I am planning on installing defaults for a program that are decided for a build and I don't want to hardcode them, I hand write an INI file and have the installer place it in the system scope location: C:/ProgramData/MyOrg/MyApp.ini
And in the case that the settings of your program are more complex than what you want to store in an INI file, I would look into using QJson, and the SaveGame example.
Hope that helps.

Embeding PySide/PyQt widget into Qt/C++ application

I have a C++/Qt application which should have its GUI extensible with modules. Extending should be simple and versatile. I am just checking a concept - is it possible to have this main C++/Qt application that would execute a Python/PySide/PyQt script which would create a QWidget (or derived class) instance and embed this widget into the main C++/Qt application?
Is there any working snippet to demonstrate the feasibility of this task? I.e. how to create and embed the widget? How to pass signals between the application and the widget?
This question is a bit old, but in case someone else is face the same issue, I will try to give a useful answer.
I think it's possible. In the following example, I create a QApplication and QMainWindow in c++, embed a python interpreter, and then, on the python side, I create a QPushButton which I add to the main window.
Try this out:
#include <QApplication>
#include <QMainWindow>
#include <iostream>
#include "Python.h"
class PythonQt
{
public:
PythonQt()
{
char name[] = "test";
Py_SetProgramName(name);
Py_Initialize();
std::string code =
"from PySide import QtGui\n"
""
"all_widgets = QtGui.QApplication.instance().allWidgets()\n"
"window = None\n"
"for widget in all_widgets:\n"
" if str(widget.objectName()) == \"main_window\":\n"
" window = widget\n"
" break\n"
""
"def message_box():\n"
" QtGui.QMessageBox.information(None, 'Test', 'Hello!', \
QtGui.QMessageBox.Ok)\n"
" QtGui.QApplication.instance().quit()\n"
""
"button = QtGui.QPushButton('Press Me')\n"
"button.clicked.connect(message_box)\n"
"window.setCentralWidget(button)\n"
"window.move(600, 300)\n"
"window.show()";
PyRun_SimpleString(code.c_str());
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow win;
win.setObjectName("main_window");
PythonQt python_code;
a.exec();
}
The python script is written here in a string to have everything in a single file, but you could of course read it in from a .py file.
Object ownership could be an issue though, as shown in the link given by Trilarion.
I don't think this is possible. PySide/PyQt are wrappers around C++/Qt. That means you create C++ objects and Python wrapper objects and somehow the wrapper objects kind of refer to the C++ objects. This works one way as far as I know.
But you want the other way. Basically a wrapper around Python objects (which themselves are wrappers around C++ objects) to use in C++. I don't think PySide/PyQt are ready for thise. However it is possible to embed Python in other languages.
Also see How to shoot yourself in the foot. about the pitfalls of communication between C++/Qt and Python.

Convert a Qt GUI-based application into a console or batch application

I have a Qt GUI-based full application but now I need to use this application in a sort of pipeline in batch mode (console). I've tried several approaches but none of them worked as expected. Here is what I have now:
QApplication a(argc, argv);
MyMainWindow *w = new MyMainWindow();
a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
a.exec();
Here is what I need:
QApplication a(argc, argv);
QString project_path = argv[1];
MyMainWindow *w = new MyMainWindow();
a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
w->loadPrjFile(project_path);
w->analyze();
w->exportResults();
a.exec();
So, basically what I need is to allow the application to get the project_path through command line (not file dialogs) and execute the methods that a user would usually follow using the GUI. The problem is that these methods should block, in the sense that one should wait for the previous one to finish.
The application itself should block, in the sense that, when executed in the shell, it should wait for the whole execution to finish before quitting. As it should work as a console application the interface should also be hidden.
If you know a way to do that, I would really appreciate a code sample of how this can be done.
The problem you have is that you're trying to develop a console app, but still using Gui widgets, such as QMainWindow. You need to start by separating the Gui classes from everything else in your main project.
I recommend you create a class, derived from QObject, which handles the processing of what you need; loadPrjFile, analyze and exportResults.
Then use an instance of this new class in your MainWindow for the GUI project and use it directly for the console project.
class Worker : public QObject
{
Q_OBJECT
public:
void loadPrjFile(const QString& path);
void analyze();
void exportResults();
};
class MyMainWindow : QMainWindow
{
private:
Worker m_pWorkerObject;
};
If you're developing a console project that doesn't need a Gui, you can use QCoreApplication, instead of QApplication.
Be aware that calling app.exec() starts Qt processing messages, so you only need that if you need a message loop to process events, which may not be the case for a console application, depending on what your app does.
This answer shows the solution that I came up with after a while. I'll put it here because it can be useful to others. The code looks like this:
QApplication a(argc, argv);
QString project_file = argv[1];
MyMainWindow *w = new MyMainWindow();
w->setVisible(false);
a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
w->setBatchMode(true);
QObject::connect(w,SIGNAL(loadingFinished()),w,SLOT(analyze()));
QObject::connect(w,SIGNAL(analysisFinished()),w,SLOT(exportResults()));
QObject::connect(w,SIGNAL(exportingFinished()),w,SLOT(close()));
w->loadPrjFile(project_file);
a.exec();
The main considerations are:
w->setVisible(false) was used to hide the MainWindow as also pointed out by #Dissident penguin.
w->setBatchMode(true) was used to set a class variable that is used to supress all the other dialogs throughout the code as also pointed out by #Dissident penguin. Inside the functions I just wrapped the dialog code with an if statement like:
if (!_batchMode) { //show dialog }
Meeting the requirement of sequencial execution was not that easy. I had to create two signals: loadingFinished(), analysisFinished() and exportingFinished(). Then I emit them in the end of loadPrjFile(), analyze() and exportResults() functions respectively. This way I garantee that they are executed in order and that one waits for the other. This is needed because slots are executed asynchronously in Qt.
Finally, I could not take out the method a.exec() because if I do that the program doesn't run properly. I think this is because I'm still using the GUI, it's just hidden. This way, a.exec() is still needed.
To read arguments from the command line in an app with a GUI, you can use the global pointer qApp anywhere in your code. This is particularly useful if you want, for example, to be able to associate your GUI application with a file type, since the file name will be pipleined by the OS to your app (at least it works in Windows).
You can see a detailed answer I gave to the same question in this thread, together with the links to the appropriate documentation, which for some reason is not in the latest versions of Qt.
The second part is not that easy.
You can use w->setVisible(false) before calling a.exec(); to hide your main window, but you will have, to the best of my knowledge, to modify every method that has a dialogue to either react to the command line argument if detected, and disable the dialogue, or use the normal dialogues if no related arguments are detected.
If you only need to call methods of the main window that have no interaction with the user, then it won't be that much work, and you might get away with not calling a.exec (if and only if no part of your code is using signals and slots in batch mode), which in reality starts the main loop of the GUI and won't be needed in that case.
Something like this might work:
QApplication a(argc, argv);
MyMainWindow *w = new MyMainWindow();
if(1 < qApp->arguments().count()) //Command line arguments detected
{
QString project_path = qApp->arguments().at(1);
w->loadPrjFile(project_path);
w->analyze();
w->exportResults();
}
else //No command line arguments detected
{
a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
a.exec();
}
If, on the other hand, the functions loadPrjFile(), analyze(); and exportResults() are slots and not, as your code suggests, methods of your main window, they will not be called sequentially and you will have no choice but using signals and slots so each function can notify the next one that it finished its job, and therefore will have to call a.exec

qt QTranslator reuse

I noticed that the Qt documentation is not very verbose on some of the aspects of the translations. I was fooling around with it trying to figure out their behaviour using trial & error. The ultimate goal is to get the translation changed on runtime but I am very confused as to what extent the QTranslator object can be re-used.
Consider this (where 'a' is the main instance of the application):
QTranslator translator;
translator.load("mytranslation_cz");
a.installTranslation(&translator);
(...)
a.removeTranslation(&translator)
Now the translator was removed from the application but what happened to the translator object?
In my tests when above code was followed by this again
translator.load("mytranslation_fr");
a.installTranslation(&translator);
it did not do anything in main() and it crashed the application when called from one of the widgets (using pointer to main app).
Therefore I am suspecting that I would need to create one QTranslator object per translation I want to load in the application and that I cannot reuse the QTranslator object. Am I right in this assumption?
And as a side question. Assuming the QTranslator object is untouched by the removeTranslation(), is it possible to simply install it later again like this?
QTranslator translator;
QTranslator translator1;
translator.load("mytranslation_cz");
translator1.load("mytranslation_fr");
a.installTranslation(&translator);
(...)
a.removeTranslation(&translator);
a.installTranslation(&translator1);
(...)
a.removeTranslation(&translator1);
a.installTranslation(&trasnlator); //Will this work?
Thanks for any clarification as I am somewhat confused as to what happens to the QTranslation objects when installing and removing translations from the application and especially if the QTranslation object can be reused for multiple translations somehow on runtime?
QTranslator::load basically in simple sense can be considered as a function that opens a given .qm file, reads in all the translated values and loads it in for a specific language.
Now in general operation you would not want to reuse this for many languages as by "reusing" (even if its allowed) your adding the overhead of parsing this given .qm file for every language every time you switch your UI language, which is just basically an overhead you don't need.
Your assumption of creating a QTranslator for each language is correct. As for your side question, Yes you can also re-use it. Thats the benefit of having individual QTranslator objects per translation. Just call qApp->removeTranslator() with the current translation and then qApp->installTranslator() with the new one. This way you are reusing the loaded translations as and when you please.
The way we structure this is by sub-classing QApplication and adding 2 functions
void Application::CreateTranslators() {
// translators_ is a QMap<QString, QTranslator*>
if (!translators_.isEmpty())
return;
QStringList languages;
languages << "en" << "ar" << "zh";
foreach(QString language, languages) {
QTranslator* translator = new QTranslator(instance());
translator->load(language);
translators_.insert(language, translator);
}
}
Now this function is called at the very start of the application.
2nd function is as following
void Application::SwitchLanguage(QString language) {
// current_translator_ is a QTranslator*
if (current_translator_)
removeTranslator(current_translator_);
current_translator_ = translators_.value(language, nullptr);
if (current_translator_)
installTranslator(current_translator_);
}
That's it. Using the second function you can switch your language at run-time as you please.
Couple things you'll also need to be aware of is changing QTranslator at run-time will update all translations from your .ui file strings marked as translatable automatically, however those set from code will not be. To get that you will have to override QWidget::changeEvent() and then check if the event is of type QEvent::LanguageChange and then set the required strings for that QWidget accordingly (Don't forget the tr() while doing so)

Resources