Qt - avoid white background when resizing (set immediately a background color) - c++

The question is in bold at the end so please read this as a whole.
I have a QAbstractScrollArea widget that I manually and completely render in the OnPaint() event.
In its constructor I set
setAttribute( Qt::WA_OpaquePaintEvent, true );
setAttribute( Qt::WA_NoSystemBackground, true );
setStyleSheet( "QWidget { background-color: rgb(0,0,77); }" );
and the paint event looks like this:
void MyArea::paintEvent (QPaintEvent *event) {
QPainter view(viewport());
view.fillRect(rect(), backgroundBrush);
renderedPixmap = heavyAndSlowRenderingOnAPixmap();
view.drawPixmap(viewRect, renderedPixmap, pixmapRect);
}
as you can see there's a "slow" function involved to render stuff into a pixmap.
The problem is: when I resize the window, I can see for a moment a white flickering in the area not yet redrawn
I know I can't avoid the white region until the viewport has been redrawn, but I'd like to draw that white region with the background color immediately.
So here's the question: can I display a background color immediately before the heavy-pixmap rendering?
I can't seem to find a way to achieve this.. it seems all graphics operations are buffered and then immediately bit-blitted to the screen together. I'm using Windows 8.1 x64 and Qt5. Any way to immediately draw the background color and then proceed with the rest of the rendering?

The best solution would be to move the expensive rendering out of the paintEvent(), potentially into another thread. You'll want to cache the rendered pixmap anyway. Update that pixmap when really needed and then call update() to trigger a repaint.

Related

Drawing with wxWidgets

I'm creating an application for capturing the screen or a part of it, and for that I need to select the part of the screen I want to capture.
The idea is to create a fullscreen semi-transparent window and draw thing on it so the user can see what he is doing. I have a working implementation where the selected area is dislayed as a wxPanel, but I want to rewrite it to paint everything to the main wxPanel manually.
frame = new wxFrame(NULL, -1, "", wxPoint(0,0), wxSize(0,0), wxSTAY_ON_TOP|wxFRAME_NO_TASKBAR|wxFRAME_SHAPED);
panel = new wxPanel(frame, -1);
SetTopWindow( frame );
panel->Bind(wxEVT_KEY_DOWN, &wxMiniApp::OnKeyDown, this);
panel->Bind(wxEVT_KEY_UP, &wxMiniApp::OnKeyUp, this);
panel->Bind(wxEVT_LEFT_DOWN, &wxMiniApp::OnMouseStart, this);
panel->Bind(wxEVT_LEFT_UP, &wxMiniApp::OnMouseEnd, this);
panel->Bind(wxEVT_MOTION, &wxMiniApp::OnMouseMove, this);
panel->Bind(wxEVT_PAINT, &wxMiniApp::OnPaintPanel, this);
panel->SetFocus();
panel->SetBackgroundColour(wxColor(0,0,0,100));
panel->SetBackgroundStyle(wxBG_STYLE_PAINT);
frame->SetBackgroundColour(wxColor(0,0,0,0));
frame->Show();
frame->ShowFullScreen(true);
I use panel to capture the mouse/keyboard events and I want to do the painting there too, but I don't know how to do this.
I want to have semi-transparent black background and the selected area should be transparent white or even fully transparent (without the black background).
The result of every attempt was that either it draw a solid color background or I got the famous WinXP lag effect.
Can you give me a basic OnPaintPanel(wxPaintEvent &event) implementation using x, y, with, height of the selected area (can be in wxPython too, if you're more comfortable with it) ?
You need to use SetTransparent(), wxDC doesn't support using alpha transparency anyhow (wxGraphicsContext does but it wouldn't help here).

Clear Transparent Background for QWidget

I have a widget as on overlay for another widget. The transparency works fine, as long as I don't clear the background of the overlay.
But I have to clear the widget to acutalize the displayed "effects". I tried to solve the inital problem (the background getting the default color) like described in 20848594, but except changing the color to black it had no effect...
Has anyone an idea why the widget which should be transparent does not display the underlaying widget(s)?
Here the code:
SudokuMarkerOverlayWidget::SudokuMarkerOverlayWidget(QWidget* parent, uint const fieldSize) : QWidget(parent)
{
// Translucent should be the correct one
setAttribute(Qt::WA_TranslucentBackground);
//setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TransparentForMouseEvents);
...
}
void SudokuMarkerOverlayWidget::paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.setRenderHint( QPainter::Antialiasing );
// Tried this too, no difference
// painter.setCompositionMode(QPainter::CompositionMode_Source);
// painter.fillRect( this->rect(), Qt::transparent );
painter.setCompositionMode(QPainter::CompositionMode_Clear);
painter.eraseRect( this->rect() );
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
...
}
EDIT:
Just noticed, using CompositionMode Source instead of SourceOver for my semi-transparent painting (gradient seen in first image) also causes them to be a red-to-black-gradient instead of red-to-transparent.
That means every transparency except the inital by WA_TranslucentBackground or WA_NoSystemBackground isn't working.
Widgets in Qt can be either a 'native' or 'alien' type. In one case they're a separate operating system window. In some operating systems a transparent window isn't supported.
You might want to consider using QML if you're after fancy visual effects. That's what it was designed for.

