Qt-interest Archive, April 2007
QThreads and QProcesses
Message 1 in thread
Hi,
I've been having a lot of trouble with QThread and QProcess, and getting
it to work right with Qt 4.2.3.
Below is my broken code, I'm hoping someone can show me where I am doing
it wrong. What happens
when you run this (at least on my machine, Fedora Core 6 kernel
2.6.19-1.2911.6.5 on i686, KDE 3.5.6-0.3)
the QProcess will freeze the GUI and the QThreads will run erratically
for a second then freeze the whole app.
I will also attempt to attach the files, if the list will accept it.
Thanks for the help,
John Voltz
here is the project file
-----src.pro---------------------------------------------------------------------------------------------------------------->
TARGET = MyApp
CONFIG += warn_on \
qt \
debug
TEMPLATE = app
HEADERS += myapp.h
SOURCES += main.cpp
linux {
QMAKE_CXXFLAGS_RELEASE += -pthread
QMAKE_CXXFLAGS_DEBUG += -pthread
CONFIG += debug
}
win32 {
}
<-------------------------------------------------------------------------------------------------------------------------------------------------------------
the header file
----------myapp.h---------------------------------------------------------------------------------------------------------------------->
#include <QApplication>
#include <QtGui>
#include <math.h>
class MyGadget : public QWidget
{
Q_OBJECT
public:
MyGadget(QWidget *parent = 0);
void Plot(int x, int y, int ang);
QReadWriteLock MyLock;
private:
QPixmap *MyPixmap;
QPen *MyPen;
QPainter *MyPainter;
private slots:
void paintEvent(QPaintEvent *event);
};
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread(QObject * parent = 0);
void run(void);
void stop(void);
MyGadget *MyWidget;
private:
volatile bool stopped;
};
class MyWindow : public QMainWindow
{
Q_OBJECT
public:
MyWindow(void);
void SetupUI(void);
private:
QPushButton *ThreadsButton;
private slots:
void StartThreadsClicked(void);
void StartProcessClicked(void);
};
<---------------------------------------------------------------------------------------------------------------------------------------------------------------
the code
----------------main.cpp---------------------------------------------------------------------------------------------------------------------->
#include "myapp.h"
/* pointers to threads */
MyThread *MyLoop1;
MyThread *MyLoop2;
int main(int argc, char *argv[])
{
/* create the app */
QApplication app(argc, argv);
/* create a new main window */
MyWindow *MainWindow = new MyWindow();
MainWindow->show();
/* create two new threads */
MyLoop1 = new MyThread();
MyLoop2 = new MyThread();
/* start the main event loop */
return app.exec();
}
MyWindow::MyWindow(void)
{
/* create the window layout */
SetupUI();
}
void MyWindow::SetupUI(void)
{
/* make some buttons and connect their
clicked() events */
ThreadsButton = new QPushButton(this);
QPushButton *ProcessButton = new QPushButton(this);
ThreadsButton->setGeometry(10, 10, 90, 20);
ProcessButton->setGeometry(10, 40, 90, 20);
ThreadsButton->setText("Start Threads");
ProcessButton->setText("Start Process");
ThreadsButton->show();
ProcessButton->show();
connect(ThreadsButton, SIGNAL(clicked()), this,
SLOT(StartThreadsClicked()));
connect(ProcessButton, SIGNAL(clicked()), this,
SLOT(StartProcessClicked()));
}
void MyWindow::StartThreadsClicked(void)
{
/* start or stop the threads
and change the button text */
if (MyLoop1->isRunning() || MyLoop2->isRunning()) {
MyLoop1->stop();
MyLoop2->stop();
ThreadsButton->setText("Start Threads");
} else {
MyLoop1->start(QThread::NormalPriority);
MyLoop2->start(QThread::NormalPriority);
ThreadsButton->setText("Stop Threads");
}
}
void MyWindow::StartProcessClicked(void)
{
/* start a new process to run gthumb */
QProcess *MyProcess = new QProcess();
MyProcess->execute("/usr/bin/gthumb");
}
MyThread::MyThread(QObject * parent) : QThread(parent)
{
/* set the stopped flag */
stopped = true;
/* create a new widget for the thread to
draw on */
MyWidget = new MyGadget();
MyWidget->setGeometry(10, 10, 200, 200);
MyWidget->show();
}
void MyThread::run(void)
{
int x = 100;
int y = 100;
int ang;
/* clear the stopped flag */
stopped = false;
/* make this thread loop */
while (stopped == false) {
for (ang = 0; ang < 360; ang++) {
MyWidget->Plot(x, y, ang);
}
};
}
void MyThread::stop(void)
{
/* set the stopped flag */
stopped = true;
}
MyGadget::MyGadget(QWidget *parent) : QWidget(parent)
{
/* setup drawing tools */
MyPixmap = new QPixmap(QSize(200,200));
MyPen = new QPen();
MyPen->setStyle(Qt::SolidLine);
MyPen->setWidth(1);
MyPen->setColor(QColor::fromRgb(128, 0, 0, 255));
MyPainter = new QPainter(MyPixmap);
MyPainter->setPen(*MyPen);
}
void MyGadget::Plot(int x, int y, int ang)
{
/* draw a rotating line */
x += (int)(50.0 * sin(ang * 0.0174532925));
y += (int)(50.0 * cos(ang * 0.0174532925));
/* prevent the paint event from reading
the pixmap while drawing on the pixmap */
MyLock.lockForWrite();
MyPixmap->fill(Qt::white); //clear the pixmap
MyPainter->drawLine(100, 100, x, y); //draw the line
MyLock.unlock();
this->update(); //schedule a paint event
}
void MyGadget::paintEvent(QPaintEvent *)
{
/* prevent drawing on the pixmap
while reading the pixmap */
MyLock.lockForRead();
/* draw the pixmap onto the widget */
QPainter WidgetPainter(this);
WidgetPainter.drawPixmap(rect().topLeft(), *MyPixmap, rect());
WidgetPainter.end();
MyLock.unlock();
}
<---------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "myapp.h"
#include "myapp.h"
/* pointers to threads */
MyThread *MyLoop1;
MyThread *MyLoop2;
int main(int argc, char *argv[])
{
/* create the app */
QApplication app(argc, argv);
/* create a new main window */
MyWindow *MainWindow = new MyWindow();
MainWindow->show();
/* create two new threads */
MyLoop1 = new MyThread();
MyLoop2 = new MyThread();
/* start the main event loop */
return app.exec();
}
MyWindow::MyWindow(void)
{
/* create the window layout */
SetupUI();
}
void MyWindow::SetupUI(void)
{
/* make some buttons and connect their
clicked() events */
ThreadsButton = new QPushButton(this);
QPushButton *ProcessButton = new QPushButton(this);
ThreadsButton->setGeometry(10, 10, 90, 20);
ProcessButton->setGeometry(10, 40, 90, 20);
ThreadsButton->setText("Start Threads");
ProcessButton->setText("Start Process");
ThreadsButton->show();
ProcessButton->show();
connect(ThreadsButton, SIGNAL(clicked()), this, SLOT(StartThreadsClicked()));
connect(ProcessButton, SIGNAL(clicked()), this, SLOT(StartProcessClicked()));
}
void MyWindow::StartThreadsClicked(void)
{
/* start or stop the threads
and change the button text */
if (MyLoop1->isRunning() || MyLoop2->isRunning()) {
MyLoop1->stop();
MyLoop2->stop();
ThreadsButton->setText("Start Threads");
} else {
MyLoop1->start(QThread::NormalPriority);
MyLoop2->start(QThread::NormalPriority);
ThreadsButton->setText("Stop Threads");
}
}
void MyWindow::StartProcessClicked(void)
{
/* start a new process to run gthumb */
QProcess *MyProcess = new QProcess();
MyProcess->execute("/usr/bin/gthumb");
}
MyThread::MyThread(QObject * parent) : QThread(parent)
{
/* set the stopped flag */
stopped = true;
/* create a new widget for the thread to
draw on */
MyWidget = new MyGadget();
MyWidget->setGeometry(10, 10, 200, 200);
MyWidget->show();
}
void MyThread::run(void)
{
int x = 100;
int y = 100;
int ang;
/* clear the stopped flag */
stopped = false;
/* make this thread loop */
while (stopped == false) {
for (ang = 0; ang < 360; ang++) {
MyWidget->Plot(x, y, ang);
}
};
}
void MyThread::stop(void)
{
/* set the stopped flag */
stopped = true;
}
MyGadget::MyGadget(QWidget *parent) : QWidget(parent)
{
/* setup drawing tools */
MyPixmap = new QPixmap(QSize(200,200));
MyPen = new QPen();
MyPen->setStyle(Qt::SolidLine);
MyPen->setWidth(1);
MyPen->setColor(QColor::fromRgb(128, 0, 0, 255));
MyPainter = new QPainter(MyPixmap);
MyPainter->setPen(*MyPen);
}
void MyGadget::Plot(int x, int y, int ang)
{
/* draw a rotating line */
x += (int)(50.0 * sin(ang * 0.0174532925));
y += (int)(50.0 * cos(ang * 0.0174532925));
/* prevent the paint event from reading
the pixmap while drawing on the pixmap */
MyLock.lockForWrite();
MyPixmap->fill(Qt::white); //clear the pixmap
MyPainter->drawLine(100, 100, x, y); //draw the line
MyLock.unlock();
this->update(); //schedule a paint event
}
void MyGadget::paintEvent(QPaintEvent *)
{
/* prevent drawing on the pixmap
while reading the pixmap */
MyLock.lockForRead();
/* draw the pixmap onto the widget */
QPainter WidgetPainter(this);
WidgetPainter.drawPixmap(rect().topLeft(), *MyPixmap, rect());
WidgetPainter.end();
MyLock.unlock();
}
#include <QApplication>
#include <QApplication>
#include <QtGui>
#include <math.h>
class MyGadget : public QWidget
{
Q_OBJECT
public:
MyGadget(QWidget *parent = 0);
void Plot(int x, int y, int ang);
QReadWriteLock MyLock;
private:
QPixmap *MyPixmap;
QPen *MyPen;
QPainter *MyPainter;
private slots:
void paintEvent(QPaintEvent *event);
};
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread(QObject * parent = 0);
void run(void);
void stop(void);
MyGadget *MyWidget;
private:
volatile bool stopped;
};
class MyWindow : public QMainWindow
{
Q_OBJECT
public:
MyWindow(void);
void SetupUI(void);
private:
QPushButton *ThreadsButton;
private slots:
void StartThreadsClicked(void);
void StartProcessClicked(void);
};
TARGET = MyApp
TARGET = MyApp
CONFIG += warn_on \
qt \
debug
TEMPLATE = app
HEADERS += myapp.h
SOURCES += main.cpp
linux {
QMAKE_CXXFLAGS_RELEASE += -pthread
QMAKE_CXXFLAGS_DEBUG += -pthread
CONFIG += debug
}
win32 {
}
Message 2 in thread
John Voltz wrote:
> Hi,
>
> I've been having a lot of trouble with QThread and QProcess, and
> getting it to work right with Qt 4.2.3.
> Below is my broken code, I'm hoping someone can show me where I am
> doing it wrong. What happens
> when you run this (at least on my machine, Fedora Core 6 kernel
> 2.6.19-1.2911.6.5 on i686, KDE 3.5.6-0.3)
> the QProcess will freeze the GUI and the QThreads will run erratically
> for a second then freeze the whole app.
>
> I will also attempt to attach the files, if the list will accept it.
>
> Thanks for the help,
>
> John Voltz
>
>
MyThread::MyThread(QObject * parent) : QThread(parent)
{
/* set the stopped flag */
stopped = true;
/* create a new widget for the thread to
draw on */
MyWidget = new MyGadget();
MyWidget->setGeometry(10, 10, 200, 200);
MyWidget->show();
}
Take the MyWidget creation out of here, and move it to the run function
overload. What you're doing here is creating MyWidget in the main
thread, so MyWidget's event queue will be on the main thread.
If you create it in the run function, then the event loop will be in the
thread, and signals and slots (for example, the paintEvent) will be
processed on the thread, and not in the main loop.
HTH
--
[ signature omitted ]
Message 3 in thread
On 11.04.07 10:31:58, Bill KING wrote:
> John Voltz wrote:
> > I've been having a lot of trouble with QThread and QProcess, and
> > getting it to work right with Qt 4.2.3.
> > Below is my broken code, I'm hoping someone can show me where I am
> > doing it wrong. What happens
> > when you run this (at least on my machine, Fedora Core 6 kernel
> > 2.6.19-1.2911.6.5 on i686, KDE 3.5.6-0.3)
> > the QProcess will freeze the GUI and the QThreads will run erratically
> > for a second then freeze the whole app.
> >
> > I will also attempt to attach the files, if the list will accept it.
> >
> > Thanks for the help,
> >
> MyThread::MyThread(QObject * parent) : QThread(parent)
> {
> /* set the stopped flag */
> stopped = true;
>
> /* create a new widget for the thread to
> draw on */
> MyWidget = new MyGadget();
> MyWidget->setGeometry(10, 10, 200, 200);
> MyWidget->show();
> }
>
> Take the MyWidget creation out of here, and move it to the run function
> overload. What you're doing here is creating MyWidget in the main
> thread, so MyWidget's event queue will be on the main thread.
> If you create it in the run function, then the event loop will be in the
> thread, and signals and slots (for example, the paintEvent) will be
> processed on the thread, and not in the main loop.
This won't work, only the GUI thread, i.e. the one that was started via
QApplicaton::exec() may do GUI related operations. You may have an
eventloop in another thread, but this cannot be used for gui-related
operations.
Andreas
--
[ signature omitted ]
Message 4 in thread
On Tuesday 10 April 2007 17:18, John Voltz wrote:
> Hi,
>
> I've been having a lot of trouble with QThread and QProcess, and getting
> it to work right with Qt 4.2.3.
> Below is my broken code, I'm hoping someone can show me where I am doing
> it wrong. What happens
> when you run this (at least on my machine, Fedora Core 6 kernel
> 2.6.19-1.2911.6.5 on i686, KDE 3.5.6-0.3)
> the QProcess will freeze the GUI and the QThreads will run erratically
> for a second then freeze the whole app.
[bigsnip]
> void MyThread::run(void)
> {
...
> /* make this thread loop */
> while (stopped == false) {
> for (ang = 0; ang < 360; ang++) {
> MyWidget->Plot(x, y, ang);
> }
> };
> }
>
> void MyGadget::Plot(int x, int y, int ang)
> {
...
> MyPixmap->fill(Qt::white); //clear the pixmap
> MyPainter->drawLine(100, 100, x, y); //draw the line
...
> }
This is the problem. You are doing GUI things in a thread, which is not
supported in Qt. See http://doc.trolltech.com/4.2/threads.html for more
information, specifically
http://doc.trolltech.com/4.2/threads.html#qobject-reentrancy, which says:
"Although QObject is reentrant, the GUI classes, notably QWidget and all its
subclasses, are not reentrant. They can only be used from the main thread."
And:
"In practice, the impossibility of using GUI classes in other threads than the
main thread can easily be worked around by putting time-consuming operations
in a separate worker thread and displaying the results on screen in the main
thread when the worker thread is finished. This is the approach used for
implementing the Mandelbrot and the Blocking Fortune Client example."
In short, you can't do any GUI operations in a thread, they must be done from
the main thread. Hope this helps :)
--
[ signature omitted ]