2
votes

I'm trying to create a widget like the message box at the bottom in this picture:

img

It should overlay the main widget. I have 2 ways to do this:

  • With a QFrame and rounded corners
  • With a mask

But there's a problem with each of them:

  • The QFrame approach seems like a better idea but the background isn't transparent. This means that even though the borders have a radius, the background is still making it a rectangle. It's somewhat noticeable in the picture. Unfortunately, this doesn't seem to work. Same for self.setStyleSheet("background:transparent").

img

  • The mask looks very pixelated, and it's not the expected behavior because the mask used can only be a simple QRegion. These don't have squircles, which would be ideal.

img

Here's what the code looks like:

class Message(QFrame):
    """
    A temporary message to show information in the GUI.
    """

    def __init__(self, msg: str, *args, destroy_time: int = None):
        super().__init__(*args)

        # Main layout
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)

        # The label
        label = QLabel(msg)
        label.setFont(Fonts.text)
        label.setStyleSheet(f"color: {Colors.fg};"
                            "padding: 20px;")
        self.layout.addWidget(label)
        self.setStyleSheet(f"background-color: {Colors.bg};"
                           "border-radius: 30px;")

        #  region = QRegion(self.x(), self.y(), self.sizeHint().width(),
                         #  self.sizeHint().height(), QRegion.Ellipse)
        #  self.setMask(region)

        self.adjustSize()

Edit for S. Nick: Your solution only works if the Message widget is the only widget in the application. The intended usage is this:

class MainWindow(QWidget):
    def __init__(self, player: QWidget):
        super().__init__()

        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(player)

        msg = Message("I can't make this work", self)

where player is the main widget, and the message overlays it when it appears. Imagine it being an image, which is overlayed by the message. Sorry for not explaining myself correctly.

1

1 Answers

3
votes

Try it:

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


class Message(QWidget):   #(QFrame): #
    def __init__(self, msg: str, *args, destroy_time: int = None):
        super().__init__(*args)

        self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) # <---
        self.setAttribute(Qt.WA_TranslucentBackground)                   # <---

        Colors_fg = "#fa0"
        Colors_bg = "rgba( 155, 155, 155, 150)"                          # <---

        # Main layout
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)

        # The label
        label = QLabel(msg, alignment=Qt.AlignCenter)
        label.setFont(QFont("Times", 17, QFont.Bold, italic=True))       #(Fonts.text)
        label.setStyleSheet(f"color: {Colors_fg};"
                             "padding: 0px;")
        self.layout.addWidget(label)

# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv        
        self.setStyleSheet(f"background-color: {Colors_bg};"  
                            "min-height: 70px;"
                            "max-height: 70px;"
                            "width: 200px;"
                            "border-radius: 30px;"
                           )
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        self.adjustSize()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Message("Hello \nWorld")
    w.resize(400, 200)
    w.show()
    sys.exit(app.exec_())

enter image description here


Update

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


