PDA

View Full Version : QImage and Qt 4.5.3



halmassi
9th March 2010, 14:11
Hi;
I developped an application drawing images using QPainter.
First on OpenSUSE 11.0 with Qt 4.4.0, and it WORKS and print pictures, but have warnings for each picture I print.


QPixmap: It is not safe to use pixmaps outside the GUI thread
X Error: RenderBadGlyphSet (invalid GlyphSet parameter) 160
Extension: 148 (RENDER)
Minor opcode: 25 (RenderCompositeGlyphs32)
Resource id: 0x0


Second on Ubuntu 9.10 with Qt 4.5.3 it diden't work, and didnt print anything with the same warnings.

Here is the code of paintEvent methode of the Widget I want to paint.



void paintEvent(QPaintEvent *event){

const QPoint point(100,100);
QImage image(pictureBuffer, width, height, QImage::QImage::Format_RGB888);
QPainter painter;
painter.begin(this);
painter.drawImage(point,image);
painter.end();
}


Thank you for helping.

wysota
9th March 2010, 14:16
What is pictureBuffer? What does it contain? How is it populated?

halmassi
9th March 2010, 20:51
pictureBuffer is an unsigned char array containing RGB pixels.
in OpenSUSE + Qt 4.4, it displays the content of the buffer with no problem.
The display is done by another Thread, I ll try to draw with the main thread to see

halmassi
9th March 2010, 21:41
I tried to Draw with the main thread an it works. But it didnt match what I want.
This application is a video player.
I Have a main thread who creates a mainwidow where I have a Panel to display the Video and Buttons (Play, Pause,...)
Then I create 2 threads; one to make the pictures to the other who displays them.
So, If the work done by the second thread is done by the main thread, it works but I cant Access the buttons.
Thats the probleme
thank you for any suggestion

wysota
9th March 2010, 22:01
What do you mean exactly by drawing in the other thread? What are you drawing there? The widget?

halmassi
10th March 2010, 08:19
Yes
The main window contains the widget where I draw the pictures. This widget implements the paintEvent methode like I say it first time.
I think the main thread (the GUI thread) is the only thread allowed to repaint the widgets.
Can we send signals between threads.
I ll try to modify the content of the buffer with The second thread , emit signal to the main thread to repaint.

wysota
10th March 2010, 08:40
Yes
The main window contains the widget where I draw the pictures. This widget implements the paintEvent methode like I say it first time.
Ok but how do you update it in another thread? Could we see the code for that?


I think the main thread (the GUI thread) is the only thread allowed to repaint the widgets.
Yes, that's correct.

Can we send signals between threads.
Yes, we can.

I ll try to modify the content of the buffer with The second thread , emit signal to the main thread to repaint.[/SIZE]
That's a good approach.

halmassi
10th March 2010, 09:48
The code of the thread is lot of calculations, but we can resume it in :

while(!EOS)
{
actual_picture = this->Decoder->getNextPicture();
this->Picture->repaint(); // to be changed into : emit draw()
}

Picture is the widget drawing the pictures.
Decoder : global variable decoding a stream. EOS: end of stream
actual_picture is a global variable: the actual picture to be displayed.

I tried to use signals like I said. I added the signal draw(), and the slot drawPic():


void drawPic(){
this->Picture->repaint();
}


But this solution is not correct :
The second thread gets pictures and modifies actual_picture many times before the signal is handeled by the GUI thread.
So we have less pictures displayed than decoded.

wysota
10th March 2010, 10:21
It's enough to do update() instead of repaint() and painting will be done in the main thread. You don't need any signals or anything for that. But be aware that you are accessing the same variable (the image) from two threads, you need to do proper thread synchronization (or transfer the image between threads using events or signals which will provide synchronization for you at the expense of some overhead).

halmassi
10th March 2010, 11:03
Ok, I used the signals to transfer the images between the threads
But the number of signals handeled is still < to the emitted.
I thought that signals are stored in some of queue and they'll be all handeled. am I wrong?

halmassi
10th March 2010, 11:10
How can I wait, in the second thread, the termination of the display in the GUI thread for each picture.

wysota
10th March 2010, 12:39
But the number of signals handeled is still < to the emitted.
Maybe you are generating them faster than they can be processed.

I thought that signals are stored in some of queue and they'll be all handeled. am I wrong?
They are.

How can I wait, in the second thread, the termination of the display in the GUI thread for each picture.
There are many ways, for instance you can use a wait condition (QWaitCondition) to wait on a mutex. But what exactly is the point of doing that?

halmassi
10th March 2010, 12:48
Maybe you are generating them faster than they can be processed.

Yes. thats correct.


But what exactly is the point of doing that?

Because I generate pictures faster than I display them, I want to
generate signal with the picture
wait until the GUI thread displays the picture.
generate next signal with picture.

I dont know if it is the best way to solve the probleme

wysota
10th March 2010, 12:54
It's ok. Use a wait condition to protect the shared resource. You don't even need signals at all.

halmassi
10th March 2010, 13:18
Its OK,
but I used a mutex instead of waitcondition, and I kept my signals.

