QWidget transparent background (but not the children) - qt

I have a QWidget that contains a QPixmap and a QComboxBox in its Layout. I would like to set the background of the widget transparent (but I want to show the QPixmap and the QComboBox normally). How do I do that?

You can use the attribute
widget->setAttribute(Qt::WA_NoSystemBackground);
Qt documentation :
Indicates that the widget has no background, i.e. when the widget
receives paint events, the background is not automatically repainted.
Note: Unlike WA_OpaquePaintEvent, newly exposed areas are never filled
with the background (e.g., after showing a window for the first time
the user can see "through" it until the application processes the
paint events). This flag is set or cleared by the widget's author.

It is all well-explained in QWidget documentation:
http://doc.qt.io/qt-5/qwidget.html#transparency-and-double-buffering

Related

What to paint in the paintEvent implementation?

The Qt's documentation says:
Many widgets can simply repaint their entire surface when asked to, but some slow widgets need to optimize by painting only the requested region: QPaintEvent::region(). This speed optimization does not change the result, as painting is clipped to that region during event processing. QListView and QTableView do this, for example.
Do this mean that when I write a QWidget's paintEvent function, there's no need to manually write codes about which part of the widget should be painted and it is all handled automatically by qt itself?
when I write a QWidget's paintEvent function, there's no need to manually write codes about which part of the widget should be painted [?]
That's correct. The painter passed to you is already clipped to your widget's rectangle, so you can't paint outside of your widget. If your painting is cheap, then you can certainly repaint the entire widget at once, and ignore the repaint rectangle or region passed in the paint event.
You should also not blindly assume that painting only a part of the widget will be somehow inherently faster. You must measure this!
Could you explain more about the paintevent's propagation direction, usually qt's events are propagated as child->parent, but is the paintevent different?
All widgets that need to be painted are painted. The event is not propagated, it is delivered directly to the relevant widgets, under the direction of the widget compositor.
[...] when a Child widget's paintEvent is called, it also gets its Parent widget's paintEvent called. Why is this?
Two separate widgets happen to be painted. Just because they happen to be a parent and a child doesn't mean much. Each widget cares about its paint event in isolation. The widget compositor (backing store) has the big picture. If the parent is painted for seemingly no reason just prior to the child being painted, then likely the child isn't opaque and the parent is painted so that the child can be composited on top of it. Set Qt::WA_OpaquePaintEvent if your paintEvent does opaque painting and no part of the parent is visible through what's painted.

How can I prevent a QWidget from painting, but still respond to events?

I have a QGLWidget that renders an OpenGL scene inside a Qt application. I would like to add some other translucent Qt widgets that are overlaid on top of the QGLWidget. This is more difficult than with standard widgets since the OpenGL drawing context is unknown to Qt's painter classes. So, if I just do the obvious thing and place a transparent toolbar on top of the QGLWidget for instance, the transparent part of the toolbar instead renders black (it doesn't have access to the OpenGL frame buffer when painting).
It seems that the recommended way for handling this sort of thing is to overpaint the 2D content after drawing the OpenGL scene. The linked example seems very straightforward, as long as you're just drawing simple shapes. Instead, what I would like to do is to defer the painting of some child QWidget objects to be done inside the paint event for the QGLWidget.
So, my problem boils down to this:
Prevent the overlay QWidgets from painting in the normal, 2D context.
In the paint event handler for the QGLWidget, paint the overlays after painting the 3D scene that makes up the background.
The second item on this list seems to be simple: I can use QWidget::render() inside the QGLWidget's paint event to draw the desired widgets on top of the viewport. This works just fine.
The first item is more tricky: I need a way to prevent the widgets from painting during the normal course of events and only paint them in the QGLWidget's paint handler. One obvious way to do this is to hide the overlays using QWidget::hide(). This does allow me to paint the widgets atop the OpenGL scene as I would like. However, since the widgets are hidden, they do not respond to mouse or keyboard events. As an example, I'm using a QToolBar with a few buttons on it, and the toolbar is painted properly, but is non-functional (none of the buttons respond to clicks). So, if going down this path, it seems that I would need a way to force the widget to still respond to events even though it is hidden.
Another approach that I've tried is to intercept the QToolBar's paint event using an event filter, with the goal of preventing the toolbar from painting itself. However, the toolbar is still rendered; I'm assuming that this is because the various buttons on the toolbar are child widgets that are still painted, even if I intercept the parent's paint event.
Any ideas on a way that I could accomplish my goal?
I don't understand your issue completely, but I'll try to answer the question stated in the title. You should use event filters. Install an event filter using widget->installEventFilter(object), where widget is a widget you want to block painting, and object is an object of any of your QObject-derived classes. Reimplement eventFilter of this class:
bool MyClass::eventFilter(QObject* object, QEvent* event) {
if (event->type() == QEvent::Paint) { return true; }
return false;
}
When you return true from your eventFilter, the event is filtered and paint doesn't occur.
You can also try to use widget->setUpdatesEnabled(false) to temporarily disable painting. Don't forget to re-enable it when you're done.

Qt : event invisible widget?

