第8篇:PyQt5对话框控件:QDialog与标准对话框(完整代码)
哈喽~ 欢迎来到PyQt5系列的第8篇!上一章我们掌握了QTextEdit(富文本)和QTableWidget(表格)的核心用法,今天聚焦对话框控件——对话框是PyQt5界面交互的“重要桥梁”,比如提示信息、选择文件、用户登录验证等场景都离不开它。我们会详细讲解两类对话框:标准对话框(系统预制,如消息框、文件选择框)和自定义对话框(基于QDialog开发专属交互窗口),全程搭配完整可运行代码,新手也能轻松掌握!
一、先明确:对话框的核心概念
在学习具体用法前,先理清对话框的两个核心属性,避免用错场景:
1. 模态(Modal)vs 非模态(Non-Modal)
- 模态对话框:弹出后阻塞主窗口操作,必须先处理对话框才能回到主窗口(如登录弹窗、确认删除提示),是最常用的类型;
- 非模态对话框:弹出后不阻塞主窗口,可同时操作主窗口和对话框(如悬浮的工具面板)。
2. 标准对话框 vs 自定义对话框
- 标准对话框:PyQt5预制的通用对话框(QMessageBox、QFileDialog、QColorDialog等),无需自定义界面,直接调用即可,开发效率高;
- 自定义对话框:基于QDialog类开发的专属对话框(如登录窗口、高级设置窗口),可自由设计界面和交互逻辑。
二、标准对话框详解:直接调用的通用交互窗口
PyQt5提供了一系列开箱即用的标准对话框,覆盖80%的通用交互场景,重点掌握以下5类:
1. QMessageBox:消息提示对话框(最常用)
用于显示提示、警告、错误、确认等信息,支持自定义按钮和交互逻辑。
效果图
完整代码:QMessageBox常用类型
import sys
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QPushButton, QMessageBox
)
class MessageBoxDemo(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("QMessageBox标准消息框演示")
self.resize(400, 300)
# 布局与按钮
layout = QVBoxLayout()
layout.setSpacing(15)
layout.setContentsMargins(50, 50, 50, 50)
# 不同类型的消息框按钮
info_btn = QPushButton("信息提示框")
warn_btn = QPushButton("警告提示框")
error_btn = QPushButton("错误提示框")
question_btn = QPushButton("确认对话框")
custom_btn = QPushButton("自定义按钮对话框")
# 绑定信号
info_btn.clicked.connect(self.show_info)
warn_btn.clicked.connect(self.show_warn)
error_btn.clicked.connect(self.show_error)
question_btn.clicked.connect(self.show_question)
custom_btn.clicked.connect(self.show_custom)
# 添加到布局
for btn in [info_btn, warn_btn, error_btn, question_btn, custom_btn]:
layout.addWidget(btn)
self.setLayout(layout)
# 1. 信息提示框(仅提示,无返回值)
def show_info(self):
QMessageBox.information(
self, # 父窗口
"信息", # 标题
"操作成功!这是一个信息提示框。", # 内容
QMessageBox.Ok # 按钮(默认Ok)
)
# 2. 警告提示框
def show_warn(self):
QMessageBox.warning(
self,
"警告",
"数据未保存,关闭窗口将丢失内容!",
QMessageBox.Ok | QMessageBox.Cancel # 多个按钮
)
# 3. 错误提示框
def show_error(self):
QMessageBox.critical(
self,
"错误",
"文件读取失败,请检查文件路径是否正确!",
QMessageBox.Retry | QMessageBox.Abort
)
# 4. 确认对话框(带返回值,判断用户选择)
def show_question(self):
reply = QMessageBox.question(
self,
"确认",
"确定要删除这条数据吗?",
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, # 按钮组合
QMessageBox.No # 默认选中No按钮
)
# 判断用户点击的按钮
if reply == QMessageBox.Yes:
print("用户选择:删除")
elif reply == QMessageBox.No:
print("用户选择:不删除")
else:
print("用户选择:取消")
# 5. 自定义按钮对话框
def show_custom(self):
# 创建自定义消息框
msg_box = QMessageBox(self)
msg_box.setWindowTitle("自定义对话框")
msg_box.setText("请选择操作:")
# 设置自定义按钮
btn1 = msg_box.addButton("导出", QMessageBox.ActionRole)
btn2 = msg_box.addButton("导入", QMessageBox.ActionRole)
btn3 = msg_box.addButton("取消", QMessageBox.CancelRole)
# 显示对话框并获取返回值
msg_box.exec_()
# 判断用户点击的按钮
if msg_box.clickedButton() == btn1:
print("用户选择:导出")
elif msg_box.clickedButton() == btn2:
print("用户选择:导入")
else:
print("用户选择:取消")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MessageBoxDemo()
window.show()
sys.exit(app.exec_())QMessageBox核心要点
- 常用类型:
information()(信息)、warning()(警告)、critical()(错误)、question()(确认); - 按钮组合:用
|拼接(如QMessageBox.Yes | QMessageBox.No); - 返回值判断:通过返回的枚举值(如
QMessageBox.Yes)判断用户操作; - 自定义按钮:创建
QMessageBox实例,用addButton()添加自定义按钮。
2. 其他标准对话框:文件/颜色/字体选择
除了消息框,PyQt5还提供了文件、颜色、字体选择的标准对话框,用法统一且简单:
完整代码:文件/颜色/字体对话框
效果如图
import sys
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QPushButton,
QFileDialog, QColorDialog, QFontDialog, QLabel
)
from PyQt5.QtGui import QColor, QFont
from PyQt5.QtCore import Qt
class StandardDialogsDemo(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("标准对话框(文件/颜色/字体)演示")
self.resize(500, 300)
# 布局
layout = QVBoxLayout()
layout.setSpacing(15)
layout.setContentsMargins(50, 50, 50, 50)
# 按钮
file_btn = QPushButton("选择文件")
color_btn = QPushButton("选择颜色")
font_btn = QPushButton("选择字体")
# 结果显示标签
self.result_label = QLabel("选择结果将显示在这里...")
self.result_label.setAlignment(Qt.AlignCenter)
# 绑定信号
file_btn.clicked.connect(self.choose_file)
color_btn.clicked.connect(self.choose_color)
font_btn.clicked.connect(self.choose_font)
# 添加到布局
layout.addWidget(file_btn)
layout.addWidget(color_btn)
layout.addWidget(font_btn)
layout.addWidget(self.result_label)
self.setLayout(layout)
# 1. 文件选择对话框(打开文件)
def choose_file(self):
# getOpenFileName:选择单个文件
# getOpenFileNames:选择多个文件
# getSaveFileName:保存文件
file_path, _ = QFileDialog.getOpenFileName(
self, # 父窗口
"选择文件", # 标题
"", # 默认路径(当前目录)
"Text Files (*.txt);;Image Files (*.png *.jpg);;All Files (*.*)" # 文件筛选器
)
if file_path:
self.result_label.setText(f"选中文件:{file_path}")
else:
self.result_label.setText("未选择任何文件")
# 2. 颜色选择对话框
def choose_color(self):
# 获取当前标签颜色,作为默认颜色
current_color = self.result_label.textColor()
# 弹出颜色选择框
color = QColorDialog.getColor(
current_color, # 默认颜色
self,
"选择颜色"
)
if color.isValid(): # 用户选择了有效颜色
self.result_label.setText(f"选中颜色:{color.name()}")
self.result_label.setStyleSheet(f"color: {color.name()}; font-size: 16px;")
# 3. 字体选择对话框
def choose_font(self):
# 获取当前标签字体,作为默认字体
current_font = self.result_label.font()
# 弹出字体选择框
font, ok = QFontDialog.getFont(
current_font, # 默认字体
self,
"选择字体"
)
if ok: # 用户确认选择
self.result_label.setText(f"选中字体:{font.family()},大小:{font.pointSize()}")
self.result_label.setFont(font)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = StandardDialogsDemo()
window.show()
sys.exit(app.exec_())核心方法总结
| 对话框类型 | 核心方法 | 关键参数/返回值 |
|---|---|---|
| QFileDialog | getOpenFileName() | 返回(选中路径, 筛选器),支持多文件选择(getOpenFileNames) |
| QColorDialog | getColor() | 返回QColor对象,isValid()判断是否选择有效颜色 |
| QFontDialog | getFont() | 返回(字体对象, 是否确认),ok为True时表示用户确认选择 |
三、自定义对话框:基于QDialog开发专属交互窗口
当标准对话框无法满足需求时(如登录验证、高级参数设置),需要基于QDialog开发自定义对话框,核心是“设计界面+处理返回值+设置模态”。
1. 自定义对话框基础:登录窗口案例
实现一个带“用户名/密码输入+登录/取消按钮”的登录对话框,支持模态显示和返回值判断:
步骤1:自定义登录对话框类
# login_dialog.py(可单独文件,也可写在主文件)
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLabel,
QLineEdit, QPushButton, QMessageBox
)
from PyQt5.QtCore import Qt
class LoginDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.init_ui()
# 设置模态(默认模态,也可显式设置)
self.setModal(True)
def init_ui(self):
# 对话框基础设置
self.setWindowTitle("用户登录")
self.setFixedSize(350, 200) # 固定大小,避免缩放
# 布局
layout = QVBoxLayout()
layout.setSpacing(15)
layout.setContentsMargins(40, 30, 40, 30)
# 用户名输入
user_layout = QHBoxLayout()
user_layout.addWidget(QLabel("用户名:"))
self.user_edit = QLineEdit()
self.user_edit.setPlaceholderText("请输入用户名")
user_layout.addWidget(self.user_edit)
# 密码输入
pwd_layout = QHBoxLayout()
pwd_layout.addWidget(QLabel("密 码:"))
self.pwd_edit = QLineEdit()
self.pwd_edit.setEchoMode(QLineEdit.Password) # 密码隐藏
self.pwd_edit.setPlaceholderText("请输入密码")
pwd_layout.addWidget(self.pwd_edit)
# 按钮区
btn_layout = QHBoxLayout()
login_btn = QPushButton("登录")
cancel_btn = QPushButton("取消")
login_btn.clicked.connect(self.check_login)
cancel_btn.clicked.connect(self.reject) # 关闭对话框,返回Rejected
btn_layout.addWidget(login_btn)
btn_layout.addWidget(cancel_btn)
# 添加到布局
layout.addLayout(user_layout)
layout.addLayout(pwd_layout)
layout.addLayout(btn_layout)
self.setLayout(layout)
def check_login(self):
"""验证登录信息"""
username = self.user_edit.text().strip()
password = self.pwd_edit.text().strip()
# 简单验证(实际项目中对接数据库/接口)
if username == "admin" and password == "123456":
# 验证通过,关闭对话框并返回Accepted
self.accept()
else:
QMessageBox.warning(
self,
"登录失败",
"用户名或密码错误!",
QMessageBox.Ok
)
# 可选:获取用户输入的信息(供主窗口调用)
def get_user_info(self):
return {
"username": self.user_edit.text().strip(),
"password": self.pwd_edit.text().strip()
}步骤2:主窗口调用自定义登录对话框
import sys
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QPushButton, QLabel
)
from login_dialog import LoginDialog # 若在同一文件,无需导入
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("主窗口(调用自定义登录对话框)")
self.resize(400, 300)
# 布局
layout = QVBoxLayout()
layout.setSpacing(20)
layout.setContentsMargins(50, 50, 50, 50)
# 按钮和标签
login_btn = QPushButton("点击登录")
self.status_label = QLabel("当前状态:未登录")
self.status_label.setAlignment(Qt.AlignCenter)
# 绑定信号
login_btn.clicked.connect(self.show_login_dialog)
# 添加到布局
layout.addWidget(login_btn)
layout.addWidget(self.status_label)
self.setLayout(layout)
def show_login_dialog(self):
"""显示登录对话框并处理返回值"""
# 创建登录对话框实例
login_dlg = LoginDialog(self)
# 显示对话框并获取返回值
result = login_dlg.exec_()
# 判断返回值:Accepted(登录成功)/ Rejected(取消/关闭)
if result == login_dlg.Accepted:
user_info = login_dlg.get_user_info()
self.status_label.setText(f"当前状态:已登录(用户名:{user_info['username']})")
else:
self.status_label.setText("当前状态:取消登录")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())2. 自定义对话框核心要点
- 继承QDialog:自定义对话框必须继承
QDialog类; - 模态设置:
setModal(True)设置模态(默认模态),非模态用setModal(False); 返回值处理:
self.accept():关闭对话框,返回QDialog.Accepted(表示操作成功);self.reject():关闭对话框,返回QDialog.Rejected(表示取消/失败);- 调用
exec_()显示对话框,返回值为Accepted/Rejected;
- 数据传递:通过自定义方法(如
get_user_info())将对话框内的输入数据传递给主窗口; - 窗口大小:自定义对话框建议用
setFixedSize()固定大小,避免用户缩放导致界面混乱。
四、综合案例:整合标准对话框与自定义对话框
实现一个“简易文本编辑器”,整合:
- 自定义登录对话框(启动时要求登录);
- 标准文件对话框(打开/保存文件);
- 标准颜色/字体对话框(设置文本样式);
- 标准消息框(提示操作结果)。
完整代码


