I have a string that I need at various points in my program. I know that Qt can manage image resources, but I need similar functionality for a couple of strings. Currently I'm using a string resource class, which is a sloppy solution.
class StringRes {
public:
static const QString& appName() { return _appName; }
static const QString& appVersion() { return _appVersion; }
private:
static const QString _appName;
static const QString _appVersion;
};
Besides, this solution causes a segfault at a certain point in my code.
_fileStream << QString("This is ")
+ StringRes::appName()
+ " "
+ StringRes::appVersion()
+ " reporting for duty.\n";
How do Qt programmers (or C++ programmers in general) manage their string resources?
For storing just application's name and version and organization's name and domain you can use QCoreApplications's properties applicationName, applicationVersion, organizationDomain and organizationName.
I usually set them in main() function:
#include <QApplication>
#include "MainWindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// These functions are member of QCoreApplication, QApplication's
// parent class:
app.setApplicationName("My Application");
app.setApplicationVersion("3.5.2");
app.setOrganizationName("My Company, or just My Name");
app.setOrganizationDomain("http://example.com/");
MainWindow window;
window.show();
return app.exec();
}
And I can use them to show a nice about message:
#include "MainWindow.h"
#include <QCoreApplication>
...
// Slot called when ? -> About menu is clicked.
void MainWindow::on_aboutAction_triggered()
{
QString message = tr("<strong>%1</strong> %2<br />"
"Developed by %3")
.arg(QCoreApplication::applicationName())
.arg(QCoreApplication::applicationVersion())
.arg(QString("%2")
.arg(QCoreApplication::organizationDomain())
.arg(QCoreApplication::organizationName()))
;
QMessageBox::about(this, tr("About"), message);
}
Related
Question: Is there a way to check at compile-time if qRegisterMetaType<T>() was called for a custom type T?
The custom type T needs to be registered in Qt meta-type system in order to be used in e.g. queued connections.
If such a connection is made, and a signal triggered, the runtime warning will be shown:
QObject::connect: Cannot queue arguments of type 'T'
(Make sure 'T' is registered using qRegisterMetaType().)
This is hard to track, so I would prefer to check this at compile-time. Is that in any way possible?
(I understand that if it was possible, it would probably already be a part of Qt Framework itself, but maybe...?)
Note: I know I can check if a type was declared as metatype (Check if type is declared as a meta type system (for SFINAE)), but this doesn't solve my problem.
The code example would be:
#include <QCoreApplication>
#include <QDebug>
#include <QMetaMethod>
#include <QObject>
#include <QThread>
#include <QTimer>
struct Payload {
Payload() = default;
};
// Type is declared as metatype
Q_DECLARE_METATYPE(Payload)
class ObjectOne : public QObject {
Q_OBJECT
public:
using QObject::QObject;
void emitPayloadChanged() { Payload p; emit payloadChanged(p); }
signals:
void payloadChanged(const Payload& p);
};
class ObjectTwo : public QObject {
Q_OBJECT
public:
using QObject::QObject;
void handlePayload(const Payload& p) { qDebug() << "handling payload"; }
};
int main(int argc, char* argv[]) {
QCoreApplication app(argc, argv);
// Uncommenting the following line fixes the runtime warning
// qRegisterMetaType<Payload>();
QThread t1, t2;
ObjectOne o1;
o1.moveToThread(&t1);
ObjectTwo o2;
o2.moveToThread(&t2);
t1.start();
t2.start();
QObject::connect(&o1, &ObjectOne::payloadChanged, &o2, &ObjectTwo::handlePayload);
QTimer::singleShot(0, &o1, [&] { QMetaObject::invokeMethod(&o1, &ObjectOne::emitPayloadChanged); });
return app.exec();
}
#include "main.moc"
I have studied the qt documentation of qRegisterMetaType() where it says that this function must be called before the corresponding type can be used in signal/slot mechanism. However I couldn't find any code example where this has to be done by hand.
This page states, that the registration is done automatically by the moc if it can determine that the type may be registered as meta-type. It looks like this is right, because I tested QSignalSpy, QObject::connect() (direct and queued connection) and QVariant - with just using Q_DECLARE_METATYPE(type) and none of them needed a explicit call to qRegisterMetaType to work.
So my question is: when do I have to call qRegisterMetaType(), because otherwise the code won't work?
The Qt docs say that Q_DECLARE_METATYPE is necessary in case one has a connect being a queued connection.
Adding a Q_DECLARE_METATYPE() makes the type known to all template
based functions, including QVariant. Note that if you intend to use
the type in queued signal and slot connections or in QObject's
property system, you also have to call qRegisterMetaType() since the
names are resolved at runtime.
For this I build a small testing app, that exemplifies the behavior.
Just try to remove the Q_DECLARE_METATYPE(Message) and watch the warnings and output change. In case of the normal connect the macro seems to be unnecessary.
main.cpp
#include <QApplication>
#include <QThread>
#include "MyHeaderView.h"
Q_DECLARE_METATYPE(Message);
int main(int argc, char **args)
{
QApplication app(argc, args);
{
TestObject sender;
TestObject receiver;
QObject::connect(&sender, &TestObject::sendMessage, &receiver, &TestObject::onMessage);
sender.emitMessage(1, 2);
}
// This requires Q_DECLARE_METATYPE(Message);
QThread workerThread;
TestObject sender2;
TestObject receiver2;
receiver2.moveToThread(&workerThread);
workerThread.start();
QObject::connect(&sender2, &TestObject::sendMessage, &receiver2, &TestObject::onMessage, Qt::ConnectionType::QueuedConnection);
sender2.emitMessage(3, 4);
app.exec();
}
TestObject.h
#pragma once
#include <QObject>
#include <QDebug>
struct Message
{
int x;
int y;
};
class TestObject : public QObject
{
Q_OBJECT
public:
void emitMessage(int x, int y) { emit sendMessage(Message{ x,y }); }
signals:
void sendMessage(const Message&);
public slots:
void onMessage(const Message& m) { qDebug() << m.x << m.y; }
};
I am creating a Qt application and I added dynamic translation (I followed the example at http://www.qtcentre.org/wiki/index.php?title=Dynamic_translation_in_Qt4_applications) with a QCombobox which lists different languages. It works well but the problem is that I don't see how to translate dynamically the text in the dialog windows (for example YES and NO buttons).
In the main.cpp, before executing the app, I have :
QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
a.installTranslator(&qtTranslator);
which translate the dialog Windows in the user system language but I would like to do it dynamically like the rest of my app.
Here are the code of the example :
application.h :
#ifndef APPLICATION_H
#include <QApplication>
#include <QHash>
#include <QStringList>
class QDir;
class QTranslator;
typedef QHash<QString, QTranslator*> Translators;
class Application : public QApplication
{
Q_OBJECT
public:
explicit Application(int& argc, char* argv[]);
~Application();
static void loadTranslations(const QString& dir);
static void loadTranslations(const QDir& dir);
static const QStringList availableLanguages();
public slots:
static void setLanguage(const QString& locale);
private:
static QTranslator* current;
static Translators translators;
//static QTranslator* qtTranslator;//test to translate dialog windows
};
#endif // APPLICATION_H
application.cpp :
#include <QDir>
#include <QFileInfo>
#include <QTranslator>
#include <QLibraryInfo>
#include "application.h"
QTranslator* Application::current = 0;
//QTranslator* Application::qtTranslator = 0;//test to translate dialog windows
Translators Application::translators;
Application::Application(int& argc, char* argv[])
: QApplication(argc, argv)
{
}
Application::~Application()
{
}
void Application::loadTranslations(const QString& dir)
{
loadTranslations(QDir(dir));
QString locale = QLocale::system().name().section('_', 0, 0);
QString language=locale+ "_" + locale;
if(!QFile::exists(":Localization/Localization/"+language+".qm"))//if system language is not available, load english version
setLanguage("en_en");
else
setLanguage(language);
}
void Application::loadTranslations(const QDir& dir)
{
// <language>_<country>.qm
QString filter = "*_*.qm";
QDir::Filters filters = QDir::Files | QDir::Readable;
QDir::SortFlags sort = QDir::Name;
QFileInfoList entries = dir.entryInfoList(QStringList() << filter, filters, sort);
foreach (QFileInfo file, entries)
{
// pick country and language out of the file name
QStringList parts = file.baseName().split("_");
QString language = parts.at(parts.count() - 2);
QString country = parts.at(parts.count() - 1);
// construct and load translator
QTranslator* translator = new QTranslator(instance());
if (translator->load(file.absoluteFilePath()))
{
QString locale = language + "_" + country;
translators.insert(locale, translator);
}
}
}
const QStringList Application::availableLanguages()
{
// the content won't get copied thanks to implicit sharing and constness
return QStringList(translators.keys());
}
void Application::setLanguage(const QString& locale)
{
//test to translate dialog windows
/*
QTranslator qtTranslator;
QString qTLocale=locale.mid(0,2);
qtTranslator->load("qt_"+ qTLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
installTranslator(qtTranslator);
//*/
// remove previous
if (current)
{
removeTranslator(current);
}
// install new
current = translators.value(locale, 0);
if (current)
{
installTranslator(current);
}
}
I added the lines commented with "//test to translate dialog Windows" to try the dynamic translation of the dialog Windows but it doesn't work (no error at compilation but the application isn't launched with error message "the program stopped suddenly", I am on Qt Creator). Thanks!
So I finally got this to work after having the same problems. There are two things which were wrong in my case:
Name of the qt translation file:
QTranslator qtTranslator;
qtTranslator.load("qt_de"); // worked in older qt versions
qtTranslator.load("qtbase_de"); // works for qt5.2
a.installTranslator(&qtTranslator);
Have the correct parent for the QMessageBox. This is obvious after you think about it but pretty easy to miss.
QMessageBox::information(someChildOfMainWindow, ...);
For the latter, if you happen to be in a class which is a QObject but not a QWidget you can also use the following code to access your MainWindow from anywhere:
QMainWindow* mw = 0;
foreach(QWidget* widget, QApplication::topLevelWidgets()) {
if(widget->objectName() == "<your-main-window-class-name-here>") {
mw = qobject_cast<QMainWindow>(widget);
}
}
Ok Sébastian Lange, so finally I created the box and didn't use the static ones (
QMessageBox::question(..) for example)
QMessageBox quitMessageBox;
quitMessageBox.setWindowTitle(tr("Quit"));
quitMessageBox.setWindowIcon(QIcon("myIcon.jpg"));
quitMessageBox.setIcon(QMessageBox::Question);
quitMessageBox.setText(tr("Quit the application?"));
quitMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
quitMessageBox.setDefaultButton(QMessageBox::No);
quitMessageBox.button(QMessageBox::Yes)->setText(tr("Yes"));
quitMessageBox.button(QMessageBox::No)->setText(tr("No"));
And then
quitMessageBox.exec();
Like that it's ok. Thanks again!
When providing buttons for the dialog use
tr("Yes")
as for default dialogs, the created .ts-language file (to be edited via QtLinguist) should have default translations included.
The tr() marks the given argument to be translated. This concludes to if you do not know what will be written on a given label, you cannot translate it...
I am working on building a GUI around a console application. I would like to be able to click a button to run the console app and show the console output inside of the GUI itself. How might I accomplish this? I am working in Linux.
You could also try QProcess. It provides a Qt interface to launching external processes, reading their I/O and waiting, or not, on their completion.
For your purpose, it sounds like you want the process to run asynchronously, so code might look like :
myprocessstarter.h :
#include <QObject>
#include <QProcess>
#include <QDebug>
class MyProcessStarter : public QObject
{
Q_OBJECT
public:
MyProcessStarter() : QObject() {};
void StartProcess();
private slots:
void readStandardOutput();
private:
QProcess *myProcess;
};
main.cpp:
#include "myprocessstarter.h"
void MyProcessStarter::StartProcess()
{
QString program = "dir";
QStringList arguments;
// Add any arguments you want to be passed
myProcess = new QProcess(this);
connect(myProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(readStandardOutput()));
myProcess->start(program, arguments);
}
void MyProcessStarter::readStandardOutput()
{
QByteArray processOutput;
processOutput = myProcess->readAllStandardOutput();
qDebug() << "Output was " << QString(processOutput);
}
void main(int argc, char** argv)
{
MyProcessStarter s;
s.StartProcess();
}
I wanted to do something similar in one of my applications. I redirected all output from the standard stream (cout) to my console window. To periodically read out the stream contents I use a timer loop. Works fine for me.
StdRedirector.cpp
#include "StdRedirector.h"
QMutex coutMutex;
void outcallback(const char* ptr, std::streamsize count, void* bufferString)
{
string *b = (string *) bufferString;
string t;
for (int i=0; i < count; i++)
{
if (ptr[i] == '\n')
{
t = t + "\n";
} else {
t = t + ptr[i];
}
}
coutMutex.lock();
*b = *b + t;
coutMutex.unlock();
}
void ConsoleWindow::updateTimer(void)
{
coutMutex.lock();
if (bufferString.size() > 0)
{
consoleBox->insertPlainText(QString(bufferString.c_str()));
bufferString.clear();
QScrollBar *sb = consoleBox->verticalScrollBar();
sb->setValue(sb->maximum());
}
coutMutex.unlock();
}
ConsoleWindow::ConsoleWindow(QWidget *parent) : QWidget(parent)
{
consoleBox = new QTextEdit(this);
consoleBox->setReadOnly(true);
stdRedirector = new StdRedirector<>(std::cout, outcallback, &bufferString);
QVBoxLayout *vb = new QVBoxLayout();
vb->addWidget(consoleBox);
vb->setMargin(0);
vb->setSpacing(0);
setLayout(vb);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateTimer()));
timer->start(100);
}
ConsoleWindow::~ConsoleWindow()
{
delete stdRedirector;
}
StdRedirector.h
#ifndef STD_REDIRECTOR
#define STD_REDIRECTOR
#include <QWidget>
#include <QTextEdit>
#include <QString>
#include <QVBoxLayout>
#include <QTimer.h>
#include <QMutex>
#include <QScrollBar>
#include <iostream>
#include <string>
using namespace std;
template<class Elem = char, class Tr = std::char_traits<Elem>>
class StdRedirector : public std::basic_streambuf<Elem, Tr>
{
typedef void (*pfncb) ( const Elem*, std::streamsize _Count, void* pUsrData );
public:
StdRedirector(std::ostream& a_Stream, pfncb a_Cb, void* a_pUsrData) :
m_Stream(a_Stream),
m_pCbFunc(a_Cb),
m_pUserData(a_pUsrData)
{
m_pBuf = m_Stream.rdbuf(this);
}
~StdRedirector()
{
m_Stream.rdbuf(m_pBuf);
}
std::streamsize xsputn(const Elem* _Ptr, std::streamsize _Count)
{
m_pCbFunc(_Ptr, _Count, m_pUserData);
return _Count;
}
typename Tr::int_type overflow(typename Tr::int_type v)
{
Elem ch = Tr::to_char_type(v);
m_pCbFunc(&ch, 1, m_pUserData);
return Tr::not_eof(v);
}
protected:
std::basic_ostream<Elem, Tr>& m_Stream;
std::streambuf* m_pBuf;
pfncb m_pCbFunc;
void* m_pUserData;
};
class ConsoleWindow : public QWidget
{
Q_OBJECT
public:
ConsoleWindow(QWidget *parent = 0);
~ConsoleWindow();
public slots:
void updateTimer(void);
public:
QTextEdit *consoleBox;
StdRedirector<> *stdRedirector;
string bufferString;
};
#endif
The StdRedirector class is based on code from this forum post: http://www.qtforum.org/article/24554/displaying-std-cout-in-a-text-box.html
Take a look at the popen() function, it might do what you need.
Then you could pass the FILE * to a QTextStream and work in Qt style with it.
I suggest, rather than showing stdout in GUI, having own console output, which essentially means all messages you want to show to users you are sending to your own output.
This way you can have debug messages and such still available from console, wtih potential errors with connections and whatever that can happen and have fully controlled console output in GUI application. Of course this output can also be outputted to stdout so it is visible in console, but it also allows you to append a prefixs like WARNING LOG NOTICE NO_THIS_WENT_WRONG or whatever you want to show to users as your console entry.
The function CheckSite() is called with an url like http://example.com, it initializes a QNetworkAccessManager object and connect() slots and signals.
The manger->get() call seems work (it generates http traffic) but does not call the slot replyFinished() at the request end.
What's wrong with this code?
#include <QtCore>
#include <QtNetwork>
class ClientHandler : public QObject
{
Q_OBJECT
QNetworkAccessManager *manager;
private slots:
void replyFinished(QNetworkReply *);
public:
void CheckSite(QString url);
};
void ClientHandler::replyFinished(QNetworkReply *reply) { qDebug() << "DONE"; }
void ClientHandler::CheckSite(QString url) {
QUrl qrl(url);
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(qrl));
}
Nothing. I wrapped it so it was fully functional and it works fine:
// placed in client.cpp
#include <QtDebug>
#include <QCoreApplication>
/* YOUR CODE */
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
ClientHandler handler;
handler.CheckSite("www.google.com");
return app.exec();
}
#include "client.moc"
It output "DONE" as expected. Maybe the site you're checking really isn't returning? Maybe it needs authentication or is producing ssl errors?
What code do you have around that? Do you spin an event loop somewhere? e.g. qapp.exec() ?