class Message(QDialog):      #(QFrame):
    def __init__(self, msg: str, *args, destroy_time: int = None):
        super().__init__(*args)
        self.setWindowFlags(self.windowFlags() | 
                            Qt.FramelessWindowHint |
                            Qt.WindowStaysOnTopHint) 
        self.setAttribute(Qt.WA_TranslucentBackground)                   

        self.widget = QWidget(self)
        self.widget.setObjectName('Custom_Widget')
        layout = QVBoxLayout(self)
        layout.addWidget(self.widget)

        self.layout = QGridLayout(self.widget)
        self.layout.addItem(QSpacerItem(
            40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 0)
        self.layout.addWidget(QPushButton('r', self, 
                                          clicked=self.accept, 
                                          objectName='closeButton'), 0, 1)
        # The label
        label = QLabel(msg)
        label.setFont(QFont("Times", 17, QFont.Bold, italic=True))         #((Fonts.text)
        self.layout.addWidget(label, 2, 0, 5, 2, alignment=Qt.AlignCenter)                           
        self.adjustSize()

    def mousePressEvent(self, event):
        self.old_Pos    = event.globalPos()
        self.old_width  = self.width()
        self.old_height = self.height()

    def mouseMoveEvent(self, event):
        if (event.buttons() == Qt.LeftButton): 
            delta = QPoint (event.globalPos() - self.old_Pos)
            if (self.old_Pos.x() > self.x() + self.old_width - 20) or \
               (self.old_Pos.y() > self.y() + self.old_height - 20):
                w = self.old_width+delta.x()  if self.old_width+delta.x()  > 500 else 500
                h = self.old_height+delta.y() if self.old_height+delta.y() > 400 else 400
                self.setFixedSize(w, h)
            else:
                self.move(self.x() + delta.x(), self.y() + delta.y())
                self.old_Pos = event.globalPos()


class MainWindow(QWidget):
    def __init__(self, player: QWidget):
        super().__init__()

        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(player)

        self.msg = Message("I can't make this work")#, self)        
        self.msg.show()


Stylesheet = """
#Custom_Widget {
    background: rgba( 155, 155, 155, 150);
    border-radius: 20px;
    border: 2px solid #ff2025;                   
}
#closeButton {
    min-width: 36px;
    min-height: 36px;
    font-family: "Webdings";
    qproperty-text: "r";
    border-radius: 10px;
}
#closeButton:hover {
    color: #ccc;
    background: red;
}
"""

if __name__ == '__main__':
    app = QApplication(sys.argv)

    app.setStyleSheet(Stylesheet)

    w = MainWindow(QPushButton("player"))
    w.resize(400, 200)
    w.show()
    sys.exit(app.exec_())

enter image description here


Update 2

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


class Message(QFrame):      # QDialog
    def __init__(self, msg: str, *args, destroy_time: int = None):
        super().__init__(*args)
#        self.setWindowFlags(self.windowFlags() | 
#                            Qt.FramelessWindowHint #|Qt.WindowStaysOnTopHint) 
        self.setAttribute(Qt.WA_TranslucentBackground)                   

        self.widget = QWidget(self)
        self.widget.setObjectName('Custom_Widget')
        layout = QVBoxLayout(self)
        layout.addWidget(self.widget)

        self.layout = QGridLayout(self.widget)
        self.layout.addItem(QSpacerItem(
            40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 0)
        self.layout.addWidget(QPushButton('r', self, 
#                                          clicked=self.accept, 
                                          clicked=self.close, 
                                          objectName='closeButton'), 0, 1)
        # The label
        label = QLabel(msg)
        label.setFont(QFont("Times", 15, QFont.Bold, italic=True))         #((Fonts.text)
        self.layout.addWidget(label, 2, 0, 5, 2, alignment=Qt.AlignCenter)                           
        self.adjustSize()


class MainWindow(QWidget):
    def __init__(self, player: QWidget):
        super().__init__()

        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(player)
        player.clicked.connect(self._msg)

    def _msg(self):
        self.msg = Message("I can't make this work\nself.msg.setGeometry(10, 10, 480, 150)", self)        
        self.msg.setGeometry(10, 10, 480, 150)
        self.msg.show()


Stylesheet = """
#Custom_Widget {
    background: rgba( 155, 155, 155, 150);
    border-radius: 20px;
    border: 2px solid #ff2025;                   
}
#closeButton {
    min-width: 36px;
    min-height: 36px;
    font-family: "Webdings";
    qproperty-text: "r";
    border-radius: 10px;
}
#closeButton:hover {
    color: #ccc;
    background: red;
}
"""

if __name__ == '__main__':
    app = QApplication(sys.argv)

    app.setStyleSheet(Stylesheet)

    w = MainWindow(QPushButton("player"))
    w.resize(500, 400)
    w.show()
    sys.exit(app.exec_())

enter image description here