Qt: How to catch an error with system call? - c++

I am building a GUI application where I do a system call and call for gnuplot to run a script. Now i want to build in an error message that says when something is wrong (e.g. gnuplot is not installed or in the wrong path).
So I've been thinking about just putting out a QMessageBox, but I don't know how I can check whether the system call succeeded or not.
if(//System call didn't work)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Error");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText("GNUPLOT was not installed");
msgBox.exec();
}
My system call looks like this:
system(gnuplot script.txt);
Any suggestions?

You should use QProcess rather than low-level system call because it is a nice abstraction in a Qt codebase. Otherwise, you will end up dealing with platform specific bits. QProcess already solves that for you. If you happen to be fine with a blocking approach, aka. sync, you could write something like that code below.
QProcess process;
process1.start("gnuplot arg1 arg2 etc");
// Wait for it to start
if(!process.waitForStarted())
return 0;
bool retval = false;
QByteArray buffer;
while ((retval = process.waitForFinished()));
buffer.append(process.readAll());
if (!retval) {
qDebug() << "Process 2 error:" << process.errorString();
msgBox.setText(buffer);
return 1;
}
If you would not like to block while the gnuplot script of yours is running, you could connect a slot, or simply lambda with C++11 and on, to the readyRead() signal. For sure, you would also need to connect to the error() signal. The code without lambda to work with pre C++11 environments would look something like this:
GnuPlotReader::GnuPlotReader(QQProcess *process, QObject *parent)
: QObject(parent)
, m_process(process)
, m_standardOutput(stdout)
{
connect(m_process, SIGNAL(readyRead()), SLOT(handleReadyRead()));
connect(m_process, SIGNAL(error(QProcess::ProcessError)), SLOT(handleError(QProcess::ProcessError)));
connect(&m_timer, SIGNAL(timeout()), SLOT(handleTimeout()));
m_timer.start(5000);
}
GnuPlotReader::~GnuPlotReader()
{
}
void GnuPlotReader::handleReadyRead()
{
m_readData = m_process->readAll();
if (!m_timer.isActive())
m_timer.start(5000);
}
void GnuPlotReader::handleTimeout()
{
if (m_readData.isEmpty()) {
m_standardOutput << QObject::tr("No data was currently available for reading from gnuplot") << endl;
} else {
m_standardOutput << QObject::tr("GnuPlot successfully run")<< endl;
}
}
void GnuPlotReader::handleError(QProcess::ProcessError processError)
{
if (processError == QProcess::ReadError) {
m_standardOutput << QObject::tr("An I/O error occurred while reading the data, error: %2").arg(m_process->errorString()) << endl;
messageBox.setText(m_readData);
}
}

Related

Qt Concurrent with signals & slots

I'm a novice in threads and someone advises me to use Qt Concurrent (Qt C++).
I'm trying to run a function in a thread by using Qt Concurrent, my functions runs well but my signal/slot is never emitted.
However for your information if I launch my function without using thread everything works fine.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFutureWatcher<void> *watcher1 = new QFutureWatcher<void>();
connect(watcher1, SIGNAL(finished()), this, SLOT(getSizeFinished()));
QString string = "http://ipv4.download.thinkbroadband.com/50MB.zip";
QFuture<void> future1 = QtConcurrent::run(this, &MainWindow::getRemoteFileSize, string);
watcher1->setFuture(future1);
}
void MainWindow::getSizeFinished()
{
qDebug() << "--- FINISHED ---";
}
void MainWindow::getRemoteFileSize(const QString &url)
{
qDebug() << "Function - getRemoteFileSize";
QNetworkRequest req;
m_netmanager = new QNetworkAccessManager();
req.setUrl(QUrl(url));
m_reply = m_netmanager->get(req);
connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(remoteHTTPHeader()));
}
void MainWindow::remoteHTTPHeader()
{
qDebug() << "Function - remoteHTTPHeader";
remoteSize = m_reply->header(QNetworkRequest::ContentLengthHeader).toInt();
qDebug() << "Remote File Size: " << remoteSize;
m_reply->deleteLater();
m_netmanager->deleteLater();
qDebug() << "SIZE " << remoteSize;
}
You probably don't need to create a connection in this case, you could call MainWindow::remoteHTTPHeader() right after m_reply = m_netmanager->get(req);.
You might want to check if the reply is effectively finished like so:
if (m_reply->isFinished()) {
remoteHTTPHeader();
} else {
connect(m_reply, &QNetworkReply::finished, this, &MainWindow::remoteHTTPHeader);
}
This way you handle both fast and slow connections. Also notice how I created the connection using function pointers instead of SIGNAL and SLOT macro, this syntax is better since it checks at compile time if the functions exist so you avoid making typos and the like.

QMediaPlayer Not Loading Media And Not Emitting mediaStatusChanged() Signals

