How to translate standard buttons in Qt? - qt

I have a problem with translate standard buttons in QMessageBox. If I check the language, the buttons are translated very well, but if I don't check the language, the buttons are not translated. How can I get translated buttons without checking the language every time when I need to show QMessageBox?
#include "application.h"
#include "main_window.h"
#include <QTranslator>
#include <qlibraryinfo.h>
int main( int argc, char *argv[] )
{
Application application( argc, argv );
QString language = app()->settings().value("language").toString();
if (language == "Russian") // Here I check the language
{
QTranslator translator_ru;
if (translator_ru.load(QString("translations/qtbase_ru.qm")))
application.installTranslator(&translator_ru);
if (QMessageBox::question(0, "Delete?", "First test") == QMessageBox::Yes) {} // In this message, the standard buttons are in Russian
}
if (QMessageBox::question(0, "Delete?", "Second test") == QMessageBox::Yes) {} // In this message, the standard buttons are in English
MainWindow window;
window.show();
return application.exec();
}

I've found the solution:
#include "application.h"
#include "main_window.h"
#include <QTranslator>
#include <qlibraryinfo.h>
int main( int argc, char *argv[] )
{
Application application( argc, argv );
QString language = app()->settings().value("language").toString();
QString base_translate_file_name;
if (language == "Russian") base_translate_file_name = "qtbase_ru.qm";
else base_translate_file_name = "qtbase_en.qm";
QTranslator qtBaseTranslator;
if (qtBaseTranslator.load(QString("translations/" + base_translate_file_name), application.applicationDirPath()))
{
application.installTranslator(&qtBaseTranslator);
}
MainWindow window;
window.show();
return application.exec();
}

Related

Qt widget does not receive keyPressEvent

