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)
Related
What is an optimal and an appropriate way to save the state of a Qt GUI so I get the same state I had back when I closed the application ?
By state I mean : current indexes (for combo box ...), color palette, widgets positions... right before closing the application
You can use the QSettings class.
Simple use of QSettings class (code inspired from Qt's documentation):
In the main-window of your application code member functions that saves and restore the settings:
void MainWindow::writeSettings()
{
QSettings settings("reaffer Soft", "reafferApp");
settings.beginGroup("MainWindow");
settings.setValue("size", size());
settings.setValue("pos", pos());
settings.endGroup();
}
void MainWindow::readSettings()
{
QSettings settings("reaffer Soft", "reafferApp");
settings.beginGroup("MainWindow");
resize(settings.value("size", QSize(400, 400)).toSize());
move(settings.value("pos", QPoint(200, 200)).toPoint());
settings.endGroup();
}
Call those 2 functions from the MainWindow constructor and from the closeEvent override, like this:
MainWindow::MainWindow()
{
// code from constructor
//...
readSettings();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
//optional check if the user really want to quit
// and/or if the user want to save settings
writeSettings();
event->accept();
}
The direct answer requires specific elaborated design for your code and not really a short Qt question or even the question specific to Qt. That is about C++ which is not the VM-based language that assists with serializing the state of program code to data. Having all objects serializable we can then attempt to apply certain C++/Qt classes/techniques.
This task is much easier to accomplish with languages like Java, though. And with C++/Qt you have to routinely make serialize-able / serialize / restore everything that is running in your code and still no guarantee that works as long as the context is not fully captured. This task is not easy for sure and makes sense only in specific application.
The most you can get directly from Qt is to save/restore QMainWindow and other independent widgets geometry (position/size):
saveGeometry
restoreGeometry
... and that solution is still somewhat incomplete or you may/not use QSettings for the storage.
I use QSettings for this. With routines similar to Zlatomir's.
For each window I have in the project I use a different section in QSettings and have readSettings() and writeSettings() in the source for each window.
Anything on the form that I want to persist I have to explicitly save and recall. In the case of a QComboBox it would be something like:
QSettings settings("Organisation", "MySoftware");
settings.beginGroup("WindowNumberTwo");
settings.setValue("ComboIndex", combobox->currentIndex());
// save more values here
// ...
settings.endGroup();
I don't know of a built in way to persist your window states - it has to be don't value by value.
I am currently working on a Qt project where dynamic plugin loading is central. I am loading DLLs by using Qt's QPluginLoader. The different plugins are accessed through CameraPluginStructs, defined as follows:
struct CameraPluginStruct
{
QString name;
QString filePath;
CameraPluginInterface* plugin;
QPluginLoader* pluginLoader;
CameraPluginStruct(QString filePath=nullptr, QString name=nullptr):
filePath(filepath), name(name), plugin(nullptr), pluginLoader(nullptr){}
};
To load a plugin, the following function is called:
void loadPlugin(CameraPluginStruct& p)
{
p.pluginLoader = new QPluginLoader(p.filePath);
p.pluginLoader->load();
QObject* possiblePlugin = p.pluginLoader->instance(); //QPluginLoader.instance();
if(possiblePlugin)
{
// cast from QObject to correct type:
p.plugin = qobjectcast<CameraPluginInterface>(possiblePlugin);
}
}
And to unload a plugin, I use this function:
void unloadPlugin(CameraPluginStruct& p)
{
p.pluginLoader->unload(); // QPluginLoader.unload();
p.pluginLoader->~QPluginLoader();
p.pluginLoader = nullptr;
p.plugin = nullptr;
}
I have made a few, simple test plugins that write messages to the console when the constructor is called. For simplicity, let's say that I have two test plugins, DLL A and DLL B. When I load A by using the loadPlugin() function, the constructor in the plugin is called, and the corresponding message is written in the console. I can do the same thing with B, and everything seems to work – B's constructor message is written to the console, and the other functions seem to work as well.
Now, the problem occurs when I try to create another CameraPluginStruct connected to either A or B. No message is written to the console, which leads me to think that the constructor is not called. Even though, I am able to call the other test functions in the plugin with success (DoSomething(), see below). If I unload all CameraPlugins connected to A or B, and then load the DLL again, the constructor is called again on the first load.
The QPluginLoader.instance() call is described as follows in the documentation:
"Returns the root component object of the plugin. (...) The component object is a QObject. Use qobject_cast() to access interfaces you are
interested in." http://doc.qt.io/qt-5/qpluginloader.html#instance
Wouldn't it then be natural that the constructor in the DLL would be called each time, and not only the first time?
As I've understood, a DLL is only loaded once for any program. Therefore I have also tried to use only one QPluginLoader per DLL file, with the same result. Qt also says that:
"Multiple instances of QPluginLoader can be used to access the same physical plugin." http://doc.qt.io/qt-5/qpluginloader.html#details
Therefore I can't see how this can be a source to the problem anyway.
I would really appreciate if anyone could clarify how the QPluginLoader.instance() really works. Why is the constructor – at least how it seems – only called the first time I use the instance() call?
Thank you!
Here is the code found in the DLLs (the output texts differ in A and B):
TestDLL::TestDLL()
{
std::cout << "This is written from the constructor in A \n";
}
QString TestDLL::Name() const
{
return "Hello, writing from Name() \n";
}
void TestDLL::DoSomething() const
{
qDebug() << "Hello, this text comes from DoSomething()"\n;
}
When your plugin is loaded (i.e. the first time QPluginLoader::instance() is called), then a single instance of it is created - this is your root instance. The root instance is the only instance QPluginLoader will ever create for you.
If you want more, then you create a createInstance() or clone() method for your plugin class, so that new instances can be created from the root instance. Or more conventionally, make your plugin class a factory for the class type you wish to expose.
I am building a JSON-object with Qt and convert it to a QString using QJson. This (normally) works fine and it does in this case but in the destructor of my Qt data structure, it crashes with an Access Violation. The Object is built fine, it is sent over my network connection and after the function ends, my application crashes.
My code looks like this:
void bar()
{
QVariantMap data;
data.insert("Id", 1);
QList<QVariant> list; //QVariantList
for (QMap<...>:ConstIterator ... ) //Loop through a Map
{
QMap<QString, QVariant> singleEntry; //QVariantMap
singleEntry.insert("LocalId", it.value());
QList<QVariant> entryList; //QVariantList
for (...) //Loop through another structure
{
entryList.append("foo");
}
singleEntry.insert("List", entryList);
list.append(singleEntry);
}
data.insert("Entries", list);
QJson::Serializer.serialize(data); // Works fine
} // Crash here
If I remove the inner loop, which builds up entryList, everything works fine. It seems that the destructor of data cannot delete the contents but I have no idea, why. The whole data structure seems to be fine while serializing it (and I hope that QJson does not change anything in the given data) but it cannot be cleaned up..
Best Regards,
Tobias
As Raiv said this can happen when mixing debug and release dlls, but in my oppinion this can also happen if the application and the Qt DLL's use different CRT libraries. Some people say that when they recompiled Qt on their machines the problem dissapears and I think this is because the CRT dlls after Qt rebuild are the same as the app's. Try to set the Runtime Library option in C/C++ Code Generation is set to Multi-threaded Debug DLL (/MDd) or Multi-threaded DLL (/MD) respectively for Debug and Release. Some Qt types as QVariantMap, QVariantList, QModelIndexList are probably allocated with /MD (in Qt's dll) and when they are deallocated with /MT (in the app) I think this causes the crash. This can also fix the crash on QString::toStdWString(). In order for this to link maybe the Ignore All Default Libraries should be set to No and Ignore Specific Library should not mention crt dlls used by Qt.
I got a little workaround, which fits my needs. I have still no idea, why this crash happens, but I know, which should be the problem.
I tried to build up a static structure like this:
QVariantMap
QVariantList
QVariantMap
QVariantList
and it crashes. If I remove the QVariantList at the bottom and add QVariantMap or anything else instead, it is working fine. I think this is a problem with the nesting level in this case.
I have now just joined my list as a comma-seperated QString and then it works fine.
If anyone of you has an idea, why the crash in destructing such a nested struct (another information: doesnt matter if a allocate the QVariants in heap and delete them myself or stack) and how to fix it, please let me know.
I have problem concerning translations in qt. All translations in my porject work fine, but one, which is in a static variable of a class. Corresponding part of code looks as follows
The header file is similar to this:
typedef struct {
int type;
QString problematicString;
} info;
MyClass::QObject_Descendant
{
Q_OBJECT;
//some functions like constructor, destructor... etc.
....
static info myClassInfo;//class that makes problems
}
and in implementation file I initialize the variable as follows:
info MyClass::myClassInfo={
1,
tr("something to be translated")
};
And whatever I do (trying with QT_TR_NOOP, then tr() and others) I cannot get myClassInfo.problematicString translated. The weirdest thing is that the text "something to be translated"
appears in *.ts file.
If someone has any hints, please share them with me. Thanks in advance.
Chris.
Static variables are instantiated (and thus, constructor code run) before your int main function is run. The translation code is set up in the QApplication constructor (I believe), which isn't run until your int main function has been entered. Thus, you are trying to get the translation of a string before the code to support it has been initialized.
To avoid this, you could either accept that the given string isn't translated and explicitly translate it every time it is used, or use the Construct on First Use idiom instead of a static member variable.
Can someone explain to me the basic idea of Qt signals&slots mechanism IMPLEMENTATION?
I want to know what all those Q_OBJECT macros do "in plain C++".
This question is NOT about signals&slots usage.
added:
I know that Qt uses moc compiler to transform Qt-C++ in plain C++.
But what does moc do?
I tried to read "moc_filename.cpp" files but I have no idea what can something like this mean
void *Widget::qt_metacast(const char *_clname)
{
if (!_clname) return 0;
if (!strcmp(_clname, qt_meta_stringdata_Widget))
return static_cast<void*>(const_cast< Widget*>(this));
return QDialog::qt_metacast(_clname);
}
Concerning the signals and slots, the Q_OBJECT macro adds a virtual function qt_metacall() declaration into the class’s declaration which is to be defined later by the the moc. (It also adds some declarations for conversion but that’s not too important here.)
The moc then reads the header file and when it sees the macro, it generates another .cpp file named moc_headerfilename.cpp with the definitions to the virtual functions and – you might have asked yourself why you can get away with mentioning the signals: in your header file without a proper definition – of the signals.
So, when a signal is called, the definition from the mocfile is executed and QMetaObject::activate() is called with the signal’s name and the signal’s arguments.
The activate() function then figures out which connections have been established and fetches the names for the appropriate slots.
Then it calls qt_metacall with the slot names and the arguments given to the signal and the metacall function delegates this with the help of a large switch—case statement to the real slots.
As there is no real runtime information possible in C++ concerning the actual names for the signals and slots, as has already been noticed, these will be encoded by the SIGNAL and SLOT macros to simple const char*s (with either "1" or "2" added to the name to distinguish signals from slots).
As is defined in qobjectdefs.h:
#define SLOT(a) "1"#a
#define SIGNAL(a) "2"#a
—
The other thing the Q_OBJECT macro does is defining the tr() functions inside your object which can be used to translate your application.
Edit
As you asked what the qt_metacast is doing. It checks whether an object belongs to certain class and if it does returns the pointer to it. If it doesn’t, it returns 0.
Widget* w = new Widget();
Q_ASSERT(w->qt_metacast("Widget") != 0);
Q_ASSERT(w->qt_metacast("QWidget") != 0);
Q_ASSERT(w->qt_metacast("QObject") != 0);
Q_ASSERT(w->qt_metacast("UnrelatedClass") == 0);
This is needed to provide some runtime reflection which is not possible otherwise. The function is called in QObject::inherits(const char *) for example and simply checks for inheritance.
Those macros do absolutely nothing "in plain C++", - they expand to empty strings (I think).
QT uses a meta-object compiler, that generates C++ code for Q_OBJECT-enabled classes (implementing the signals/slots you define, among other things).
You can read more about it in the official documentation.
The basic idea is that you can connect your objects allowing them to execute a method (slot) when a signal is done.
connect(pistol,SIGNAL(sigShoot()),runner,SLOT(slotRun()))
Doing the connection above, when the pistol emits the signal, the runner will execute its slot.
To do this, you have to declare your signals and slots in your respective classes.
Is the basic idea.
Good luck!