I've recently started working with Qt and am trying to play a sound file using QMediaPlayer.
My program compiles and runs but the sound file is not played, the QMediaPlayer seems stuck in the QMediaPlayer::LoadingMedia state.
Also - and possibly related - the QMediaPlayer doesn't ever seem to emit its mediaStatusChanged or its error signals (though perhaps this is me not connecting them properly?)
When I run the program as below, it reaches the while loop and never leaves. If I query for player->mediaStatus() inside the loop, it constantly returns 2 (QMediaPlayer::LoadingMedia).
When I run it with the while loop omitted, the program continues to run until it reaches end of execution with no run-time errors but - as you may expect - the file is not played.
Interestingly, the two couts before the while loop, which report player's mediaStatus and state show that the mediaStatus changes from 1 (in the first instance, before setting the media) to 2 (after setting the media) but my ChangedStatus slot is never called, despite connecting to the mediaStatusChanged at the start of the run function.
Running: Debian Jessie, Qt5.7/Qt5.9
AudioPlayer.h
#include <QThread>
#include <QMediaPlayer>
class AudioPlayer : public QThread {
Q_OBJECT
public:
AudioPlayer();
public slots:
void ChangedStatus(QMediaPlayer::MediaStatus);
void MediaError(QMediaPlayer::Error);
protected:
void run();
};
AudioPlayer.cpp:
AudioPlayer::AudioPlayer(){}
void AudioPlayer::run()
{
QMediaPlayer* player = new QMediaPlayer();
connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(ChangedStatus(QMediaPlayer::MediaStatus)));
connect(player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(MediaError(QMediaPlayer::Error)));
std::cout << "Got player!" << std::endl;
std::cout << "\n\n\tPlayer state: " << player->state() << "\n\tMediaState: " << player->mediaStatus() << std::endl;
player->setMedia(QUrl::fromLocalFile("/home/me/test.wav") );
std::cout << "Set source" << std::endl;
std::cout << "\n\n\tPlayer state: " << player->state() << "\n\tMediaState: " << player->mediaStatus() << std::endl;
while(player->mediaStatus() != QMediaPlayer::MediaStatus::LoadedMedia)
{//
}
player->setVolume(100);
std::cout << "Set volume" << std::endl;
player->play();
std::cout << "Played" << std::endl;
}
void AudioPlayer::MediaError(QMediaPlayer::Error error)
{
std::cout << "Media Error: " << error << std::endl;
}
void AudioPlayer::ChangedStatus(QMediaPlayer::MediaStatus status)
{
std::cout << "Status changed to: " << status << std::endl;
}
main.cpp:
#include "audioplayer.h"
using namespace std;
int main()
{
cout << "Hello World!" << endl;
AudioPlayer myAudioPlayer;
myAudioPlayer.start();
std::cout << "myAudioPlayer started. Waiting...." << std::endl;
myAudioPlayer.wait();
std::cout << "myAudioPlayer returned." << std::endl;
return 0;
}
Extra Info:
Now, initially, I hadn't used QThread and was trying to do this all in main.cpp (just declaring a QMediaPlayer and attempting to set the media and play) but this was giving me QObject::startTimer: timers cannot be started from another thread warning run-time errors in a couple of places (declaration of the QMediaPlayer and, I think, the play command), so I adopted the above approach - although I'm not sure that subclassing QThread is, necessarily, the best way. This is also why everything (declarations etc.) is done in the run function - having the QMediaPlayer as a member of AudioPlayer and initialising it in the constructor gave the same error.
I have compiled and run Qt's Player example (from multimediawidgets) and, by browsing and selecting my test.wav, it can play the file so I don't think it's a compatibility issue. I looked through the Player example source but couldn't see anything jumping out which I had missed and which looked like the cause of my problem.
You should create an QApplication object and use it's message loop. I would suggest you to test following:
#include "audioplayer.h"
using namespace std;
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
AudioPlayer myAudioPlayer;
myAudioPlayer.start();
return a.exec();
}
You will at least get your signals raised. If the media state reaches QMediaPlayer::StoppedState or any error occured, you could call QApplication::instance()->quit() to stop your application.
Edit: Better use the new style connections like:
connect(player, &QMediaPlayer::mediaStatusChanged, this, &QMediaPlayer::ChangedStatus);
It is more reliable and you don't have to register specific parameter types like QMediaPlayer::MediaStatus with Q_DECLARE_METATYPE()
Because QMediaPlayer class contains another method called error with a different signature, signal connection is a bit more complicated. This is because the compiler don't know which error method you are referring to. In this case static_cast is the way to solve this ambiguity:
connect(
player,
static_cast<void(QMediaPlayer::*)(QMediaPlayer::Error )>(&QMediaPlayer::error),
this,
&AudioPlayer::MediaError
);
Please note, a Wave file is only a container file that can contain an arbitrary compressed data stream. It may be necessary to install the appropriate operating system multimedia codec first. In Microsoft Windows Qt-Framework relies on installed multimedia codecs (.ax file).
Your AudioPlayer::run method will end without waiting for the media being played. So you should wait for the Stopped status some where before the thread ends. However it is better not to use the run method directly but using QThreads message loop instead.
class AudioPlayer : public QThread {
public:
AudioPlayer() : _Player(nullptr) {
moveToThread(this); // AudioPlayer object become part of this new thread
}
public slots:
void setVolume(int);
void load(QString Media);
// ...
void play() {
// Never directly access any members since they may belong to a different thread
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "play", Qt::QueuedConnection);
} else {
_Player->play();
}
}
void stop() {
quit(); // Quiting thread message loop
}
private:
QMediaPlayer* _Player;
void run() {
_Player = new QMediaPlayer(this);
connect(...) // All connections go here...
int Result = QThread::exec();
_Player->stop();
delete _Player;
}
private slots:
void HandleStatusChange(QMediaPlayer::MediaStatus Status) {
emit statusChanged(Status); // Redirect so that the main application can handle this signal too
}
signals:
void statusChanged((QMediaPlayer::MediaStatus);
};

