Standard Windows background color in QBackingStore application
-
Dear Qt experts,
Good luck creating great Qt software in 2018! I hope this one is appealing to answer. Well, at least I tried :-)
So, most Qt applications have QWidget as their parent, right? But I'm now looking at the rasterwindow example code. I've read somewhere in Qt assistant that because the widgets stuff is not used here this can be a little more efficient. This one directly instantiate QGuiApplication and then for rendering you can select to use QBackingStore or OpenGL. But these applications do not obey the default Windows background colors. So what is the most straightforward way to implement that? I'm using Qt 5.3.2 on GCC 5.1.0 on Windows 7. I currently have something that works but I'm aiming for you to beat me in straightforwardness.
There doesn't seem a way to obtain the default Windows colors through the Qt API, does it? So I've used the standard windows API to get that:
QRgb m_bkcolor = __builtin_bswap32( GetSysColor( COLOR_WINDOW )) >> 8;
I don't understand why that byte order is reversed from GetSysColor function that would make that byte swap function necessary.
Later in the code, in the renderNow function, the fillRect line is replaced with:
painter.fillRect(0, 0, width(), height(), QColor( m_bkcolor ) );
So, no matter how deep my ego is crushed if you can provide something better I'm a happy guy. Something like getting rid of that QRect and set a background on the QWindow itself?
Thanks,
Maarten -
A few words on how it works underneath: on Windows to show a window you first need to register a window class (with
RegisterClass()
orRegisterlClassEx()
) and then create an instance of that class (withCreateWindow()
orCreateWindowEx()
).
To register a window you need to pass a struct that describes that window and one of the fields in that struct is the brush (HBRUSH
) that is used by Windows to erase the background of the window when it needs to repaint. PassingNULL
here means the system doesn't erase the background and any drawing is done solely by the app in its paint message handler.Qt sets this brush to
GetSysColorBrush(COLOR_WINDOW)
, so the background will automatically be the right color for the window.
Painting with QPainter or OpenGL happens on top of it i.e. first the system erases the background and then you paint over it.
In the raster window example the painter draws a white rect, so it covers entire window.
If you want your window to have the default background just don't draw over it at all, or draw with transparent ink:painter.setCompositionMode(QPainter::CompositionMode_Clear); painter.fillRect(0, 0, width(), height(), Qt::red); //color param is ignored in the Clear composition mode so it can be anything
-
Hi Chris,
Thanks for taking the time to answer my question. I did tried your setCompositionMode function. But I'm getting a black background then. The OpenGLWindow example also has a black background. So, I think
GetSysColorBrush(COLOR_WINDOW)
is not actually the case here. I think it's only the case with QWidget applications but QBackingStore and OpenGL applications are different. Maybe we can squeezeGetSysColorBrush(COLOR_WINDOW)
in somewhere in the QWindow construction? -
Ah, right, the black rectangle...
TheGetSysColorBrush(COLOR_WINDOW)
is used for theQWindow
just like for widgets, so there's no problem there (try not to flush from the backing store to see that it draws fine).
The problem is (and I forgot, sorry), that when the backing store is flushed it blits its image in a replace mode, not blend, so it completely replaces the native background. So you do indeed need to clear the backing store with the system color first like you did in the original post.
I think the only way to avoid that would be to make the backing store flush its contents by blending them with the existing window contents (i.e. the system drawn background), but I'm not sure this is always possible (I don't think you can draw an OpenGL framebuffer this way). Even if it's possible I don's see any Qt API that would expose that. -
Ok, can we conclude that what I initially had is the most practical way to get where I'm looking for? i.e. QBackingStore application with standard Windows background color? When I remove the flush function my window isn't drawn. So, I think that one is necessary. The full function is shown below:
void RasterWindow::renderNow() { if (!isExposed()) return; QRect rect(0, 0, width(), height()); m_backingStore->beginPaint(rect); QPaintDevice *device = m_backingStore->paintDevice(); QPainter painter(device); painter.fillRect(0, 0, width(), height(), QColor( m_bkcolor ) ); render(&painter); m_backingStore->endPaint(); m_backingStore->flush(rect); }
-
Yeah, that looks ok.
I wasn't suggesting to remove the flush permanently, just to see that the system background is drawing when you don't flush.Btw. Keep in mind that the system color can change at any time so if you want to be 100% correct you should either get it every time you render or handle the
WM_SYSCOLORCHANGE
andWM_THEMECHANGED
messages.