how to translate string added dynamicaly in Qapp with Qtranslator? - qt

i trying to created a Qt application multilanguage with Qt linguist.
I place this code in an function of my MainWindow :
translator.load(":/lang/English");
qApp->installTranslator(&translator);
ui->retranslateUi(this);
With the QTranslator declare in my MainWindow.h and all my string i want translate enclose by tr() . But with that, all QObject added dynamicaly by the code of my MainWindow.cpp, for example the title of a QTableWidget, are not translated.
If i place an other translator in my main.cpp, all my string are translate but i must created language button in my application and so i can't place translator in main.cpp.
Do you have an idea to help me?
Thx for your answers.
Gat

When you add a translation in your application using qApp->installTranslator(& aTranslator) then all the following calls to QObject::tr() (and similar functions) will look up in the translator for a translated text. So you should call retranslateUi() after qApp->installTranslator(). Actually you might event not call it there, you may reimplement QWidget::changeEvent() and intercept any QEvent::LanguageChange event.
void MainWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
// Someone called qApp->installTranslator() with a new translation.
// Let's update the user visible strings.
// This function was created by uic from the Designer form.
// It translates all user visible texts in the form.
ui->retranslateUi(this);
// This function must be written by you. It translates other user visible
// texts that are not in the form. See down for an example.
this->retranslateUi();
break;
default:
break;
}
}
ui->retranslateUi() just calls QObject::tr() for each user visible string in the ui. It is called automatically at the end of setupUi() and populates the form's widgets with translated text (have a look, it is defined by uic in ui_MainWindow.h file). You may want to take a similar approach with other user visible texts, like the title of a QTableWidget. All the strings are set in a function (named perhaps retranslateUi() for consistency) which is called at application starts (or, better, after the relevant widgets are created) and every time a new translations is loaded.
MainWindow::MainWindow(QWidget * parent)
: QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Creates other widgets, but do not set their user visible texts.
tableWidget = new QTableWidget(this);
...
someControl = new QLineEdit(this);
someOtherControl = new QSpinBox(this);
someModel = new MyModel(this);
...
// Ok, *now* we set their texts.
this->retranslateUi();
}
...
void MainWindow::retranslateUi()
{
// This function will be called (either manually or automatically by
// `changeEvent()`) when a new translator has installed, hence all the `tr()`
// calls will return the right translation for the last installed language.
QStringList labels;
labels.append(tr("First column"));
labels.append(tr("Second column"));
labels.append(tr("Third column"));
tableWidget->setHorizontalHeaderLabels(labels);
someControl->setText(tr("Control name"));
someOtherControl->setText(tr("Other control name"));
// Perhaps you have a model that need to be retranslated too
// (you need to write its `retranslateUi()` function):
someModel->retranslateUi();
...
}
Also, please note that if you are doing something like
void MainWindow::someFunction()
{
...
QTranslator translator;
translator.load(":/lang/English");
qApp->installTranslator(& translator);
...
}
as soon as that function returns the variable translator gets destroyed, so next time QObject::tr() is called the address stored with qApp->installTranslator(& translator) will be invalid. So you must allocate translator on the heap with new (and possibly deleting it with delete when you do not need it anymore). An exception is if you are doing that in main() before calling QCoreApplication::exec() since that function is blocking and will not return until application is closed.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
...
QTranslator translator;
translator.load(":/lang/English");
app.installTranslator(& translator);
...
app.exec(); // This function will return only on application's exit.
// Hence `translator` will outlive the application, there is
// no need to worry about it.
}

Related

qt share variables between slots