How to intercept mouse events of a transparent window?

I have a transparent window (created with WS_EX_LAYERED) and I'd like to receive mouse events of the zero-alpha regions.
As far as I know, I could:
1) Use mouse hook
2) Paint the background with almost completely transparent color (that has an opacity of 1)
However, the first solution is time consuming and the 2nd one will slow my rendering time as my window is stretched almost all over the desktop and most of the pixels are completely transparent at the moment.
Is there another way receiving those mouse events?
According to MSDN:
Hit testing of a layered window is
based on the shape and transparency of
the window. This means that the areas
of the window that are color-keyed or
whose alpha value is zero will let the
mouse messages through. However, if
the layered window has the
WS_EX_TRANSPARENT extended window
style, the shape of the layered window
will be ignored and the mouse events
will be passed to other windows
underneath the layered window.
However, in a new thread you could get continuously the coordinates of the mouse with GetCursorPos and if the position is inside one of your icons (regardless, that it's over a zero alpha pixel inside the icon) you handle it. Not too much better than the hook

Stringray Grid transparent background

In Stringray grid, there is the ability to use a transparent background which allows the background of the dialog to be shown through the grid.
In the documentation it states:
But be careful; you should disable scrolling or you have to redraw the grid each time it is scrolled (by overriding DoScroll).
I have a scrollable gird and override the DoScroll and make sure I call Redraw and also tried Invalidate, however the grid is still not completely erasing and redrawing.
I also tried using the old drawing method by setting m_bForceOldDrawing to TRUE.
How can I create a grid that has a transparent background that paint correctly after a scroll without leaving artifacts?
Yes you have to redraw the grid by overriding DoScroll because it is no longer using ScrollWindow to scroll contents because the background is transparent.
However you now have artifacts of the grid over your background.
This is because the background behind the grid is not getting redrawn.
Do you have clipchildren set for the parent?
Another potential problem is that the background is not being drawn because it doesn't realize it has been exposed.
Try calling the parent with the following.
Parent.Invalidate();
Parent.UpdateWindow();
before calling...
Invalidate();

QT mouse event handling problem

Greetings all,
As seen in the picture
I have an extended QWidget object (which draws the cell images and some countour data) inside a QScrollBar.
User can zoom in/out the Image (QWidget size is changed according to the zoomed size of the QImage ) using mouse wheel.
I process the events (mouseMoveEvent(),wheelEvent()..etc) by implementing the listener methods in QWidget.
My problem is ,I can only perform zooming (and other events) when the mouse pointer is over the QWidget.
If the mouse point is over the QScrollBar (the gray area in the image) ,those events are consumed by the QScroolBar.
Any tips,
[Edit] Sorry I was refering to QScrollArea , not QScrollBar.
thanks,
umanga
I'm uncertain if you want the scroll wheel to only ever be used for zooming the image or if you want the scroll wheel to control zooming when the image is smaller than the scroll area viewport and then use the scroll wheel to do scrolling when the image is larger than the scroll area viewport. In either case, you should be able to customize how the wheel is handled with the following:
Since I've not actually tried this one, I'm not sure if it will work. The hope is that if you install an event filter and set ignore on the event, the event will still be propagated back to your image widget. This will allow you to leave your current mouse handling in the image widget intact.
bool YourImageWidget::eventFilter(QObject *obj, QEvent *event)
{
if((obj == scrollAreaPointer) && (event->type() == QEvent::Wheel))
{
if(!scrollAreaShouldHandleWheel)
{
event->ignore();
}
}
return false; // always pass the event back to the scroll area
}
The scrollAreaShouldHandleWheel flag is a boolean you would set from your image widget based on whether or not you want the scroll area to handle the wheel events.
Somewhere in your code you would install your image widget as an event filter for the scrollarea.
scrollArea->installEventFilter(imageWidget);
If this doesn't work, you can always use this filter to make sure your widget gets the event and handle it directly and then return true so that the scroll area won't be able to receive the event.
I recommend you use QGraphicsScene and QGraphicsView. The graphics framework already provides a lot of useful features (including viewport transformation). And the QGraphicsView is a scroll area.
have you done grabMouse() for Qwidget i.e for the one which you display image?