当前位置: 首页 > news >正文

PyQt5—Qt QDialog 学习笔记

第二章 控件学习


        QDialog 是 Qt 框架中用于创建对话框窗口的基础类,它继承自 QWidget。对话框通常用于短期任务或获取用户输入,分为模态对话框(必须关闭才能继续操作应用程序)和非模态对话框(可在不关闭的情况下继续操作应用程序)。

1. 最简单的 QDialog 示例

从一个最基本的 QDialog 开始,创建一个带有 "确定" 按钮的对话框:

import sys
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton, QVBoxLayout# 创建应用实例
app = QApplication(sys.argv)# 创建对话框实例
dialog = QDialog()
dialog.setWindowTitle("简单对话框")
dialog.resize(300, 200)# 创建按钮
button = QPushButton("确定")
button.clicked.connect(dialog.accept)  # 点击按钮关闭对话框并返回Accepted状态# 设置布局
layout = QVBoxLayout(dialog)
layout.addWidget(button)# 显示对话框(模态方式)
result = dialog.exec_()# 根据结果判断
if result == QDialog.Accepted:print("对话框被接受")
else:print("对话框被拒绝")sys.exit(app.exec_())

 

代码解读:

  • QDialog() 创建一个对话框窗口
  • dialog.exec_() 以模态方式显示对话框,阻塞主窗口直到对话框关闭
  • button.clicked.connect(dialog.accept) 将按钮的点击事件连接到对话框的 accept() 方法,点击后对话框关闭并返回 QDialog.Accepted
  • result 获取对话框的返回结果,可以是 QDialog.Accepted 或 QDialog.Rejected

2. 创建带输入功能的对话框

下面创建一个带输入框的对话框,用于获取用户输入的文本:

import sys
from PyQt5.QtWidgets import QApplication, QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButtonclass InputDialog(QDialog):def __init__(self, parent=None):super().__init__(parent)self.setWindowTitle("输入对话框")# 创建UI组件self.label = QLabel("请输入您的姓名:")self.input = QLineEdit()self.button = QPushButton("确定")# 设置布局layout = QVBoxLayout(self)layout.addWidget(self.label)layout.addWidget(self.input)layout.addWidget(self.button)# 连接信号和槽self.button.clicked.connect(self.onButtonClick)def onButtonClick(self):# 获取输入的文本text = self.input.text()if text:# 如果输入不为空,存储文本并接受对话框self.user_input = textself.accept()else:self.label.setText("输入不能为空,请重新输入!")# 使用示例
if __name__ == "__main__":app = QApplication(sys.argv)dialog = InputDialog()if dialog.exec_() == QDialog.Accepted:print(f"用户输入的姓名是: {dialog.user_input}")else:print("操作已取消")sys.exit(app.exec_())

 

代码解读:

  • 创建了一个自定义对话框类 InputDialog,继承自 QDialog
  • 添加了标签、输入框和按钮,并使用垂直布局管理器排列它们
  • 当用户点击 "确定" 按钮时,检查输入是否为空:
    • 不为空则将输入存储到 user_input 属性并接受对话框
    • 为空则显示错误提示
  • 在主程序中,可以通过 dialog.user_input 获取用户输入的内容

3. 使用标准按钮和信号

Qt 提供了 QDialogButtonBox 类来简化标准按钮的创建,下面是一个使用标准按钮的示例:

import sys
from PyQt5.QtWidgets import (QApplication, QDialog, QVBoxLayout, QLabel, QLineEdit, QDialogButtonBox)class LoginDialog(QDialog):def __init__(self, parent=None):super().__init__(parent)self.setWindowTitle("登录")# 创建UI组件self.username_label = QLabel("用户名:")self.username_input = QLineEdit()self.password_label = QLabel("密码:")self.password_input = QLineEdit()self.password_input.setEchoMode(QLineEdit.Password)  # 密码模式# 创建标准按钮盒(包含确定和取消按钮)self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)# 设置布局layout = QVBoxLayout(self)layout.addWidget(self.username_label)layout.addWidget(self.username_input)layout.addWidget(self.password_label)layout.addWidget(self.password_input)layout.addWidget(self.button_box)# 连接信号和槽self.button_box.accepted.connect(self.accept)  # 点击确定按钮self.button_box.rejected.connect(self.reject)  # 点击取消按钮def get_credentials(self):"""获取用户名和密码"""return self.username_input.text(), self.password_input.text()# 使用示例
if __name__ == "__main__":app = QApplication(sys.argv)dialog = LoginDialog()if dialog.exec_() == QDialog.Accepted:username, password = dialog.get_credentials()print(f"登录信息 - 用户名: {username}, 密码: {password}")else:print("登录已取消")sys.exit(app.exec_())