My child widget does not get keyPressEvents, while if I put the same widget as top level window, it does. I try to set it get focus, but it has no effect on this. Code is below, showing what I try to get to work.
#include <QApplication>
#include <QKeyEvent>
#include <QLCDNumber>
#include <QLabel>
#include <QVBoxLayout>
class DigitSummer: public QLCDNumber {
Q_OBJECT
public:
DigitSummer(QWidget *parent = nullptr) : QLCDNumber(parent) {
}
protected:
void keyPressEvent(QKeyEvent *event) override {
display(intValue() + event->text().toInt());
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
#if 1 // this version does not work, number does not increase
QWidget widget;
widget.setLayout(new QVBoxLayout());
widget.layout()->addWidget(new QLabel("Press digits!"));
DigitSummer summer; // in stack: must be after widget to avoid child delete
widget.layout()->addWidget(&summer);
widget.setFocusProxy(&summer); // I notice no effect!
widget.show();
#else // this version works, number grows with keypresseas
DigitSummer summer;
summer.show();
#endif
return a.exec();
}
#include "main.moc"
And for completenes, .pro file for the same:
QT += core gui widgets
TARGET = QtMCVE
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11
QMAKE_CXXFLAGS += -Wall -Wextra
SOURCES += main.cpp
How to fix the widget to receive key events?
This related question suggests installing event filter, but I don't want to do that, there must be a self-contained way to fix the widget itself.
I think you need to set the focus policy for the widget before it will accept keyboard input. In your ctor try...
setFocusPolicy(Qt::StrongFocus);
Having said that, I'm really not sure why the behaviour would differ for top-level and non-top-level widgets.
Working version of the question code:
#include <QApplication>
#include <QKeyEvent>
#include <QLCDNumber>
#include <QLabel>
#include <QVBoxLayout>
class DigitSummer: public QLCDNumber {
Q_OBJECT
public:
DigitSummer(QWidget *parent = nullptr) : QLCDNumber(parent) {
setFocusPolicy(Qt::StrongFocus);
}
protected:
void keyPressEvent(QKeyEvent *event) override {
display(intValue() + event->text().toInt());
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.setLayout(new QVBoxLayout());
widget.layout()->addWidget(new QLabel("Press digits!"));
widget.layout()->addWidget(new DigitSummer);
widget.show();
return a.exec();
}
#include "main.moc"

Qt Console Application with optional GUI

I have written a console application for my server. It works very well, I can start it over the terminal and all is okay.
For desktop environments it would be very nice, to set a flag in the settings.ini file of the program to open a MainWindow to show some information of the running console application. The console in the background can be left open. All I need is a window and some SINGAL/SLOTS between the main application running in console and the MainWindow.
How to realize this? I figured out, I have to handle with QApplication and QCoreApplication right?
Simply put this line to your pro file:
CONFIG += console
In Qt5.x from Win7 to Win10 you could do something like this:
//includes
#include <QApplication>
#include <QMainWindow>
#include <QString>
#include <QObject>
#include <QDir>
#include <QSettings>
#include <windows.h>
#include <stdio.h>
#include <iostream>
//
// Add to project file:
// CONFIG += console
//
The server:
//Here we have the Server class able to send signals
//Server will be used in main.cpp for console
//and/or in MainWindow to handle the signals
class Server : public QObject
{
Q_OBJECT
public:
Server( QObject *parent = 0 ) : QObject( parent )
{
//do server stuff
//this->setName( "Test" );
//std::cout << this->getName( ) << std::endl;
//std::cout << "Enter URL: << std::endl;
//std::string url;
//std::cin >> url;
//_url = QString::fromStdString( url );
//emit finished();
}
signals:
void finished( );
private:
QString _url;
};
The MainWindow:
//Here is the MainWindow using Server
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow( QWidget *parent = 0 ) : QMainWindow()
{
server = new Server( this ); //use server in hybrid mode (console and gui)
connect( server, SIGNAL(finished()), this, SLOT(close()) ); //establish connection
}
private:
Server *server;
};
The main:
int main( int argc, char *argv[] )
{
QString iniPath = QFileInfo( QDir::fromNativeSeparators(argv[0]) ).absolutePath(); //get the current dir
QSettings settings( iniPath+"/settings.ini", QSettings::IniFormat ); //open ini
bool gui = settings.value( "gui", false ).toBool(); //read ini
if( gui ) //decide
{
#if defined( Q_OS_WIN )
// hide console window, but not in your case
// ::ShowWindow( ::GetConsoleWindow(), SW_HIDE );
#endif
//std::cout will print to the console in the bg like you wished
QApplication a( argc, argv );
MainWindow *w = new MainWindow;
w->show();
int e = a.exec();
delete w; //needed to execute deconstructor
exit( e ); //needed to exit the hidden console
return e;
}
else
{
QCoreApplication a( argc, argv );
Server *instance = new Server; //use server in console only
exit( 0 );
return a.exec();
}
}
I tried it also without the "CONFIG += console", but then you need to redirect the streams and create the console on your own:
#ifdef _WIN32
if (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()){
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
freopen("CONIN$", "r", stdin);
}
#endif
BUT this only works if you start it through a debugger, otherwise all inputs are directed towards the system too. Means, if you type a name via std::cin the system tries to execute the name as a command. (very strange)
Two other warnings to this attempt would be, that you can't use ::FreeConsole() it won't close it and if you start it through a console the app won't close.
Last there is a Qt help section in QApplication to this topic. I tried the example there with an application and it doesn't work for the GUI, it stucked somewhere in an endless loop and the GUI won't be rendered or it simply crashes:
QCoreApplication* createApplication(int &argc, char *argv[])
{
for (int i = 1; i < argc; ++i)
if (!qstrcmp(argv[i], "-no-gui"))
return new QCoreApplication(argc, argv);
return new QApplication(argc, argv);
}
int main(int argc, char* argv[])
{
QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
if (qobject_cast<QApplication *>(app.data())) {
// start GUI version...
} else {
// start non-GUI version...
}
return app->exec();
}
So if you are using Windows and Qt simply use the console option, hide the console if you need the GUI and close it via exit.

Capture QML drawing buffer, without displaying

I need to grab each QML (QtQuick 2) drawing frame and sent it over the network.
At the moment I have used method listed below, but this method has two big disadvantage
1) Due to Qt5 documentation grabWindow() function has performance issues
2) It can't work with hidden QML window
Is it possible to get OpenGL render buffer right after QQuickWindow::afterRendering ?
Using FBOs ? Shared opengl context ?
class Grab: public QObject
{
public:
Grab( QQuickWindow * wnd ) : wnd_(wnd) {}
public slots:
void Grabme()
{
QImage image = wnd_->grabWindow();
}
private:
QQuickWindow *wnd_;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/grab1/main.qml"));
viewer.showExpanded();
Grab grab( &viewer );
QObject::connect( &viewer, &QtQuick2ApplicationViewer::frameSwapped,
&grab, &Grab::Grabme, Qt::DirectConnection );
return app.exec();
}
Example bellow can grab any qml content to FBO and then sent it as Image via signal.
Only one problem of this approach is visibility, grab window must be visible for successful grabbing. If anybody knows how to prevent this you can help me and provide more advanced approach.
// main.cpp
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
GrabWindow grab;
grab.setResizeMode( QQuickView::SizeViewToRootObject );
grab.setSource( QUrl::fromLocalFile("qml/main.qml") );
grab.setFlags( Qt::Popup );
grab.show();
return app.exec();
}
// grabwindow.hpp
#pragma once
#include <QOpenGLFramebufferObject>
#include <QScopedPointer>
#include <QQuickView>
#include <QImage>
class GrabWindow: public QQuickView
{
Q_OBJECT
signals:
void changeImage( const QImage &image );
public:
GrabWindow( QWindow * parent = 0 );
private slots:
void afterRendering();
void beforeRendering();
private:
QScopedPointer<QOpenGLFramebufferObject> fbo_;
};
// grabwindow.cpp
#include "grabwindow.hpp"
#include <limits>
GrabWindow::GrabWindow( QWindow * parent ) :
QQuickView( parent )
{
setClearBeforeRendering( false );
setPosition( std::numeric_limits<unsigned short>::max(), std::numeric_limits<unsigned short>::max() );
connect( this, SIGNAL( afterRendering() ), SLOT( afterRendering() ), Qt::DirectConnection );
connect( this, SIGNAL( beforeRendering() ), SLOT( beforeRendering() ), Qt::DirectConnection );
}
void GrabWindow::afterRendering()
{
if( !fbo_.isNull() )
{
emit changeImage( fbo_->toImage() );
}
}
void GrabWindow::beforeRendering()
{
if (!fbo_)
{
fbo_.reset(new QOpenGLFramebufferObject( size(), QOpenGLFramebufferObject::NoAttachment) );
setRenderTarget(fbo_.data());
}
}
I managed to find a trick to make grabWindow() work when the Window is "not visible". The trick is to set the window's visibility: Window.Minimized and the flags: Qt.Tool. The window is not displayed to the user, but to the Qt's internals it appears to be visible and the grabWindow() method call works as expected. Remember to call that method only once the scene has been initialised.
The only problem with this solution (that I have come across) is that if the window's color property is set to transparent, the captured content has black background.
With later versions of Qt 5.X you can also use the software render backend.
The following renders any scene in the background without any visible window or OpenGL tricks:
// main.cpp
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickItem>
#include <QQuickWindow>
#include <QQuickRenderControl>
int main(int argc, char *argv[])
{
const char *source = "qrc:/main.qml";
if (argc > 1) source = argv[1];
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
QGuiApplication app{argc, argv};
QQuickRenderControl renderControl;
QQuickWindow window{&renderControl};
QQmlEngine engine;
QQmlComponent component{
&engine,
QUrl{QString::fromUtf8(source)}
};
QQuickItem *rootItem = qobject_cast<QQuickItem *>(component.create());
window.contentItem()->setSize(rootItem->size());
rootItem->setParentItem(window.contentItem());
window.resize(rootItem->size().width(), rootItem->size().height());
QImage image = renderControl.grab();
image.save("output.png");
return 0;
}