Second THREAD:
-- get picture
-- send signal with the picture
-- block on mutex

GUI Thread Slot:
-- display picture
-- wake up 2nd thread

thanks wysota

wysota
10th March 2010, 15:15
If that's all you did then it's kind of wrong. You should wake the other thread before you display the picture and you should do that only if the picture to display changed since the last time you displayed it. Otherwise you might be releasing an already released mutex or blocking the other thread unnecessary.

halmassi
10th March 2010, 15:36
No it works. Perhaps I didnt explain very well.
The GUI thread displays the picture only if he had received a signal from the other thread.
receiving the signal means two things: 1) the picture had been changed by the other thread, 2) the thread sending the signal is blocked on the Mutex
So when the GUI thread finishes displaying he woke up the other thread.

wysota
10th March 2010, 16:24
No it works. Perhaps I didnt explain very well.
The fact that it works for situations you test it against doesn't mean it will work in every conditions.


The GUI thread displays the picture only if he had received a signal from the other thread.
What if you resize the widget or obscure it with another window? It will get paint events. Won't it interfere with your mechanism?

halmassi
10th March 2010, 17:05
The mutex mecanism is in the slot drawPic() witch works only with the signal draw();
So, other painting events causes repainting widget using paintEvent(); and will repaint the last picture
and no mutex is touched. We cant release a mutex already released and same with locking it.
I ll test these conditions to know the results.

wysota
10th March 2010, 17:20
Can you show us the code from both threads that accesses the mutex?

Here is a mutex-free implementation, by the way:

connect(worker, SIGNAL(newImage(QImage)), widget, SLOT(drawPic(QImage)), Qt::BlockingQueuedConnection);
//...
void Widget::drawPic(QImage img){
m_image = img;
update();
}

void Widget::paintEvent(QPaintEvent *pe){
QPainter p(this);
p.drawImage(m_image, ...);
}

halmassi
10th March 2010, 17:49
/*initialisation*/
pthread_mutex_t Mutex;
pthread_mutex_init(&Mutex,NULL);
pthread_mutex_lock(&Mutex); // init to 0

connect(worker, SIGNAL(newImage(QImage)), widget, SLOT(drawPic(QImage)), Qt::BlockingQueuedConnection);
//...
void Widget::drawPic(QImage img){
m_image = img;
update();
pthread_mutex_unlock(&Mutex);
}

void Widget::paintEvent(QPaintEvent *pe){
QPainter p(this);
p.drawImage(m_image, ...);
}

void worker:: play(){
while(condition){
QImage img = decoder->getImage();
emit newImage(img);
pthread_mutex_lock(&Mutex); //block until waked by widget
}
}

wysota
10th March 2010, 18:12
Is that your code or mine? :) BTW. There is QMutex, you know. And if you are using a blocking queued connection the mutex is not required (provided that "worker" lives in the worker thread). Either way this solution is inefficient - your worker threads will continue to produce images regardless of the widget being able to refresh itself. update() schedules a repaint and if two update() calls occur before the repaint is actually done, they are merged into one. So it is likely to happen that you will produce 10 images but only display the last one.

halmassi
10th March 2010, 19:35
I used your code to resume mine. :)

I prefer use the Posix threads and mutexes.

How can we use a blocking queued connection.

The worker cant produce several images without displaying them all.
Because, After the first image he will block on the mutex until Widget wake him

wysota
10th March 2010, 19:42
I used your code to resume mine. :)

I prefer use the Posix threads and mutexes.
That's what Qt uses too but it gives you a nicer interface.


How can we use a blocking queued connection.
If you are not using QThread then you can't.


The worker cant produce several images without displaying them all.
Because, After the first image he will block on the mutex until Widget wake him
No, that's not true. This is because the worker thread is woken up directly after a call to update(). And a call to update() doesn't cause the widget to be painted. So the following sequence of events may happen:

worker::newImage()
widget::drawPic()
widget::update()
worker::newImage()
widget::drawPic()
widget::update()
worker::newImage()
widget::drawPic()
widget::update()
worker::newImage()
widget::drawPic()
widget::update()
worker::newImage()
widget::drawPic()
widget::update()
widget::paintEvent() <- here the widget is repainted

Especially if you're not using QThread...

halmassi
10th March 2010, 20:09
Ok thanks, You are Right.
Now I understand.
so you think, it will be better to switch to QThread.
I have another aspect of the application, where I create several Threads (the number of threads is known at the execution)
dynamicaly but using the same function:
How can I do that with QThread.

wysota
10th March 2010, 21:29
You can subclass QThread and reimplement its run() method. Remember to push the QThread object into its own thread using QObject::moveToThread() (search the forum for details on what it does and why it's needed). You can then use blocking queued connection to obtain the same effect you have now or use a mutex and optionally a wait condition to make sure the worker thread is activated only after the image is really painted.

halmassi
10th March 2010, 21:49
Ok, I ll spend time to switch to Qthread.
We close this thread, and I ll be back in few days.
Thanks for all wysota.:D