import sys
import os
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QTextEdit, QFileDialog, QColorDialog,
QFontDialog, QMessageBox, QDialog, QLabel, QLineEdit
)
from PyQt5.QtGui import QColor, QFont
from PyQt5.QtCore import Qt
# 自定义登录对话框
class LoginDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.init_ui()
self.setModal(True)
def init_ui(self):
self.setWindowTitle("用户登录")
self.setFixedSize(350, 200)
layout = QVBoxLayout()
layout.setSpacing(15)
layout.setContentsMargins(40, 30, 40, 30)
# 用户名
user_layout = QHBoxLayout()
user_layout.addWidget(QLabel("用户名:"))
self.user_edit = QLineEdit()
self.user_edit.setPlaceholderText("admin")
user_layout.addWidget(self.user_edit)
layout.addLayout(user_layout)
# 密码
pwd_layout = QHBoxLayout()
pwd_layout.addWidget(QLabel("密 码:"))
self.pwd_edit = QLineEdit()
self.pwd_edit.setEchoMode(QLineEdit.Password)
self.pwd_edit.setPlaceholderText("123456")
pwd_layout.addWidget(self.pwd_edit)
layout.addLayout(pwd_layout)
# 按钮
btn_layout = QHBoxLayout()
login_btn = QPushButton("登录")
cancel_btn = QPushButton("取消")
login_btn.clicked.connect(self.check_login)
cancel_btn.clicked.connect(self.reject)
btn_layout.addWidget(login_btn)
btn_layout.addWidget(cancel_btn)
layout.addLayout(btn_layout)
self.setLayout(layout)
def check_login(self):
if self.user_edit.text().strip() == "admin" and self.pwd_edit.text().strip() == "123456":
self.accept()
else:
QMessageBox.warning(self, "失败", "用户名/密码错误!")
# 主编辑器窗口
class EditorWindow(QWidget):
def __init__(self):
super().__init__()
# 先显示登录对话框,验证失败则退出
if not self.check_login():
sys.exit()
# 初始化主界面
self.init_ui()
self.current_file = None
def check_login(self):
"""显示登录对话框,返回是否登录成功"""
login_dlg = LoginDialog(self)
return login_dlg.exec_() == login_dlg.Accepted
def init_ui(self):
self.setWindowTitle("简易编辑器(整合对话框)")
self.resize(800, 600)
# 布局
main_layout = QVBoxLayout()
main_layout.setSpacing(10)
main_layout.setContentsMargins(15, 15, 15, 15)
# 功能按钮区
btn_layout = QHBoxLayout()
open_btn = QPushButton("打开文件")
save_btn = QPushButton("保存文件")
color_btn = QPushButton("字体颜色")
font_btn = QPushButton("字体设置")
for btn in [open_btn, save_btn, color_btn, font_btn]:
btn.setFixedSize(100, 30)
btn_layout.addWidget(open_btn)
btn_layout.addWidget(save_btn)
btn_layout.addStretch()
btn_layout.addWidget(color_btn)
btn_layout.addWidget(font_btn)
# 文本编辑区
self.text_edit = QTextEdit()
self.text_edit.setFont(QFont("微软雅黑", 12))
# 添加到布局
main_layout.addLayout(btn_layout)
main_layout.addWidget(self.text_edit)
self.setLayout(main_layout)
# 绑定信号
open_btn.clicked.connect(self.open_file)
save_btn.clicked.connect(self.save_file)
color_btn.clicked.connect(self.set_font_color)
font_btn.clicked.connect(self.set_font)
# 打开文件(标准文件对话框)
def open_file(self):
file_path, _ = QFileDialog.getOpenFileName(
self, "打开文件", "", "Text Files (*.txt);;All Files (*.*)"
)
if file_path and os.path.exists(file_path):
with open(file_path, "r", encoding="utf-8") as f:
self.text_edit.setText(f.read())
self.current_file = file_path
QMessageBox.information(self, "成功", f"已打开文件:{os.path.basename(file_path)}")
# 保存文件(标准文件对话框)
def save_file(self):
if self.current_file:
with open(self.current_file, "w", encoding="utf-8") as f:
f.write(self.text_edit.toPlainText())
QMessageBox.information(self, "成功", "文件保存成功!")
else:
file_path, _ = QFileDialog.getSaveFileName(
self, "保存文件", "", "Text Files (*.txt)"
)
if file_path:
if not file_path.endswith(".txt"):
file_path += ".txt"
with open(file_path, "w", encoding="utf-8") as f:
f.write(self.text_edit.toPlainText())
self.current_file = file_path
QMessageBox.information(self, "成功", f"文件已保存到:{file_path}")
# 设置字体颜色(标准颜色对话框)
def set_font_color(self):
color = QColorDialog.getColor(Qt.black, self, "选择字体颜色")
if color.isValid():
fmt = self.text_edit.currentCharFormat()
fmt.setForeground(color)
self.text_edit.mergeCurrentCharFormat(fmt)
# 设置字体(标准字体对话框)
def set_font(self):
font, ok = QFontDialog.getFont(self.text_edit.font(), self, "选择字体")
if ok:
self.text_edit.setCurrentFont(font)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = EditorWindow()
window.show()
sys.exit(app.exec_())五、常见问题排查
1. 对话框相关问题
- 问题1:自定义对话框非模态,主窗口可操作 → 解决:调用
setModal(True),或用exec_()显示(show()显示为非模态); - 问题2:文件对话框选择中文路径乱码 → 解决:读写文件时指定
encoding="utf-8",Windows系统可尝试encoding="gbk"; - 问题3:登录对话框关闭后主窗口也退出 → 解决:主窗口初始化时,登录失败调用
sys.exit()需确保是在app创建后; - 问题4:标准对话框标题/按钮显示英文 → 解决:PyQt5默认是英文,可通过设置Qt的语言环境(需额外配置翻译文件),或使用自定义对话框替换。
2. 模态对话框的坑
exec_():显示模态对话框,阻塞主线程,返回值为Accepted/Rejected;show():显示非模态对话框,不阻塞主线程,无返回值;- 自定义对话框若用
show()显示,需手动设置setModal(True)才能变为模态。
总结
- 标准对话框:PyQt5预制的通用交互窗口(消息框、文件选择框等),直接调用即可,覆盖80%通用场景;
- 自定义对话框:继承
QDialog开发专属窗口,核心是setModal()设置模态、accept()/reject()处理返回值; - 模态vs非模态:模态对话框阻塞主窗口(常用),非模态不阻塞(适合悬浮面板);
- 综合应用:实际项目中常结合标准对话框和自定义对话框,比如登录验证+文件操作+消息提示。
下一章我们将学习PyQt5的容器控件(QTabWidget、QGroupBox),实现多标签页、分组管理的复杂界面,进一步提升界面的层次感和实用性。如果在对话框开发中遇到问题,或者想拓展更复杂的自定义对话框(如带验证码的登录窗口),欢迎在评论区留言讨论~




