PDA

View Full Version : gdi and QPainter



Elder Orb
1st September 2006, 10:53
Is it possible to use subj in the same paintEvent without flickering ?

e8johan
4th September 2006, 07:21
The best way to avoid flickering is to use double buffering - paint to a QPixmap and copy to the screen in the paint event.

wysota
4th September 2006, 11:55
What do you need GDI for? You can surely use it to paint on an offscreen device and then convert the result to QImage or QPixmap and render them using QPainter as Johan suggested. I wouldn't mix the two approaches (QPainter and GDI) directly though... Especially in Qt4.

Elder Orb
4th September 2006, 19:53
I need gdi for visualizing frame grabber's output. If I try to do it with help of QImage - all is terrible slow. From other side I need to show different graphical and text data over the grabber's capture and it might be useful to do it with QPainter because its cool features.

wysota
4th September 2006, 21:10
Maybe you should try to optimise it? Which Qt version are you using? Have you tried tweaking by changing window flags?

Elder Orb
5th September 2006, 09:01
Maybe you should try to optimise it? Which Qt version are you using? Have you tried tweaking by changing window flags?

1.
I tried!.. What can be more optimal than just drawing QImage in paintEvent ? (QImage is created with QImage ( uchar * data, int width, int height, Format format ) constructor
over the data, filled up with a thread of acquisition library. And all it was done one time in other place than paintEvent.)
2. Qt 4.1.4
3. I tried Qt::WA_OpaquePaintEvent, Qt::WA_NoSystemBackground, Qt::WA_StaticContents, Qt::WA_PaintOnScreen and setAutoFillBackground(false);
in different combinations. No one was really helpful. (But if I use gdi bitmap functions to paint grabbers's data I have to set Qt::WA_NoSystemBackground and Qt::WA_PaintOnScreen or overload paintEngine() = 0 const in order to not have a flickering)

wysota
5th September 2006, 09:03
How do you construct the QImage object?

Elder Orb
5th September 2006, 09:48
How do you construct the QImage object?

here is the sample:

bool mvsstand::eventFilter(QObject *o, QEvent *e)
{
if(o == w) {
if(e->type() == QEvent::Paint) {

QWidget *that_widget = w;
w = 0;
QApplication::sendEvent(o, e);
w = that_widget;

// due code above it will be called AFTER paintEvent

CProImage* const image = acquisitor->getImage();

QTime t;
t.start();

QImage img((uchar*)image->GetData(), image->GetWidth(), image->GetHeight(), QImage::Format_Indexed8);
img.setNumColors(256);
for(int i = 0; i < 256; ++i)
img.setColor(i, QColor(i, i, i).rgb());

QPainter p(w);

qDebug("time: %d", t.elapsed()); // usually from 0 to 16 ms.

p.drawImage(0, 0, img); // the most time-consuming operation

return true;
}
return false;
}

wysota
5th September 2006, 10:21
What's the sendEvent() here for?

Some things might be optimised here. You are wasting time creating (and destructing) the image object again and again. I think you should have a single image which you reuse everytime and use QImage::loadFromData() to fill it wih contents.

Also make sure you have Qt::WA_OpaquePaintEvent and Qt::WA_NoSystemBackground attributes set for the widget (I wouldn't expect miracles here, though -- using native painting methods it will probably be much faster).

Edit: Qt docs suggest that using native methods along Qt methods should be safe, you might just need to set some attributes for the widget (like PaintOnScreen for X11).

Elder Orb
5th September 2006, 10:52
What's the sendEvent() here for?

Some things might be optimised here. You are wasting time creating (and destructing) the image object again and again. I think you should have a single image which you reuse everytime and use QImage::loadFromData() to fill it wih contents.

Also make sure you have Qt::WA_OpaquePaintEvent and Qt::WA_NoSystemBackground attributes set for the widget (I wouldn't expect miracles here, though -- using native painting methods it will probably be much faster).

Edit: Qt docs suggest that using native methods along Qt methods should be safe, you might just need to set some attributes for the widget (like PaintOnScreen for X11).

1. usually eventFilter stands between event emitter and emit receiver. But what if I want to do in eventFilter some operations after event will be processed by receiver ? I such a case
this construction works
"
QWidget *that_widget = w;
w = 0;
QApplication::sendEvent(o, e);
w = that_widget;
"

sendEvent here is for receiver to receive its event.
w = 0 is just prohibits eventFilter to reenter...
btw its not my idea. Somewhere in Qt demos I found such approach and it works.

2. I know I wasting time on creating and destructing, but it is just example, although creating and destruction take only 0-16 ms.

3. I also thought that all must be ok mixing native and qt paint engines and I saw that it works together, but with a great flickering which take place when QPainter just initialized.

here is the one more sample to show the problem

QPainter p;
p.setBackgroundMode(Qt::TransparentMode);
p.setBrush(Qt::NoBrush);
p.begin(w);
p.fillRect(0,0, 60, 100, Qt::blue);
p.end();

HDC hdc = w->getDC();
HBRUSH hBrush = CreateSolidBrush(RGB(0xff,00,00));

int width = w->width();
int height = w->height();

RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = width;
rect.bottom = height;

FillRect(hdc, &rect, hBrush);
w->releaseDC(hdc);

(window flags are set to
w->setAttribute(Qt::WA_OpaquePaintEvent); // фон становится чёрным, перерисовка кривая
w->setAttribute(Qt::WA_NoSystemBackground); // то же самое
w->setAttribute(Qt::WA_StaticContents); // фон нормальный, перерисовка кривая
w->setAttribute(Qt::WA_PaintOnScreen); //то же самое
w->setAutoFillBackground(false); // то же самое.. если true - аналогично
)

wysota
5th September 2006, 15:02
1. usually eventFilter stands between event emitter and emit receiver. But what if I want to do in eventFilter some operations after event will be processed by receiver ? I such a case
this construction works
"
QWidget *that_widget = w;
w = 0;
QApplication::sendEvent(o, e);
w = that_widget;
"

sendEvent here is for receiver to receive its event.
w = 0 is just prohibits eventFilter to reenter...
btw its not my idea. Somewhere in Qt demos I found such approach and it works.
How about just returning false (or better yet BaseClass::eventFilter(o,e)) from the eventFilter() method? It basically forwards the event to the receiver without any additional hacks.

Edit: Ah... I missed the word "after". I'd use a timer with 0 timeout here instead. BTW. Why don't you just move all the functionality to the widget itself? You'd end up with a much simpler code.


2. I know I wasting time on creating and destructing, but it is just example, although creating and destruction take only 0-16 ms.
16ms*25fps = 400ms, so only half of the time remains for showing images with 25fps.


3. I also thought that all must be ok mixing native and qt paint engines and I saw that it works together, but with a great flickering which take place when QPainter just initialized.
It is probably clearing the background. You must be missing a proper attribute.


w->setAttribute(Qt::WA_OpaquePaintEvent); // фон становится чёрным, перерисовка кривая
w->setAttribute(Qt::WA_NoSystemBackground); // то же самое
w->setAttribute(Qt::WA_StaticContents); // фон нормальный, перерисовка кривая
w->setAttribute(Qt::WA_PaintOnScreen); //то же самое
w->setAutoFillBackground(false); // то же самое.. если true - аналогично
)
You must be missing the proper combination here. When do you set those attributes? Before "w" is shown or afterwards?

Elder Orb
5th September 2006, 20:07
How about just returning false (or better yet BaseClass::eventFilter(o,e)) from the eventFilter() method? It basically forwards the event to the receiver without any additional hacks.

Edit: Ah... I missed the word "after". I'd use a timer with 0 timeout here instead. BTW. Why don't you just move all the functionality to the widget itself? You'd end up with a much simpler code.


Agree. But sometimes I just too lazy to subclass :)




16ms*25fps = 400ms, so only half of the time remains for showing images with 25fps.


I've tried to get out all besides "painter.drawImage(&image);" from the paintEvent but it was still too slow. btw I unsure that grabber makes a frame ready (callback function to trigger) with a frequancy 25. I have to check it tomorrow, maybe I should skip some frames... Tnx for idea!




It is probably clearing the background. You must be missing a proper attribute.

You must be missing the proper combination here. When do you set those attributes? Before "w" is shown or afterwards?


I've tried almost all combinations for all flags I thought important enough (those 4 flags). I just don't know what to try else..

wysota
5th September 2006, 20:36
Agree. But sometimes I just too lazy to subclass :)
You'd achieve better results, some function calls would be omitted.


I've tried to get out all besides "painter.drawImage(&image);" from the paintEvent but it was still too slow. btw I unsure that grabber makes a frame ready (callback function to trigger) with a frequancy 25. I have to check it tomorrow, maybe I should skip some frames... Tnx for idea!
In general I would first calculate the frequency of updates (fps) and then I'd use a timer set to timeout according to this frequency. Then, in the timeout slot I would grab a frame which needs to be displayed now (you'll probably have to skip a frame now and then), I'd store a ready image and then call update() to repaint the widget. The paint event would just have to render the widget. You just have to find a way to calculate how many frames should be skipped (you can probably to that by substracting times of subsequent timeouts as the timeouts won't be exactly as you set them -- they'll be not more often than what you set).




I've tried almost all combinations for all flags I thought important enough (those 4 flags). I just don't know what to try else..

Try using OpenGL. Use a QGLWidget and draw images as textures (I think that's the way it is usually done, others might know better, I've never tried this). As OpenGL is (probably) hardware accellerated, this should go much faster and you'll waste less CPU cycles for that and with a good graphics cards you get different "manipulations" of the movie for free -- you'll be able to rotate, mirror, fade, crop, resize, colorise, etc. without much effort, you can even display your move as a rotating cube :) And you should definitely buffer a number of frames ahead. I'm sure your gfx board will be able to store more than one texture at a time ;)

Elder Orb
29th September 2006, 13:43
I'm sorry , but i've missed you last reply and see it just now ;). Although I've solved my problem with a low-level win api calls (without QPainter at all), I stiil interesting in pure-qt solution. Some time ago I tried QGLWidget, but probably did it in a wrong way, because no acceleration has been acquired... :( I didn't know about gl functions using, so I used QPainter (which in gl widget must be gl-accelerated ? ).. What should I do to achieve opengl-accelerated QImage output ?