qt waitforreadyread() inconsistent?

Under Qt 4.7.1, OS X 10.6.8
(have to use this -- later versions
of Qt and/or OS X introduce severe
incompatibilities for my users)
The following works. Sometimes. Then sometimes not.
When it doesn't work, it returns "Unknown Error"
hst is good in all cases, qDebug returns same correct
data for hst every time.
The idea is, use ->get to pull a CGI URL; the CGI
returns some data, which I can ignore in this case.
Then I'm done.
hst is a well formed URL,
http://yadda.com/cgi-bin/whatever.py
QString hst;
QNetworkReply *qnr;
QNetworkAccessManager *qqnap = NULL;
qqnap = new(std::nothrow) QNetworkAccessManager(tmw);
if (qqnap != NULL)
{
hst = loaduphst(); // get qstring to send
qnr = qqnap->get(QNetworkRequest(QUrl(hst))); // report in and fetch update info
if (qnr->waitForReadyRead(3000) == FALSE)
{
qDebug() << "waitForReadyRead() returned FALSE -- error or timeout:" << qnr->errorString();
}
}
else
{
qDebug() << "qqnap is NULL";
}
yadda.com is up; the target script is dead simple
and works fine from browser or cmd line every time.
This is running within the context of
MainWindow::closeEvent(QCloseEvent *ce)
before I emit ce->accept() GUI is still up,
etc.
Hints? Tips? Abuse? Thanks!
waitForReadyRead is not implemented in QNetworkReply. The default implementation does nothing:
bool QIODevice::waitForReadyRead(int msecs)
{
Q_UNUSED(msecs);
return false;
}
Use the readyRead signal to find out when there is data available to be read.
More-or-less synchronous use of async networking is very problematic in the context of the main GUI loop. Signals that don't appear (finished OR readyRead), URLs that sometimes send and sometimes don't... and of course, as the kind person above pointed out, unimplemented functions. Zebras!
What we can do, though, is fire up an event loop and a timer on our own, and this will in a more-or-less friendly way act synchronous.
Perhaps some poor soul will need to poke a website CGI as I do; here's the code. It works. At least under Qt 4.7.1 it does!
So anyway, here it is:
QNetworkReply *qnr;
QNetworkAccessManager *qqnap;
QNetworkRequest qnwr;
QEventLoop w;
QTimer arf;
if ((qqnap = new(std::nothrow) QNetworkAccessManager(this)))
{
qnwr.setUrl(myUrl()); // Build web goodness
qnwr.setRawHeader("User-Agent", myUserAgent());
arf.setSingleShot(true);
if (connect(&arf, SIGNAL(timeout()), // timer firing blows...
&w, SLOT(quit()) // ...out event loop
) == FALSE)
{ return(BAD_CONNECT_TOUT); }
if (connect(qqnap, SIGNAL(finished(QNetworkReply*)), // notify we finished...
this, SLOT(qqnapReplyQ(QNetworkReply*)) // ...cuz I need to know
) == FALSE)
{ return(BAD_CONNECT_FINISHED_NOTIFY); }
if (connect(qqnap, SIGNAL(finished(QNetworkReply*)), // finishing blows out...
&w, SLOT(quit()) // ...event loop
) == FALSE)
{ return(BAD_CONNECT_FINISHED_ELOOP); }
if ((qnr = qqnap->get(qnwr))) // Go if qnr is good
{
arf.start(6000); // timeout in ms // Watchdog timer on
w.exec(); // handle all that
if (arf.isActive()) { arf.stop(); } // kill timer if needed
}
else { return(BAD_WWWGET); } // FAIL
}
else
{
return(BAD_NWAM); // FAIL
}
return(ZEN_NETWORKING);

