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

12,140

Solution 1

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)

Solution 2

I just wanted to provide another solution in case someone else runs into this problem. The way I solved it is like this.

First set your background to be completely transparent. This only applies to the window's background, and not the children objects.

self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)

You can remove the border if you want with this.

self.setWindowFlags(QtCore.Qt.FramelessWindowHint)

Now, if you want there to still be some background color, apply a QFrame to your window, and place all child objects within it. Using your style sheet, set the color of the frame and your desired opacity like this. The last value is the opacity percentage.

self.main_frame.setStyleSheet("background-color: rgba(0, 120, 185, 60)")
Share:
12,140

Related videos on Youtube

Enuy
Author by

Enuy

Updated on September 16, 2022

Comments

  • Enuy
    Enuy over 1 year

    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_())
    
  • Enuy
    Enuy over 8 years
    What happens is that the button is still semi-transparent, and the background gets set a system defined color (instead of black, it's now whiteish). I'm not sure setWindowOpacity works on non-window widgets.
  • Aaron
    Aaron over 8 years
    Edited my answer. I did it like this sometimes and it always worked as you need it.
  • Enuy
    Enuy over 8 years
    Okay, I'll try to convert this QT pseudocode to PyQT and see how this works out.
  • Enuy
    Enuy over 8 years
    I updated the original question. I converted the code to PyQt, however, it seems as though nothing is being painted. (see the update)
  • Enuy
    Enuy over 8 years
    Oh, it seems it works if I set some dimensions for the QRect()! Now only to detect the screen size.
  • Aaron
    Aaron over 8 years
    Ok, I'm from C++ .. my rect() means for you self.rect(), please accept the answer if it works for you. Sorry, quite a while since using PyQt for me ..
  • Enuy
    Enuy over 8 years
    not a problem, the pseudocode helped me to get it working. I replaced the manual size detection with self.rect(). Thanks.
  • Aaron
    Aaron over 8 years
    actually in your "solution" part above you never paint 'ON' the canvas. If you compare to my pseudo code were the painter paints first to the canvas and then transfers the canvas to self. but for you it seems sufficient to draw on self (´painter = QPainter(self)´). so it seems you can remove first and last line of your paint event. (for me that never worked directly on self/(this in c++). but very good if it does for you).
  • Enuy
    Enuy over 8 years
    Correct obserivation. I had to remove the painter.start call, as this interface is not available in PyQt5. Also I created the painter directly on the window widget. I corrected the solution to remove the dead code.