代码解读:

  • 使用 QDialogButtonBox 创建标准按钮,包含 "确定" 和 "取消" 按钮
  • 自动连接按钮的点击事件到对话框的 accept() 和 reject() 方法
  • 添加了密码输入框,并设置为密码模式(显示圆点)
  • 通过 get_credentials() 方法获取用户输入的用户名和密码

4. 非模态对话框示例

前面的例子都是使用模态对话框,下面看一个非模态对话框的例子:

import sys
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton, QVBoxLayout, QLabelclass NonModalDialog(QDialog):def __init__(self, parent=None):super().__init__(parent)self.setWindowTitle("非模态对话框")# 创建UI组件self.counter = 0self.label = QLabel(f"计数器: {self.counter}")self.button = QPushButton("增加计数")# 设置布局layout = QVBoxLayout(self)layout.addWidget(self.label)layout.addWidget(self.button)# 连接信号和槽self.button.clicked.connect(self.onButtonClick)def onButtonClick(self):self.counter += 1self.label.setText(f"计数器: {self.counter}")# 使用示例
if __name__ == "__main__":app = QApplication(sys.argv)# 创建主窗口(这里用QDialog代替)main_window = QDialog()main_window.setWindowTitle("主窗口")main_window.resize(300, 200)# 创建显示对话框的按钮show_dialog_button = QPushButton("显示非模态对话框")layout = QVBoxLayout(main_window)layout.addWidget(show_dialog_button)# 创建非模态对话框dialog = NonModalDialog()# 连接按钮点击事件show_dialog_button.clicked.connect(dialog.show)  # 使用show()方法显示非模态对话框main_window.show()sys.exit(app.exec_())

 

代码解读:

  • 使用 show() 方法显示对话框,而不是 exec_()
  • 非模态对话框显示后,用户可以继续与主窗口交互
  • 点击 "增加计数" 按钮会更新对话框中的计数器,而不影响主窗口

5. QRubberBand 简介与使用

QRubberBand 是 Qt 中用于创建橡皮筋选择框的类,常用于图像选择、区域标记等场景。它可以显示一个矩形或椭圆选择框,用户可以拖动调整大小。

QRubberBand 的常用方法:

方法描述
setGeometry()设置选择框的位置和大小
show()显示选择框
hide()隐藏选择框
move()移动选择框到指定位置
resize()调整选择框大小
setShape()设置选择框形状(矩形或椭圆)
size()获取选择框大小
pos()获取选择框位置

QRubberBand 的常用信号:

信号描述
rubberBandChanged当选择框的几何形状发生变化时发出

6. QRubberBand 示例代码