For some reasons, I need to draw a widget onto one another.
The structure is the following (see the image) :
I have an original QTableWigetItem
On the QTableWigetItem, I create a QWidget at the foreground with the same geometry
This QWidget contains a QBoxLayout
This QBoxLayout contains a QPixmap and a QComboBox
I want to do the following things :
The QWidget is just a "container" for my QBoxLayout and I would like to set him completely "invisible" for the user. If the user click or move at the position of the widget, I want the event of the QTableWigetItem in the background to be trigerred. But the problem is that I want the QPixmap and the QComboBox to be at the foreground, visible and "normal". For me it's just a trick to be able to put children widgets in a QTableWidget of a HeaderView.
How to make the QWidget "completely invisible" (from the event/signals point of view) ?
Thank you very much.
Try QWidget::setWindowOpacity(0)

Semi-transparent QWidget over QGLWidget: Strange results

I have a full size QGLWidget which paints the application background using QPainter (might change to native openGL commands in the future).
On top of this QGLWidget I use QWidgets (non-GL) for the user interface elements. These are, for example, QLineEdits and QPushButtons. I put them into a custom painted QWidget which uses semi-transparent background painting. The paintEvents of the QLineEdit and QPushButton are overwritten and use semi-transparent backgrounds, too.
The whole UI should look like the following (This is a screenshot where I disabled OpenGL and used QWidget instead of QGLWidget for the background. Note the semi-transparent top bar which also draws a shadow (within its own region)):
When the QLineEdit has the focus, it should have a higher opacity but still not fully opaque:
So now, with OpenGL enabled (The background then is a QGLWidget), the semi-transparent widgets above don't paint on top of the background but on (it seems to be) uninitialized data. The image shining through the top bar is sometimes the whole window itself and sometimes other windows currently being on my desktop.
This currently looks like the following (In this screenshot, the data on which the semi-transparent painting operations are painted on seems to be an image of the widget itself, having an offset.):
When I wrote text into the line edit (here: "This is some text which has been there before!"), removed it and set the focus back to the background widget (so the magnifier icon and the placeholder text appear), the previously painted things still shine through (Note that the visible border should not be visible anymore, but also still shines through):
So the problem is: Instead of being painted on top of the underlying widgets, the semi-transparent widget is painted on top of the old results, initially being something like "uninitialized memory".
Why does this happen? How can I solve the problem?
Things you should know before answering:
The background scene is a composition of tiles which are rendered off-screen. So it can be painted very fast and repainting of the background for every little change of the overlay isn't problematic.
The top bar is a custom QWidget with manual painting and arrangement of the contained two widgets (the button and line edit).
The two widgets overwrite the paintEvent, only draw their own (semi-transparent) background when they have focus and don't use frames already provided by Qt. So the white border in the second screenshot is drawn in my custom paintEvent.
I want the background and the composition of overlay widgets to be separately implementable. The background is an AbstractMapView which has some concrete map view classes. The whole window is an AbstractView (there are multiple concrete views, too), which contains both a concrete map view and the overlay widgets, composed in a way itself decides. Therefore, I don't want the logic of the overlay widgets to be part of the underlying map view. (I hope you understood this, as it is a bit complicated.)
This sounds like an issue where the GL content (i.e. your background aka the QGLWidget) is not in the Qt context. While I'm not a pro on GL painting with Qt, you may want to look at this discussion regarding GL painting and a QLabel for some direction/potential hints.
http://www.qtcentre.org/threads/40335-QLabel-on-top-of-a-QGLWidget-background-issue
In short, we here at the office use OpenGL painting and offscreen rendering of maps and it's very important to make sure Qt is aware of the pixels so your foreground widgets can have the semi-transparency applied to their backgrounds.
The particular product we use also renders the map in tiles and supports providing the GL output in a buffer (i.e. it's call a snapshot and is provided as a bitmap) at which point we use the paintEvent of a regular QWidget to paint the buffer so that the painted pixels are in Qt context.
You can define a Qframe with Qt::SplashScreen flag as the search box and set its opacity. Put your widgets inside it such as the search textbox and positon it where it should appear on the mainwindow. It will also be a good idea to reposition it as the mainwindow is moved or resized overriding its moveEvent.

Turn off opaque resizing of QMainWindow / QDockWidget separator

Is it possible to turn off opaque resizing on QMainWindow / QDockWidget separators?
(i.e. the central widget only resizes once the separator is dropped, and not during the drag.)
QMainWindow has an animated property that specifies whether animation is used for manipulation of dock widgets and tool bars. By default, this property is enabled so you'll get dynamic (and animated) resizing of the central widget when moving dock widgets around. Clearing it should give you functionality similar to turning off opaque resize in a QSplitter.
QMainWindow* mainWin(new QMainWindow);
mainWin->setAnimated(false);
Maybe you could subclass QSlider and override sliderChange(SliderChange change) and not invoce the superclass method as long as the slider is still being dragged? That is assuming that this is the correct method that will eventually emit the signals that update the guy around. Haven't tested it, but thats generally a pattern that works in QT (for instance one can subclass the reject slot of QDialgo to abort dialogs).

Resources