Display and get the result of a QMessageBox from outside of a QObject

I am trying to display and get the result a message box from outside of a QObject class. I seem to be able to generate the dialog like this:
#include <iostream>
#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>
class DialogHandler : public QObject
{
Q_OBJECT
signals:
void MySignal();
public:
DialogHandler()
{
connect( this, SIGNAL( MySignal() ), this, SLOT(MySlot()) );
}
void EmitSignal()
{
emit MySignal();
}
public slots:
void MySlot()
{
QMessageBox* dialog = new QMessageBox;
dialog->setText("Test Text");
dialog->exec();
int result = dialog->result();
if(result)
{
std::cout << "ok" << std::endl;
}
else
{
std::cout << "invalid" << std::endl;
}
}
};
#include "main.moc" // For CMake's automoc
void MyFunction(DialogHandler* dialogHandler)
{
dialogHandler->EmitSignal();
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
DialogHandler* dialogHandler = new DialogHandler;
MyFunction(dialogHandler);
return app.exec();
}
To get the result back in MyFunction, it seems to work to do simply pass an object to fill with the result like this:
#include <iostream>
#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>
class DialogHandler : public QObject
{
Q_OBJECT
signals:
void MySignal(int* returnValue);
public:
DialogHandler()
{
connect( this, SIGNAL( MySignal(int*) ), this, SLOT(MySlot(int*)), Qt::BlockingQueuedConnection );
}
void EmitSignal(int* returnValue)
{
emit MySignal(returnValue);
}
public slots:
void MySlot(int* returnValue)
{
std::cout << "input: " << *returnValue << std::endl;
QMessageBox* dialog = new QMessageBox;
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
dialog->setText("Test Text");
dialog->exec();
int result = dialog->result();
if(result == QMessageBox::Yes)
{
*returnValue = 1;
}
else
{
*returnValue = 0;
}
}
};
#include "main.moc" // For CMake's automoc
void MyFunction(DialogHandler* dialogHandler)
{
int returnValue = -1;
dialogHandler->EmitSignal(&returnValue);
std::cout << "returnValue: " << returnValue << std::endl;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
DialogHandler* dialogHandler = new DialogHandler;
QtConcurrent::run(MyFunction, dialogHandler);
std::cout << "End" << std::endl;
return app.exec();
}
Does that seem reasonable? Is there a better way to do it?
This isn't possible quite like you have it, but with a bit of work it could be done. One option, of course, would be to convert your class to a QObject, at which point you could send signals. It doesn't help for the delay during exec, however. If that is necessary, you could have a messaging class that lives in the main UI thread, but can be called from other threads. The function called from other threads would need to lock, make a semaphore, and send an event to itself with the semaphore and message to be displayed. Then, in customEvent (which would be in the UI thread), you would create the message box, exec it, and trigger the semaphore after the message box is cleared.
Of course, things get a bit more complicated if you need to send information back the other way as well. Then you'll need a complete subsystem for your program, instead of just one basic class like I describe here.

