python - PyQt,在窗口中创建一个弹出窗口
问题描述
我正在尝试使用 PyQt5 创建一个 GUI,我想添加一个类似于下面显示的弹出窗口(示例取自 Discord,因为这是我能想到的第一件事)。我将如何在我已经拥有的窗口中创建一个弹出窗口。我不想使用消息框之类的东西创建一个全新的窗口,弹出窗口将是静态的(不能在屏幕上手动移动),并且应该能够通过单击关闭/x按钮来关闭,按 esc,或在小部件外部单击
解决方案
创建“嵌入式”小部件实际上很容易:您唯一需要做的就是在创建它时将其父级设置为窗口(或小部件)。Reparenting 也可以在创建小部件后完成(但始终需要调用show()
or )。setVisible(True)
问题是,一旦小部件显示在父级“内部”,仍然可以访问其他小部件。
诀窍是创建一个隐藏父级内容的小部件,并在较小的小部件中显示实际内容。为此,您必须在父级上安装事件过滤器,并在调整父级大小时始终重新定位小部件。
然后,为了提供类似于 QDialog 的机制,您可以创建一个事件循环并exec()
像普通的 Qt 对话框一样调用它,并允许 ui 元素与之交互。
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 更简单、更安全。
推荐阅读
- excel - Excel VBA Selenium FindElementXpath 变量错误
- php - 要求(供应商/autoload.php):无法打开流:没有这样的文件或目录
- python - 使用python的pymysql数据库存储错误
- asp.net - 从弹出窗口中选择一个项目到文本框中
- mongodb - 使用连接到 mongo atlas 的 mongoose 运行插入时出错
- css - 问题:每条评论只等于该行的一个字母
- java - java中list中set方法和add方法的区别
- oracle - Hibernate 无法从 OracleDB 物化视图中一致地获取数据
- angular - 我已经将角度项目从一台计算机迁移到另一台计算机,当我运行“npm i”时,它给了我这些错误
- javascript - React 17.0.1 Form onChange 跳过最后一个数字