Dark transparent layer over a QMainWindow in Qt
This answer is in a series of my overlay-related answers: first, second, third.
The most trivial solution is to simply add a child transparent widget to QMainWindow
. That widget must merely track the size of its parent window. It is important to properly handle changes of widget parentage, and the z-order with siblings. Below is a correct example of how to do it.
If you want to stack overlays, subsequent overlays should be the children of OverlayWidget
, in the z-order. If they were to be siblings of the OverlayWidget
, their stacking order is undefined.
This solution has the benefit of providing minimal coupling to other code. It doesn't require any knowledge from the widget you apply the overlay to. You can apply the overlay to a QMainWindow
or any other widget, the widget can also be in a layout.
Reimplementing QMainWindow
's paint event would not be considered the best design. It makes it tied to a particular class. If you really think that a QWidget
instance is too much overhead, you better had measurements to show that being the case.
It is possible, of course, to make the overlay merely a QObject
and to put the painting code into an event filter. That'd be an alternative solution. It's harder to do since you have to also properly deal with the parent widget's Qt::WA_StaticContents
attribute, and with the widget potentially calling its scroll()
method. Dealing with a separate widget is the simplest.
// https://github.com/KubaO/stackoverflown/tree/master/questions/overlay-widget-19362455
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
class OverlayWidget : public QWidget
{
void newParent() {
if (!parent()) return;
parent()->installEventFilter(this);
raise();
}
public:
explicit OverlayWidget(QWidget * parent = {}) : QWidget{parent} {
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TransparentForMouseEvents);
newParent();
}
protected:
//! Catches resize and child events from the parent widget
bool eventFilter(QObject * obj, QEvent * ev) override {
if (obj == parent()) {
if (ev->type() == QEvent::Resize)
resize(static_cast<QResizeEvent*>(ev)->size());
else if (ev->type() == QEvent::ChildAdded)
raise();
}
return QWidget::eventFilter(obj, ev);
}
//! Tracks parent widget changes
bool event(QEvent* ev) override {
if (ev->type() == QEvent::ParentAboutToChange) {
if (parent()) parent()->removeEventFilter(this);
}
else if (ev->type() == QEvent::ParentChange)
newParent();
return QWidget::event(ev);
}
};
class LoadingOverlay : public OverlayWidget
{
public:
LoadingOverlay(QWidget * parent = {}) : OverlayWidget{parent} {
setAttribute(Qt::WA_TranslucentBackground);
}
protected:
void paintEvent(QPaintEvent *) override {
QPainter p{this};
p.fillRect(rect(), {100, 100, 100, 128});
p.setPen({200, 200, 255});
p.setFont({"arial,helvetica", 48});
p.drawText(rect(), "Loading...", Qt::AlignHCenter | Qt::AlignVCenter);
}
};
int main(int argc, char * argv[])
{
QApplication a{argc, argv};
QMainWindow window;
QLabel central{"Hello"};
central.setAlignment(Qt::AlignHCenter | Qt::AlignTop);
central.setMinimumSize(400, 300);
LoadingOverlay overlay{¢ral};
QTimer::singleShot(5000, &overlay, SLOT(hide()));
window.setCentralWidget(¢ral);
window.show();
return a.exec();
}
How to change the opacity of Qt MainWindow?
The below works for me most of times (as long as we can run in stylesheet override problem with other ways). Consider change the last component of rgba to less than 255 for making it semi-transparent.
widget->setStyleSheet("background-color: rgba(255, 255, 255, 255);");
Mind that child widgets may inherit the transparent background of parent widget by default unless you specify "background-color: rgba(255, 255, 255, 0);" for them or limit the visibility of upper stylesheet somehow e.g. "QMainWindow(background-color: rgba(255, 255, 255, 255);}".
Draw transparent image over the screen with Qt
I have tried your code and it works fine, apparently it seems to be a problem of the image, I recommend you to use some kind of external application to erase the transparent parts like this.
Here is a test that works with your code:
If you want to do this automatically I recommend you to use opencv.
Qt mainwindow transparency background
You can do something like this:
this->setWindowFlags(Qt::FramelessWindowHint);
this->setAttribute(Qt::WA_TranslucentBackground);
Then paint the background of your window in the paint event.
Note: As pointed out by Dmitry, in Qt4 there is a bug with minimizing and restoring the window.
Another option is to use QWidget::setMask(const QBitmap & bitmap), but this could be slow if the region is complex.
How to create a transparent QWidget on top of a sibling QVideoWidget?
Demo of transparent background overlay on the video:
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsVideoItem>
#include <QMediaPlayer>
#include <QFileDialog>
#include <QPushButton>
#include <QLabel>
#include <QGraphicsProxyWidget>
const QSizeF VideoItemSize(500, 500);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMediaPlayer player;
QGraphicsView v;
QGraphicsScene scene;
QGraphicsVideoItem video;
v.setScene(&scene);
video.setSize(VideoItemSize);
scene.setSceneRect(QRectF(QPointF(0, 0), VideoItemSize)); // VideoItem full fill the scene
scene.addItem(&video);
player.setVideoOutput(&video);
player.setMedia(QMediaContent(QFileDialog::getOpenFileUrl()));
// Recommend using QGraphicsItems for overlay component
QGraphicsTextItem text("Loading...",&video);
text.setPos(100, 150);
// If you need a button...
QPushButton button("ButtonTest");
QGraphicsProxyWidget* proxyButton = scene.addWidget(&button);
proxyButton->setPos(100, 200);
// Instead of QGraphicsItems, if you really need a QWidget...
QLabel label("LabelTest");
label.setAttribute(Qt::WA_TranslucentBackground); // You can delete this line to see different
QGraphicsProxyWidget* proxyLabel = scene.addWidget(&label);
proxyLabel->setPos(100, 250);
v.show();
player.play();
return a.exec();
}
Related Topics
Dynamically Allocated Memory After Program Termination
Prefix/Postfix Increment Operators
Get Function Pointer from Std::Function When Using Std::Bind
Read Process Memory of a Process Does Not Return Everything
Std::Unique_Ptr for C Functions That Need Free
Efficiency of the Stl Priority_Queue
Expansion with Variadic Templates
How to Make G++ Preprocessor Output a Newline in a MACro
How to Convert a Struct Tm (Expressed in Utc) to Time_T Type
On Local and Global Static Variables in C++
What Does It Mean for a C++ Function to Be Inline
Easiest Way to Rotate by 90 Degrees an Image Using Opencv
How to Replace All Instances of a String with Another String
How Bad Is Redefining/Shadowing a Local Variable
Differencebetween Packaged_Task and Async
Why Can't Templates Be Within Extern "C" Blocks
How to Store a 64 Bit Integer in Two 32 Bit Integers and Convert Back Again