[Qt-interest] Problem drawning in the GUI Thread with Data from another Thread

Malyushytsky, Alex alex at wai.com
Fri Jul 3 03:40:49 CEST 2009


Just to add, I don't think You can call an
update();
as you do cause it would be called from the non-gui thread.

Instead you can either use postEvents or just emit signal

Which you connect to update() with Qt::QueuedConnection flag.


Regards,
    Alex



-----Original Message-----
From: qt-interest-bounces at trolltech.com [mailto:qt-interest-bounces at trolltech.com] On Behalf Of Malyushytsky, Alex
Sent: Thursday, July 02, 2009 6:05 PM
To: qt-interest at trolltech.com
Subject: Re: [Qt-interest] Problem drawning in the GUI Thread with Data from another Thread

Please don't reply to me personally, reply to the mailing list.

You did not do what I suggested.
What you did not do, you did not put any safety guards, so nothing really changed.
You need to put guard to insure that you don't access/change the same data from two different threads at the same time.

In your case I would use QMutex. Just to explain an idea.

QMutex.mutex; // somewhere
........
void PopGraph::paintEvent(QPaintEvent*){
    QPainter painter2(this);
    mutex.lock();
    painter2.drawImage(QPoint(0,0), *buffer);
    mutex.unlock();
}


void PopGraph::graphUpdate(){
   delete(buffer2);
   buffer2 = new QImage(size(), QImage::Format_ARGB32);
......
.......

/* when image is ready
    mutex.lock();
    qSwap(buffer2, buffer);
    mutex.unlock();

/* don't forget to enforce Widget update
   .........
}

Also I would suggest you to read about Threading Classes in QT.
Especially Synchronizing Threads chapter.

Regards,
    Alex



From: qt-interest-bounces at trolltech.com [mailto:qt-interest-bounces at trolltech.com] On Behalf Of O Caldas
Sent: Thursday, July 02, 2009 5:49 PM
To: qt-interest at trolltech.com
Subject: Re: [Qt-interest] Problem drawning in the GUI Thread with Data from another Thread

ive tyed this


void PopGraph::paintEvent(QPaintEvent*){
    QPainter painter2(this);
    buffer2 = buffer;
    painter2.drawImage(QPoint(0,0)
, *buffer2);
}

and also tryed this


void PopGraph::paintEvent(QPaintEvent*){
    QPainter painter2(this);
    temp = buffer2;
    buffer2 = buffer;
    buffer = temp;
    painter2.drawImage(QPoint(0,0), *buffer2);

}

and in the two methods the program still crashs
and btw, the calculation part is correct, the application only crash when is drawning the graphic

2009/7/2 Malyushytsky, Alex <alex at wai.com>
I am not sure I correctly followed your design.
I assume graphUpdate is called  from the working thread (through signal slot mechanics or directly).
In this case you have to make sure graphUpdate is not running when painter2.drawImage(QPoint(0,0), *buffer);
is executed.
Otherwise for example buffer may be deleted when drawImage is still drawing. This means application crash or termination (well exception handling would be great any way).

QMutex or other thread safety mechanics can be a friend here.

The simplest approach would be to lock  paintEvent and graphUpdate with mutex, but I would suggest to add some code make them actually run simultaneously, in order to allow graphUpdate to draw into its buffer while Widget is painting.

In this case 2 buffers are required. One buffer is used to draw from the paintEvent. Another buffer is used by graphUpdate to draw into.

Widget itself paints from the buffer any time it needs an update (no matter who caused the update).
graphUpdate is always drawing into second buffer, but when it finish drawing and the image is ready to be shown, it swaps the buffers (it means just changing pointers) and then forces Widget update (through thread safe procedures, like Queued connection).



You still will need to guard the pointers when swapping them and in the paintEvent, but this operations is very fast.

Hope this helps,
    Alex







From: qt-interest-bounces at trolltech.com [mailto:qt-interest-bounces at trolltech.com] On Behalf Of O Caldas
Sent: Thursday, July 02, 2009 4:33 PM
To: qt-interest at trolltech.com
Subject: Re: [Qt-interest] Problem drawning in the GUI Thread with Data from another Thread

oh, and here is the code of the paintEvent() method:

void PopGraph::paintEvent(QPaintEvent*){
   QPainter painter2(this);
   painter2.drawImage(QPoint(0,0), *buffer);

}

buffer is an atribute QImage* of the class
2009/7/2 O Caldas <caldaz.sheep at gmail.com>
hi, my application draws a horizontal graphics of the data generated by the
thread. it uses vectors that are directly modified by the worker thread. ive
tryed to send the vectors in the signal but it stays the same.

here is the code that create the image that will be draw :

void PopGraph::graphUpdate(){
   delete(buffer);
   buffer = new QImage(size(), QImage::Format_ARGB32);
   QSize d = size();
   long long nMax = (int)ceil(log10(_d->getPopulationMax()));
   double yscale = d.height()/(double)nMax;
   int tickscale = 1;

   // draw horizontal tick marks
   Qt::PenStyle style = Qt::PenStyle(Qt::SolidPattern);
   Qt::PenCapStyle cap = Qt::PenCapStyle(Qt::FlatCap);
   Qt::PenJoinStyle join = Qt::PenJoinStyle(Qt::MiterJoin);
   QPainter painter(buffer);
   painter.fillRect(rect(), Qt::white);

   painter.setPen(QPen(Qt::black, 1, style ,cap,join));
   for (int i=0; i<=nMax; i++) {
       int y = size().height()-(int)(i*yscale);
       painter.drawLine(0, y, d.width(), y);
       char st[6] = "10^";
       strcat(st, intToStr(i));
       painter.drawText(0, y, QString(st));//("10^" + String.valueOf(i), 0, y);
   }
   // draw vertical (date) tick marks
   long long lastday = -1;
   vector<long long> hours = _d->getPopulationHours();
   int end = d.width();
   if (hours.size()<d.width())
       end = hours.size();
   for (int i=0; i<end; i++) {
       if (lastday != hours[hours.size()-i-1]/24) {
           lastday = hours[hours.size()-i-1]/24;
           painter.drawLine(d.width()-i, 0, d.width()-i, d.height());
           painter.drawText(d.width()-i+1, 11, QString( intToStr(lastday+1)));
       }
   }
   // draw antigen level curves
   vector<Antigen*>* pops = _d->getAntigens();
   vector<QColor> popcolors = _d->getAntigenColors();
   int s = _d->getAntigenLevels().size();

   for (int j=0; j<s; j++) {
       painter.setPen(popcolors[j]);
       // draw legend
       painter.drawText(0, painter.fontMetrics().ascent()*(j+1),QString(pops->at(j)->getName()->data()));

       // draw curves
       vector< long long > data = _d->getAntigenLevels()[j];
       end = d.width();
       if (data.size()<(unsigned )d.width())
           end = data.size();
       for (int i=0; i<end; i+=2)  // just plot every other hour
           if (data[data.size()-i-1]>0){

               painter.drawRect(d.width()-i-1,
                       d.height()-1-(int)
                       (log10(data[data.size()-i-1])*yscale),
                       2,
                       2);
           }
   }
   // draw T cell level curves
   s = _d->getTCellColors().size();
   for (int j=0; j<s; j++) {
       painter.setPen(_d->getTCellColors()[j]);
       // draw legend
       if (_d->getTCells()->size()<10)
           painter.drawText(0, painter.fontMetrics().ascent()*(j+1+(_d->getAntigens()->size())), QString(_d->getTCells()->at(j)->getName()->data()));
       // draw curves
       vector<long long> data = _d->getTCellLevels()[j];
       end = d.width();
       if (data.size()<(unsigned )d.width()) end = data.size();
       for (int i=0; i<end-1; i++){
           if ((data[data.size()-i-1]>0) && (data[data.size()-i-2]>0)){
               painter.drawLine(d.width()-i-1,
                       d.height()-(int)
                       (log10(data[data.size()-i-2])*yscale),
                       d.width()-i,
                       d.height()-(int)
                       (log10(data[data.size()-i-1])*yscale));

           }
       }

   }
//    if(contador == 1){
//        contador =0;
       update();

//    }

}

thanks for your help!
2009/7/2 Sean Harmer <sean.harmer at maps-technology.com>

On Thursday 02 Jul 2009 06:27:05 O Caldas wrote:
> Hi, im working on a project where I have two threads, one Worker thread,
> that do some calculations,
> and one GUI Thread, that draw some graphics in a QWidget.
>
> The Worker thread is a class that inherits from QThread, and the GUI Thread
> is the main thread obviously.
>
> Im geting the worker thread to send a signal every end of calculation step,
> and connecting this signal
> to the method that draws the graphic in the GUI Thread
>
> like this:
>
> connect(calculator, SIGNAL(calculated(),this,
>             SLOT(updateView()));
>
> The problem is, when the calculation gets to fast, the GUI thread just bugs
> and the program terminates.
> It seems that the calculator thread starts sending too much signals and the
> GUI thread cant handle it,
> or something like this.
>
> i've tryed to use Qt::BlockingQueuedConnection but it is too slow.
>
> if someone knows what im doing wrong, or the right way to build a program
> like that please help!
It sounds like you are taking the right general approach. What does your app
have to do in order to visualise the results? How do you get the data to the
main thread as I notice your signal does not marshall any data across to it?

If the visualisation in the main thread really is very expensive to perform
then maybe you should only visualise every n'th frame. You could maybe make
this an option that the user can set depending upon the performance of their
graphics subsystem.

Sounds like you have identified the bottle neck in your app. If you provide
some more information we might be able to help you improve it.

Cheers,

Sean

_______________________________________________
Qt-interest mailing list
Qt-interest at trolltech.com
http://lists.trolltech.com/mailman/listinfo/qt-interest



--
Daniel Mendes Caldas



--
Daniel Mendes Caldas

---------------------------------------------------------------------------------------------------
Weidlinger Associates, Inc. made the following annotations.

"This message and any attachments are solely for the intended recipient and may contain confidential or privileged information. If you are not the intended recipient, any disclosure, copying, use, or distribution of the information included in this message and any attachments is prohibited. If you have received this communication in error, please notify us by reply e-mail and immediately and permanently delete this message and any attachments. Thank you."

"Please consider our environment before printing this email."

_______________________________________________
Qt-interest mailing list
Qt-interest at trolltech.com
http://lists.trolltech.com/mailman/listinfo/qt-interest



--
Daniel Mendes Caldas

"This message and any attachments are solely for the intended recipient and may contain confidential or privileged information. If you are not the intended recipient, any disclosure, copying, use, or distribution of the information included in this message and any attachments is prohibited. If you have received this communication in error, please notify us by reply e-mail and immediately and permanently delete this message and any attachments. Thank you."

_______________________________________________
Qt-interest mailing list
Qt-interest at trolltech.com
http://lists.trolltech.com/mailman/listinfo/qt-interest

"This message and any attachments are solely for the intended recipient and may contain confidential or privileged information. If you are not the intended recipient, any disclosure, copying, use, or distribution of the information included in this message and any attachments is prohibited. If you have received this communication in error, please notify us by reply e-mail and immediately and permanently delete this message and any attachments. Thank you."




More information about the Qt-interest-old mailing list