Save QList<int> to QSettings

I want to save a QList<int> to my QSettings without looping through it.
I know that I could use writeArray() and a loop to save all items or to write the QList to a QByteArray and save this but then it is not human readable in my INI file..
Currently I am using the following to transform my QList<int> to QList<QVariant>:
QList<QVariant> variantList;
//Temp is the QList<int>
for (int i = 0; i < temp.size(); i++)
variantList.append(temp.at(i));
And to save this QList<Variant> to my Settings I use the following code:
QVariant list;
list.setValue(variantList);
//saveSession is my QSettings object
saveSession.setValue("MyList", list);
The QList is correctly saved to my INI file as I can see (comma seperated list of my ints)
But the function crashes on exit.
I already tried to use a pointer to my QSettings object instead but then it crashes on deleting the pointer ..
QSettings::setValue() needs QVariant as a second parameter. To pass QList as QVariant, you have to declare it as a Qt meta type. Here's the code snippet that demonstrates how to register a type as meta type:
#include <QCoreApplication>
#include <QDebug>
#include <QMetaType>
#include <QSettings>
#include <QVariant>
Q_DECLARE_METATYPE(QList<int>)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>");
QList<int> myList;
myList.append(1);
myList.append(2);
myList.append(3);
QSettings settings("Moose Soft", "Facturo-Pro");
settings.setValue("foo", QVariant::fromValue(myList));
QList<int> myList2 = settings.value("foo").value<QList<int> >();
qDebug() << myList2;
return 0;
}
You might have to register QList as a meta-type of its own for it to work. This is a good starting point to read up on meta-types in Qt: http://qt.nokia.com/doc/4.6/qmetatype.html#details .
I was also struggling with this ... and I believe I now have a decent solution.
I hope this saves someone the trouble, it caused me.
#include <QCoreApplication>
#include <QSettings>
#include <QList>
#include <QDataStream>
#include <QVariant>
#include <QVariantList>
#include <QDebug>
#include <deque>
template <class T> static QVariant toVariant(const QList<T> &list)
{
QVariantList variantList;
variantList.reserve(list.size());
for (const auto& v : list)
{
variantList.append(v);
}
return variantList;
}
template <class T> static QList<T> toList(const QVariant &qv)
{
QList <T> dataList;
foreach(QVariant v, qv.value<QVariantList>()) {
dataList << v.value<T>();
}
return dataList;
}
void Gen()
{
QList<QString> data {"hello", "world","how", "are", "you"};
QList<int> ages {10,20,30,40};
QSettings setup("stuff.ini", QSettings::IniFormat);
setup.beginWriteArray("rules");
for (int groups=0;groups<3;groups++)
{
setup.setArrayIndex(groups);
setup.setValue("rule",QString("Rule-%1").arg(groups));
setup.setValue("data", toVariant (data));
setup.setValue("ages", toVariant (ages));
}
setup.endArray();
setup.sync();
}
void Read()
{
QSettings setupR("stuff.ini", QSettings::IniFormat);
int rule_count = setupR.beginReadArray("rules");
for (int groups=0;groups<rule_count;groups++)
{
setupR.setArrayIndex(groups);
QString nameRead = setupR.value("rule").toString();
QList<QString> dataRead = toList<QString>(setupR.value("data"));
qDebug() << "Rule " << groups;
qDebug() << "Rule Read" << nameRead;
qDebug() << "Data Read2" << dataRead;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Write
Gen();
// Read
Read();
exit(0);
//return a.exec();
}
You should end up with an INI file which looks like this ...
[rules]
1\ages=10, 20, 30, 40
1\data=hello, world, how, are, you
1\rule=Rule-0
2\ages=10, 20, 30, 40
2\data=hello, world, how, are, you
2\rule=Rule-1
3\ages=10, 20, 30, 40
3\data=hello, world, how, are, you
3\rule=Rule-2
size=3
The nice thing here is you can edit this outside of QT (Just be careful) ...

Resources