Im having trouble figuring out how to use variables across slots. I have read the signals and slots page a few times over and haven't been able to figure out exactly how to use the connect function. I'm trying to have one button to select the directory, and then have it sent over to the other slot for when I hit print, so it can use that directory. Thanks for the help.
void MainWindow::on_pushButton_clicked()
{
QApplication app(int argc, char** argv());
int n = 107;
for (int q = 1; q <= n; q++)
{
QString fileName =(folderName + "/batch_%1.jpg").arg(q);
// QPrinter printer;
//QPrintDialog *dlg = new QPrintDialog(&printer,0);
//if(dlg->exec() == QDialog::Accepted) {
QPrinter printer(QPrinter::HighResolution);
printer.setResolution(300);
printer.setCopyCount(1);
printer.setDoubleSidedPrinting(false);
printer.setDuplex(QPrinter::DuplexNone);
printer.setColorMode(QPrinter::Color);
printer.setPageSize(QPrinter::Letter);
printer.setPaperSize(QPrinter::Letter);
printer.setPaperSource(QPrinter::Auto);
printer.setOrientation(QPrinter::Portrait);
printer.setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Inch);
QString outputFileName = QStringLiteral("/Users/alexdotzler/Desktop/testing/test%1.pdf").arg(q);
printer.setOutputFileName(outputFileName);
printer.setOutputFormat(QPrinter::PdfFormat);
QImage img(fileName);
QPainter painter(&printer);
painter.drawImage(QPoint(0,0),img);
painter.end();
}
//delete dlg;
}
void MainWindow::on_pushButton_2_clicked()
{
QString folderName = QFileDialog::getExistingDirectory(0, ("Select file"), QDir::currentPath());
}
At first, remember about Minimal reproducible example. This is not only about example, but also about minimal. In this case, your printing code is not nessessary.
Following your question, slot is just a function of class. And in your case, you can jist create printing function, that will be called from both pushButton 1 and 2.
Also, don't be await of real magic from sig/slots. It looks like, but not. Connect slot to signal is just virtual call to function B when function A called.
Then let's go a bit deeply into the world of signals and slots.
You can connect (slots or signals) to signals . No other way.
Basically, signals and slots used to pass data between different objects or to inform them about something.
// foo.h
class First {
...
signals:
void newData(QVariant val);
};
// bar.h
class Another {...
public slots:
void getNewData(QVariant val);
};
// main.cpp
Another another1;
First first1;
QObject::connect(first1, &First::newData, another1, &Another::getNewData);
You can also create something can be called "Connections tree", when signal [Another::sig1] connected to [Third::sig2], which connected to signal [Fourth::sig3] and slot [Fifth::slot1], and so on. I'm not sure someone really need it, but be free to play with.
AFAIK, the only reason to use signal/slot connection inside single class, is when you need to pass some data to GUI thread from another thread running in GUI. Please, don't do that in real projects.
Make folderName a member of your MainWindow class so it will be accessible by your pushButton clicked slots. Then you can read/write it as necessary from those slots.

ReferenceError: Can't find variable: QTimer - why is Qtimer not available to my scripts? [duplicate]