下面是一个使用 QRubberBand 的完整示例,允许用户在窗口中拖动鼠标选择区域:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QRubberBand
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtGui import QPixmap, QPainter, QColorclass RubberBandDemo(QWidget):def __init__(self):super().__init__()self.setWindowTitle("QRubberBand 示例")self.setGeometry(100, 100, 800, 600)# 初始化变量self.rubber_band = Noneself.selection_start = Noneself.selection_rect = QRect()# 创建一个背景图(这里使用纯色填充)self.background = QPixmap(self.size())self.background.fill(QColor(240, 240, 240))# 启用鼠标追踪self.setMouseTracking(True)def paintEvent(self, event):# 绘制背景painter = QPainter(self)painter.drawPixmap(0, 0, self.background)# 如果有选择区域,绘制半透明覆盖层if not self.selection_rect.isNull():painter.setBrush(QColor(0, 120, 215, 50))  # 半透明蓝色painter.setPen(Qt.NoPen)painter.drawRect(self.selection_rect)def mousePressEvent(self, event):# 鼠标按下时开始选择if event.button() == Qt.LeftButton:self.selection_start = event.pos()if self.rubber_band is None:self.rubber_band = QRubberBand(QRubberBand.Rectangle, self)self.rubber_band.setGeometry(QRect(self.selection_start, QRect().size()))self.rubber_band.show()def mouseMoveEvent(self, event):# 鼠标移动时更新选择框if self.selection_start is not None:self.selection_rect = QRect(self.selection_start, event.pos()).normalized()self.rubber_band.setGeometry(self.selection_rect)def mouseReleaseEvent(self, event):# 鼠标释放时完成选择if event.button() == Qt.LeftButton and self.selection_start is not None:self.selection_rect = QRect(self.selection_start, event.pos()).normalized()self.rubber_band.hide()# 输出选择区域信息print(f"选择区域: 左上角({self.selection_rect.x()}, {self.selection_rect.y()}) "f"大小({self.selection_rect.width()}, {self.selection_rect.height()})")# 可以在这里处理选择区域,例如截图、处理选中的内容等self.selection_start = Noneif __name__ == "__main__":app = QApplication(sys.argv)window = RubberBandDemo()window.show()sys.exit(app.exec_())

代码解读:

  • 创建了一个继承自 QWidget 的窗口类 RubberBandDemo
  • 在 mousePressEvent 中初始化并显示 QRubberBand
  • 在 mouseMoveEvent 中根据鼠标位置更新选择框的大小和位置
  • 在 mouseReleaseEvent 中完成选择并隐藏选择框
  • 使用 paintEvent 绘制半透明的选择区域覆盖层
  • 打印选择区域的位置和大小信息

7. 高级应用:图像选择工具

下面是一个更实用的例子,结合 QLabel 和 QRubberBand 创建一个图像选择工具:

import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QRubberBand)
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtGui import QPixmap, QPainter, QColorclass ImageSelector(QWidget):def __init__(self):super().__init__()self.setWindowTitle("图像选择工具")self.resize(800, 600)# 初始化UIself.initUI()# 初始化变量self.rubber_band = Noneself.selection_start = Noneself.current_image = Nonedef initUI(self):# 创建主布局main_layout = QVBoxLayout(self)# 创建图像显示区域self.image_label = QLabel("请打开一张图片")self.image_label.setAlignment(Qt.AlignCenter)self.image_label.setMinimumSize(400, 400)self.image_label.setStyleSheet("border: 1px solid #cccccc;")main_layout.addWidget(self.image_label)# 创建按钮区域button_layout = QHBoxLayout()self.open_button = QPushButton("打开图片")self.open_button.clicked.connect(self.openImage)button_layout.addWidget(self.open_button)self.crop_button = QPushButton("裁剪选中区域")self.crop_button.clicked.connect(self.cropImage)self.crop_button.setEnabled(False)button_layout.addWidget(self.crop_button)main_layout.addLayout(button_layout)def openImage(self):file_path, _ = QFileDialog.getOpenFileName(self, "打开图片", "", "图像文件 (*.png *.jpg *.jpeg *.bmp)")if file_path:self.current_image = QPixmap(file_path)self.image_label.setPixmap(self.current_image.scaled(self.image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))self.crop_button.setEnabled(True)def resizeEvent(self, event):# 窗口大小改变时重绘图像if self.current_image:self.image_label.setPixmap(self.current_image.scaled(self.image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))super().resizeEvent(event)def mousePressEvent(self, event):# 只在图像区域内响应鼠标事件if self.current_image and self.image_label.geometry().contains(event.pos()):# 计算在图像上的相对位置pos_in_image = event.pos() - self.image_label.pos()# 确保位置在图像内pixmap_size = self.image_label.pixmap().size()if (0 <= pos_in_image.x() < pixmap_size.width() and 0 <= pos_in_image.y() < pixmap_size.height()):self.selection_start = pos_in_imageif self.rubber_band is None:self.rubber_band = QRubberBand(QRubberBand.Rectangle, self.image_label)# 转换为label坐标系self.rubber_band.setGeometry(QRect(self.selection_start, QRect().size()))self.rubber_band.show()def mouseMoveEvent(self, event):# 只在图像区域内响应鼠标事件if (self.current_image and self.selection_start is not None and self.image_label.geometry().contains(event.pos())):# 计算在图像上的相对位置pos_in_image = event.pos() - self.image_label.pos()# 确保位置在图像内pixmap_size = self.image_label.pixmap().size()if (0 <= pos_in_image.x() < pixmap_size.width() and 0 <= pos_in_image.y() < pixmap_size.height()):# 更新选择框self.rubber_band.setGeometry(QRect(self.selection_start, pos_in_image).normalized())def mouseReleaseEvent(self, event):if self.selection_start is not None:self.selection_start = Nonedef cropImage(self):if self.rubber_band and not self.rubber_band.geometry().isNull():# 获取选择框在label中的位置selection = self.rubber_band.geometry()# 获取当前显示的pixmapdisplay_pixmap = self.image_label.pixmap()# 计算选择区域相对于原始图像的比例scale_x = self.current_image.width() / display_pixmap.width()scale_y = self.current_image.height() / display_pixmap.height()# 转换选择区域到原始图像坐标original_rect = QRect(int(selection.x() * scale_x),int(selection.y() * scale_y),int(selection.width() * scale_x),int(selection.height() * scale_y))# 裁剪图像cropped_pixmap = self.current_image.copy(original_rect)# 显示裁剪后的图像self.image_label.setPixmap(cropped_pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))# 隐藏选择框self.rubber_band.hide()if __name__ == "__main__":app = QApplication(sys.argv)window = ImageSelector()window.show()sys.exit(app.exec_())

 

