1 Attachment(s)
Printing QwtPlot on QImage from inside a thread
Hi, I am new to Qwt,,
I am facing a problem in which I get crash when I call QwtPlot::replot or QwtPlot::Print from inside a thread. My use case is, I need to send data from a thread, and to speed up things I wanted to print the plot and send the image.
But when I call replot or print,,, I get the following as attached in the image.
I guess it is because we cant paint to QPixmap from thread, and I have seen that QwtPlot uses Qpixmap as cache. But i also tried disabling the caching by -
plot->canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached,fals e); but still getting crash.
From call stack I see that qwt accesses font metric and -
In QWidget::metric() --
HDC gdc = qt_win_display_dc();
In Q_GUI_EXPORT HDC qt_win_display_dc() // get display DC
{
Q_ASSERT(qApp && qApp->thread() == QThread::currentThread());
Thats where it fails.
I searched the forum but cudnt get a solution to my problem. I just need the plot in QImage inside the thread. I need it to send to some other client. I am very much stuck and help will be highly appreciated.
Re: Printing QwtPlot on QImage from inside a thread
QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Originally Posted by
dbzhang800
QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.
I know that. But there must be some way I can use QImage from the thread to print the plot ?
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Originally Posted by
aamer4yu
I know that. But there must be some way I can use QImage from the thread to print the plot ?
so why are you still trying to use QwtPlot in your thread ?
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Originally Posted by
aamer4yu
But there must be some way I can use QImage from the thread to print the plot ?
I never tried it myself, but QwtPlot::print() in combination with a QImage should be possible. But moving an operation to a second thread doesn't speed up anything.
Guess what you want to do is to distribute the composition of the plot to several threads running parallel. This is possible for rendering a spectrogram ( implemented in trunk ), but not for operations using a QPainter. There might be operations like polygon clipping, that could benefit from multithreading, but ...
Anyway if you have performance issues I would expect more succes from algorithmic strategies (like f.e. incremental painting, introducing level of details).
Uwe
Re: Printing QwtPlot on QImage from inside a thread
Quote:
I never tried it myself, but QwtPlot::print() in combination with a QImage should be possible.
No it was not working as far as I tried. however QwtPlot::drawCanvas() was working but I was not getting correct image.
Well, my problem is not much of multi threading, I read data from a device in a thread and need to send spectrogram of it to some client. Since the class which reads data from device is in thread, I need to use plot from that thread itself.
I would say if you can work around this problem it would be really nice, since many others might be facing this problem. It would be nice if one could call Qwt::print() from thread on a QImage.
Currently I tried a hack - I send signal to main class, and callback the sending QImage function from that main thread,, need to test,, but still its a hack :(
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Well, my problem is not much of multi threading, I read data from a device in a thread and need to send spectrogram of it to some client. Since the class which reads data from device is in thread, I need to use plot from that thread itself.
This is no argument for distributing paint operations across different threads.
But if you mean by spectrogram the same what is meant by a QwtPlotSpectrogram you have to render a QImage by mapping values int colors. As a QImage is simply an array of RGB values I don't see why you need the QwtPlot widget for this or why it shouldn't be possible to do it in a separate thread.
Quote:
I would say if you can work around this problem it would be really nice, since many others might be facing this problem.
Well if you would post your requirements ( of your application not of your implementation) I could try to understand if it something of common interest. But at the moment all I see is that you have problems with an implementation using the wrong calls for a situation I don't know.
Uwe
Re: Printing QwtPlot on QImage from inside a thread
Well, my requirement for application is as follows -
Read data from a spectrometer and plot its values(wavelength vs intensities) on a QImage / QPixmap.[This is a simple 2D plot, NOT 3D] The values we get from spectrometer are array of double. The reading from spectrometer is done in a thread.
I could very well send the data to client and do the rendering there. But to speed up things, I wanted to send image instead of data from the thread where reading from spectrometer is taken.
Current implementation-
Using QwtPlot / QwtPlotCurve to plot the same. I call QwtPlotCurve::setData to set the values and try to get a print of QwtPlot on Qimage.
I hope am a bit more clear this time. I didnt look into QwtPlotSpectrogram, since I needed a simple plot of intensities for given wavelengths...something like scatter diagram in excel.
Am not sure if other class will do better job.
Can you help ??
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Originally Posted by
aamer4yu
Well, my requirement for application is as follows -
Read data from a spectrometer and plot its values(wavelength vs intensities) on a QImage / QPixmap.
Isn't the requirement of your application to plot to screen - if not why do you need a QwtPlot widget ?
Isn't QPixmap/QImage only part of your implementation trying to speed up the rendering process ?
Quote:
...something like scatter diagram in excel.
Rendering your curve points to an image and flushing the image for each point doesn't speed up anything - in fact it is probably the worst (horrible slow) implementation I can imagine.
If your axis scales don't change for each point you could try to paint your points incrementally. This is supported on X11 (best) and on platforms using the raster paint engine ( f.e windows). Have a look at the realtime example and check if this is the performance you expect from your application.
In qwt trunk you find an oscilloscope example, that shows an implementation with a thread producing values and how they are painted incrementelly in the GUI thread.
But If you don't have many points and high refresh rates (several frames per second) I wouldn't spend much time on optimizations and use QwtPlot in its default settings (beside disabling the paint cache) - using replot for each update.
Uwe
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Isn't the requirement of your application to plot to screen - if not why do you need a QwtPlot widget ?
Isn't QPixmap/QImage only part of your implementation trying to speed up the rendering process ?
Well, we have a separate application for gui. This thread is another app which is meant for communicating with devices. Now when you get data from the device we need to send it to the gui app through sockets. The image is finally shown there. So using QImage is part of speeding things for data transfer since data of Qimage will be smaller than the actual data.
Quote:
Rendering your curve points to an image and flushing the image for each point doesn't speed up anything - in fact it is probably the worst (horrible slow) implementation I can imagine.
Well am not plotting in the same app as explained above.. and sending QImage does speed up things.
I have tried a hack.. from the thread I emit a signal to main thread of device app. This then calls a function of that device class which sends the qimage to gui app through socket. and this is working and it does speed up things. But what I want is,, instead of emitting signal and calling the function from main thread, I want to directly call the function.
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Well am not plotting in the same app as explained above.. and sending QImage does speed up things.
Assuming your plot has a size of 500x500 pixels your image will have a size of 250000 * 4 bytes = 1MB. After you rendered your point to the image and sent the complete image over the socket to your GUI application the image needs to be sent through the graphics pipeline of Qt.
Do you really expect, that this is faster than sending 2 doubles over a socket and painting it incrementally ( skipping a lot of steps of the Qt graphics pipeline ) to the plot widget ? F.e for a XCross symbol on X11 this means only 2 x XDrawLine - probably hardware accelerated.
Quote:
I have tried a hack.. from the thread I emit a signal to main thread of device app. This then calls a function of that device class which sends the qimage to gui app through socket.
Does it mean you have an application with a GUI thread, that has an invisible plot widget. The only purpose of this widget is, that you want to render an offscreen image that is sent to a different aplication, that is able to display this image - probably not in a QwtPlot widget ?
Quote:
... and this is working and it does speed up things
Compared to what ?
Anyway - Qwt isn't able to render a plot without a plot widget. The reason for this is that many attributes of the plot are stored in members of the plot widget ( having a QwtPlotScene object like in the QGV framework is on my TODO list ). So you need to have a plot widget and this needs to be allocated in the GUI thread.
But QwtPlot::print to a QImage should be possible from another thread. If it doesn't work in your application you need to report what the problems are in detail.
But IMO the overhead of rendering the image in a different thread is the last thing you have to worry.
Uwe
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Does it mean you have an application with a GUI thread, that has an invisible plot widget. The only purpose of this widget is, that you want to render an offscreen image that is sent to a different aplication, that is able to display this image - probably not in a QwtPlot widget ?
Yes.
But the plotting is not incremental. One takes a snap of spectrum from the spectrometer, makes 3 different curves based on previous data. and plots it. Each time whole new set of data is computed.
Quote:
Assuming your plot has a size of 500x500 pixels your image will have a size of 250000 * 4 bytes = 1MB.
Well after paying attention to this, I found indeed sending data through image was larger. With image I found the message to be 940KB while without image it was 153KB. Seems its a matter of how the data was parsed. All messages are sent in xml. So in case of image, image data was stored in CDATA section. One CDATA for 3 curves, while in other case, values were sent as attribute, double array converted in base64. So it seems things were faster with image even with larger data because of xml parsing.
Quote:
But IMO the overhead of rendering the image in a different thread is the last thing you have to worry.
I admit you are right.
Quote:
But QwtPlot::print to a QImage should be possible from another thread. If it doesn't work in your application you need to report what the problems are in detail.
The Qwt::print crash is still a problem. Lets say I function device::sendData, which calls qwtPlot::print() function. device class is a member of another class deviceManager which is a thread. owner of deviceManager is say mainManager. Now say if i call device::sendData from another function device::readSpectrum then program crashes in device::sendData.
But if I emit a signal from device::readSpectrum to a slot(slotSendData) in mainManager, and call device::sendData from the mainManager::slotSendData, then it works fine.
The call stack in case of crash is -
Code:
> QtCored4.dll!qt_message_output(QtMsgType msgType=QtFatalMsg, const char * buf=0x097e4260) Line 2019 C++
QtCored4.dll!qFatal(const char * msg=0x67238224, ...) Line 2216 + 0x29 bytes C++
QtCored4.dll!qt_assert(const char * assertion=0x65914cac, const char * file=0x65914c90, int line=902) Line 1786 + 0x16 bytes C++
QtGuid4.dll!qt_win_display_dc() Line 902 + 0x47 bytes C++
QtGuid4.
dll!QWidget::metric(QPaintDevice::PaintDeviceMetric m
=PdmDpiY
) Line
1658 + 0x5 bytes C
++ QtGuid4.
dll!QPaintDevice::logicalDpiY() Line
96 + 0x16 bytes C
++ qwtd5.dll!03550fc8()
[Frames below may be incorrect and/or missing, no symbols loaded for qwtd5.dll]
qwtd5.dll!03550697()
qwtd5.dll!03552032()
qwtd5.dll!0351ad2c()
qwtd5.dll!0354a55d()
ntdll.dll!7c9623e0()
ntdll.dll!7c91005d()
msvcr80d.dll!_free_base(void * pBlock=0x095ee988) Line 109 + 0x13 bytes C
qwtd5.dll!03549373()
msvcr80d.dll!_free_dbg(void * pUserData=0x00000005, int nBlockUse=25) Line 1225 + 0x7 bytes C++
msvcr80d.dll!free(void * pUserData=0x012fa350) Line 1178 + 0xb bytes C++
qwtd5.dll!03590016()
qwtd5.dll!0358f699()
qwtd5.dll!0351c80f()
qwtd5.dll!0351ecdb()
qwtd5.dll!03590132()
qwtd5.dll!0356addd()
qwtd5.dll!03566606()
In short, you are right about image overhead. And I have given some detail about the print crash. Can you help ,,, may be I am missing something or may be doing some mistake.
Re: Printing QwtPlot on QImage from inside a thread
Quote:
All messages are sent in xml.
Well, this is second in the ranking of really slow implementations. The bottleneck of this solution is the XML parser not the amount of data you have to send over the socket. If you have ever worked with SVG you will know that the parser takes much longer, than the painting itsself.
Why don't you simply send the points as they are - everything could be so easy.
All I can see is, that the crash is in Qt, but without knowing the Qwt calls in your stack I can't tell you if it is possible to avoid the QFont initialization.
Check the paint device in the QFont constructor: is it your image or one of the widgets ( or internal pixmaps ) of your plot widget ?
Uwe
Re: Printing QwtPlot on QImage from inside a thread
Quote:
Why don't you simply send the points as they are - everything could be so easy.
I wish I could.. but cant.
Quote:
Check the paint device in the QFont constructor: is it your image or one of the widgets ( or internal pixmaps ) of your plot widget ?
I will do that when I get time, right now I got busy with some other work.
Thanks a lot for your help till now :)