QProcess does not complete creating file

I am just trying to create a file with QProcess by the following source code:
void Processmethod()
{
QDialog *ProcessMessage = new QDialog;
Ui::DialogProcessMessage Dialog;
Dialog.setupUi(ProcessMessage);
ProcessMessage->setModal(true);
ProcessMessage->setAttribute(Qt::WA_DeleteOnClose);
ProcessMessage->show();
processmethodONE();
}
void processmethodONE()
{
QString ProcessCommand = "w8 " + blablubli";
Prozess.setWorkingDirectory(Path); //QProcess "Prozess" is globaly defined
Prozess.setStandardOutputFile(Path); //in my class
Prozess.start(ProcessCommand);
QProcess::ExitStatus Status = Prozess.exitStatus();
if (Status == 0)
{
std::cout << "File created!" << std::endl;
}
}
This process creates out of another file which is located in the QString "Path" a new file, let me call it "PROFILE" (PRO for Problem :). The Profile also is created, but is never completed, I guess not even 50% of the file are completed.
Just when using
while(!Prozess.waitForFinished())
{
qApp->processEvents();
std::cerr << "Process Create PROFile running " << std::endl;
}
the file is written completely.
Furthermore I tried to use the QProcess finished(int) Signal to start another method and deleted the while loop (which freezes the whole GUI). I declared it in the constructor with:
connect(&Prozess, SIGNAL(finished(int)), this, (SLOT(processmethodTWO())));
But I guess this could not work because the first process isn't finished completely. Where is the problem?
There is no warranty that right after Prozess.start(ProcessCommand); process will be finished, so calling "Prozess.exitStatus();" right after it will give you "false positive". You certainly will have to wait until process is finished. You may either do it with with while loop, that you suggested or with QEventLoop
// ...
Prozess.setStandardOutputFile(Path); //in my class
QEventLoop loop;
connect(&Prozess, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
connect(&Prozess, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
Prozess.start();
loop.exec();
// Now your process status should be valid:
QProcess::ExitStatus Status = Prozess.exitStatus();

How to use a process (QProcess) in a new thread (QThread)?

I have the following code:
void Processmethod()
{
QDialog *ProcessMessage = new QDialog;
Ui::DialogProcessMessage Dialog;
Dialog.setupUi(ProcessMessage);
ProcessMessage->setModal(true);
ProcessMessage->setAttribute(Qt::WA_DeleteOnClose);
ProcessMessage->show();
qApp->processEvents();
processmethodONE();
processmethodTWO();
processmethodTHREE();
}
void processmethodONE()
{
QString ProcessCommand = "w8 " + blablubli";
Prozess.setWorkingDirectory(Path); //QProcess "Prozess" is globaly defined
Prozess.setStandardOutputFile(Path); //in my class
QThread* thread = new QThread;
Prozess.moveToThread(thread);
Prozess.start(ProcessCommand);
while(!Prozess.waitForFinished(2000))
{
std::cerr << "Process running " << std::endl;
}
QProcess::ExitStatus Status = Prozess.exitStatus();
if (Status == 0)
{
std::cout << "File created!" << std::endl;
}
}
In this source code I try to open a popup dialog before some processes are starting. problem is that the dialog is not clickable, but on the dialog I want to create a button to abort the running method. As you can see I tried using QThread to run the process(es) in another thread, but still I can't click the dialog. Furthermore if I open my application (GUI) with the "application/x-executable"-file the dialogs content is missing when activating the above shown method. How can I fix these problems? Where am I wrong? greetings
void processmethodONE()
{
QThread* thread = new QThread;
Prozess.moveToThread(thread);
Prozess.start(ProcessComand);
Here you moved the QProcess to another thread. But then you call start() on it. That's already not thread-safe.
while(!Prozess.waitForFinished(2000))
{
std::cerr << "Process running " << std::endl;
}
This blocks and makes using a thread useless. Also, it's not thread-safe.
You should instead not use threads but:
remove the waitForFinished() call
Connect the finished() and error() signals of the QProcess to slots which then start the next step, i.e. processMethodTWO.
I would also advise against reusing QProcess objects and just create a new one for each step.
While I still don't fully understand your recently updated code example, I feel this might be your issue:
while(!Prozess.waitForFinished(2000))
{
std::cerr << "Process running " << std::endl;
}
Wherever you are really calling this in your original code is blocking while waiting for Prozess to finish.
Use a brand new QProcess instance for each one, and connect their finished() signals to a SLOT that will get called when they have finished. Don't manually poll them and block. This will allow you to completely get rid of QThreads altogether.