Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 3

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 ]