找到
7
篇与
pyqt5
相关的结果
-
PyQt5布局管理器进阶:网格布局与表单布局(附实战代码) 第6篇:PyQt5布局管理器进阶:网格布局与表单布局(完整代码) mjw558rv.png图片 哈喽~ 欢迎来到PyQt5系列的第6篇!上一章我们通过“简易文本编辑器”实战,巩固了线性布局(QVBoxLayout/QHBoxLayout)的用法。但在实际开发中,很多复杂界面(比如计算器、用户信息表单、数据展示表格)无法只用线性布局满足——要么控件排列混乱,要么自适应效果差。今天我们就来学习两种进阶布局管理器:网格布局(QGridLayout)和表单布局(QFormLayout),彻底解决复杂界面的排版难题! 一、先明确:进阶布局的核心作用 在学习具体布局前,先搞清楚两种布局的适用场景,避免用错地方: 网格布局(QGridLayout):控件按“行×列”的网格排列,适合需要精准控制控件位置的场景(如计算器按钮、表格数据展示);支持控件跨多行/多列,灵活性极高; 表单布局(QFormLayout):专门用于“标签+输入框”的表单场景(如用户注册/登录表单、信息填写窗口),自动对齐标签和输入框,界面规整且开发高效。 核心优势:两种布局都支持自适应——窗口缩放时,控件会按预设规则自动调整大小和位置,无需手动计算坐标。 二、网格布局(QGridLayout)详解:从基础到实战 网格布局的核心逻辑是“划分网格、给控件分配行和列”,比如将界面划分为3行3列,每个控件占1个“格子”,也可以让控件占2行1列(跨行吗)、1行2列(跨列)。 1. 网格布局基础用法(完整代码) 先实现一个简单的3×3网格,放置9个按钮,演示基础的行、列分配: import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QPushButton, QGridLayout, QLabel ) from PyQt5.QtCore import Qt class GridLayoutDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("QGridLayout网格布局基础演示") self.resize(400, 300) # 1. 创建网格布局实例 grid_layout = QGridLayout() # 设置控件间距(格子之间的距离) grid_layout.setSpacing(10) # 设置边距(布局与窗口边缘的距离) grid_layout.setContentsMargins(20, 20, 20, 20) # 2. 创建控件并添加到网格布局 # 核心方法:addWidget(控件, 行号, 列号, 占用行数, 占用列数) # 行号、列号从0开始;占用行数/列数默认是1(可不写) for i in range(3): # 行:0、1、2 for j in range(3): # 列:0、1、2 btn = QPushButton(f"按钮({i},{j})") # 添加到网格的(i,j)位置,占1行1列 grid_layout.addWidget(btn, i, j) # 3. 额外添加一个跨列控件(占1行2列) cross_col_btn = QPushButton("跨2列按钮") grid_layout.addWidget(cross_col_btn, 3, 0, 1, 2) # 第4行(索引3),第0列,占1行2列 # 4. 额外添加一个跨行控件(占2行1列) cross_row_btn = QPushButton("跨2行按钮") grid_layout.addWidget(cross_row_btn, 0, 3, 2, 1) # 第0行,第4列(索引3),占2行1列 # 5. 将布局绑定到窗口 self.setLayout(grid_layout) if __name__ == "__main__": app = QApplication(sys.argv) window = GridLayoutDemo() window.show() sys.exit(app.exec_())mjw55cag.png图片 2. 网格布局关键方法解析 网格布局的核心是addWidget()方法,参数含义必须记牢: addWidget(控件, row, column, rowSpan, columnSpan) # row:控件所在的行号(从0开始) # column:控件所在的列号(从0开始) # rowSpan:控件占用的行数(默认1,即不跨行) # columnSpan:控件占用的列数(默认1,即不跨列)其他常用方法: setSpacing(像素值):设置相邻控件之间的间距,避免控件挤在一起; setContentsMargins(左, 上, 右, 下):设置布局与窗口边缘的距离,提升界面美观度; setRowStretch(行号, 拉伸系数):设置某一行的拉伸权重(窗口缩放时,拉伸系数大的行占更多空间); setColumnStretch(列号, 拉伸系数):设置某一列的拉伸权重,同理。 3. 网格布局实战:简易计算器界面(核心场景) 计算器是网格布局的典型应用——按钮按固定网格排列,部分按钮(如“0”“=”)跨列。我们实现一个简易计算器的界面(含输入框+按钮区): import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QPushButton, QGridLayout, QLineEdit ) from PyQt5.QtCore import Qt class SimpleCalculator(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("简易计算器(网格布局实战)") self.resize(350, 400) # 1. 创建主布局(垂直布局:输入框在上,按钮区在下) main_layout = QGridLayout() main_layout.setSpacing(10) main_layout.setContentsMargins(15, 15, 15, 15) # 2. 创建输入框(占1行4列) self.input_edit = QLineEdit() self.input_edit.setAlignment(Qt.AlignRight) # 文本右对齐(符合计算器习惯) self.input_edit.setStyleSheet("font-size: 20px; padding: 10px;") self.input_edit.setReadOnly(True) # 输入框只读,通过按钮输入 main_layout.addWidget(self.input_edit, 0, 0, 1, 4) # 第0行,第0列,占1行4列 # 3. 定义计算器按钮文本(按网格顺序排列) btn_texts = [ '7', '8', '9', '/', '4', '5', '6', '*', '1', '2', '3', '-', '0', '.', '=', '+', 'C' # 清空按钮 ] # 4. 给按钮分配行和列,添加到网格布局 row = 1 # 从第1行开始(第0行是输入框) col = 0 for text in btn_texts: btn = QPushButton(text) btn.setStyleSheet("font-size: 16px; padding: 15px;") # 特殊处理:0按钮跨2列,C按钮跨2列 if text == '0': main_layout.addWidget(btn, row, col, 1, 2) # 占1行2列 col += 2 # 列号+2(跳过一个格子) elif text == 'C': main_layout.addWidget(btn, row, col, 1, 2) # 占1行2列 col += 2 else: main_layout.addWidget(btn, row, col) # 默认占1行1列 col += 1 # 每4列换行(计算器是4列按钮) if col >= 4: col = 0 row += 1 # 5. 绑定布局到窗口 self.setLayout(main_layout) # 6. 绑定按钮点击信号(简单演示:点击按钮显示文本到输入框) for i in range(main_layout.count()): widget = main_layout.itemAt(i).widget() if isinstance(widget, QPushButton): widget.clicked.connect(self.on_btn_click) def on_btn_click(self): """按钮点击槽函数:将按钮文本显示到输入框""" sender = self.sender() text = sender.text() if text == 'C': # 清空输入框 self.input_edit.clear() elif text == '=': # 简单计算(实际项目需处理异常,此处简化) try: result = eval(self.input_edit.text()) self.input_edit.setText(str(result)) except: self.input_edit.setText("错误") else: # 拼接文本 current_text = self.input_edit.text() self.input_edit.setText(current_text + text) if __name__ == "__main__": app = QApplication(sys.argv) calculator = SimpleCalculator() calculator.show() sys.exit(app.exec_())4. 计算器界面亮点 用网格布局精准还原计算器的按钮排列,“0”和“C”按钮跨列,符合实际计算器的交互习惯; 结合垂直布局的思路,将输入框和按钮区整合,界面层次清晰; 实现了基础的计算逻辑(数字拼接、清空、结果计算),信号与槽绑定简洁高效。 三、表单布局(QFormLayout)详解:高效实现表单界面 表单布局是“标签+输入框”的专用布局,无需手动调整对齐方式——它会自动将标签放在左侧、输入控件放在右侧,且所有标签和输入框分别对齐,开发效率极高。 1. 表单布局基础用法(完整代码) 实现一个简单的用户注册表单,包含“用户名、密码、邮箱、电话”四个字段: import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QLineEdit, QPushButton, QFormLayout, QVBoxLayout, QComboBox ) from PyQt5.QtCore import Qt class FormLayoutDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("QFormLayout表单布局基础演示") self.resize(400, 300) # 1. 创建主布局(垂直布局:表单在上,按钮在下) main_layout = QVBoxLayout() main_layout.setSpacing(20) main_layout.setContentsMargins(30, 30, 30, 30) # 2. 创建表单布局实例 form_layout = QFormLayout() form_layout.setSpacing(15) # 标签与输入框、行与行之间的间距 # 3. 创建标签和输入控件,添加到表单布局 # 核心方法:addRow(标签文本/标签控件, 输入控件) # 方式1:直接传标签文本(自动生成QLabel) form_layout.addRow("用户名:", QLineEdit()) # 方式2:传QLabel控件(可自定义标签样式) pwd_label = QLabel("密码:") pwd_label.setStyleSheet("color: #e74c3c;") pwd_edit = QLineEdit() pwd_edit.setEchoMode(QLineEdit.Password) # 密码隐藏 form_layout.addRow(pwd_label, pwd_edit) # 方式3:输入控件可以是其他类型(如下拉框) form_layout.addRow("性别:", QComboBox()) # 方式4:添加提示标签(跨两列) tip_label = QLabel("* 带星号的为必填项") tip_label.setStyleSheet("color: #95a5a6; font-size: 12px;") form_layout.addRow(tip_label) # 无输入控件时,标签跨两列 # 4. 调整标签对齐方式(默认左对齐,可改为右对齐) form_layout.setLabelAlignment(Qt.AlignRight) # 5. 创建提交按钮 submit_btn = QPushButton("提交表单") submit_btn.setStyleSheet("background-color: #3498db; color: white; padding: 8px;") # 6. 将表单布局和按钮添加到主布局 main_layout.addLayout(form_layout) main_layout.addWidget(submit_btn, alignment=Qt.AlignCenter) # 7. 绑定布局到窗口 self.setLayout(main_layout) if __name__ == "__main__": app = QApplication(sys.argv) window = FormLayoutDemo() window.show() sys.exit(app.exec_())2. 表单布局关键方法解析 addRow(标签, 输入控件):核心方法,添加一行表单;标签可以是字符串(自动生成QLabel)或QLabel实例,输入控件可以是QLineEdit、QComboBox等任意交互控件; setLabelAlignment(对齐方式):设置标签的对齐方式(如Qt.AlignRight让标签右对齐,与输入框间距更紧凑); setSpacing(像素值):设置“标签与输入框之间”和“相邻行之间”的间距; setFieldGrowthPolicy(策略):设置输入控件的拉伸策略(如QFormLayout.ExpandingFieldsGrow让输入控件随窗口缩放而拉伸)。 3. 表单布局实战:完整用户信息登记表单 整合表单布局和之前学的控件,实现一个完整的用户信息登记表单,包含输入框、下拉框、复选框,添加表单验证逻辑: import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QLineEdit, QPushButton, QFormLayout, QVBoxLayout, QComboBox, QCheckBox, QMessageBox ) from PyQt5.QtCore import Qt class UserInfoForm(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("用户信息登记表单(表单布局实战)") self.resize(450, 350) self.setStyleSheet("font-size: 14px; color: #2c3e50;") # 1. 主布局 main_layout = QVBoxLayout() main_layout.setSpacing(20) main_layout.setContentsMargins(30, 30, 30, 30) # 2. 表单布局 form_layout = QFormLayout() form_layout.setSpacing(15) form_layout.setLabelAlignment(Qt.AlignRight) # 设置输入控件拉伸(随窗口缩放) form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) # 3. 表单控件初始化 self.user_edit = QLineEdit() self.user_edit.setPlaceholderText("请输入用户名(3-10个字符)") self.pwd_edit = QLineEdit() self.pwd_edit.setEchoMode(QLineEdit.Password) self.pwd_edit.setPlaceholderText("请输入密码(6-16个字符)") self.email_edit = QLineEdit() self.email_edit.setPlaceholderText("请输入邮箱(如xxx@xxx.com)") self.gender_combo = QComboBox() self.gender_combo.addItems(["男", "女", "保密"]) self.phone_edit = QLineEdit() self.phone_edit.setPlaceholderText("请输入手机号(11位数字)") # 4. 添加表单行 form_layout.addRow("用户名*:", self.user_edit) form_layout.addRow("密码*:", self.pwd_edit) form_layout.addRow("邮箱*:", self.email_edit) form_layout.addRow("性别:", self.gender_combo) form_layout.addRow("手机号:", self.phone_edit) # 同意条款复选框(跨两列) self.agree_check = QCheckBox("我已阅读并同意《用户服务条款》") form_layout.addRow(self.agree_check) # 5. 提交按钮 self.submit_btn = QPushButton("提交信息") self.submit_btn.setStyleSheet(""" QPushButton { background-color: #27ae60; color: white; padding: 10px; border: none; border-radius: 4px; } QPushButton:hover { background-color: #219653; } """) # 6. 结果显示标签 self.result_label = QLabel("") self.result_label.setAlignment(Qt.AlignCenter) # 7. 添加到主布局 main_layout.addLayout(form_layout) main_layout.addWidget(self.submit_btn, alignment=Qt.AlignCenter) main_layout.addWidget(self.result_label) # 8. 绑定布局和信号 self.setLayout(main_layout) self.submit_btn.clicked.connect(self.check_form) def check_form(self): """表单验证逻辑:检查必填项、格式正确性""" # 1. 获取表单内容 username = self.user_edit.text().strip() password = self.pwd_edit.text().strip() email = self.email_edit.text().strip() gender = self.gender_combo.currentText() phone = self.phone_edit.text().strip() # 2. 验证必填项 if not username: QMessageBox.warning(self, "验证失败", "用户名不能为空!") return if not password: QMessageBox.warning(self, "验证失败", "密码不能为空!") return if not email: QMessageBox.warning(self, "验证失败", "邮箱不能为空!") return if not self.agree_check.isChecked(): QMessageBox.warning(self, "验证失败", "请同意用户服务条款!") return # 3. 验证格式(简化版) if len(username) < 3 or len(username) > 10: QMessageBox.warning(self, "验证失败", "用户名长度需在3-10个字符之间!") return if len(password) < 6 or len(password) > 16: QMessageBox.warning(self, "验证失败", "密码长度需在6-16个字符之间!") return if "@" not in email: QMessageBox.warning(self, "验证失败", "邮箱格式不正确(需包含@)!") return if phone and len(phone) != 11: QMessageBox.warning(self, "验证失败", "手机号需为11位数字!") return # 4. 验证通过,显示结果 result = f""" 信息提交成功! 用户名:{username} 性别:{gender} 邮箱:{email} 手机号:{phone if phone else "未填写"} """ self.result_label.setText(result) self.result_label.setStyleSheet("color: #27ae60;") if __name__ == "__main__": app = QApplication(sys.argv) form = UserInfoForm() form.show() sys.exit(app.exec_())5. 实战表单亮点 用表单布局快速实现规整的表单界面,标签右对齐,输入框自适应拉伸,开发效率高; 添加了完整的表单验证逻辑(必填项检查、格式验证),用QMessageBox弹出提示,提升用户体验; 结合了多种控件(输入框、下拉框、复选框),覆盖实际表单的常见场景; 美化了按钮样式(悬停效果、圆角),界面更美观。 四、布局嵌套技巧:复杂界面的核心思路 实际开发中,很少用单一布局完成复杂界面,而是通过“布局嵌套”组合使用——比如“主布局(垂直)”包含“表单布局”和“网格布局”,再搭配“水平布局”排列按钮。 嵌套核心原则: 先划分界面大区域(如“顶部标题区、中间内容区、底部按钮区”),用主布局(垂直/水平)管理; 每个小区域内部,根据控件类型选择合适的子布局(网格/表单/线性); 用addLayout()方法将子布局添加到主布局,实现层次化管理。 五、常见问题排查 问题1:网格布局控件重叠/位置错乱 → 解决:检查行号、列号是否正确,避免多个控件分配到同一个格子;跨行列时注意rowSpan和columnSpan的数值; 问题2:表单布局标签和输入框不对齐 → 解决:用setLabelAlignment()统一标签对齐方式;避免手动设置输入控件的固定宽度(让布局自动适配); 问题3:窗口缩放时控件不拉伸 → 解决:给布局添加拉伸系数(setRowStretch/setColumnStretch),或设置输入控件的拉伸策略(如表单布局的setFieldGrowthPolicy); 问题4:布局嵌套后界面混乱 → 解决:先画界面草图,明确大区域和子区域的划分;给每个布局添加setSpacing和setContentsMargins,避免间距混乱。 总结 本章我们掌握了两种进阶布局管理器的核心用法: 网格布局(QGridLayout):适合“行×列”的规整排列场景(如计算器),支持控件跨行列,灵活性极高; 表单布局(QFormLayout):专门用于“标签+输入框”的表单场景,自动对齐,开发效率高; 布局嵌套是复杂界面的核心思路,先划分大区域,再用子布局管理小区域。 下一章我们将学习PyQt5的文本与表格控件(QTextEdit与QTableWidget),进一步拓展界面的功能边界。如果在布局实操中遇到问题,或者有复杂界面的排版需求,欢迎在评论区留言讨论~ -
Python 微信自动回复工具 | 带 PyQt5 图形界面 支持 Excel 关键词配置 微信自动回复工具(带PyQt界面) 最近帮朋友处理微信客服消息,重复回复太费时间,干脆写了个带图形界面的自动回复工具。不用记命令,填个Excel路径点按钮就能跑,还能实时看日志,日常用着挺顺手。下面把完整代码和用法贴出来,有需要的可以直接拿去改。 mjr3m247.png图片 先说说要准备的东西 环境:Python 3.8+(版本太高可能和pywin32不兼容) 要装的库:直接复制下面的命令到cmd里跑 pip install pywin32 pandas pyqt5 openpyxlmjr3dd3v.png图片 Excel回复表:建个Excel文件,第一列叫“关键词”,第二列叫“回复内容”,比如这样: 关键词回复内容你好您好~有什么可以帮您?下班时间我们18点下班,急事可留言~价格具体报价请发需求给我哦~mjr3ggkk.png图片 没有的朋友们不要急,我提供了默认数据文件下载,方便测试 微信自动回复表 下载地址:https://pan.quark.cn/s/75a06eed928c 提取码: 完整代码 import sys import time import win32gui import win32api import win32con import pandas as pd from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel, QLineEdit, QPushButton, QTextEdit, QVBoxLayout, QHBoxLayout, QFileDialog) from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtGui import QFont, QPalette, QColor # 消息监听线程:单独开线程是为了不让界面卡住 class ReplyWorker(QThread): # 用来给界面发日志的信号 log_send = pyqtSignal(str) # 用来告诉界面线程是否正常启动 thread_status = pyqtSignal(bool) def __init__(self, excel_path): super().__init__() self.excel_path = excel_path self.reply_rules = {} # 存关键词和回复的对应关系 self.wechat_handle = 0 # 微信窗口的句柄 self.running = False # 控制线程运行的开关 def load_excel_rules(self): """加载Excel里的关键词和回复""" try: # 用openpyxl读xlsx格式,避免编码问题 df = pd.read_excel(self.excel_path, engine='openpyxl') # 转成字典,查起来快 self.reply_rules = dict(zip(df['关键词'], df['回复内容'])) self.log_send.emit(f"回复表加载成功!共{len(self.reply_rules)}条规则") return True except Exception as e: # 捕获各种错误:文件没找到、格式不对、列名错 err_msg = f"加载Excel失败:{str(e)}" self.log_send.emit(err_msg) return False def find_wechat(self): """找到微信窗口,返回是否找到""" # 微信PC端的窗口名一般是“微信”,类名留空不限制 self.wechat_handle = win32gui.FindWindow(None, "微信") if self.wechat_handle == 0: self.log_send.emit("没找到微信窗口!请先打开微信并登录") return False # 把微信窗口提到最前面 win32gui.SetForegroundWindow(self.wechat_handle) self.log_send.emit(f"找到微信啦!窗口句柄:{self.wechat_handle}") return True def find_child(self, parent_handle, class_name=None): """找窗口里的子控件(比如输入框、发送按钮)""" child_handles = [] # 递归遍历所有子控件 def _enum_child(hwnd, extra): if class_name is None or win32gui.GetClassName(hwnd) == class_name: extra.append(hwnd) return True win32gui.EnumChildWindows(parent_handle, _enum_child, child_handles) # 返回第一个找到的控件(一般够用) return child_handles[0] if child_handles else 0 def get_last_msg(self, chat_panel): """获取聊天面板里最后一条消息""" # 微信聊天面板的文本就是所有消息,按换行分割取最后一行 all_msg = win32gui.GetWindowText(chat_panel) if all_msg: return all_msg.split("\n")[-1].strip() return "" def send_reply(self, input_box, send_btn, reply_content): """模拟输入并发送回复""" # 先清空输入框:选中所有文本再删除 win32gui.SendMessage(input_box, win32con.EM_SETSEL, 0, -1) win32gui.SendMessage(input_box, win32con.WM_CLEAR, 0, 0) # 逐个字符输入(太快会乱码,加个小延迟) for char in reply_content: win32gui.SendMessage(input_box, win32con.WM_CHAR, ord(char), 0) time.sleep(0.02) # 点击发送按钮 win32gui.SendMessage(send_btn, win32con.BM_CLICK, 0, 0) def run(self): """线程主逻辑:启动后一直监听""" self.running = True # 先加载规则和找微信,有一个失败就退出 if not self.load_excel_rules() or not self.find_wechat(): self.thread_status.emit(False) self.running = False return self.thread_status.emit(True) # 定位微信的核心控件(不同版本可能要改class_name,用Spy++看) session_list = self.find_child(self.wechat_handle, "ContactPanel") # 会话列表 input_box = self.find_child(self.wechat_handle, "Edit") # 输入框 send_btn = self.find_child(self.wechat_handle, "Button") # 发送按钮 chat_panel = self.find_child(self.wechat_handle, "ChatPanel") # 聊天面板 # 检查控件是否都找到 if not all([session_list, input_box, send_btn, chat_panel]): self.log_send.emit("没找到微信的核心控件!可能版本不兼容") self.running = False return self.log_send.emit("所有控件已定位,开始监听消息...") # 循环监听未读消息 while self.running: # 遍历所有会话找带“未读”的 sessions = [] win32gui.EnumChildWindows(session_list, lambda hwnd, extra: extra.append(hwnd), sessions) for session in sessions: session_text = win32gui.GetWindowText(session) if "未读" in session_text: # 点击未读会话,切换到聊天界面 win32gui.SendMessage(session, win32con.BM_CLICK, 0, 0) time.sleep(0.5) # 等消息加载出来 # 提取联系人(去掉“未读”字样) contact = session_text.replace("未读", "").strip() # 提取最后一条消息 last_msg = self.get_last_msg(chat_panel) if not last_msg: continue self.log_send.emit(f"\n收到[{contact}]的消息:{last_msg}") # 匹配关键词找回复 reply = "抱歉呀,我暂时没理解你的意思~" for keyword, content in self.reply_rules.items(): if keyword in last_msg: reply = content break self.log_send.emit(f"准备回复:{reply}") # 发送回复 self.send_reply(input_box, send_btn, reply) self.log_send.emit("回复发送成功!") # 每2秒查一次,别占太多CPU time.sleep(2) def stop(self): """停止线程""" self.running = False self.log_send.emit("\n监听已停止") # 主界面窗口 class ReplyWindow(QMainWindow): def __init__(self): super().__init__() self.worker = None # 监听线程对象 self.init_ui() # 初始化界面 def init_ui(self): """画界面:布局、按钮、输入框这些""" # 窗口基本设置 self.setWindowTitle("微信自动回复工具 v1.0 - 作者:寒烟似雪 2025/12/29发布在字节曜www.ziyeyao.com博客") self.setFixedSize(1200, 1000) # 固定大小,不允许拉伸 self.setStyleSheet("background-color: #f5f5f5;") # 中心部件(主窗口必须有个中心部件才能放内容) central_widget = QWidget() self.setCentralWidget(central_widget) # 主布局(垂直排列:路径输入→按钮→日志) main_layout = QVBoxLayout(central_widget) main_layout.setSpacing(15) # 控件之间的间距 main_layout.setContentsMargins(25, 25, 25, 25) # 边距 # 1. Excel路径选择区域(水平排列:标签→输入框→选择按钮) path_layout = QHBoxLayout() # 路径标签 path_label = QLabel("回复表路径:") path_label.setFont(QFont("微软雅黑", 10)) # 路径输入框 self.path_edit = QLineEdit() self.path_edit.setFont(QFont("微软雅黑", 9)) self.path_edit.setPlaceholderText("点击右侧按钮选择Excel文件") self.path_edit.setStyleSheet(""" QLineEdit { padding: 6px; border: 1px solid #ddd; border-radius: 4px; background-color: white; } """) # 路径选择按钮 self.select_btn = QPushButton("选择文件") self.select_btn.setFont(QFont("微软雅黑", 9)) self.select_btn.setStyleSheet(""" QPushButton { padding: 6px 12px; border: none; border-radius: 4px; background-color: #4285f4; color: white; } QPushButton:hover { background-color: #3367d6; } """) self.select_btn.clicked.connect(self.select_excel) # 绑定选择文件事件 # 把三个控件加到水平布局里 path_layout.addWidget(path_label) path_layout.addWidget(self.path_edit, stretch=1) # 输入框占满剩余空间 path_layout.addWidget(self.select_btn, stretch=0) main_layout.addLayout(path_layout) # 2. 启动/停止按钮区域(水平排列) btn_layout = QHBoxLayout() # 启动按钮 self.start_btn = QPushButton("启动监听") self.start_btn.setFont(QFont("微软雅黑", 10)) self.start_btn.setStyleSheet(""" QPushButton { padding: 8px 0; border: none; border-radius: 4px; background-color: #34a853; color: white; } QPushButton:disabled { background-color: #a0d9a0; } QPushButton:hover:enabled { background-color: #2d8d46; } """) self.start_btn.clicked.connect(self.start_listen) # 绑定启动事件 # 停止按钮 self.stop_btn = QPushButton("停止监听") self.stop_btn.setFont(QFont("微软雅黑", 10)) self.stop_btn.setStyleSheet(""" QPushButton { padding: 8px 0; border: none; border-radius: 4px; background-color: #ea4335; color: white; } QPushButton:disabled { background-color: #e8a098; } QPushButton:hover:enabled { background-color: #d33526; } """) self.stop_btn.clicked.connect(self.stop_listen) # 绑定停止事件 self.stop_btn.setEnabled(False) # 初始状态:停止按钮禁用 # 两个按钮平分宽度 btn_layout.addWidget(self.start_btn, stretch=1) btn_layout.addSpacing(10) # 按钮之间的间距 btn_layout.addWidget(self.stop_btn, stretch=1) main_layout.addLayout(btn_layout) # 3. 日志显示区域 # 日志标签 log_label = QLabel("运行日志:") log_label.setFont(QFont("微软雅黑", 10)) main_layout.addWidget(log_label) # 日志文本框(只读) self.log_edit = QTextEdit() self.log_edit.setFont(QFont("Consolas", 9)) self.log_edit.setReadOnly(True) self.log_edit.setStyleSheet(""" QTextEdit { padding: 8px; border: 1px solid #ddd; border-radius: 4px; background-color: white; color: #333; } """) main_layout.addWidget(self.log_edit, stretch=1) # 日志框占满剩余空间 def select_excel(self): """选择Excel文件,把路径填到输入框""" # 打开文件选择对话框,只显示Excel文件 file_path, _ = QFileDialog.getOpenFileName( self, "选择回复表", "", "Excel Files (*.xlsx; *.xls)" ) if file_path: self.path_edit.setText(file_path) def start_listen(self): """启动监听线程""" # 先检查路径是否填了 excel_path = self.path_edit.text().strip() if not excel_path: self.add_log("请先选择Excel回复表!") return # 检查线程是否已经在跑了 if self.worker and self.worker.isRunning(): self.add_log(" 监听已经在运行啦,不用重复启动") return # 创建线程对象,绑定信号 self.worker = ReplyWorker(excel_path) self.worker.log_send.connect(self.add_log) # 接收日志信号 self.worker.thread_status.connect(self.set_btn_status) # 接收线程状态信号 self.worker.finished.connect(self.thread_finished) # 线程结束时的信号 # 启动线程 self.worker.start() # 暂时禁用启动按钮 self.start_btn.setEnabled(False) self.add_log("正在初始化监听...") def stop_listen(self): """停止监听线程""" if self.worker and self.worker.isRunning(): self.worker.stop() # 禁用停止按钮,启用启动按钮 self.stop_btn.setEnabled(False) self.start_btn.setEnabled(True) else: self.add_log(" 监听还没启动呢,不用停止") def add_log(self, msg): """往日志框里加内容,自动滚到最下面""" # 加个时间戳,方便看什么时候发生的 time_str = time.strftime("[%H:%M:%S]", time.localtime()) self.log_edit.append(f"{time_str} {msg}") # 自动滚动到最后一行 self.log_edit.moveCursor(self.log_edit.textCursor().End) def set_btn_status(self, is_running): """根据线程状态设置按钮是否可用""" self.start_btn.setEnabled(not is_running) self.stop_btn.setEnabled(is_running) def thread_finished(self): """线程结束时的处理""" self.set_btn_status(False) self.add_log("监听线程已结束") def closeEvent(self, event): """窗口关闭时,确保线程也停了""" if self.worker and self.worker.isRunning(): self.worker.stop() self.worker.wait() # 等线程彻底结束 event.accept() def add_log(self, msg): """往日志里加内容(单独写个方法,方便调用)""" time_str = time.strftime("[%H:%M:%S]", time.localtime()) self.log_edit.append(f"{time_str} {msg}") self.log_edit.ensureCursorVisible() # 自动滚屏 # 程序入口 if __name__ == "__main__": app = QApplication(sys.argv) window = ReplyWindow() window.show() sys.exit(app.exec_()) 怎么用? 先建好转发的Excel表(列名必须是“关键词”和“回复内容”) 运行代码,会弹出一个窗口 点击“选择文件”,找到你建的Excel表 点击“启动监听”,然后打开微信(必须是PC端,登录状态) 有人发消息含关键词,就会自动回复了,日志里能看到过程 不想用了就点“停止监听”,或者直接关窗口 mjr3et38.png图片 注意事项 微信版本兼容问题 这个工具是靠pywin32识别微信窗口控件来实现的,不同版本的微信,控件的类名(比如ContactPanel、ChatPanel)可能不一样。如果运行时提示“没找到核心控件”,可以用Spy++(VS自带工具)查看你电脑上微信的控件类名,然后修改代码里find_child方法传入的class_name参数。 必须打开微信PC端 工具没法模拟微信登录,运行前一定要手动打开微信PC端并登录账号,而且最好不要最小化微信窗口,否则可能识别不到控件。 Excel格式要求 Excel文件里必须有两列,列名严格对应“关键词”和“回复内容”,建议保存为.xlsx格式,避免编码问题。如果提示“加载Excel失败”,检查一下文件路径有没有中文或特殊字符,或者是不是被其他软件占用了。 避免重复回复 工具默认每2秒扫描一次未读消息,如果你担心同一条消息被重复回复,可以在代码里加一个“已处理消息列表”,把已经回复过的消息内容存进去,下次扫描时先判断是否在列表里。 权限问题 运行程序时,如果遇到“权限不足”的提示,右键点击Python.exe,选择“以管理员身份运行”,或者给当前用户授予窗口控制的权限。 常见问题解决 问题1:启动后提示“没找到微信窗口” 解决:确认微信PC端已经打开,并且窗口标题是“微信”(不是其他自定义标题);如果微信在任务栏隐藏,先点击显示出来。 问题2:能找到微信,但提示“没找到核心控件” 解决:用Spy++查看微信的会话列表、输入框、发送按钮的类名,替换代码里对应的class_name;比如有些版本的微信输入框类名是RichEdit20W。 问题3:回复内容发送乱码 解决:在send_reply方法里,延长字符输入的延迟时间,把time.sleep(0.02)改成time.sleep(0.05);同时确保Excel文件的编码是UTF-8。 问题4:线程启动后,界面卡死 解决:检查是不是把监听逻辑写在了主线程里——这个工具的监听代码在ReplyWorker线程里,和界面线程分离,不会卡界面;如果还是卡,大概率是控件识别耗时太长,可以减少扫描频率,把time.sleep(2)改成time.sleep(5)。 结语 这便是我制作的微信自动回复工具啦,有bug可以在评论区留言。有动手能力的朋友们可以尝试添加以下功能: 添加黑白名单:可以在Excel里加一列“是否启用”,或者单独建一个黑白名单文件,指定哪些联系人需要自动回复,哪些不需要。 支持多关键词匹配:现在是匹配到第一个关键词就回复,可以改成支持多个关键词同时匹配,比如“价格”和“优惠”同时出现时,回复特定内容。 定时启停:添加一个时间选择控件,设置每天的监听时间段,比如只在9:00-18:00运行,其他时间自动停止。 回复记录导出:把收到的消息和发送的回复记录到本地文件(比如CSV),方便后续查看和统计。 -
阶段一实战项目:仿照记事本开发简易文本编辑器(PyQt5完整代码) 阶段一实战项目:仿照记事本界面开发简易文本编辑器 哈喽~ 欢迎来到PyQt5系列的第5篇——阶段一实战项目!经过前4篇的学习,我们已经掌握了QWidget基础窗口、线性布局(QVBoxLayout/QHBoxLayout)、核心基础控件(标签、按钮、输入框、复选框等)以及信号与槽的基础用法。今天我们将把这些知识点整合起来,仿照Windows记事本的核心界面与基础功能,开发一个简易文本编辑器,实现“新建、打开、保存文本”“文本编辑”“字体加粗”等核心功能,让你快速掌握知识点的综合应用! mjr0cjri.png图片 一、项目需求分析:仿照记事本核心功能 我们聚焦Windows记事本的核心功能,本次项目实现以下需求: 界面需求:仿照记事本布局,包含“功能按钮区”(新建、打开、保存、字体加粗)和“文本编辑区”(多行文本输入/显示); 核心功能:新建空白文本、打开本地文本文件、保存文本到本地、文本加粗编辑; 交互需求:按钮点击反馈、打开/保存文件弹窗提示、文本编辑实时响应; 适配需求:窗口缩放时,文本编辑区自适应调整大小。 【界面参考】Windows记事本核心布局:顶部功能按钮区 + 中间大面积文本编辑区,我们简化实现核心按钮,保证界面简洁且功能完整。 mjr06wos.png图片 二、技术选型:贴合阶段一知识点 本次项目严格基于阶段一所学知识点,不引入新的复杂组件,技术栈如下: 窗口组件:QWidget(主窗口); 布局管理器:QVBoxLayout(垂直布局,管理按钮区和编辑区)、QHBoxLayout(水平布局,排列功能按钮); 核心控件:QPushButton(功能按钮)、QTextEdit(多行文本编辑区)、QCheckBox(字体加粗选择框); 交互核心:信号与槽(按钮点击、复选框状态变化绑定对应功能); 文件操作:基础文件读写(结合Python内置open函数)。 三、界面设计与实现步骤 我们采用“先搭框架,再填功能”的思路,分3步实现: 搭建主窗口与布局(垂直布局+水平布局组合); 添加控件(功能按钮、复选框、文本编辑区)并绑定布局; 实现控件信号与槽绑定,编写功能逻辑。 四、完整代码实现(可直接运行) mjr0a9z8.png图片 import sys import os from PyQt5.QtWidgets import ( QApplication, QWidget, QPushButton, QTextEdit, QCheckBox, QVBoxLayout, QHBoxLayout, QFileDialog ) from PyQt5.QtGui import QFont from PyQt5.QtCore import Qt class SimpleNotepad(QWidget): def __init__(self): super().__init__() # 初始化窗口基础属性 self.init_window() # 初始化控件与布局结构 self.init_widgets_layout() # 绑定信号与槽函数 self.init_signals_slots() # 记录当前打开的文件路径,初始为None表示新文件 self.current_file_path = None def init_window(self): """初始化窗口的标题、大小和位置""" self.setWindowTitle("简易文本编辑器(仿照记事本)") # 设置窗口初始尺寸 self.resize(800, 600) # 计算并设置窗口居中显示 screen_geometry = QApplication.desktop().availableGeometry() x = (screen_geometry.width() - self.width()) // 2 y = (screen_geometry.height() - self.height()) // 2 self.move(x, y) def init_widgets_layout(self): """创建所有界面控件并设置布局""" # 创建主垂直布局,设置控件间距和窗口内边距 self.main_layout = QVBoxLayout() self.main_layout.setSpacing(10) self.main_layout.setContentsMargins(15, 15, 15, 15) # 创建按钮区水平布局 self.button_layout = QHBoxLayout() self.button_layout.setSpacing(10) # 创建功能按钮并设置固定尺寸 self.new_btn = QPushButton("新建") self.open_btn = QPushButton("打开") self.save_btn = QPushButton("保存") btn_size = (80, 30) self.new_btn.setFixedSize(*btn_size) self.open_btn.setFixedSize(*btn_size) self.save_btn.setFixedSize(*btn_size) # 创建字体加粗复选框,设置文本居中显示 self.bold_check = QCheckBox("字体加粗") self.bold_check.setStyleSheet("text-align: center;") # 将按钮添加到水平布局 self.button_layout.addWidget(self.new_btn) self.button_layout.addWidget(self.open_btn) self.button_layout.addWidget(self.save_btn) # 添加伸缩项,将复选框推至布局右侧 self.button_layout.addStretch() self.button_layout.addWidget(self.bold_check) # 创建文本编辑区域,设置默认字体和占位提示文本 self.text_edit = QTextEdit() self.text_edit.setFont(QFont("微软雅黑", 12)) self.text_edit.setPlaceholderText("请输入文本内容...(支持新建、打开、保存文件)") # 将按钮布局和文本编辑区添加到主布局 self.main_layout.addLayout(self.button_layout) self.main_layout.addWidget(self.text_edit) # 设置窗口的主布局 self.setLayout(self.main_layout) def init_signals_slots(self): """绑定控件的信号与对应的槽函数""" self.new_btn.clicked.connect(self.on_new_click) self.open_btn.clicked.connect(self.on_open_click) self.save_btn.clicked.connect(self.on_save_click) self.bold_check.stateChanged.connect(self.on_bold_check_change) def on_new_click(self): """新建文件:清空编辑区,重置文件路径""" # 若有未保存内容,先提示保存 if self.text_edit.toPlainText() and not self.current_file_path: reply = QFileDialog.getSaveFileName(self, "保存当前内容", "", "Text Files (*.txt)") if reply[0]: self.save_text_to_file(reply[0]) # 清空编辑区内容 self.text_edit.clear() # 重置当前文件路径 self.current_file_path = None # 更新窗口标题 self.setWindowTitle("简易文本编辑器(仿照记事本)- 未保存文件") def on_open_click(self): """打开文件:选择txt文件并读取内容到编辑区""" # 弹出文件选择对话框,筛选文本文件 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: content = f.read() self.text_edit.setText(content) self.current_file_path = file_path # 更新窗口标题显示当前文件名 self.setWindowTitle(f"简易文本编辑器(仿照记事本)- {os.path.basename(file_path)}") def on_save_click(self): """保存文件:已有路径则直接保存,无路径则弹出保存对话框""" if self.current_file_path: # 直接保存到当前路径 self.save_text_to_file(self.current_file_path) else: # 弹出保存对话框选择路径 file_path, _ = QFileDialog.getSaveFileName( self, "保存文本文件", "", "Text Files (*.txt)" ) if file_path: # 自动补充txt后缀 if not file_path.endswith(".txt"): file_path += ".txt" self.save_text_to_file(file_path) self.current_file_path = file_path self.setWindowTitle(f"简易文本编辑器(仿照记事本)- {os.path.basename(file_path)}") def save_text_to_file(self, file_path): """将编辑区内容写入指定路径的文件""" content = self.text_edit.toPlainText() with open(file_path, "w", encoding="utf-8") as f: f.write(content) def on_bold_check_change(self, state): """根据复选框状态切换编辑区文本的加粗样式""" current_font = self.text_edit.font() # Qt.Checked对应值为2,Qt.Unchecked对应值为0 current_font.setBold(state == Qt.Checked) self.text_edit.setFont(current_font) if __name__ == "__main__": # 创建应用程序实例 app = QApplication(sys.argv) # 创建记事本窗口实例 notepad = SimpleNotepad() # 显示窗口 notepad.show() # 启动应用程序主循环 sys.exit(app.exec_())五、代码逐行解析(核心部分) 1. 类结构与初始化流程 我们将所有功能封装到SimpleNotepad类(继承QWidget),初始化流程分3步: init_window():设置窗口标题、大小、居中显示,提升用户体验; init_widgets_layout():核心布局搭建,用“垂直布局+水平布局”组合实现记事本风格,按钮区在上、编辑区在下,保证窗口缩放时编辑区自适应; init_signals_slots():绑定所有控件的信号与槽,实现“点击按钮触发功能”“复选框变化触发字体调整”。 2. 布局核心逻辑 采用“嵌套布局”思路,解决控件排列问题: 主布局(QVBoxLayout):垂直方向排列“按钮布局”和“文本编辑区”,addStretch()未使用,让编辑区占满剩余空间; 按钮布局(QHBoxLayout):水平方向排列3个功能按钮,添加addStretch()伸缩空间将“字体加粗”复选框推到右侧,让布局更美观。 3. 核心功能实现 (1)新建文件(on_new_click) 逻辑:先判断当前是否有未保存的内容,如果有则弹出保存对话框;清空编辑区,重置current_file_path(标记为新文件),更新窗口标题。 (2)打开文件(on_open_click) 逻辑:用QFileDialog.getOpenFileName()弹出文件选择框,筛选txt文件;读取选中文件的内容并显示到QTextEdit,更新current_file_path和窗口标题(显示文件名)。 (3)保存文件(on_save_click) 逻辑:如果已打开文件(current_file_path不为None),直接保存;否则弹出保存对话框,让用户选择路径,补充.txt后缀,保存内容后更新路径和标题。 (4)字体加粗(on_bold_check_change) 逻辑:监听复选框状态变化,用QFont.setBold()切换字体加粗状态,直接作用于QTextEdit的当前字体。 六、运行效果与测试步骤 1. 运行方式 将代码保存为simple_notepad.py,确保已安装PyQt5,终端运行命令: python simple_notepad.py # Windows python3 simple_notepad.py # macOS/Linux2. 测试步骤 测试新建:点击“新建”,编辑区清空,标题显示“未保存文件”; 测试打开:点击“打开”,选择本地txt文件,内容正常显示,标题显示文件名; 测试保存:编辑内容后点击“保存”,如果是新文件则弹出保存对话框,保存后可在对应路径找到txt文件; 测试字体加粗:勾选“字体加粗”,编辑区文本变为加粗;取消勾选则恢复正常。 七、常见问题排查 问题1:打开文件后中文乱码 → 解决:读取文件时指定encoding="utf-8",保存时也用utf-8编码; 问题2:窗口缩放时编辑区不自适应 → 解决:确保主布局是QVBoxLayout,且QTextEdit直接添加到主布局(未设置固定大小); 问题3:保存文件后没有.txt后缀 → 解决:代码中已添加判断,自动补充.txt后缀,无需手动输入; 问题4:按钮点击无反应 → 解决:检查信号与槽绑定是否正确(如self.new_btn.clicked.connect(self.on_new_click)是否写错函数名)。 八、功能拓展思路(阶段一知识点范围内) 如果想进一步练习,可以基于当前代码拓展以下功能: 添加“撤销/重做”按钮:利用QTextEdit.undo()和QTextEdit.redo()实现; 添加“清空”按钮:绑定self.text_edit.clear(); 添加“字体大小调整”:用QComboBox下拉选择字体大小,绑定信号修改QFont的setPointSize(); 添加“换行/不换行”复选框:用QTextEdit.setLineWrapMode()控制换行模式。 总结 本次项目完美整合了阶段一的核心知识点:窗口设置、线性布局、基础控件使用、信号与槽绑定。通过仿照记事本界面开发,你应该能深刻理解“布局管理器解决控件排列”“信号与槽实现交互”的核心逻辑。 下一篇我们将进入阶段二,学习网格布局、表单布局等进阶布局管理器,为更复杂的界面开发打基础。如果在项目实操中遇到问题,或者有拓展功能的想法,欢迎在评论区留言讨论~ -
PyQt5常用控件(二):复选框单选框下拉框 PyQt5常用基础控件(二):复选框、单选框与下拉框 哈喽~ 欢迎来到PyQt5从入门到精通的第四篇!上一篇我们掌握了标签、按钮、输入框这三个核心基础控件,这一篇聚焦进阶基础控件——QCheckBox复选框、QRadioButton单选框、QComboBox下拉框,这三类控件是“选择类交互”的核心(比如选择性别、爱好、职业),全程代码完整可直接运行,还会做一个“个人信息选择”综合案例,新手也能轻松掌握! mjqzsti2.png图片 一、核心控件详解:每个控件的用法+完整代码 这三类控件的核心作用是让用户做选择,但适用场景不同:单选框(二选一/多选一)、复选框(多选多)、下拉框(大量选项的精简选择),我们逐个拆解。 1. QCheckBox:复选框(多选多) QCheckBox用于允许多选的场景(比如选择爱好:读书、运动、听歌),支持“选中/未选中”状态,还能监听状态变化。 完整代码:QCheckBox常用用法 mjqzttv2.png图片 import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QCheckBox, QVBoxLayout, QLabel, QPushButton ) from PyQt5.QtCore import Qt class CheckBoxDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("QCheckBox复选框演示") self.resize(350, 250) layout = QVBoxLayout() layout.setSpacing(15) layout.setContentsMargins(50, 40, 50, 40) # 提示标签 self.tip_label = QLabel("请选择你的爱好(可多选):") layout.addWidget(self.tip_label) # 1. 创建复选框 self.check1 = QCheckBox("读书") self.check2 = QCheckBox("运动") self.check3 = QCheckBox("听歌") self.check4 = QCheckBox("编程") # 可选:设置默认选中状态 self.check4.setChecked(True) # 绑定状态变化信号(选中/取消选中时触发) self.check1.stateChanged.connect(self.on_check_change) self.check2.stateChanged.connect(self.on_check_change) self.check3.stateChanged.connect(self.on_check_change) self.check4.stateChanged.connect(self.on_check_change) # 添加到布局 layout.addWidget(self.check1) layout.addWidget(self.check2) layout.addWidget(self.check3) layout.addWidget(self.check4) # 按钮:获取选中的爱好 get_btn = QPushButton("获取选中的爱好") get_btn.clicked.connect(self.get_checked_hobbies) layout.addWidget(get_btn) # 结果显示标签 self.result_label = QLabel("已选中:编程") layout.addWidget(self.result_label) self.setLayout(layout) # 复选框状态变化槽函数 def on_check_change(self, state): # state:2=选中,0=未选中 sender = self.sender() # 获取触发信号的复选框 if state == Qt.Checked: print(f"选中了:{sender.text()}") else: print(f"取消选中:{sender.text()}") # 获取所有选中的爱好 def get_checked_hobbies(self): hobbies = [] if self.check1.isChecked(): hobbies.append(self.check1.text()) if self.check2.isChecked(): hobbies.append(self.check2.text()) if self.check3.isChecked(): hobbies.append(self.check3.text()) if self.check4.isChecked(): hobbies.append(self.check4.text()) if hobbies: self.result_label.setText(f"已选中:{', '.join(hobbies)}") else: self.result_label.setText("未选中任何爱好") if __name__ == "__main__": app = QApplication(sys.argv) window = CheckBoxDemo() window.show() sys.exit(app.exec_())QCheckBox关键方法解析 方法作用setChecked(True)设置默认选中状态(False为未选中)isChecked()判断是否选中(返回True/False)stateChanged.connect(槽函数)监听状态变化(选中/取消选中)text()获取复选框的文本内容sender()在槽函数中获取触发信号的控件(区分多个复选框)2. QRadioButton:单选框(多选一) QRadioButton用于只能选一个的场景(比如性别:男/女),核心是必须分组(QButtonGroup),否则多个单选框不会互斥(能同时选中)。 完整代码:QRadioButton常用用法 mjqzusvk.png图片 import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QRadioButton, QVBoxLayout, QLabel, QPushButton, QButtonGroup ) class RadioButtonDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("QRadioButton单选框演示") self.resize(350, 200) layout = QVBoxLayout() layout.setSpacing(15) layout.setContentsMargins(50, 40, 50, 40) # 提示标签 self.tip_label = QLabel("请选择你的性别(只能选一个):") layout.addWidget(self.tip_label) # 1. 创建单选框分组(核心!否则单选框不互斥) self.radio_group = QButtonGroup(self) # 2. 创建单选框 self.radio1 = QRadioButton("男") self.radio2 = QRadioButton("女") self.radio3 = QRadioButton("保密") # 3. 将单选框加入分组 self.radio_group.addButton(self.radio1, 1) # 第二个参数是自定义ID,可选 self.radio_group.addButton(self.radio2, 2) self.radio_group.addButton(self.radio3, 3) # 可选:设置默认选中 self.radio3.setChecked(True) # 绑定选中变化信号 self.radio_group.buttonClicked.connect(self.on_radio_click) # 添加到布局 layout.addWidget(self.radio1) layout.addWidget(self.radio2) layout.addWidget(self.radio3) # 按钮:获取选中的性别 get_btn = QPushButton("获取选中的性别") get_btn.clicked.connect(self.get_checked_gender) layout.addWidget(get_btn) # 结果显示标签 self.result_label = QLabel("已选中:保密") layout.addWidget(self.result_label) self.setLayout(layout) # 单选框点击槽函数 def on_radio_click(self, radio_btn): # radio_btn是被点击的单选框对象 print(f"选中了性别:{radio_btn.text()}") # 获取选中的性别 def get_checked_gender(self): # 方式1:通过分组获取选中的按钮 checked_btn = self.radio_group.checkedButton() if checked_btn: self.result_label.setText(f"已选中:{checked_btn.text()}") else: self.result_label.setText("未选中任何性别") # 方式2:逐个判断(不推荐,分组更高效) # if self.radio1.isChecked(): # self.result_label.setText("已选中:男") # elif self.radio2.isChecked(): # self.result_label.setText("已选中:女") # elif self.radio3.isChecked(): # self.result_label.setText("已选中:保密") if __name__ == "__main__": app = QApplication(sys.argv) window = RadioButtonDemo() window.show() sys.exit(app.exec_())QRadioButton关键要点 必须分组:用QButtonGroup管理单选框,否则多个单选框可同时选中; 核心方法: 方法作用QButtonGroup.addButton(单选框, ID)将单选框加入分组radio_group.checkedButton()获取分组中选中的单选框setChecked(True)设置默认选中isChecked()判断是否选中 3. QComboBox:下拉选择框(精简多选一) QComboBox用于选项较多、需要精简界面的场景(比如选择职业、城市),支持下拉展开选择,也能设置可编辑(允许用户输入)。 完整代码:QComboBox常用用法 mjqzvsjb.png图片 import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QComboBox, QVBoxLayout, QLabel, QPushButton ) class ComboBoxDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("QComboBox下拉框演示") self.resize(350, 200) layout = QVBoxLayout() layout.setSpacing(15) layout.setContentsMargins(50, 40, 50, 40) # 提示标签 self.tip_label = QLabel("请选择你的职业:") layout.addWidget(self.tip_label) # 1. 创建下拉框 self.combo = QComboBox() # 2. 添加选项(三种方式) # 方式1:逐个添加 self.combo.addItem("学生") self.combo.addItem("程序员") self.combo.addItem("教师") # 方式2:批量添加 self.combo.addItems(["设计师", "医生", "自由职业者"]) # 方式3:添加带自定义数据的选项(文本+值) self.combo.addItem("其他", "other") # 可选设置 self.combo.setCurrentIndex(1) # 设置默认选中第2个选项(索引从0开始) # self.combo.setEditable(True) # 允许用户输入自定义内容 # 绑定选中变化信号 self.combo.currentIndexChanged.connect(self.on_combo_change) # 索引变化 # self.combo.currentTextChanged.connect(self.on_text_change) # 文本变化 layout.addWidget(self.combo) # 按钮:获取选中的职业 get_btn = QPushButton("获取选中的职业") get_btn.clicked.connect(self.get_checked_job) layout.addWidget(get_btn) # 结果显示标签 self.result_label = QLabel("已选中:程序员") layout.addWidget(self.result_label) self.setLayout(layout) # 下拉框索引变化槽函数 def on_combo_change(self, index): # index是选中项的索引 text = self.combo.itemText(index) # 通过索引获取文本 data = self.combo.itemData(index) # 获取自定义数据(没有则返回None) print(f"选中索引:{index},职业:{text},自定义数据:{data}") # # 文本变化槽函数(当setEditable=True时常用) # def on_text_change(self, text): # print(f"选中/输入的职业:{text}") # 获取选中的职业 def get_checked_job(self): # 方式1:获取选中的文本 text = self.combo.currentText() # 方式2:获取选中的索引 index = self.combo.currentIndex() # 方式3:获取自定义数据 data = self.combo.currentData() self.result_label.setText(f"职业:{text}(索引:{index},自定义数据:{data})") if __name__ == "__main__": app = QApplication(sys.argv) window = ComboBoxDemo() window.show() sys.exit(app.exec_())QComboBox关键方法 方法作用addItem("文本", 自定义数据)添加单个选项addItems(["选项1", "选项2"])批量添加选项setCurrentIndex(索引)设置默认选中项(索引从0开始)currentText()获取选中的文本currentIndex()获取选中的索引setEditable(True)允许用户输入自定义内容二、综合案例:个人信息选择窗口(完整代码) 结合复选框、单选框、下拉框,做一个实用的“个人信息填写窗口”——包含性别单选、爱好复选、职业下拉,点击“提交”按钮后显示所有选中的信息,界面美化且逻辑完整。 mjqzxjc5.png图片 import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QRadioButton, QCheckBox, QComboBox, QPushButton, QVBoxLayout, QHBoxLayout, QGroupBox, QButtonGroup ) from PyQt5.QtGui import QFont from PyQt5.QtCore import Qt class InfoWindow(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): # 窗口基础设置 self.setWindowTitle("个人信息选择窗口") self.resize(450, 400) self.setStyleSheet(""" QWidget { font-size: 14px; color: #2c3e50; } QGroupBox { font-weight: bold; border: 1px solid #bdc3c7; border-radius: 6px; margin-top: 10px; padding-top: 10px; } QPushButton { background-color: #27ae60; color: white; padding: 8px 16px; border: none; border-radius: 4px; } QPushButton:hover { background-color: #219653; } """) # 主布局(垂直) main_layout = QVBoxLayout() main_layout.setSpacing(20) main_layout.setContentsMargins(40, 30, 40, 30) # 1. 性别分组(QGroupBox美化分组) gender_group = QGroupBox("性别") gender_layout = QHBoxLayout() # 单选框分组 self.gender_btn_group = QButtonGroup() self.radio_male = QRadioButton("男") self.radio_female = QRadioButton("女") self.radio_secret = QRadioButton("保密") self.gender_btn_group.addButton(self.radio_male) self.gender_btn_group.addButton(self.radio_female) self.gender_btn_group.addButton(self.radio_secret) # 默认选中保密 self.radio_secret.setChecked(True) # 添加到性别布局 gender_layout.addWidget(self.radio_male) gender_layout.addWidget(self.radio_female) gender_layout.addWidget(self.radio_secret) gender_group.setLayout(gender_layout) main_layout.addWidget(gender_group) # 2. 爱好分组 hobby_group = QGroupBox("爱好(可多选)") hobby_layout = QHBoxLayout() # 复选框 self.check_read = QCheckBox("读书") self.check_sport = QCheckBox("运动") self.check_music = QCheckBox("听歌") self.check_code = QCheckBox("编程") # 添加到爱好布局 hobby_layout.addWidget(self.check_read) hobby_layout.addWidget(self.check_sport) hobby_layout.addWidget(self.check_music) hobby_layout.addWidget(self.check_code) hobby_group.setLayout(hobby_layout) main_layout.addWidget(hobby_group) # 3. 职业分组 job_group = QGroupBox("职业") job_layout = QHBoxLayout() # 下拉框 self.combo_job = QComboBox() self.combo_job.addItems(["学生", "程序员", "教师", "设计师", "医生", "其他"]) job_layout.addWidget(QLabel("选择:")) job_layout.addWidget(self.combo_job) job_group.setLayout(job_layout) main_layout.addWidget(job_group) # 4. 提交按钮 submit_btn = QPushButton("提交信息") submit_btn.clicked.connect(self.submit_info) main_layout.addWidget(submit_btn, alignment=Qt.AlignCenter) # 5. 结果显示标签 self.result_label = QLabel("") self.result_label.setStyleSheet("color: #e67e22; margin-top: 10px;") self.result_label.setAlignment(Qt.AlignCenter) main_layout.addWidget(self.result_label) # 绑定主布局 self.setLayout(main_layout) # 提交信息槽函数 def submit_info(self): # 1. 获取性别 gender = self.gender_btn_group.checkedButton().text() # 2. 获取爱好 hobbies = [] if self.check_read.isChecked(): hobbies.append(self.check_read.text()) if self.check_sport.isChecked(): hobbies.append(self.check_sport.text()) if self.check_music.isChecked(): hobbies.append(self.check_music.text()) if self.check_code.isChecked(): hobbies.append(self.check_code.text()) hobby_text = ", ".join(hobbies) if hobbies else "无" # 3. 获取职业 job = self.combo_job.currentText() # 4. 显示结果 result = f""" 提交的信息: 性别:{gender} 爱好:{hobby_text} 职业:{job} """ self.result_label.setText(result) if __name__ == "__main__": app = QApplication(sys.argv) window = InfoWindow() window.show() sys.exit(app.exec_())综合案例亮点 用QGroupBox对控件分组,界面更规整、易读; 结合了三类选择控件,覆盖“单选、多选、下拉选”所有常用选择场景; 加入样式美化(按钮悬停、分组边框、字体颜色); 逻辑完整:提交后整合所有选中信息并显示,新手可直接复用。 三、常见问题排查 单选框不互斥: 未使用QButtonGroup分组,只需将所有单选框加入同一个分组即可; 分组时误将单选框加入不同分组(比如创建了多个QButtonGroup)。 复选框无法获取选中状态: 槽函数中未正确调用isChecked(),或控件实例名写错(比如check1写成check_1); 复选框被禁用(setDisabled(True)),导致无法选中。 下拉框选项不显示: 忘记调用addItem()/addItems()添加选项; 下拉框尺寸太小(可通过setMinimumWidth(100)设置最小宽度)。 提交后结果不显示: 槽函数未绑定到按钮的clicked信号; 结果标签被布局遮挡(可调整setContentsMargins或spacing)。 总结 三类选择控件的核心场景:QRadioButton(单选)、QCheckBox(多选)、QComboBox(精简单选); 单选框必须用QButtonGroup分组,否则无法实现互斥; 复选框通过isChecked()判断状态,下拉框通过currentText()获取选中内容; 实际开发中用QGroupBox分组控件,可提升界面可读性; 下一篇我们会讲解阶段一实战项目:仿照记事本界面开发简易文本编辑器,记得关注字节曜博客哦~ 如果在实操中遇到问题,欢迎在评论区留言讨论! -
PyQt5常用控件(一):标签按钮输入框+信号与槽入门(完整代码) PyQt5常用基础控件(一):标签、按钮与输入框+信号与槽入门(附完整代码) 哈喽~ 欢迎来到PyQt5从入门到精通的第三篇!上一篇我们搞定了QWidget窗口属性和线性布局,这一篇聚焦最常用的3个基础控件(QLabel标签、QPushButton按钮、QLineEdit输入框),再加上PyQt5交互的核心——信号与槽(Signal & Slot) 基础,手把手教你实现控件之间的互动,全程代码完整可直接运行,新手也能轻松拿捏! mjqzambw.png图片 一、核心控件详解:每个控件的用法+完整代码 这三个控件是PyQt5开发中最基础也最常用的,几乎所有桌面应用都会用到(比如登录窗口的用户名输入框、确认按钮、提示标签)。我们逐个拆解,先讲用法,再上代码,最后看效果。 1. QLabel:标签控件(显示文本/图片) QLabel的核心作用是显示内容,支持文本、图片、超链接等,是界面中的“信息展示员”。 完整代码:QLabel常用用法 import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout from PyQt5.QtGui import QPixmap, QFont from PyQt5.QtCore import Qt class LabelDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("QLabel标签控件演示") self.resize(400, 350) # 创建布局(垂直布局,让控件从上到下排列) layout = QVBoxLayout() # 1. 显示普通文本 label1 = QLabel("这是普通文本标签") # 设置文本居中对齐 label1.setAlignment(Qt.AlignCenter) layout.addWidget(label1) # 2. 显示带样式的文本(字体大小、颜色) label2 = QLabel("这是带样式的文本") # 用StyleSheet设置样式(类似CSS) label2.setStyleSheet("font-size: 16px; color: #e74c3c; font-weight: bold;") label2.setAlignment(Qt.AlignCenter) layout.addWidget(label2) # 3. 显示图片(替换为你的图片路径,支持png/jpg等格式) label3 = QLabel() # 加载图片并缩放(保持比例) pixmap = QPixmap("test.png") # 图片放在代码同目录,直接写文件名 label3.setPixmap(pixmap.scaled(200, 200, Qt.KeepAspectRatio)) label3.setAlignment(Qt.AlignCenter) # 图片居中 layout.addWidget(label3) # 4. 显示超链接(可点击跳转) label4 = QLabel('<a href="https://www.ziyeyao.com">点击访问字节曜博客</a>') label4.setAlignment(Qt.AlignCenter) label4.setOpenExternalLinks(True) # 允许打开外部链接 layout.addWidget(label4) # 绑定布局到窗口 self.setLayout(layout) if __name__ == "__main__": app = QApplication(sys.argv) window = LabelDemo() window.show() sys.exit(app.exec_())mjqxgqbv.png图片 QLabel关键方法解析 方法作用setAlignment(Qt.AlignCenter)设置内容对齐(居中/左对齐/右对齐)setStyleSheet("样式")自定义样式(字体、颜色、背景等)setPixmap(QPixmap("图片路径"))显示图片,scaled() 用于缩放图片setOpenExternalLinks(True)启用超链接点击跳转2. QPushButton:按钮控件(触发交互) QPushButton是交互核心控件,用户点击按钮后会触发特定操作(比如登录、保存、关闭窗口),必须结合“信号与槽”使用才能实现交互。 完整代码:QPushButton常用用法+信号与槽基础 import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel from PyQt5.QtCore import Qt class ButtonDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("QPushButton按钮控件演示") self.resize(300, 200) layout = QVBoxLayout() # 创建标签(用于显示按钮点击状态) self.status_label = QLabel("未点击按钮", alignment=Qt.AlignCenter) layout.addWidget(self.status_label) # 1. 普通按钮 btn1 = QPushButton("普通按钮") # 绑定信号与槽:按钮点击(信号)→ 执行on_btn1_click函数(槽) btn1.clicked.connect(self.on_btn1_click) layout.addWidget(btn1) # 2. 带图标+文本的按钮(图标路径替换为你的文件) btn2 = QPushButton("带图标按钮") btn2.setIcon(QPixmap("icon.png").scaled(20, 20, Qt.KeepAspectRatio)) btn2.clicked.connect(self.on_btn2_click) layout.addWidget(btn2) # 3. 禁用状态的按钮(无法点击) btn3 = QPushButton("禁用按钮") btn3.setDisabled(True) # 禁用按钮 layout.addWidget(btn3) self.setLayout(layout) # 槽函数:btn1点击后执行 def on_btn1_click(self): self.status_label.setText("点击了普通按钮!") self.status_label.setStyleSheet("color: #2ecc71;") # 槽函数:btn2点击后执行 def on_btn2_click(self): self.status_label.setText("点击了带图标按钮!") self.status_label.setStyleSheet("color: #3498db;") if __name__ == "__main__": app = QApplication(sys.argv) window = ButtonDemo() window.show() sys.exit(app.exec_())mjqz3lir.png图片 关键解析:信号与槽(核心!) 信号(Signal):控件的某个动作(比如按钮点击clicked、输入框内容变化textChanged); 槽(Slot):信号触发后执行的函数(比如on_btn1_click); 绑定方式:控件.信号.connect(槽函数),这是PyQt5交互的核心逻辑,记住这个公式! QPushButton关键方法 方法作用clicked.connect(槽函数)绑定点击信号与槽函数setIcon(QPixmap("图标路径"))设置按钮图标setDisabled(True)禁用按钮(False为启用)setText("按钮文本")动态修改按钮文本3. QLineEdit:单行输入框(获取用户输入) QLineEdit用于获取用户单行输入(比如用户名、密码、验证码),支持限制输入长度、密码隐藏、提示文本等功能。 完整代码:QLineEdit常用用法 import sys from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QVBoxLayout, QLabel, QPushButton class LineEditDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("QLineEdit输入框演示") self.resize(350, 250) layout = QVBoxLayout() layout.setSpacing(20) layout.setContentsMargins(50, 50, 50, 50) # 1. 普通输入框(带提示文本) self.edit1 = QLineEdit() self.edit1.setPlaceholderText("请输入用户名(最多10个字符)") self.edit1.setMaxLength(10) # 限制输入长度 layout.addWidget(self.edit1) # 2. 密码输入框(输入内容隐藏) self.edit2 = QLineEdit() self.edit2.setPlaceholderText("请输入密码") self.edit2.setEchoMode(QLineEdit.Password) # 密码隐藏模式 layout.addWidget(self.edit2) # 3. 只读输入框(无法编辑) edit3 = QLineEdit("只读文本,无法修改") edit3.setReadOnly(True) layout.addWidget(edit3) # 按钮:获取输入框内容 btn = QPushButton("获取输入内容") btn.clicked.connect(self.get_input_value) layout.addWidget(btn) # 标签:显示获取到的内容 self.result_label = QLabel("") layout.addWidget(self.result_label) self.setLayout(layout) # 槽函数:获取输入框内容并显示 def get_input_value(self): username = self.edit1.text() # 获取输入框1的内容 password = self.edit2.text() # 获取输入框2的内容 self.result_label.setText(f"用户名:{username},密码:{password}") # 清空输入框(可选) self.edit1.clear() self.edit2.clear() if __name__ == "__main__": app = QApplication(sys.argv) window = LineEditDemo() window.show() sys.exit(app.exec_())mjqz66bs.png图片 QLineEdit关键方法 方法作用setPlaceholderText("提示文本")设置输入提示(未输入时显示)setMaxLength(数字)限制最大输入长度setEchoMode(QLineEdit.Password)密码模式(输入内容显示为圆点)setReadOnly(True)设置为只读(无法编辑)text()获取输入框中的内容clear()清空输入框内容二、综合案例:用户名密码输入验证窗口(完整代码) 结合上面三个控件和信号与槽,做一个实用的“登录验证窗口”——输入用户名和密码后,点击按钮验证是否正确(模拟登录逻辑)。 import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout ) from PyQt5.QtGui import QFont, QPixmap from PyQt5.QtCore import Qt class LoginWindow(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): # 窗口基础设置 self.setWindowTitle("模拟登录窗口") self.resize(400, 300) self.setStyleSheet("background-color: #f8f9fa;") # 主布局(垂直布局) main_layout = QVBoxLayout() main_layout.setSpacing(25) main_layout.setContentsMargins(60, 50, 60, 50) # 1. 标题区域(图标+文本) title_layout = QHBoxLayout() # 标题图标 title_icon = QLabel() title_icon.setPixmap(QPixmap("login_icon.png").scaled(30, 30, Qt.KeepAspectRatio)) # 标题文本 title_label = QLabel("用户登录") title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #2c3e50;") # 添加到标题布局 title_layout.addWidget(title_icon) title_layout.addWidget(title_label) title_layout.setAlignment(Qt.AlignCenter) main_layout.addLayout(title_layout) # 2. 用户名输入区域 user_layout = QHBoxLayout() user_label = QLabel("用户名:") user_label.setStyleSheet("font-size: 14px; color: #34495e;") self.user_edit = QLineEdit() self.user_edit.setPlaceholderText("请输入用户名") self.user_edit.setStyleSheet("padding: 6px; border: 1px solid #bdc3c7; border-radius: 4px;") user_layout.addWidget(user_label) user_layout.addWidget(self.user_edit) main_layout.addLayout(user_layout) # 3. 密码输入区域 pwd_layout = QHBoxLayout() pwd_label = QLabel("密 码:") pwd_label.setStyleSheet("font-size: 14px; color: #34495e;") self.pwd_edit = QLineEdit() self.pwd_edit.setPlaceholderText("请输入密码") self.pwd_edit.setEchoMode(QLineEdit.Password) self.pwd_edit.setStyleSheet("padding: 6px; border: 1px solid #bdc3c7; border-radius: 4px;") pwd_layout.addWidget(pwd_label) pwd_layout.addWidget(self.pwd_edit) main_layout.addLayout(pwd_layout) # 4. 验证结果标签 self.result_label = QLabel("") self.result_label.setAlignment(Qt.AlignCenter) main_layout.addWidget(self.result_label) # 5. 登录按钮 login_btn = QPushButton("登录") login_btn.setStyleSheet(""" background-color: #3498db; color: white; padding: 8px; border: none; border-radius: 4px; font-size: 14px; """) # 鼠标悬停时改变颜色 login_btn.setStyleSheet(""" QPushButton { background-color: #3498db; color: white; padding: 8px; border: none; border-radius: 4px; font-size: 14px; } QPushButton:hover { background-color: #2980b9; } """) login_btn.clicked.connect(self.check_login) main_layout.addWidget(login_btn) # 绑定主布局 self.setLayout(main_layout) # 登录验证槽函数 def check_login(self): # 模拟正确的用户名和密码 correct_user = "admin" correct_pwd = "123456" # 获取用户输入 input_user = self.user_edit.text().strip() # strip()去除前后空格 input_pwd = self.pwd_edit.text().strip() # 验证逻辑 if not input_user or not input_pwd: self.result_label.setText("用户名或密码不能为空!") self.result_label.setStyleSheet("color: #e74c3c; font-size: 12px;") elif input_user == correct_user and input_pwd == correct_pwd: self.result_label.setText("登录成功!") self.result_label.setStyleSheet("color: #2ecc71; font-size: 12px;") else: self.result_label.setText("用户名或密码错误!") self.result_label.setStyleSheet("color: #e74c3c; font-size: 12px;") # 清空密码输入框 self.pwd_edit.clear() if __name__ == "__main__": app = QApplication(sys.argv) window = LoginWindow() window.show() sys.exit(app.exec_())mjqz6yqo.png图片 综合案例亮点 结合了QLabel(标题、提示)、QLineEdit(输入)、QPushButton(登录)三个核心控件; 使用嵌套布局(QVBoxLayout+QHBoxLayout),界面更规整; 加入了样式美化(背景色、边框、按钮悬停效果); 实现了完整的登录验证逻辑(非空判断、正确/错误提示)。 三、常见问题排查 按钮点击后没反应: 忘记绑定clicked.connect(槽函数); 槽函数名称写错(比如on_btn_click写成on_btn_click1); 按钮被设置为禁用状态(setDisabled(True))。 输入框无法获取内容: 没有用text()方法获取内容,或获取的是其他输入框的实例; 输入内容有前后空格,可加strip()方法去除(如self.user_edit.text().strip())。 图片/图标不显示: 图片路径错误(建议放在代码同目录,直接写文件名); 图片尺寸太大,未用scaled()缩放导致超出窗口范围。 样式设置不生效: StyleSheet语法错误(比如少写分号、引号不匹配); 控件样式被布局或父控件样式覆盖,可针对性调整。 总结 三个核心控件的核心用途:QLabel显示、QPushButton触发、QLineEdit输入; 信号与槽是PyQt5交互的核心,记住控件.信号.connect(槽函数)的绑定方式; 实际开发中建议用布局管理器组织控件,配合StyleSheet美化界面; 下一篇我们会讲解复选框、单选框、下拉框等进阶基础控件,以及更复杂的信号与槽用法,记得关注字节曜博客哦~ 如果在实操中遇到问题,欢迎在评论区留言讨论! -
PyQt5核心基础:QWidget窗口属性与线性布局入门 PyQt5核心基础:QWidget基础窗口与布局入门(附完整可运行代码) 哈喽~欢迎来到PyQt5从入门到精通的第二篇!上一篇我们搞定了环境搭建和第一个空白窗口,这一篇我们聚焦QWidget基础窗口的进阶属性和布局管理器入门——解决新手最头疼的“控件位置混乱、窗口缩放后控件错位”问题,全程代码完整可直接运行,新手也能轻松跟上! mjqx9i5t.png图片 一、先回顾:QWidget是什么? QWidget是PyQt5中所有可视化控件的基类(可以理解为“所有窗口/控件的老祖宗”),我们上一篇创建的空白窗口就是QWidget的实例。它不仅能作为独立窗口使用,还能作为其他控件的容器,掌握它的属性设置是PyQt5界面开发的核心基础。 二、QWidget窗口进阶属性设置(完整代码) 上一篇我们只设置了窗口标题、大小、位置,这一节我们拓展更多实用属性:设置窗口图标、固定窗口大小、窗口置顶、自定义背景色、监听关闭事件等,先上完整代码,再逐行解析。 完整代码:QWidget窗口进阶属性 import sys # 导入必要控件:QWidget(窗口)、QApplication(应用)、QIcon(图标) from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtGui import QIcon from PyQt5.QtCore import Qt class MyWindow(QWidget): def __init__(self): # 继承QWidget的初始化方法 super().__init__() # 调用自定义的窗口设置方法 self.init_ui() def init_ui(self): # 1. 基础属性(复习) self.setWindowTitle("QWidget进阶窗口") # 窗口标题 self.resize(400, 300) # 初始大小(宽×高) self.move(300, 200) # 初始位置(屏幕坐标:左×上) # 2. 进阶属性:设置窗口图标 # 注意:替换为你本地的图标路径(支持.ico/png/jpg格式) # 新手提示:可先找一张小图片(比如16×16/32×32像素),放在代码同目录下 self.setWindowIcon(QIcon("icon.png")) # 3. 进阶属性:固定窗口大小(禁止用户缩放) # 取消注释以下行,窗口将固定为400×300,无法拖动边缘缩放 # self.setFixedSize(400, 300) # 4. 进阶属性:窗口置顶(始终显示在其他窗口上方) self.setWindowFlags(Qt.WindowStaysOnTopHint) # 5. 进阶属性:设置窗口背景色(浅灰色) self.setStyleSheet("background-color: #f0f0f0;") # 6. 进阶:监听窗口关闭事件(比如关闭前弹出提示) def closeEvent(self, event): # 这里先简单打印提示,后续会讲弹窗提示 print("你点击了关闭按钮!") # 允许窗口关闭(如果想阻止关闭,可调用event.ignore()) event.accept() if __name__ == "__main__": # 创建应用实例 app = QApplication(sys.argv) # 创建自定义窗口实例 window = MyWindow() # 显示窗口 window.show() # 启动主循环 sys.exit(app.exec_())mjqxa0yu.png图片 代码关键解析 类的封装:我们把窗口逻辑封装到MyWindow类中(继承QWidget),这是PyQt5开发的标准写法,便于后续拓展功能; 窗口图标:setWindowIcon(QIcon("图标路径")),新手注意路径要正确(代码和图标同目录可直接写文件名); 固定大小:setFixedSize() 适合不需要缩放的工具窗口,取消注释即可生效; 窗口置顶:Qt.WindowStaysOnTopHint 是Qt的内置常量,实现窗口始终在最上层; 关闭事件:重写closeEvent()方法,可在窗口关闭前执行自定义逻辑(比如保存数据、弹出确认弹窗)。 运行效果 运行代码后,会弹出一个浅灰色、置顶显示、带自定义图标的窗口,关闭窗口时控制台会打印“你点击了关闭按钮!”。 三、布局管理器入门:解决控件排列混乱问题 新手最容易踩的坑:直接用setGeometry()手动设置控件位置,窗口缩放后控件会错位、重叠。PyQt5提供布局管理器自动管理控件位置,核心是: 无需手动设置控件坐标,布局会自动分配空间; 窗口缩放时,控件会按比例自适应。 我们先学最基础的两种线性布局: QVBoxLayout:垂直布局(控件从上到下排列); QHBoxLayout:水平布局(控件从左到右排列)。 完整代码:线性布局实操(带按钮+标签) import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout # 导入布局管理器 ) class LayoutWindow(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("线性布局示例") self.resize(400, 300) # 1. 创建控件 label = QLabel("这是一个标签") btn1 = QPushButton("按钮1") btn2 = QPushButton("按钮2") # 2. 方式1:垂直布局(控件从上到下) # 创建垂直布局实例 layout = QVBoxLayout() # 向布局中添加控件(可添加多个) layout.addWidget(label) layout.addWidget(btn1) layout.addWidget(btn2) # 设置布局的间距(控件之间的距离,可选) layout.setSpacing(20) # 设置布局的边距(布局和窗口边缘的距离,可选) layout.setContentsMargins(50, 50, 50, 50) # 2. 方式2:水平布局(控件从左到右) # 取消注释以下代码,替换垂直布局 # layout = QHBoxLayout() # layout.addWidget(label) # layout.addWidget(btn1) # layout.addWidget(btn2) # 3. 将布局设置到窗口上(核心步骤,否则布局不生效) self.setLayout(layout) if __name__ == "__main__": app = QApplication(sys.argv) window = LayoutWindow() window.show() sys.exit(app.exec_())代码关键解析 布局使用三步法: 创建布局实例(QVBoxLayout()/QHBoxLayout()); 用addWidget()向布局中添加控件; 用setLayout()将布局绑定到窗口; 间距/边距设置: setSpacing():控件之间的间距(单位:像素); setContentsMargins(左, 上, 右, 下):布局和窗口边缘的距离; 自适应效果:运行后拖动窗口边缘缩放,控件会自动调整位置,不会错位。 对比:手动布局 vs 布局管理器 如果用手动布局(label.setGeometry(50,50,100,30)),窗口放大后标签和按钮仍停留在原地;而布局管理器会让控件均匀分布在窗口中,这是开发中必须掌握的核心技巧。 四、综合案例:带布局的多功能窗口(完整代码) 结合本节所有知识点,做一个带图标、置顶、垂直布局的完整窗口: import sys from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QPushButton, QVBoxLayout ) from PyQt5.QtGui import QIcon from PyQt5.QtCore import Qt class ComprehensiveWindow(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): # 窗口基础属性 self.setWindowTitle("综合布局窗口") self.setWindowIcon(QIcon("icon.png")) # 替换为你的图标路径 self.setWindowFlags(Qt.WindowStaysOnTopHint) self.resize(400, 300) # 创建控件 title_label = QLabel("PyQt5布局演示") # 设置标签字体大小(简单美化) title_label.setStyleSheet("font-size: 18px; font-weight: bold;") btn_ok = QPushButton("确认") btn_cancel = QPushButton("取消") # 创建垂直布局 layout = QVBoxLayout() layout.addWidget(title_label) layout.addWidget(btn_ok) layout.addWidget(btn_cancel) layout.setSpacing(30) layout.setContentsMargins(80, 80, 80, 80) # 绑定布局 self.setLayout(layout) def closeEvent(self, event): print("窗口即将关闭!") event.accept() if __name__ == "__main__": app = QApplication(sys.argv) window = ComprehensiveWindow() window.show() sys.exit(app.exec_())运行效果:一个置顶的窗口,内部有大号标题标签和两个按钮,垂直排列且间距均匀,窗口缩放时控件自动适配。 mjqxbt56.png图片 五、常见问题排查 布局不生效:忘记调用setLayout(),或布局添加控件后未绑定到窗口; 图标不显示:图标路径错误(建议将图标放在代码同目录,直接写文件名); 窗口无法置顶:部分系统(如macOS)对窗口置顶有权限限制,属正常现象; 控件重叠:未使用布局,手动设置的控件坐标重复,优先用布局管理器解决。 总结 QWidget是PyQt5所有控件的基类,掌握setWindowIcon()/setFixedSize()等属性可自定义窗口样式; 布局管理器(QVBoxLayout/QHBoxLayout)是解决控件排列的核心,无需手动设置坐标,支持自适应; PyQt5开发建议封装成类,便于后续拓展功能(如监听事件、添加更多控件)。 下一篇我们会深入讲解PyQt5常用基础控件(标签、按钮、输入框)和信号与槽的核心逻辑,记得关注哦~如果有任何问题,评论区留言讨论! -
PyQt5入门指南:环境搭建与第一个桌面程序(超详细图文版) PyQt5入门指南:环境搭建与第一个桌面程序(超详细图文版) 哈喽,各位想入门GUI开发的小伙伴们~ 我是字节曜编辑寒烟似雪,今天开始,我们正式开启PyQt5从入门到精通的系列之旅!作为系列的第一篇,我会用最细致的步骤,带大家搞定PyQt5的环境搭建,并且写出第一个属于自己的桌面程序。全程无废话、无跳步,零基础也能轻松跟上,遇到问题还会附上排查方案,放心往下看~ 一、先搞懂:什么是PyQt5?为什么选它做GUI开发? 在动手之前,先简单了解下我们要学的工具: PyQt5是Qt框架的Python绑定版本,Qt是一款跨平台的C++图形用户界面开发框架,支持Windows、macOS、Linux等多个系统。简单说,PyQt5就是让我们能用Python这种简单易懂的语言,开发出专业、美观且能在多系统运行的桌面应用(比如办公软件、小游戏、数据可视化工具等)。 选择PyQt5的3个核心理由: 跨平台:写一次代码,多系统直接运行,不用重复开发; 功能强大:自带丰富的控件(按钮、表格、对话框等),满足大部分GUI开发需求; 学习成本低:结合Python的简洁语法,比直接学Qt(C++)门槛低很多,适合新手入门。 适用场景:桌面应用开发(如文本编辑器、数据管理工具、本地小工具等),不适合开发网页或手机APP哦~ 二、环境搭建:3大系统(Windows/macOS/Linux)详细步骤 环境搭建是入门的第一步,也是最容易踩坑的一步。这里分三个系统详细说明,你根据自己的电脑系统对应操作即可。核心准备:先安装Python,再安装PyQt5及配套工具包。 1. 前置准备:安装Python(所有系统通用) PyQt5需要依赖Python环境,建议安装Python 3.7-3.10版本(兼容性最好,太高版本可能存在部分依赖包不支持的问题)。 步骤: 访问Python官方下载地址:https://www.python.org/downloads/(无需科学上网,但是慢,可用阿里云镜像:https://mirrors.aliyun.com/python-release/,选择你的系统后选择版本即可); mjpq1alv.png图片 根据系统选择对应版本:Windows选“Windows Installer (64-bit)”,macOS选“macOS 64-bit universal2 installer”,Linux可通过系统自带软件商店安装; 安装注意事项: Windows系统:一定要勾选“Add Python 3.x to PATH”(自动配置环境变量,否则后续需手动配置,新手容易忘),然后点击“Install Now”默认安装即可; macOS/Linux系统:默认安装即可,安装完成后可通过终端输入命令验证。 验证Python是否安装成功: 打开终端(Windows按Win+R输入cmd,macOS按Command+空格输入terminal,Linux直接打开终端),输入以下命令:python --version # 或 python3 --version(macOS/Linux可能需要用python3)如果显示类似“Python 3.9.7”的版本号,说明安装成功;如果提示“不是内部或外部命令”,则是环境变量未配置好(Windows可重新运行安装包,勾选Add to PATH后修复安装)。 mjpq2x0t.png图片 mjpq4kpo.png图片 mjpq57lf.png图片 2. 安装PyQt5及工具包(所有系统通用) Python安装完成后,通过pip命令安装PyQt5核心包和配套工具包(pyqt5-tools包含Qt Designer可视化设计工具,后续做界面会用到,建议一起安装)。 步骤: 打开终端,输入以下安装命令(Windows/macOS/Linux通用): pip install pyqt5 等待安装完成:网络正常的情况下,几分钟即可安装完成。如果安装速度慢,可切换国内镜像源(推荐阿里云),命令如下: 验证是否安装成功: 在终端输入python(或python3),进入Python交互式环境,输入以下命令: import sys from PyQt5.QtWidgets import QWidget # 导入PyQt5的核心控件 print("导入成功")如果没有报错,显示“导入成功”,说明PyQt5安装成功;如果报错,参考下方“常见问题排查”部分。 mjpq6vqq.png图片 3. 环境搭建常见问题排查 新手在安装过程中容易遇到以下问题,提前整理好解决方案,遇到直接对应排查: 问题1:Windows系统安装pyqt5-tools失败,提示“error: Microsoft Visual C++ 14.0 or greater is required...” 解决方案:需要安装Microsoft Visual C++ Build Tools。访问微软官网下载:https://visualstudio.microsoft.com/visual-cpp-build-tools/,勾选“Desktop development with C++”,点击安装(无需安装完整的Visual Studio,仅安装工具集即可),安装完成后重新执行pip安装命令。 问题2:导入PyQt5时提示“ModuleNotFoundError: No module named 'PyQt5'” 解决方案:① 检查是否用错了Python版本(比如安装了Python3.9,但终端用的是python2),尝试用python3导入;② 检查pip是否对应正确的Python版本,Windows可输入“pip --version”查看,确保pip的路径是Python安装目录下的Scripts文件夹;③ 重新执行安装命令,加上--user参数:pip install pyqt5 pyqt5-tools --user。 问题3:macOS/Linux系统安装后,无法打开Qt Designer 解决方案:macOS/Linux系统的pyqt5-tools可能需要手动查找Designer路径,后续在讲解Qt Designer使用时会详细说明,此处先确保PyQt5能正常导入即可。 三、编写第一个PyQt5程序:Hello World窗口 环境搭建完成后,我们来写第一个程序——一个简单的“Hello World”窗口,感受一下PyQt5的魅力~ 这里用纯代码编写(后续会讲可视化设计工具),每一行代码都加详细注释,方便理解。 1. 步骤:新建文件并编写代码 新建一个文本文件,重命名为“first_pyqt5.py”(注意后缀名是.py,不是.txt,Windows系统要确保显示文件后缀名); 用记事本、VS Code、PyCharm等编辑器打开文件,粘贴以下代码(建议手动敲一遍,加深记忆): # 导入必要的模块 import sys # 导入PyQt5基础窗口相关控件 from PyQt5.QtWidgets import QApplication, QWidget if __name__ == "__main__": # 1. 创建应用程序实例(PyQt5程序必须有且仅有一个) app = QApplication(sys.argv) # 2. 创建基础窗口实例 window = QWidget() # 3. 设置窗口属性 window.setWindowTitle("我的第一个PyQt5程序") # 窗口标题 window.resize(400, 300) # 窗口大小:宽400px,高300px window.move(500, 200) # 窗口位置:距离屏幕左侧500px,顶部200px # 4. 显示窗口(默认隐藏,需手动调用show()) window.show() # 5. 启动应用主循环,确保程序正常退出 sys.exit(app.exec_())2. 运行程序 运行方式有2种,新手推荐第一种: 通过终端运行: 打开终端,切换到“first_pyqt5.py”文件所在的目录(比如文件放在桌面,Windows输入“cd Desktop”,macOS/Linux输入“cd ~/Desktop”); 输入运行命令:python first_pyqt5.py 如果没有报错,会弹出一个标题为“我的第一个PyQt5程序”的窗口,说明运行成功! 用编辑器运行:如果用VS Code或PyCharm,直接点击编辑器右上角的“运行”按钮即可(需要确保编辑器已选择正确的Python解释器)。 mjpqfq31.png图片 mjpqosc2.png图片 四、第一个程序运行原理解析 可能有小伙伴会疑惑,上面几行代码为什么能实现窗口?这里拆解核心逻辑,帮大家理解PyQt5程序的运行流程: QApplication(应用程序对象):每个PyQt5程序都必须有且只有一个应用程序对象,它负责管理程序的所有资源(如事件循环、窗口、控件等),sys.argv是为了让程序能接收命令行参数,简单程序也可以传空列表[]。 QWidget(窗口对象):这是PyQt5的基础窗口控件,所有的UI元素(按钮、标签等)都基于它。我们创建它的实例,就相当于创建了一个空白窗口。 窗口属性设置:setWindowTitle(标题)、resize(大小)、move(位置)都是QWidget的方法,用于自定义窗口样式,这些方法也可以省略,此时会显示默认大小和标题的窗口。 show()方法:PyQt5的窗口默认是隐藏状态,必须调用show()方法才能显示出来。 主循环(exec_()):这是程序的核心,启动后程序会进入“等待状态”,不断监听用户的操作(比如点击窗口、关闭窗口),并做出响应。sys.exit()确保程序关闭时能正确释放资源,避免内存泄漏。 简单总结流程:创建应用程序对象 → 创建窗口对象 → 设置窗口属性 → 显示窗口 → 启动主循环响应用户操作。 五、拓展:修改窗口,添加简单交互(可选) 如果觉得空白窗口太单调,可以简单修改代码,给窗口添加一个标签(显示“Hello World!”),代码如下(在原有代码基础上修改,新增部分标红): import sys # 新增导入QLabel(标签控件) from PyQt5.QtWidgets import QApplication, QWidget, QLabel if __name__ == "__main__": app = QApplication(sys.argv) window = QWidget() window.setWindowTitle("我的第一个PyQt5程序") window.resize(400, 300) window.move(500, 200) # 新增:创建标签控件,父对象是window(表示标签放在这个窗口里) label = QLabel("Hello World!", window) # 设置标签的位置和大小(距离窗口左侧150像素,顶部130像素,宽100像素,高40像素) label.setGeometry(150, 130, 100, 40) window.show() sys.exit(app.exec_())运行修改后的代码,窗口中会显示“Hello World!”文字,是不是更有成就感啦~ 这里的QLabel就是PyQt5的基础控件之一,后续我们会详细讲解各种控件的使用。 mjpqpf9u.png图片 六、总结 本篇我们完成了PyQt5入门的核心第一步: 了解了PyQt5的基本概念和优势; 完成了Windows/macOS/Linux三大系统的环境搭建(Python + PyQt5 + pyqt5-tools); 编写并运行了第一个PyQt5窗口程序,理解了程序的运行原理; 拓展添加了标签控件,实现简单的文字显示。 下一篇我们会深入学习QWidget基础窗口的更多属性设置,以及布局管理器的使用,解决控件排列混乱的问题。如果在环境搭建或程序运行过程中遇到问题,欢迎在评论区留言讨论~