PyQt5: Create semi-transparent window with non-transparent children

Enuy picture Enuy · Nov 29, 2015 · Viewed 7.6k times · Source

I want to create a fullscreen window with semitransparent background, but fully visible children widgets (kind of overlay effect).

Here's what I have so far:

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

app = QApplication(sys.argv)

# Create the main window
window = QMainWindow()

window.setWindowOpacity(0.3)
window.setAttribute(Qt.WA_NoSystemBackground, True)
window.setWindowFlags(Qt.FramelessWindowHint)

# Create the button
pushButton = QPushButton(window)
pushButton.setGeometry(QRect(240, 190, 90, 31))
pushButton.setText("Finished")
pushButton.clicked.connect(app.quit)

# Center the button
qr = pushButton.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
pushButton.move(qr.topLeft())

# Run the application
window.showFullScreen()
sys.exit(app.exec_())

This creates a semi-transparent effect, but even the button is semi-transparent.

I also tried to substitute the

window.setWindowOpacity(0.3)

with this call

window.setAttribute(Qt.WA_TranslucentBackground, True)

but to no avail, in this case the background was fully transparent (while the button was correctly fully visible).

Solution: (implemented thanks to Aaron's suggestion):

The trick is in implementing a custom paintEvent for the main window.

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class CustomWindow(QMainWindow):
    def paintEvent(self, event=None):
        painter = QPainter(self)

        painter.setOpacity(0.7)
        painter.setBrush(Qt.white)
        painter.setPen(QPen(Qt.white))   
        painter.drawRect(self.rect())


app = QApplication(sys.argv)

# Create the main window
window = CustomWindow()

window.setWindowFlags(Qt.FramelessWindowHint)
window.setAttribute(Qt.WA_NoSystemBackground, True)
window.setAttribute(Qt.WA_TranslucentBackground, True)

# Create the button
pushButton = QPushButton(window)
pushButton.setGeometry(QRect(240, 190, 90, 31))
pushButton.setText("Finished")
pushButton.clicked.connect(app.quit)

# Center the button
qr = pushButton.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
pushButton.move(qr.topLeft())

# Run the application
window.showFullScreen()
sys.exit(app.exec_())

Answer

Aaron picture Aaron · Dec 1, 2015

Ok, while is seems not to work with the available flags you can still use Qt.WA_TranslucentBackground because it is possible to draw a semitranparent rect on that transparency.

Derive your mainwindow from QMainWindow and use that class instead.

Apply self.setAttribute(Qt.WA_TranslucentBackground, True) to that class

Implement the paintEvent of your mainwindow class like this (similar, might contain errors, but the principle should work):

QPixmap canvas(rect())

canvas.fill(Qt.transparent) # fill transparent (makes alpha channel available)

QPainter p(canvas)           # draw on the canvas
p.setOpacity(0.3)
p.setBrush(QBrush(Qt.white)) # use the color you like
p.setPen(QPen(Qt.transparen))

p.drawRect(rect()) # draws the canvas with desired opacity

p.start(self)      # now draw on the window itself
p.drawPixmap(rect(), canvas)