PyQt5 对话框控件:QDialog 与标准对话框(附登录实战代码)

寒烟似雪
1月4日发布 /正在检测是否收录...

第8篇:PyQt5对话框控件:QDialog与标准对话框(完整代码)

哈喽~ 欢迎来到PyQt5系列的第8篇!上一章我们掌握了QTextEdit(富文本)和QTableWidget(表格)的核心用法,今天聚焦对话框控件——对话框是PyQt5界面交互的“重要桥梁”,比如提示信息、选择文件、用户登录验证等场景都离不开它。我们会详细讲解两类对话框:标准对话框(系统预制,如消息框、文件选择框)和自定义对话框(基于QDialog开发专属交互窗口),全程搭配完整可运行代码,新手也能轻松掌握!
mjzpc9v3.png

一、先明确:对话框的核心概念

在学习具体用法前,先理清对话框的两个核心属性,避免用错场景:

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还提供了文件、颜色、字体选择的标准对话框,用法统一且简单:

完整代码:文件/颜色/字体对话框

效果如图
mjzp4re8.png

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

核心方法总结

对话框类型核心方法关键参数/返回值
QFileDialoggetOpenFileName()返回(选中路径, 筛选器),支持多文件选择(getOpenFileNames
QColorDialoggetColor()返回QColor对象,isValid()判断是否选择有效颜色
QFontDialoggetFont()返回(字体对象, 是否确认),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. 自定义对话框核心要点

  1. 继承QDialog:自定义对话框必须继承QDialog类;
  2. 模态设置setModal(True)设置模态(默认模态),非模态用setModal(False)
  3. 返回值处理

    • self.accept():关闭对话框,返回QDialog.Accepted(表示操作成功);
    • self.reject():关闭对话框,返回QDialog.Rejected(表示取消/失败);
    • 调用exec_()显示对话框,返回值为Accepted/Rejected
  4. 数据传递:通过自定义方法(如get_user_info())将对话框内的输入数据传递给主窗口;
  5. 窗口大小:自定义对话框建议用setFixedSize()固定大小,避免用户缩放导致界面混乱。

四、综合案例:整合标准对话框与自定义对话框

实现一个“简易文本编辑器”,整合:

  • 自定义登录对话框(启动时要求登录);
  • 标准文件对话框(打开/保存文件);
  • 标准颜色/字体对话框(设置文本样式);
  • 标准消息框(提示操作结果)。

完整代码

mjzp8xp5.png
mjzp9112.png

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)才能变为模态。

总结

  1. 标准对话框:PyQt5预制的通用交互窗口(消息框、文件选择框等),直接调用即可,覆盖80%通用场景;
  2. 自定义对话框:继承QDialog开发专属窗口,核心是setModal()设置模态、accept()/reject()处理返回值;
  3. 模态vs非模态:模态对话框阻塞主窗口(常用),非模态不阻塞(适合悬浮面板);
  4. 综合应用:实际项目中常结合标准对话框和自定义对话框,比如登录验证+文件操作+消息提示。

下一章我们将学习PyQt5的容器控件(QTabWidget、QGroupBox),实现多标签页、分组管理的复杂界面,进一步提升界面的层次感和实用性。如果在对话框开发中遇到问题,或者想拓展更复杂的自定义对话框(如带验证码的登录窗口),欢迎在评论区留言讨论~

© 版权声明
THE END
喜欢就支持一下吧
点赞 0 分享 收藏
评论 抢沙发
OωO
取消
SSL