PDA

View Full Version : QProcess in a QThread



chombium
11th January 2006, 15:05
Hi,

I needed repeatidly to start some external program from the app that I'm writing. The idea was to repeaiidly play wav files with aplay, at first I created QProcess in the gui thread and tested, but there were some delays in the playing due to other data processing in the app.

I subclassed QThread and I've combined it with QProcess so that I can execute the external app.
I'm using QT 3. I've read that the event loop runs in the gui thread and that threads are not well supported in Qt3.

Here is the code:

The header:


// execthread.h
// Description: Thread class for executing external applications repeatedly.
// The external app can be executed repeatedly until the thread is stopped,
// or a definite number of times. The executed command can be given, or it
// can be read from some conf file which path will be given at thread creation.

#ifndef EXECTHREAD_H
#define EXECTHREAD_H

#include <qthread.h>
#include <qprocess.h>
#include <qstringlist.h>
#include <qstring.h>
#include <qapplication.h>


class ExecThread : public QThread
{
Q_OBJECT
public:
ExecThread(const QString Conf, int RunTimes, bool config);
virtual ~ExecThread();

void setCommand(const QString Cmd);
void setExecTimes(const int &ExecTimesParam);
int execsTillStop();
void run();
void stop();


signals:
void ExecFinished(); // when in RepeatMode emits a signal after it finishes the last // execution


private:
QProcess *ExecProcess;
QString CommandStr; // Command to be executed
int ExecTimes; // number of executions to be done till it stops
bool RepeatMode; // flag if the thread is in repeated execution
volatile bool stopped; // flag if thread was stopped

QString GetCommandStr(const QString Path);
void SetArgs(QString Cmd);

private slots:
void processExitedSlot();
};

#endif


And the implementation:


// execthread.cpp


#include "execthread.h"

// Function: ExecThread::ExecThread
// Description: The constructor
// Parameters:
// QString Conf - the command to be executed, or the conf file to be read where the command is
// int RunTimes - number of times the command should be executed. Default is 0, meaning
// unlimited times ( till stop() method is called)
// bool config - if true the first parameter (Conf) is the path to the config file where the
// command is situated. By default it's false which means Conf holds the
// command to be executed

ExecThread::ExecThread(const QString Conf, int RunTimes, bool config)
{

stopped = true;

// setting the command to be executed
if (!config)
CommandStr = Conf;
else
CommandStr = GetCommandStr(Conf);


// should the command be exectuted limited number of times
if (RunTimes > 0)
{
ExecTimes = RunTimes;
RepeatMode = true;
}
else
{
ExecTimes = 0;
RepeatMode = false;
}

CommandStr = CommandStr.stripWhiteSpace();
CommandStr = CommandStr.simplifyWhiteSpace();

// create the process which will execute the command
ExecProcess = new QProcess();

// QObject::connect( ExecProcess, SIGNAL(processExited()),
// this, SLOT(processExitedSlot()) );

connect( ExecProcess, SIGNAL(processExited()),
this, SLOT(processExitedSlot()) );


SetArgs(CommandStr);
}

// Function: ExecThread::~ExecThread
// Description: The destructor

ExecThread::~ExecThread()
{
delete ExecProcess;
}

// Funciton: GetCommandStr
// Description: Parser for the file with the command string. It accepts # style comments
// Parameters:
// QString Path - the path to the file

QString ExecThread::GetCommandStr (const QString Path)
{
QFile file;
QTextStream stream( &file );
QString CmdStr = "";
QString line;
int pos;

file.setName(Path);
if (!file.exists()) return "";
if (file.open(IO_ReadOnly))
while (( !stream.atEnd() ) or (CmdStr == ""))
{
line = stream.readLine();

if ((pos = line.find('#',0))>=0) // if there is a # in the line
{
if (pos > 0) // if the line is like " aplay /usr/test.wav # play test.wav"
CmdStr = line.left(pos);
}
else
{
CmdStr = line;
}
}
return CmdStr;
}

// Funciton: setCommand
// Description: Sets the command to be executed
// Parameters:
// QString Cmd - the command

void ExecThread::setCommand(const QString Cmd)
{


CommandStr = Cmd;
CommandStr = CommandStr.stripWhiteSpace();
CommandStr = CommandStr.simplifyWhiteSpace();
SetArgs(CommandStr);
return;
}

// Funciton: setExecTimes
// Description: Sets the number of times command should be exectuted.
// Calling this function autmatically sets RepeatMode to true
// Parameters:
// int &ExecTimesParam - number of times command to be executed

void ExecThread::setExecTimes(const int &ExecTimesParam)
{
if (ExecTimesParam > 0)
{
ExecTimes = ExecTimesParam;
RepeatMode = true;
}
else
{
ExecTimes = 0;
RepeatMode = false;
}
}


// Funciton: run
// Description: Executes the command until stopped is true. In RepeatMode
// decreases the number of execs to be done be executed

void ExecThread::run()
{
stopped = false;
ExecProcess->start();
if (RepeatMode)
{
ExecTimes--;
if (ExecTimes == 0)
{
stopped = true;
emit ExecFinished();
}
}
}

// Function: stop
// Description: Sets stopped to true. Stops the execution, but the command that is currently
// running is not terminated

void ExecThread::stop()
{
stopped = true;
}

// Function: SetArgs
// Description: Sets the command arguments to ExecProcess
// Parameters:
// QString Cmd - the command

void ExecThread::SetArgs(QString Cmd)
{
int pos;

ExecProcess->clearArguments();
while ((pos = Cmd.find(" ",0))> 0)
{
ExecProcess->addArgument(Cmd.left(pos));
Cmd = Cmd.mid(pos + 1, 1000);
}
ExecProcess->addArgument(Cmd);
}

// Function: execsTillStop
// Description: Returns the number of exectutions till it stops ExecProcess
int ExecThread::execsTillStop()
{
return ExecTimes;
}



void ExecThread::processExitedSlot()
{
if (!stopped)
{
ExecProcess->start();
if (RepeatMode)
{
ExecTimes--;
if (ExecTimes == 0)
{
stopped = true;
emit ExecFinished();
}
}
}
}


I've hit the wall twice

1. the connect statment gives me an error when compiling

execthread.cpp:56: error: no matching function for call to Object::connect(QProcess*&, const char [17], ExecThread* const, const char [21])
/usr/include/qt3/qobject.h:116: note: candidates are: static bool QObject::connect(const QObject*, const char*, const QObject*, const char*)
/usr/include/qt3/qobject.h:226: note: bool QObject::connect(const QObject*, const char*, const char*) const


2. If I comment the connect statement, the code compiles well but I get error in linking stage. I don't understand why do I get this error, ExitFinished() is defined as signal. confused

execthread.cppunhappy .text+0x7c): undefined reference to `ExecThread::ExecFinished()'execthread.o: In function `ExecThread::processExitedSlot()':
execthread.cppunhappy .text+0xee): undefined reference to `ExecThread::ExecFinished()'collect2: ld returned 1 exit status


TIA, chombium

Dusdan
11th January 2006, 15:10
afaik QThreads are not QObjects in Qt3, so you can't emit signals and activate slots. I think you have to derive from both QThread and QObject if you need this features, but be careful.

in Qt4, instead, QThreads are Qobjects (so they have signals and slots) and they have their own event loop.

anda_skoa
11th January 2006, 16:52
QProcess is an event loop driven class, it can only be used in the main thread (event loop thread)

You could use a Thread and system() to start the external player

Cheers,
_