Skip to content Skip to sidebar Skip to footer

Pyqt, Creating A Popup In The Window

I am trying to create a GUI using PyQt5 and I want to add a popup similar to the one shown below(example taken from Discord because that's the first thing I could think of). How wo

Solution 1:

Creating an "embedded" widget is actually easy: the only thing you need to do is to set its parent as the window (or widget) when you create it. Reparenting can also be done after a widget is created (but a call to show() or setVisible(True) is always required).

The problem is that once the widget is shown "inside" the parent, access to other widgets is still possible.

The trick is to create a widget that shadows the content of the parent, and show the actual contents in a smaller widget. In order to do this you must install an event filter on the parent and always reposition the widget whenever the parent is resized.

Then, to provide a mechanism similar to that of QDialog, you can create an event loop and call its exec() just like a normal Qt dialog, and allow ui elements to interact with it.

example login screenshot

from PyQt5 import QtCore, QtWidgets

classLoginPopup(QtWidgets.QWidget):
    def__init__(self, parent):
        super().__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setAttribute(QtCore.Qt.WA_StyledBackground)
        self.setAutoFillBackground(True)
        self.setStyleSheet('''
            LoginPopup {
                background: rgba(64, 64, 64, 64);
            }
            QWidget#container {
                border: 2px solid darkGray;
                border-radius: 4px;
                background: rgb(64, 64, 64);
            }
            QWidget#container > QLabel {
                color: white;
            }
            QLabel#title {
                font-size: 20pt;
            }
            QPushButton#close {
                color: white;
                font-weight: bold;
                background: none;
                border: 1px solid gray;
            }
        ''')

        fullLayout = QtWidgets.QVBoxLayout(self)

        self.container = QtWidgets.QWidget(
            autoFillBackground=True, objectName='container')
        fullLayout.addWidget(self.container, alignment=QtCore.Qt.AlignCenter)
        self.container.setSizePolicy(
            QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)

        buttonSize = self.fontMetrics().height()
        self.closeButton = QtWidgets.QPushButton(
            '×', self.container, objectName='close')
        self.closeButton.setFixedSize(buttonSize, buttonSize)
        self.closeButton.clicked.connect(self.reject)

        layout = QtWidgets.QVBoxLayout(self.container)
        layout.setContentsMargins(
            buttonSize * 2, buttonSize, buttonSize * 2, buttonSize)

        title = QtWidgets.QLabel(
            'Enter an email address', 
            objectName='title', alignment=QtCore.Qt.AlignCenter)
        layout.addWidget(title)

        layout.addWidget(QtWidgets.QLabel('EMAIL'))
        self.emailEdit = QtWidgets.QLineEdit()
        layout.addWidget(self.emailEdit)
        layout.addWidget(QtWidgets.QLabel('PASSWORD'))
        self.passwordEdit = QtWidgets.QLineEdit(
            echoMode=QtWidgets.QLineEdit.Password)
        layout.addWidget(self.passwordEdit)

        buttonBox = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel)
        layout.addWidget(buttonBox)
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)
        self.okButton = buttonBox.button(buttonBox.Ok)
        self.okButton.setEnabled(False)

        self.emailEdit.textChanged.connect(self.checkInput)
        self.passwordEdit.textChanged.connect(self.checkInput)
        self.emailEdit.returnPressed.connect(lambda:
                self.passwordEdit.setFocus())
        self.passwordEdit.returnPressed.connect(self.accept)

        parent.installEventFilter(self)

        self.loop = QtCore.QEventLoop(self)
        self.emailEdit.setFocus()

    defcheckInput(self):
        self.okButton.setEnabled(bool(self.email() and self.password()))

    defemail(self):
        return self.emailEdit.text()

    defpassword(self):
        return self.passwordEdit.text()

    defaccept(self):
        if self.email() and self.password():
            self.loop.exit(True)

    defreject(self):
        self.loop.exit(False)

    defclose(self):
        self.loop.quit()

    defshowEvent(self, event):
        self.setGeometry(self.parent().rect())

    defresizeEvent(self, event):
        r = self.closeButton.rect()
        r.moveTopRight(self.container.rect().topRight() + QtCore.QPoint(-5, 5))
        self.closeButton.setGeometry(r)

    defeventFilter(self, source, event):
        if event.type() == event.Resize:
            self.setGeometry(source.rect())
        returnsuper().eventFilter(source, event)

    defexec_(self):
        self.show()
        self._raise()
        res = self.loop.exec_()
        self.hide()
        return res


classMainWindow(QtWidgets.QMainWindow):
    def__init__(self):
        super().__init__()
        central = QtWidgets.QWidget()
        self.setCentralWidget(central)
        layout = QtWidgets.QVBoxLayout(central)
        button = QtWidgets.QPushButton('LOG IN')
        layout.addWidget(button)
        button.clicked.connect(self.showDialog)
        self.setMinimumSize(640, 480)

    defshowDialog(self):
        dialog = LoginPopup(self)
        if dialog.exec_():
            print(dialog.email(), dialog.password())

import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

Note: QDialog has its own way of dealing with parenthood, and it doesn't always allow correct management of resizing and displaying, so instead of trying to work around those "limitations", using a QWidget is much simpler and safer.

Post a Comment for "Pyqt, Creating A Popup In The Window"