Not much to add; what is the equivalent of JavaScript's setTimeout on qtScript?
Here's how you can extend your script language, by providing a self-contained C++ method (no need for bookkeeping of timer ids or so). Just create the following slot called "setTimeout":
void ScriptGlobalObject::setTimeout(QScriptValue fn, int milliseconds)
{
if (fn.isFunction())
{
QTimer *timer = new QTimer(0);
qScriptConnect(timer, SIGNAL(timeout()), QScriptValue(), fn);
connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
timer->setSingleShot(true);
timer->start(milliseconds);
} else
context()->throwError(tr("Passed parameter '%1' is not a function.").arg(fn.toString()));
}
And introduce that slot as function to the global object of your script engine. This can be done in different ways, e.g. just creating a QScriptValue-function via the QScriptEngine instance, and setting an accordingly named property on the engine's existing global object. In my case however, the entire ScriptGlobalObject instance is set as new global object, like this:
mScriptGlobalObject = new ScriptGlobalObject(this);
engine->setGlobalObject(engine->newQObject(mScriptGlobalObject));
Note that if you want to use "context()" as shown in the setTimeout code above, your ScriptGlobalObject should derive also from QScriptable, like this:
class ScriptGlobalObject : public QObject, protected QScriptable
In the script you can now use setTimeout to have a method invoked at a later time (as long as the QScriptEngine instance from which it originates isn't deleted in the meantime):
setTimeout(function() {
// do something in three seconds
}, 3000);
You can expose the QTimer as an instantiable class to the script engine. You can then instantiate it via new QTimer().
This is documented in Making Applications Scriptable.
Below is a complete example. The timer fires a second after the script is evaluated, prints timeout on the console, and exits the application.
// https://github.com/KubaO/stackoverflown/tree/master/questions/script-timer-11236970
#include <QtScript>
template <typename T> void addType(QScriptEngine * engine) {
auto constructor = engine->newFunction([](QScriptContext*, QScriptEngine* engine){
return engine->newQObject(new T());
});
auto value = engine->newQMetaObject(&T::staticMetaObject, constructor);
engine->globalObject().setProperty(T::staticMetaObject.className(), value);
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
QScriptEngine engine;
addType<QTimer>(&engine);
engine.globalObject().setProperty("qApp", engine.newQObject(&app));
auto script =
"var timer = new QTimer(); \n"
"timer.interval = 1000; \n"
"timer.singleShot = true; \n"
"var conn = timer.timeout.connect(function(){ \n"
" print(\"timeout\"); \n"
" qApp.quit(); \n"
"}); \n"
"timer.start();\n";
engine.evaluate(script);
return app.exec();
}
setTimeout and setInterval are not defined in ECMAScript specification because they are not JavaScript features. These functions are part of browser environments. So, QTscript does not have them.
You can use QTimer to achive this functionality.
Here is a quick code how to use it in QTScript.
var timer = new QTimer();
timer.interval = 100; // set the time in milliseconds
timer.singleShot = true; // in-case if setTimout and false in-case of setInterval
timer.timeout.connect(this, function(){console("in setTimout")});
timer.start();
Watch out for any bugs, I just coded it here.
Qt provides an example in the 'context2d' project a way to access to the setInterval/clearInterval setTimeout/clearTimeout functionalities from the script.
In the 'Environment' class of this project, the startTimer function of a QObject is called each time that the script invokes setInterval/setTimeout.
Then the timer identifier is added in a QHash with a reference on the javascript callback.
When the countdown of the timer (c++ part) is over, the timerEvent of the 'Environment' class is called and then the javascript callback called..
Note that all the timers are killed in the Environment::reset() in order to clean the context.
see: https://doc.qt.io/archives/qt-4.7/script-context2d.html
Here's my submission for "kludge of the year"... but this works AND doesn't require recompiling the C++ back end, in a context where that is way too much work to get involved with! I'm not sure how the rest of you may be using the now defunk Qt Script, but my need is for the Qt Installer Framework, and I want to just use it out of the box - not via a custom fork of the whole tool set, for me to then try to maintain (since QtIFW itself is still actively updated), or have to port around, compile on alternate platforms, share with collaborators...
So what's my solution? Well there's no QTimer exposed the to standard script engine, but you can define your own custom (i.e. "dynamic") installer wizard pages, defining the interface via Qt forms (.ui files). With that, you can drop in any QWidget, and then add connections for the signals and slots on the Qt Script side... And so, I just used the first, and perhaps most simplistic, set of widget signals and slots which I saw had some sort of timer mechanism built in that I might capitalize upon.
In a custom form, drop this in somewhere, adding a hidden QPushButton:
<widget class="QPushButton" name="timerKludgeButton">
<property name="visible">
<bool>false</bool>
</property>
</widget>
Then, when you load the component containing the .ui (form), connect the button's "released" signal to a custom "slot" (i.e. Qt Script function):
Component.prototype.componentLoaded = function(){
var page = gui.pageWidgetByObjectName("DynamicMyTimerKludgePage");
page.timerKludgeButton.released.connect(this, this.onTimeOut);
}
Next, define the slot:
Component.prototype.onTimeOut = function(){
console.log("Asynchronous time out!");
}
Finally, where applicable, start your "timer" with the core trick here, using the QPushButton's "animateClick" function:
var page = gui.pageWidgetByObjectName("DynamicMyTimerKludgePage");
page.timerKludgeButton.animateClick(2000);

How to report errors from custom QML components?

I've made a MyComponent QML component with some property:
Q_PROPERTY(PendingList* list MEMBER list)
and a function:
Q_INVOKABLE void send();
It can be created in QML:
MyComponent {
id: myComponent
}
When I call somewhere the myComponent.send() while the list property is not defined, how can I properly report the problem in the stderr? I'd like to see the *.qml file name and line number where send() was called or the line number of the myComponent creation.
Is there a proper way to maybe get the QML stack trace or generate QQmlError or throw an exception that will be handled by the QML engine?
Will have to use some private stuff.
QTDD14 - Categorized logging in QML - Giuseppe D'Angelo
Git repository with the slides and the code: qmllogging
Short Version
Add to CMakeLists.txt:
include_directories(${Qt5Quick_PRIVATE_INCLUDE_DIRS})
Add some QV8Engine stuff to your Q_INVOKABLE function or slot:
#include <QtQml>
#include <private/qv4engine_p.h>
#include <private/qv8engine_p.h>
void Logger::log(const QString &message)
{
const QV4::StackFrame frame = QV8Engine::getV4(m_engine)->currentStackFrame();
QMessageLogger(qPrintable(frame.source),
frame.line,
qPrintable(frame.function)).warning("%s", qPrintable(message));
}
Geting the engine for that function:
QQuickView view;
m_engine = view.engine();
Also set the message pattern to actually show the line number (somewhere in the beginning of main.cpp is fine):
qSetMessagePattern("%{file}:%{line} - %{message}");

Working of signals and slots

I have a basic doubt about how signals and slots actually work. Here's my code segment.
finddialog.cpp :
#include "finddialog.h"
#include <QtGui>
#include <QHBoxLayout>
#include <QVBoxLayout>
FindDialog::FindDialog(QWidget *parent) : QDialog(parent) {
//VAR INITIALIZATIONS
label = new QLabel(tr("Find &what:"));
lineEdit = new QLineEdit;
label->setBuddy(lineEdit);
caseCheckBox = new QCheckBox(tr("Match &case"));
backwardCheckBox = new QCheckBox(tr("Search &backward"));
findButton = new QPushButton("&Find");
findButton->setDefault(true);
findButton->setEnabled(false);
closeButton = new QPushButton(tr("&Quit"));
//SIGNALS & SLOTS
connect (lineEdit, SIGNAL(textChanged(const QString&)),this, SLOT(enableFindButton(const QString&)));
connect (findButton, SIGNAL(clicked()), this, SLOT(findClicked()));
connect (closeButton,SIGNAL(clicked()), this, SLOT(close()));
//Layout
QHBoxLayout *topLeftLayout = new QHBoxLayout;
topLeftLayout->addWidget(label);
topLeftLayout->addWidget(lineEdit);
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(backwardCheckBox);
QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(findButton);
rightLayout->addWidget(closeButton);
rightLayout->addStretch();
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
//Complete window settings
setLayout(mainLayout);
setWindowTitle(tr("Find"));
setFixedHeight(sizeHint().height());
}
//Function Definition
void FindDialog::findClicked() {
QString text = lineEdit->text();
Qt::CaseSensitivity cs = caseCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive;
if(backwardCheckBox->isChecked())
emit findPrevious(text, cs);
else
emit findNext(text,cs);
}
void FindDialog::enableFindButton(const QString &text1) {
findButton->setEnabled(!text1.isEmpty());
}
With this code, how does the compiler know what is passed to enableFindButton(QString &) function. There are no function calls to enableFindButton(). In the connect statement there is a reference to enableFindButton() but isn't that more like a prototype because we are not providing the name of the variables to work with in its arguments?
connect (lineEdit, SIGNAL(textChanged(const QString&)),this, SLOT(enableFindButton(const QString&)));
Here only (const QString &) is the argument and a variable is not given. How does the application know what its argument is without passing it explicitly?
void FindDialog::enableFindButton(const QString &text1) {
findButton->setEnabled(!text1.isEmpty());
}
Here also &text1 is a reference argument. But to what ? I don't understand anything now after typing this all!
:-|
Qt is generating the code that makes it work when you build the project.
SIGNAL, SLOT, etc are preprocessor macros defined in qobjectdefs.h
These are then picked up by moc in QT when you build your project, and all the code required is generated, then compiled.
A decent page that explains this in more detail can be found here
C++ source code is processed by Qt meta object compiler (moc). It generates 'signatures' strings for QObject slots. Signatures contain method name and arguments (types, arguments' names do not matter in signatures). Whenever signal is emitted a signature match is performed directly (in case of direct connections) or in the event loop (for queued connections), and corresponding method is called. Moc compiler generates all necessary code that will match signatures and call the methods. If interested, look inside of one of those *.cxx files generated.

Displaying a second form in Qt

I know this question has been asked a few time, (http://stackoverflow.com/questions/4436511/display-a-form-in-qt)
I am trying to open an existing form in my Qt C++ project.
it needs to be a subform instead of QDialog box.
both forms have a .ui, .h and .cpp file with them.
in my mwindowtest.cpp I have
//this is used to handle the button click to open the new form
connect(btnConnect, SIGNAL(click()), this, SLOT(openNewWindow()));
for function is:
void mWindowTest::openNewWindow(){
mForm = new dialog (this);
mForm->show();
}
in my mwindowtest.cpp I have:
#include <dialog.h> //second form
class dialog;
I'm now getting the error mForm was not declared in this scope.
but I am not sure what to declare mForm as in my Header file.
any tips would be greatly appreciated.
Thanks
In your mywindowtest.hpp you have to declare the pointer first:
// mytestwindow.hpp
// ...
private:
dialog* mForm;
// ...
// mytestwindow.cpp
void mWindowTest::openNewWindow()
{
mForm = new dialog (this);
form->show();
}
Or you declare it directly in your cpp, but then it is no member and only known in openNewWindow().
void mWindowTest::openNewWindow()
{
dialog* form = new dialog (this);
form->show();
}
When you work with Qt you should know the basics of C++. This example is one of those basics. Use Google and read some stuff about pointer tutorials and dynamic memory allocation.
Hope that helps. :)
In your example you will have memory leak since each time button btnConnect is clicked you will reallocate memory for your form, without deleting the previous first.
Concerning your problem, we need to know how is declare dialog in dialog.h to be able to really help you.
In your mywindowtest.cpp you have included the file and redefined the class. Try to put
class dialog
in your hpp file and
#include <dialog.h>
in your cpp file.
Hope that helps
Edit:
In your slot:
delete mForm;
mForm = new dialog();
dialog->show();
It's the minimum to avoid memory leaks; And don't forget to delete mForm in MyWindowTest destructor too if not null;

Resources