PyQt,在窗口中创建一个弹出窗口 [英] PyQt, Creating a popup in the window

查看:77
本文介绍了PyQt,在窗口中创建一个弹出窗口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 PyQt5 创建一个 GUI,我想添加一个类似于下面显示的弹出窗口(示例取自 Discord,因为这是我能想到的第一件事).我将如何在我已经拥有的窗口内创建一个弹出窗口.我不想使用消息框之类的东西创建一个全新的窗口,弹出窗口将是静态的(不能手动在屏幕上移动),并且应该能够通过单击关闭/x 按钮关闭,按 esc,或在小部件外点击

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 would I go about creating a popup inside of a window that I already have. I don't want to create a whole new window using something like a message box, the popup will be static(can't be moved around the screen manually), and should be able to be closed by clicking the close/x button, pressing esc, or clicking outside the widget

推荐答案

创建嵌入式"小部件实际上很简单:您唯一需要做的就是在创建它时将其 设置为窗口(或小部件).也可以在创建小部件后重新设置父级(但show()setVisible(True) 的调用总是是必需的).

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.

然后,为了提供类似于 QDialog 的机制,您可以创建一个事件循环并像普通 Qt 对话框一样调用其 exec(),并允许 ui 元素与其交互.

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.

from PyQt5 import QtCore, QtWidgets

class LoginPopup(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()

    def checkInput(self):
        self.okButton.setEnabled(bool(self.email() and self.password()))

    def email(self):
        return self.emailEdit.text()

    def password(self):
        return self.passwordEdit.text()

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

    def reject(self):
        self.loop.exit(False)

    def close(self):
        self.loop.quit()

    def showEvent(self, event):
        self.setGeometry(self.parent().rect())

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

    def eventFilter(self, source, event):
        if event.type() == event.Resize:
            self.setGeometry(source.rect())
        return super().eventFilter(source, event)

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


class MainWindow(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)

    def showDialog(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_())

注意:QDialog 有自己的处理父母身份的方式,它并不总是允许正确管理调整大小和显示,因此与其试图解决这些限制",不如使用 QWidget 更简单、更安全.

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.

这篇关于PyQt,在窗口中创建一个弹出窗口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