代码解读:

  • 创建了一个功能完整的图像选择工具,支持打开图片、选择区域和裁剪
  • 使用 QRubberBand 实现图像上的区域选择
  • 处理了图像缩放和坐标转换问题,确保选择区域能正确映射到原始图像
  • 提供了裁剪功能,将选中区域保存为新图像
http://www.dtcms.com/a/287543.html

相关文章:

  • 【RK3576】【Android14】SDK源码编译
  • 【RK3576】【Android14】UART开发调试
  • JavaScript基础语法和简单数据结构
  • 【小沐学GIS】基于Rust绘制三维数字地球Earth(Rust、OpenGL、GIS)
  • RPC(Remote Procedure Call,远程过程调用)介绍
  • MySQL数据丢失救援办法
  • 下一场范式革命:Transformer架构≠最终解法
  • 《全栈博客系统的技术肌理:从接口构建到体验升维的实践路径》
  • 美国VPS服务器Linux内核参数调优的实践与验证
  • 第二次总结(xss、js原型链)
  • 【2025最新】使用neo4j实现GraphRAG所需的向量检索
  • OAIF:基于在线 AI 反馈的语言模型直接对齐
  • [MarkdownGithub] 使用块引用高亮显示“注意“和“警告“和其他注意方式的选项
  • Django母婴商城项目实践(九)- 商品列表页模块
  • vue2 面试题及详细答案150道(121 - 130)
  • [Python] -实用技巧10- 时间处理:datetime 和 time 模块入门
  • React 的 `cache()` 函数
  • 使用 Gunicorn 部署 Django 项目
  • XSS相关理解
  • Ubuntu20.04 samba配置
  • 针对大规模语言模型的上下文工程技术调研与总结(翻译并摘要)
  • 考研复习-数据结构-第七章-查找
  • 论文略读:Are Large Language Models In-Context Graph Learners?
  • 编程实现Word自动排版:从理论到实践的全面指南
  • 【Linux服务器】-zabbix通过proxy进行分级监控
  • Vue3生命周期函数
  • 多进程服务器
  • 【愚公系列】《MIoT.VC》001-认识、安装 MIoT.VC 软件
  • 安装postgresql
  • 深度学习Depth Anything V2神经网络实现单目深度估计系统源码