热门文章
推广宣传
最新发布
-
PyQt5事件处理:重写鼠标/键盘/窗口事件函数(附绘图实战) 第13篇:PyQt5事件处理:重写事件函数实现灵活交互(完整代码) 哈喽~ 欢迎来到PyQt5系列的第13篇!前面我们学了信号与槽和自定义信号,这两种方式能解决大部分交互需求,但它们是基于控件预设的“高级通知”(比如按钮点击、输入框文本变化)。而在实际开发中,我们还需要捕获更底层的用户操作——比如鼠标点击的具体位置、键盘按下的快捷键、窗口缩放的实时大小等,这就需要用到 PyQt5事件处理。 mk9obcxb.png图片 今天我们就来学习如何通过重写事件函数,捕获并处理鼠标、键盘、窗口等底层事件,实现更灵活的界面交互逻辑,全程搭配完整可运行代码,新手也能轻松掌握! 一、先搞懂:事件与信号的区别(核心概念) 很多同学会混淆事件(Event) 和信号(Signal),其实它们是两个不同层级的概念,核心区别如下: 对比维度事件(Event)信号(Signal)本质底层的输入/系统消息(如鼠标移动、键盘按下、窗口缩放)控件发出的高级通知(如按钮点击、文本变化)触发源操作系统/用户直接操作(如鼠标点一下、按键盘)控件状态变化(如按钮被点击后发出clicked信号)处理方式重写控件的事件函数(如mousePressEvent)绑定信号到槽函数(如btn.clicked.connect(func))灵活性极高,可捕获最细节的操作(如鼠标坐标、按键类型)中等,只能响应控件预设的信号关系信号是基于事件封装的(比如按钮的clicked信号,底层就是鼠标点击事件)-举个例子:点击按钮时,操作系统会先发送鼠标点击事件给按钮,按钮接收到事件后,会触发clicked信号,最终执行我们绑定的槽函数。 二、事件处理的核心:重写事件函数 PyQt5中所有控件都继承自QWidget,而QWidget内置了大量事件函数(如mousePressEvent、keyPressEvent)。我们只需在自定义控件/窗口类中重写这些函数,就能捕获并处理对应的事件。 核心规则 事件函数是固定名称的(比如处理鼠标点击的函数必须叫mousePressEvent),不能自定义名称; 事件函数的参数固定,第一个参数是事件对象(如QMouseEvent、QKeyEvent),包含事件的详细信息; 重写事件函数时,若需要保留控件的原有行为,需调用父类的同名事件函数(如super().mousePressEvent(event))。 三、实战1:鼠标事件处理(最常用) 鼠标事件是最常见的底层事件,包括按下、释放、双击、移动四种,对应的事件函数如下: 事件函数作用事件对象核心方法mousePressEvent(event)鼠标按下时触发QMouseEventevent.pos():获取鼠标位置;event.button():判断鼠标键(左/右/中)mouseReleaseEvent(event)鼠标释放时触发QMouseEvent同上mouseDoubleClickEvent(event)鼠标双击时触发QMouseEvent同上mouseMoveEvent(event)鼠标移动时触发QMouseEvent同上完整代码:鼠标事件捕获演示 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel from PyQt5.QtCore import Qt from PyQt5.QtGui import QFont class MouseEventDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("鼠标事件处理演示") self.resize(500, 400) self.setMouseTracking(True) # 关键:开启鼠标追踪(不按下也能捕获移动事件) # 布局与标签(显示事件信息) layout = QVBoxLayout() self.info_label = QLabel("鼠标状态:未操作\n位置:(0, 0)\n按键:无", alignment=Qt.AlignCenter) self.info_label.setFont(QFont("微软雅黑", 12)) layout.addWidget(self.info_label) self.setLayout(layout) # ---------- 1. 鼠标按下事件 ---------- def mousePressEvent(self, event): # 获取鼠标位置(相对于当前窗口) x = event.x() y = event.y() # 判断按下的鼠标键 if event.button() == Qt.LeftButton: btn_text = "左键" elif event.button() == Qt.RightButton: btn_text = "右键" elif event.button() == Qt.MiddleButton: btn_text = "中键" else: btn_text = "未知" # 更新标签信息 self.info_label.setText(f"鼠标状态:按下\n位置:( , {y})\n按键:{btn_text}") # 保留父类的原有行为(可选,比如让窗口能被拖动) super().mousePressEvent(event) # ---------- 2. 鼠标释放事件 ---------- def mouseReleaseEvent(self, event): x = event.x() y = event.y() self.info_label.setText(f"鼠标状态:释放\n位置:( , {y})\n按键:无") super().mouseReleaseEvent(event) # ---------- 3. 鼠标双击事件 ---------- def mouseDoubleClickEvent(self, event): x = event.x() y = event.y() self.info_label.setText(f"鼠标状态:双击\n位置:( , {y})\n按键:左键") # 双击窗口标题栏可以最大化,这里保留该行为 super().mouseDoubleClickEvent(event) # ---------- 4. 鼠标移动事件 ---------- def mouseMoveEvent(self, event): x = event.x() y = event.y() # 实时显示鼠标位置(需开启setMouseTracking(True)) self.info_label.setText(f"鼠标状态:移动\n位置:( , {y})\n按键:无") super().mouseMoveEvent(event) if __name__ == "__main__": app = QApplication(sys.argv) window = MouseEventDemo() window.show() sys.exit(app.exec_())关键要点 鼠标追踪:默认情况下,mouseMoveEvent只有在鼠标按下时才会触发。调用self.setMouseTracking(True)后,不按下鼠标也能捕获移动事件; 鼠标位置:event.x()/event.y()获取的是相对于当前窗口的坐标,event.globalX()/event.globalY()获取的是相对于屏幕的坐标; 鼠标键判断:通过event.button()配合Qt.LeftButton/Qt.RightButton/Qt.MiddleButton判断按下的按键。 四、实战2:键盘事件处理(快捷键实现) 键盘事件用于捕获按键操作,比如实现快捷键(如Ctrl+S保存、ESC关闭窗口),核心事件函数是keyPressEvent和keyReleaseEvent。 事件函数作用事件对象核心方法keyPressEvent(event)按键按下时触发QKeyEventevent.key():获取按键;event.modifiers():判断组合键(如Ctrl/Shift)keyReleaseEvent(event)按键释放时触发QKeyEvent同上完整代码:键盘事件与快捷键实现 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QMessageBox from PyQt5.QtCore import Qt from PyQt5.QtGui import QFont class KeyEventDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("键盘事件与快捷键演示") self.resize(400, 300) layout = QVBoxLayout() self.info_label = QLabel(""" 快捷键说明: 1. 按ESC键:关闭窗口 2. 按Ctrl+S:触发保存提示 3. 按方向键:移动标签位置 """, alignment=Qt.AlignCenter) self.info_label.setFont(QFont("微软雅黑", 11)) # 可移动的标签 self.move_label = QLabel("我是可移动的标签", alignment=Qt.AlignCenter) self.move_label.setStyleSheet("color: #e74c3c; font-size: 14px;") self.move_label.setFixedSize(150, 30) layout.addWidget(self.info_label) layout.addWidget(self.move_label) self.setLayout(layout) # 让窗口获得焦点,否则无法捕获键盘事件 self.setFocusPolicy(Qt.StrongFocus) # ---------- 键盘按下事件 ---------- def keyPressEvent(self, event): # 获取当前标签位置 x = self.move_label.x() y = self.move_label.y() # 1. 处理方向键:移动标签 if event.key() == Qt.Key_Left: self.move_label.move(x - 10, y) elif event.key() == Qt.Key_Right: self.move_label.move(x + 10, y) elif event.key() == Qt.Key_Up: self.move_label.move(x, y - 10) elif event.key() == Qt.Key_Down: self.move_label.move(x, y + 10) # 2. 处理ESC键:关闭窗口 elif event.key() == Qt.Key_Escape: self.close() # 3. 处理组合键:Ctrl+S elif event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier: QMessageBox.information(self, "快捷键触发", "Ctrl+S:模拟保存成功!") # 保留父类的原有行为 super().keyPressEvent(event) if __name__ == "__main__": app = QApplication(sys.argv) window = KeyEventDemo() window.show() sys.exit(app.exec_())关键要点 焦点问题:控件必须获得焦点才能捕获键盘事件。调用self.setFocusPolicy(Qt.StrongFocus)让窗口强制获得焦点; 组合键判断:通过event.modifiers()判断组合键,常用的有Qt.ControlModifier(Ctrl键)、Qt.ShiftModifier(Shift键)、Qt.AltModifier(Alt键); 常用按键常量:Qt.Key_Escape(ESC)、Qt.Key_Enter(回车)、Qt.Key_A-Qt.Key_Z(字母键)。 五、实战3:窗口事件处理(关闭/缩放/移动) 窗口事件用于捕获窗口的状态变化,比如关闭、缩放、移动,最常用的是closeEvent(关闭窗口时触发,用于确认是否关闭)。 事件函数作用事件对象核心方法closeEvent(event)窗口关闭时触发QCloseEventevent.accept():允许关闭;event.ignore():阻止关闭resizeEvent(event)窗口缩放时触发QResizeEventevent.size():获取新大小;event.oldSize():获取旧大小moveEvent(event)窗口移动时触发QMoveEventevent.pos():获取新位置;event.oldPos():获取旧位置完整代码:窗口事件处理演示 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QMessageBox from PyQt5.QtCore import Qt from PyQt5.QtGui import QFont class WindowEventDemo(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("窗口事件处理演示") self.resize(400, 300) layout = QVBoxLayout() self.info_label = QLabel("窗口状态:正常\n大小:400×300\n位置:(0, 0)", alignment=Qt.AlignCenter) self.info_label.setFont(QFont("微软雅黑", 12)) layout.addWidget(self.info_label) self.setLayout(layout) # ---------- 1. 窗口关闭事件(最常用) ---------- def closeEvent(self, event): # 弹出确认框,询问是否关闭 reply = QMessageBox.question( self, "关闭确认", "确定要关闭窗口吗?未保存的内容可能会丢失!", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if reply == QMessageBox.Yes: event.accept() # 允许关闭窗口 else: event.ignore() # 阻止关闭窗口 # ---------- 2. 窗口缩放事件 ---------- def resizeEvent(self, event): # 获取窗口新大小 new_size = event.size() width = new_size.width() height = new_size.height() # 更新状态信息 self.info_label.setText(f"窗口状态:缩放\n大小:{width}×{height}\n位置:({self.x()}, {self.y()})") super().resizeEvent(event) # ---------- 3. 窗口移动事件 ---------- def moveEvent(self, event): # 获取窗口新位置 new_pos = event.pos() x = new_pos.x() y = new_pos.y() # 更新状态信息 self.info_label.setText(f"窗口状态:移动\n大小:{self.width()}×{self.height()}\n位置:( , {y})") super().moveEvent(event) if __name__ == "__main__": app = QApplication(sys.argv) window = WindowEventDemo() window.show() sys.exit(app.exec_())关键要点 关闭事件控制:closeEvent中,event.accept()允许窗口关闭,event.ignore()阻止关闭。这是实现“关闭确认”的核心方法; 大小/位置获取:resizeEvent中用event.size()获取新大小,moveEvent中用event.pos()获取新位置; 父类方法调用:重写窗口事件时,必须调用父类的同名方法,否则窗口的正常功能会被破坏(比如无法缩放、移动)。 六、综合案例:简易绘图工具(鼠标事件实战) 结合鼠标的按下、移动、释放事件,实现一个简易的绘图工具——按下鼠标拖动时绘制线条,释放鼠标时结束绘制。 完整代码:简易绘图工具 import sys from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtCore import Qt, QPoint from PyQt5.QtGui import QPainter, QPen class PaintTool(QWidget): def __init__(self): super().__init__() self.init_ui() # 绘图相关变量 self.is_drawing = False # 是否正在绘图 self.start_point = QPoint() # 绘图起始点 self.end_point = QPoint() # 绘图结束点 def init_ui(self): self.setWindowTitle("简易绘图工具(鼠标拖动绘制线条)") self.resize(600, 500) self.setMouseTracking(True) # ---------- 鼠标按下:开始绘图 ---------- def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.is_drawing = True self.start_point = event.pos() # 记录起始点 self.end_point = self.start_point # 初始结束点等于起始点 super().mousePressEvent(event) # ---------- 鼠标移动:更新绘图 ---------- def mouseMoveEvent(self, event): if self.is_drawing: self.end_point = event.pos() # 更新结束点 self.update() # 触发重绘(调用paintEvent) super().mouseMoveEvent(event) # ---------- 鼠标释放:结束绘图 ---------- def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.is_drawing = False self.end_point = event.pos() self.update() # 绘制最后一条线 super().mouseReleaseEvent(event) # ---------- 重绘事件:实际绘制线条 ---------- def paintEvent(self, event): # QPainter是PyQt5的绘图工具 painter = QPainter(self) # 设置画笔样式(颜色、宽度、线条类型) pen = QPen(Qt.red, 3, Qt.SolidLine) painter.setPen(pen) # 绘制线条:从起始点到结束点 painter.drawLine(self.start_point, self.end_point) if __name__ == "__main__": app = QApplication(sys.argv) window = PaintTool() window.show() sys.exit(app.exec_())核心逻辑 绘图状态控制:用is_drawing标记是否正在绘图,鼠标按下时设为True,释放时设为False; 重绘触发:self.update()会强制触发paintEvent方法,实现实时绘图; QPainter绘图:QPainter是PyQt5的绘图核心类,drawLine方法用于绘制线条,还支持绘制矩形、圆形等图形。 七、常见问题排查 1. 鼠标移动事件不触发 问题原因:未开启鼠标追踪,默认只有鼠标按下时才会触发mouseMoveEvent; 解决方案:调用self.setMouseTracking(True)开启鼠标追踪。 2. 键盘事件不响应 问题原因1:控件没有获得焦点; 解决方案:调用self.setFocusPolicy(Qt.StrongFocus)让控件获得焦点; 问题原因2:重写事件函数时忘记调用父类方法; 解决方案:在函数末尾添加super().keyPressEvent(event)。 3. 窗口无法关闭/缩放 问题原因:重写closeEvent/resizeEvent时忘记调用父类方法,破坏了窗口的原有功能; 解决方案:必须调用super().closeEvent(event)或super().resizeEvent(event)。 4. 绘图时线条闪烁 问题原因:每次update()都会重绘整个窗口,导致闪烁; 解决方案:开启双缓冲绘图(PyQt5默认开启,无需手动设置),或只重绘需要更新的区域。 总结 事件与信号的区别:事件是底层操作,信号是控件的高级通知,信号基于事件封装; 事件处理核心:重写固定名称的事件函数,调用父类方法保留原有功能; 常用事件: 鼠标事件:mousePressEvent/mouseMoveEvent(需开启追踪); 键盘事件:keyPressEvent(需获得焦点); 窗口事件:closeEvent(用于关闭确认); 实战价值:事件处理能实现信号与槽无法覆盖的交互(如绘图、快捷键、窗口状态监控)。 下一章我们将学习PyQt5多线程编程——解决耗时操作导致界面卡顿的问题,这是开发高性能GUI程序的必备技能!如果在事件处理中遇到绘图、快捷键实现的问题,欢迎在评论区留言讨论~ -
Joomla v6.0.2 开源PHP商业CMS - 企业门户建站系统(多语言+SEO) Joomla v6.0.2:开源PHP商业级CMS,企业门户建站首选 给大家推荐一款国外知名的开源内容管理系统——Joomla v6.0.2!基于PHP+MySQL开发,主打企业级门户网站场景,以“技术先进、生态丰富、定制灵活、多语言适配”为核心优势,覆盖商业网站从搭建到运营的全需求,不管是企业用户搭建官方门户,还是开发者定制商业项目,都能高效落地! mk9ndied.png图片 一、核心功能模块:覆盖商业建站全场景 1. 先进技术集成:提升网站效能与传播力 网站缓存技术:加速页面响应速度,优化高并发访问体验,降低服务器资源消耗。 RSS新闻功能:支持RSS新闻联播与读取,适配博客、资讯类商业网站的内容分发需求。 内核级SEO优化:作为核心功能集成,无需额外插件即可实现基础优化,助力网站收录与排名。 2. 丰富生态与附加套件 全球上千种应用套件:涵盖功能拓展、美工布景等,安装流程类似Windows装软件,操作简单。 商用套件市场成熟:提供价廉物美的软件与主题模板,满足商业网站的个性化视觉与功能需求。 开发社群活跃:持续产出创新套件,适配不同行业商业场景,降低功能拓展成本。 3. 易用操作与内容管理 美观且简易的操作接口:初次使用需简单学习,熟练后可高效管理;搭配所见即所得编辑器,非专业用户也能轻松编辑网站文章。 全流程内容管理:支持文章、菜单、模块、分类等核心单元的精细化管理,适配商业网站多维度内容运营需求。 4. 定制化与整合能力 高度客制弹性:在强大功能与定制空间之间找到平衡,支持开发者进行二次开发与美工设计优化。 完善整合能力:兼容各类拓展需求,受到知名厂商认可,适合定制化商业项目开发。 5. SEO与多语言核心优势 全方位SEO机制:文章、菜单、全局可分别设置Meta信息,自动规范Title、H1-H6标签、图片ALT属性,贴合搜索引擎抓取逻辑。 多语言全面支持:覆盖70+国家语言,菜单、文章等管理单元可独立设置语言,3.7.5版本后集成“多语言管理中心”,方便创建多语言切换网站。 二、核心特色:商业CMS的差异化优势 商业场景精准适配:主打企业门户网站类型,完美适配音乐、教育、汽车、服装、宠物等各类商业行业网站需求。 生态成熟性价比高:附加套件与主题资源丰富,商用套件价格亲民,降低商业网站搭建与运营成本。 兼顾易用与定制:既满足普通管理者的简易操作需求,又为开发者保留充足的二次开发空间,适配不同团队能力。 多语言与SEO双加持:多语言管理中心简化跨国业务网站搭建,内核级SEO机制助力商业网站抢占搜索流量,提升转化效率。 三、适用场景:谁适合用Joomla? 企业用户:搭建官方门户网站、行业垂直网站,支持私有化部署,适配GEO推广、AI搜索推荐等商业需求。 开发者:承接商业网站定制项目,借助其成熟生态与定制弹性,缩短开发周期、降低项目成本。 跨国业务用户:创建多语言商业网站(如外贸平台、跨国资讯站),利用70+语言支持覆盖全球目标用户。 非专业管理者:需要自主运营商业网站内容,借助所见即所得编辑器与简易操作接口,无需专业技术即可完成日常维护。 源码获取 Joomla v6.0.2 开源PHP商业CMS 下载地址:https://pan.quark.cn/s/ca6ed297e344 提取码: -
DedeCMS织梦CMS V5.7.118 - 开源PHP建站工具 安装部署全指南 DedeCMS织梦内容管理系统:开源PHP建站神器,安装部署与更新全解析 给大家推荐一款国内老牌且口碑极佳的开源建站工具——DedeCMS(织梦内容管理系统)!作为深耕行业多年的PHP开源CMS,它以“易用性强、功能全面、生态成熟、持续维护”为核心优势,无需复杂开发即可快速搭建企业官网、个人博客、资讯门户等各类网站,适配从新手到资深开发者的不同需求,妥妥的建站必备利器! mk9n7i96.png图片 一、核心功能模块:覆盖建站全场景需求 1. 高效内容管理:轻松搞定内容发布与维护 支持富文本编辑、Markdown语法、图片/视频上传等多种内容创作形式,图文、音视频内容均可灵活发布。 内置内容批量操作、定时发布、草稿保存、版本回溯功能,多栏目管理高效省心,避免误操作导致内容丢失。 2. 灵活模板引擎与插件扩展 采用自主研发的模板引擎,支持HTML模板自定义,海量免费/付费模板资源可直接下载使用,一键切换网站风格。 成熟插件生态,支持在线安装留言板、会员系统、支付接口等功能插件,第三方开发者可自主开发拓展。 3. 强大SEO与多端适配 支持自定义页面Title、Keywords、Description,自动生成静态HTML页面与站点地图,优化搜索引擎收录。 模板支持响应式设计,自动适配PC、手机、平板等不同设备,支持多语言配置,适配外贸场景。 4. 安全防护与稳定运行 内置SQL注入防护、XSS攻击拦截、密码加密存储等多重安全机制,定期更新安全补丁。 高效率标签缓存机制,降低系统资源消耗,确保高并发场景下流畅运行。 二、环境与安装核心要点(新增补充) 1. 详细运行环境要求 Windows环境:IIS/Apache/Nginx + PHP5.6+ + MySQL5.7+/MariaDB,PHP需非安全模式运行。 Linux/Unix环境:Apache/Nginx + PHP5.6+ + MySQL5.7+/MariaDB,同样要求PHP非安全模式。 推荐环境:OpenBSD + Nginx + PHP5.6+ + MariaDB,依托OpenBSD的安全特性与PF防火墙,搭配chroot模式可保障系统安全,即使程序被攻破也不影响主系统。 PHP函数库依赖:必须启用allow_url_fopen、GD扩展库、MySQL扩展库,支持phpinfo、dir等系统函数。 2. 安装包与版本规则 最新正式版:DedeCMS V5.7.118(2025年7月30更新),安装包MD5值为52f2871223c7b753148918766b372506。 解压命令:Linux/Unix环境使用tar -zxvf DedeCMS-V5.7.118-UTF8.zip解压。 版本维护规则:V5.7.73及后续版本遵循SemVer(语义化版本)规范,更新逻辑清晰。 3. 目录结构与权限要求 核心目录:/install(安装目录,安装后可删除)、/dede(默认后台,可改名)、/uploads(上传目录)、/html(静态文件目录)、/data(缓存目录)等。 必设权限:uploads、html、data、special等目录需开启可写入权限,否则会导致上传失败、缓存异常或无法登录后台。 4. 三步简易安装流程 下载安装包解压,将/uploads目录上传至网站根目录; 访问http://你的域名或IP/install/index.php,按向导完成数据库配置与安装; 安装完成后可删除/install目录,建议修改/dede后台目录名称提升安全性。 5. 常见问题与解决方案 验证码无法显示、无法登录后台:大概率是data目录无写入权限,导致session无法使用,需配置目录可写权限。 文件上传提示“413 Request Entity Too Large”:调整php.ini中upload_max_filesize、post_max_size参数,及Nginx的client_max_body_size设置。 安装页面空白:可能是未装载MySQL扩展,新手可下载DedeCMS PHP套件包简化配置。 上传功能失效:检查PHP临时文件夹是否设置正确且具备写入权限。 三、核心特色:织梦CMS的差异化优势 1. 易用性与生态双在线 后台界面简洁直观,操作逻辑清晰,零基础用户也能快速完成建站与内容维护。 海量模板、插件资源与活跃社区,遇到问题可快速获取解决方案,运维成本低。 2. 开源免费且高度可定制 核心功能完全免费,源码开放可查看、修改,支持二次开发,降低建站与定制成本。 模型与模块概念并存,可通过自定义模型拓展功能,搭配插件满足个性化需求。 3. 持续更新维护,安全有保障 迭代更新频繁,2022年至今累计数十次更新,以安全更新和功能优化为主,及时修复漏洞。 官方定期发布补丁,支持版本平滑升级,确保网站长期稳定运行。 4. 兼容性广泛,部署灵活 适配Nginx/IIS/Apache等主流Web服务器,支持Windows、Linux/Unix等多种操作系统。 与PHP5.6+、MySQL5.7+版本兼容良好,无需复杂配置即可部署。 四、适用场景:谁适合用DedeCMS? 个人站长/新手:零基础快速搭建个人博客、兴趣网站,模板+插件组合即可快速上线。 中小企业:低成本、高效率搭建企业官网、产品展示站、新闻资讯站,方便自主维护。 电商从业者:通过插件拓展支付接口、订单管理功能,快速落地电商展示或小型电商平台。 开发者:作为PHP CMS学习案例,或基于源码二次开发,定制教育、医疗、本地生活等行业专属网站。 下载源码 DedeCMS织梦CMS V5.7.118.zip 下载地址:https://pan.quark.cn/s/bd266c50f22d 提取码: -
2026全新聚合登录系统源码 一栈式配置全部快捷登录接口 2026全新聚合登录系统源码,一站式配置所有快捷登录接口 这是一款完全兼容彩虹聚合登录API的社交登录系统,支持多平台OAuth登录,能够无缝替代彩虹聚合登录。 支持平台: QQ、微信、微博、支付宝、GitHub、Google、Gitee、百度、抖音、微软、小米、钉钉、飞书、企业微信 核心功能: 完全适配彩虹聚合登录API接口规范,可进行多应用管理,支持独立域名授权 设有用户中心,支持绑定多种社交账号 具备后台管理功能,涵盖平台配置、应用审核、用户管理 拥有计费系统,支持免费额度、次数包、按量计费模式 支持邮箱/短信验证码(腾讯云、阿里云) 提供调用统计与日志记录功能 API接口: 登录接口:/connect.php?act=login 回调接口:/connect.php?act=callback 查询接口:/connect.php?act=query ecf437b560.png图片 ecf437b127.png图片 ecf437b780.png图片 2a98bcd968.png图片 2a98bcd431.png图片 2a98bcd702.png图片 2a98bcd605.png图片 隐藏内容,请前往内页查看详情 -
PyQt5自定义信号:跨窗口通信与多线程进度传递(完整代码) 第12篇:PyQt5自定义信号:满足复杂交互与跨组件通信需求(完整代码) 哈喽~ 欢迎来到PyQt5系列的第12篇!上一章我们吃透了内置信号与槽的基础用法和进阶技巧,但在实际开发中,内置信号往往无法满足复杂场景(比如两个窗口之间传递数据、自定义控件的状态变化、多线程间的进度通知)。今天我们就来学习自定义信号——PyQt5中实现跨组件、跨线程通信的核心武器,全程搭配完整可运行代码,帮你彻底掌握自定义信号的定义、发射与绑定逻辑! mk7oiwng.png图片 一、先明确:自定义信号的核心使用场景 当以下场景出现时,内置信号就不够用了,必须用自定义信号: 跨窗口通信:主窗口和子窗口之间传递数据(如子窗口输入的内容同步到主窗口); 自定义控件:开发自己的控件(如自定义进度条),需要向外发送状态变化信号; 多线程通信:子线程不能直接操作主界面,需通过自定义信号将数据传递给主线程; 复杂业务逻辑:业务状态变化(如支付成功、数据加载完成)需要触发多个槽函数响应。 自定义信号的核心规则(必记!) 自定义信号必须定义在继承自QObject的类中(PyQt5所有控件都继承了QObject,所以自定义窗口类也可以); 自定义信号是类属性,需用pyqtSignal()方法创建,不能在__init__中定义; 信号通过emit()方法发射,发射时的参数必须与信号定义的参数类型一致; 自定义信号同样支持connect()绑定槽函数、disconnect()断开绑定。 二、自定义信号的基础用法:定义、发射与绑定 1. 无参数自定义信号(基础入门) 先从最简单的无参数自定义信号入手,掌握“定义→发射→绑定”的核心流程。 完整代码:无参数自定义信号 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel from PyQt5.QtCore import QObject, pyqtSignal # 自定义信号的载体类(必须继承QObject) # 注意:如果是自定义窗口类,本身继承QWidget(已继承QObject),可直接在窗口类中定义信号 class MySignal(QObject): # 定义无参数的自定义信号,类属性 custom_signal = pyqtSignal() class SignalDemo(QWidget): def __init__(self): super().__init__() self.init_ui() self.init_signal() def init_ui(self): self.setWindowTitle("无参数自定义信号演示") self.resize(350, 200) layout = QVBoxLayout() self.label = QLabel("信号未触发", alignment=1) self.trigger_btn = QPushButton("点击发射自定义信号") layout.addWidget(self.label) layout.addWidget(self.trigger_btn) self.setLayout(layout) def init_signal(self): # 1. 创建自定义信号的实例 self.my_signal = MySignal() # 2. 绑定自定义信号到槽函数 self.my_signal.custom_signal.connect(self.on_custom_signal_trigger) # 3. 绑定按钮点击信号,触发自定义信号的发射 self.trigger_btn.clicked.connect(self.emit_custom_signal) def emit_custom_signal(self): """发射自定义信号""" print("按钮点击,准备发射自定义信号...") # 核心方法:emit() 发射信号 self.my_signal.custom_signal.emit() def on_custom_signal_trigger(self): """自定义信号的槽函数""" self.label.setText("自定义信号触发成功!") print("自定义信号槽函数执行完成") if __name__ == "__main__": app = QApplication(sys.argv) window = SignalDemo() window.show() sys.exit(app.exec_())核心步骤解析 定义信号:在继承QObject的类中,用pyqtSignal()创建类属性custom_signal,这就是自定义信号; 创建信号实例:在窗口类中实例化MySignal,得到信号载体; 绑定槽函数:用custom_signal.connect(槽函数)将信号与响应逻辑绑定; 发射信号:通过custom_signal.emit()发射信号,触发槽函数执行。 2. 带参数的自定义信号(高频实战) 大多数场景下,信号需要传递数据(如文本、数值、对象),这就需要定义带参数的自定义信号。pyqtSignal()支持指定参数类型(如int、str、tuple等),发射时必须传递对应类型的参数。 完整代码:带参数的自定义信号 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel, QLineEdit from PyQt5.QtCore import pyqtSignal # 窗口类已继承QObject,可直接定义信号 class ParamSignalDemo(QWidget): # 定义带参数的自定义信号:支持多种参数类型 # 格式:pyqtSignal(参数类型1, 参数类型2, ...) text_signal = pyqtSignal(str) # 传递字符串 num_signal = pyqtSignal(int, str) # 传递整数+字符串 dict_signal = pyqtSignal(dict) # 传递字典 def __init__(self): super().__init__() self.init_ui() self.bind_signals() def init_ui(self): self.setWindowTitle("带参数自定义信号演示") self.resize(400, 300) layout = QVBoxLayout() self.edit = QLineEdit() self.edit.setPlaceholderText("输入文本,点击按钮发射信号") self.label1 = QLabel("字符串信号:未触发", alignment=1) self.label2 = QLabel("整数+字符串信号:未触发", alignment=1) self.label3 = QLabel("字典信号:未触发", alignment=1) self.btn = QPushButton("发射所有带参数信号") layout.addWidget(self.edit) layout.addWidget(self.label1) layout.addWidget(self.label2) layout.addWidget(self.label3) layout.addWidget(self.btn) self.setLayout(layout) def bind_signals(self): """绑定自定义信号到槽函数""" self.text_signal.connect(self.on_text_signal) self.num_signal.connect(self.on_num_signal) self.dict_signal.connect(self.on_dict_signal) self.btn.clicked.connect(self.emit_param_signals) def emit_param_signals(self): """发射带参数的自定义信号""" input_text = self.edit.text().strip() or "默认文本" # 发射信号:参数类型必须与定义一致 self.text_signal.emit(input_text) # 传递字符串 self.num_signal.emit(2026, input_text) # 传递整数+字符串 self.dict_signal.emit({"name": input_text, "year": 2026}) # 传递字典 # 对应不同参数的槽函数 def on_text_signal(self, text): self.label1.setText(f"字符串信号:{text}") def on_num_signal(self, num, text): self.label2.setText(f"整数+字符串信号:{num} | {text}") def on_dict_signal(self, data): self.label3.setText(f"字典信号:{data}") if __name__ == "__main__": app = QApplication(sys.argv) window = ParamSignalDemo() window.show() sys.exit(app.exec_())关键要点 信号参数定义:pyqtSignal(str)表示信号传递字符串,pyqtSignal(int, str)表示传递两个参数(整数+字符串),支持Python基本数据类型和自定义对象; 发射参数匹配:emit()的参数数量和类型必须与信号定义完全一致,否则会触发TypeError; 槽函数接收参数:槽函数的参数数量要与信号传递的参数数量一致,顺序对应。 三、实战案例1:主窗口与子窗口的跨窗口通信 跨窗口通信是自定义信号最常用的场景之一。比如点击主窗口按钮弹出子窗口,子窗口输入内容后,通过自定义信号将数据传递回主窗口并显示。 步骤1:定义子窗口类(含自定义信号) 子窗口负责接收用户输入,输入完成后发射自定义信号传递数据。 # 子窗口类 from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QPushButton, QLabel from PyQt5.QtCore import pyqtSignal class ChildWindow(QDialog): # 定义自定义信号:传递用户输入的文本 child_signal = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) self.init_ui() def init_ui(self): self.setWindowTitle("子窗口(输入数据)") self.setFixedSize(300, 200) layout = QVBoxLayout() self.edit = QLineEdit() self.edit.setPlaceholderText("请输入要传递的内容") self.confirm_btn = QPushButton("确认并传递给主窗口") layout.addWidget(self.edit) layout.addWidget(self.confirm_btn) self.setLayout(layout) # 绑定按钮点击信号,发射自定义信号 self.confirm_btn.clicked.connect(self.on_confirm) def on_confirm(self): input_text = self.edit.text().strip() if input_text: # 发射信号,传递输入内容 self.child_signal.emit(input_text) self.close() # 关闭子窗口 else: QLabel("请输入内容!").show()步骤2:定义主窗口类(接收子窗口信号) 主窗口点击按钮弹出子窗口,绑定子窗口的自定义信号,接收数据并显示。 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel from PyQt5.QtCore import Qt class MainWindow(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("主窗口(接收子窗口数据)") self.resize(400, 250) layout = QVBoxLayout() self.result_label = QLabel("子窗口传递的数据:无", alignment=Qt.AlignCenter) self.result_label.setStyleSheet("font-size: 16px; color: #2ecc71;") self.open_child_btn = QPushButton("打开子窗口") layout.addWidget(self.result_label) layout.addWidget(self.open_child_btn) self.setLayout(layout) # 绑定按钮点击信号,打开子窗口 self.open_child_btn.clicked.connect(self.open_child_window) def open_child_window(self): # 创建子窗口实例 self.child_win = ChildWindow(self) # 绑定子窗口的自定义信号到槽函数 self.child_win.child_signal.connect(self.on_child_data_received) # 显示子窗口(模态) self.child_win.exec_() def on_child_data_received(self, data): """接收子窗口传递的数据""" self.result_label.setText(f"子窗口传递的数据:{data}") if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())案例核心亮点 子窗口信号定义:子窗口类中定义child_signal = pyqtSignal(str),用于传递输入文本; 跨窗口信号绑定:主窗口创建子窗口后,通过child_win.child_signal.connect()绑定槽函数,实现数据监听; 模态窗口通信:子窗口用exec_()显示为模态窗口,用户操作完子窗口后,数据同步回主窗口。 四、实战案例2:多线程中用自定义信号传递进度(避免界面卡顿) PyQt5中子线程不能直接操作主界面控件,否则会导致界面卡顿甚至崩溃。正确的做法是:子线程执行耗时任务,通过自定义信号将进度传递给主线程,主线程更新界面。 完整代码:多线程进度传递 import sys import time from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QProgressBar, QLabel from PyQt5.QtCore import pyqtSignal, QThread, Qt # 自定义子线程类:执行耗时任务,发射进度信号 class WorkThread(QThread): # 定义自定义信号:传递进度值(整数) progress_signal = pyqtSignal(int) # 定义任务完成信号 finish_signal = pyqtSignal(str) def __init__(self, total_steps=100): super().__init__() self.total_steps = total_steps self.is_running = True def run(self): """线程执行的核心方法:耗时任务""" for step in range(1, self.total_steps + 1): if not self.is_running: break # 模拟耗时操作(如文件下载、数据处理) time.sleep(0.05) # 发射进度信号 self.progress_signal.emit(step) # 任务完成,发射完成信号 self.finish_signal.emit("任务执行完成!" if self.is_running else "任务被取消!") def stop(self): """停止线程""" self.is_running = False # 主窗口类:显示进度条,控制线程 class ThreadSignalDemo(QWidget): def __init__(self): super().__init__() self.init_ui() self.thread = None # 线程实例 def init_ui(self): self.setWindowTitle("多线程自定义信号传递进度") self.resize(400, 250) layout = QVBoxLayout() self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.status_label = QLabel("状态:未开始", alignment=Qt.AlignCenter) self.start_btn = QPushButton("开始任务") self.stop_btn = QPushButton("停止任务") self.stop_btn.setEnabled(False) layout.addWidget(self.progress_bar) layout.addWidget(self.status_label) layout.addWidget(self.start_btn) layout.addWidget(self.stop_btn) self.setLayout(layout) # 绑定按钮信号 self.start_btn.clicked.connect(self.start_task) self.stop_btn.clicked.connect(self.stop_task) def start_task(self): """启动子线程""" self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) self.status_label.setText("状态:任务执行中...") # 创建线程实例 self.thread = WorkThread(total_steps=100) # 绑定线程的自定义信号到槽函数 self.thread.progress_signal.connect(self.update_progress) self.thread.finish_signal.connect(self.on_task_finish) # 启动线程 self.thread.start() def stop_task(self): """停止子线程""" if self.thread and self.thread.isRunning(): self.thread.stop() self.status_label.setText("状态:任务停止中...") def update_progress(self, step): """更新进度条(主线程执行)""" self.progress_bar.setValue(step) def on_task_finish(self, msg): """任务完成回调""" self.status_label.setText(f"状态:{msg}") self.progress_bar.setValue(0) self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) # 释放线程资源 self.thread = None if __name__ == "__main__": app = QApplication(sys.argv) window = ThreadSignalDemo() window.show() sys.exit(app.exec_())案例核心知识点 线程信号定义:子线程类WorkThread中定义progress_signal = pyqtSignal(int),用于传递进度值; 线程安全通信:子线程在run()方法中执行耗时任务,通过emit()发射进度,主线程的槽函数update_progress()更新进度条,避免了子线程直接操作界面; 线程控制:通过is_running标志控制线程是否继续执行,stop()方法安全停止线程,避免强制终止导致的资源泄漏。 五、自定义信号的高级用法:重载信号与信号断开 1. 重载信号(支持多种参数类型) 重载信号指同一个信号名支持多种参数类型,比如data_signal既可以传递int,也可以传递str。定义时用元组包裹不同的参数类型组合。 完整代码:重载信号 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel from PyQt5.QtCore import pyqtSignal class OverloadSignalDemo(QWidget): # 定义重载信号:支持两种参数类型(int)或(str) data_signal = pyqtSignal([int], [str]) def __init__(self): super().__init__() self.init_ui() self.bind_signals() def init_ui(self): self.setWindowTitle("重载自定义信号演示") self.resize(350, 200) layout = QVBoxLayout() self.label = QLabel("信号未触发", alignment=1) self.btn1 = QPushButton("发射整数信号") self.btn2 = QPushButton("发射字符串信号") layout.addWidget(self.label) layout.addWidget(self.btn1) layout.addWidget(self.btn2) self.setLayout(layout) def bind_signals(self): # 绑定重载信号:指定参数类型,对应不同槽函数 self.data_signal[int].connect(self.on_int_signal) self.data_signal[str].connect(self.on_str_signal) self.btn1.clicked.connect(lambda: self.data_signal[int].emit(2026)) self.btn2.clicked.connect(lambda: self.data_signal[str].emit("重载信号演示")) def on_int_signal(self, num): self.label.setText(f"整数信号:{num}") def on_str_signal(self, text): self.label.setText(f"字符串信号:{text}") if __name__ == "__main__": app = QApplication(sys.argv) window = OverloadSignalDemo() window.show() sys.exit(app.exec_())2. 自定义信号的断开(disconnect) 和内置信号一样,自定义信号也可以用disconnect()方法断开绑定,适用于动态控制信号是否生效的场景。 关键代码片段 # 断开指定槽函数的绑定 self.data_signal.disconnect(self.on_int_signal) # 断开该信号的所有绑定槽函数 self.data_signal.disconnect()六、自定义信号常见问题排查 1. 信号定义后无法发射(最常见!) 问题原因:自定义信号定义在__init__方法中,而非类属性; 解决方案:信号必须是类属性,直接在类中定义(如class MyClass(QObject): signal = pyqtSignal()),不能在__init__里用self.signal = pyqtSignal()。 2. 发射信号时报错TypeError: emit() takes 1 positional argument but 2 were given 问题原因:发射信号的参数数量与信号定义的不一致; 解决方案:检查pyqtSignal()的参数类型和emit()的参数数量,确保完全匹配。 3. 子线程发射信号,主线程槽函数不执行 问题原因1:线程实例被垃圾回收(比如线程是局部变量); 解决方案:将线程实例设为窗口类的属性(如self.thread = WorkThread()); 问题原因2:信号未绑定成功(线程启动后才绑定信号); 解决方案:先绑定信号,再启动线程(connect()要在start()之前)。 4. 跨窗口信号绑定后无响应 问题原因:子窗口实例被销毁(比如子窗口是局部变量,函数执行完后被回收); 解决方案:将子窗口实例设为主窗口的属性(如self.child_win = ChildWindow())。 总结 自定义信号核心流程:定义类属性信号(pyqtSignal())→ 绑定槽函数(connect())→ 发射信号(emit()); 核心使用场景:跨窗口通信、多线程进度传递、自定义控件状态通知; 关键注意事项:信号必须是类属性、参数类型要匹配、子线程不能直接操作界面; 下一章预告:我们将学习PyQt5事件处理——重写事件函数(如鼠标事件、键盘事件),实现更灵活的界面交互逻辑。 如果在自定义信号的使用中遇到跨窗口通信、多线程同步的问题,或者想了解更复杂的信号应用场景,欢迎在评论区留言讨论~ -
LibArea仿知乎PHP源码 - 多功能博客问答社区平台 LibArea仿知乎多功能博客问答网站PHP源码:综合性社区平台解决方案 给大家分享一款仿知乎风格的优质开源资源——LibArea多功能博客问答网站PHP源码!基于PHP+MySQL开发,整合博客发布、社交互动、论坛讨论、问答服务四大核心场景,以标签分类系统为特色,支持高度定制与全端适配,开源免费且安全可靠,妥妥的开发者、个人或企业搭建多元化在线社区的理想选择! https://img.zhanid.com/uploads/2025/05/26/202505265267.webp图片 一、核心功能模块:覆盖社区互动全需求 1. 多合一综合平台:满足多样化交流场景 集成博客发布、社交媒体互动、论坛讨论、问答服务,无需单独搭建多个平台,一站式满足用户内容分享、问题求助、话题交流等需求。 设计灵感源自Stack Exchange、知乎、Quora等知名平台,兼顾专业性与易用性,适配不同类型社区运营场景。 2. 高效内容与信息管理 标签分类系统:通过标签对内容进行精准分类与检索,用户可快速查找感兴趣的话题、文章或问答,提升信息获取效率。 内容发布功能:支持富文本编辑、图片上传、视频嵌入,内容呈现形式丰富,满足图文、音视频等多种创作需求。 高效搜索功能:内置搜索引擎,可快速匹配内容、用户等信息,避免信息冗余导致的查找困难。 3. 用户与互动管理 完善用户体系:支持用户注册、登录、个人信息管理及权限控制,保障用户隐私与平台安全。 社交互动功能:包含评论、点赞、分享等互动操作,增强用户间连接,提升社区活跃度与用户留存率。 二、核心特色:社区平台的差异化优势 高度可定制:架构设计灵活,可深度调整界面风格、功能模块,适配不同社区定位(如技术交流、兴趣分享、行业问答等)。 全端适配流畅:采用响应式布局,在PC、移动设备等不同终端上均能提供一致优质的浏览与操作体验。 性能与安全兼顾:代码与数据库结构经过优化,高并发场景下仍能流畅运行;内置数据加密、SQL注入防护、XSS攻击防护等多重安全机制,保障平台稳定。 社区支持活跃:作为开源项目,拥有活跃的开发者社区,可获取技术帮助、分享经验,助力项目持续迭代优化。 三、适用场景:谁适合用这款源码? 开发者:作为PHP开源社区项目学习案例,研究标签分类、互动功能、安全防护等模块设计;或基于源码二次开发,快速搭建定制化社区平台。 个人创业者:搭建垂直领域社区(如技术问答、兴趣分享、职场交流等),无需从零开发,缩短项目上线周期。 企业/团队:构建内部交流社区、客户问答平台或行业交流站点,促进内部协作或用户互动,提升品牌凝聚力。 下载 下载 下载地址:https://pan.quark.cn/s/bd69cc20ad2d 提取码: -
pyqt5学了有用吗?能赚钱吗 前言 前2天没更新文章,主要是我一直在忙一个客户的开发工作。主要是WP站点的战群管理程序,可以方便的把生成的文章快速发布到各个站群里面,还集成了后台功能,主要都是数据库读写 做了啥? 给各位看个图片 mk7m6f7c.png图片 这是开发过程中迭代的备份文件,终于在昨天晚上完工了 mk7m71pj.png图片 给大家看看是怎么样的一个工程 mk7m873o.png图片 程序是python310写的,总共2900行代码,界面没怎么美化,只是做了排版 mk7m9j1w.png图片 里边做了挺多功能,还插入了md语法解析工具,因为客户是用ai生成的文档批量导入 它的界面也是pyqt5写的,总体程序就不给大家看完了,毕竟客户花钱开发的 结语 接下来我会恢复更新文章,还是希望大家能好好学pyqt5+python,因为是真能赚钱 -
Web/H5跳转小程序观看激励广告后下载 系统源码 实现流量变现赚取广告收益 演示Web H5跳转小程序观看激励广告后下载程序源码,实现流量变现赚取广告收益 和资源变现差不多,就是让用户观看广告获取想要的资源或者其他东西 用户看完广告你就会获取利润,从而进行流量变现 8660bce851.png图片 8660bce920.png图片 Web/H5跳转小程序观看激励广告后下载 系统源码 实现流量变现赚取广告收益 下载地址:https://pan.quark.cn/s/62aeead175fc 提取码: Web/H5跳转小程序观看激励广告后下载 系统源码 实现流量变现赚取广告收益 下载地址:https://pan.baidu.com/s/1KPV6vCls7kjYKXWdK1qnKA?pwd=v501 提取码:v501 -
多平台短视频解析水印 v3.0 程序源码 多平台视频解析水印 v3.0 程序源码与视频解析工具 当下已能对4大主流平台的视频或图文进行解析: 抖音 – 视频、图文 快手 – 视频、图集 小红书 – 视频、图文(自动转换为JPG格式) B站 – 视频(支持BV号、完整链接) 核心功能如下: 无水印视频下载 – 自动去除平台添加的水印 批量图片下载 – 图文自动保存文本与图片 图片格式转换 – WebP/PNG自动转换为JPG 下载进度展示 – 实时呈现下载速度和进度 文件管理 – 支持打开目录、删除文件 界面特色: 窗口比例为16:9,最小化尺寸1024×576 采用无边框设计,可自定义标题栏 左侧为菜单,右侧是内容区的布局 具备启动闪屏与进度条加载效果 现代卡片式UI,带有阴影效果 下载规则: 目录命名:[平台标识]+6字符标题 视频文件:[平台标识]视频文件.mp4 文本文件:[平台标识]文本内容.txt 图片文件:1.jpg、2.jpg、3.jpg… 自动去重:若文件已存在则跳过下载 使用方法: 步骤1:获取分享链接 在支持的平台APP里,点击分享按钮,复制分享链接 步骤2:粘贴解析 打开软件,在输入框右键粘贴链接,点击“开始解析”按钮 步骤3:下载内容 解析成功后,点击“下载视频”或“下载全部图片”按钮 步骤4:查看文件 在“下载管理”页面可查看进度,完成后点击“打开”按钮查看文件 注意事项: 支持的链接格式: 完整链接(https://…) 短链接(各平台短链接) 分享文本(包含链接的完整文本) 文件路径限制处理: 自动应对Windows 260字符路径限制 文件名过长时自动截断 特殊字符自动替换为下划线 下载设置: 可自定义下载目录 支持自动下载(解析后即刻下载) 下载记录持久保存 特殊说明: B站支持BV号直接解析 默认下载1080P清晰度 DASH格式暂不支持(需音视频合并) 45e4ece836.jpeg图片 隐藏内容,请前往内页查看详情 -
工商年报申报系统源码 个体工商户年报注销H5搭建源码 此程序花费四百购入,各项功能均已测试,可正常使用,附带前端 uniapp 未编译的源码。 该程序采用 thinkphp 框架,使用 php 语言编写,前后端未分离,只需将编译好的源码放置于后端目录即可。 支付对接的是微信官方 v3 接口。 源码中的短信通知功能仅在注册和登录环节可用,若需下单通知等功能,可进行二次开发。 搭建教程如下: 环境要求:PHP8.0 及以上版本,MySQL 5.7。 将前端源码导入 HBuilder X 工具,修改 appid 和.env.development 中的请求域名,替换为自己的域名后,选择打包发行网站。将打包好的文件存至网站根目录/public/mobile 下。 若不想进行打包操作,可直接在源码根目录/public/mobile/assets/index.6bbd7918.js 文件中搜索“canyin.dkewl.com”,并替换为你的域名。 将修改后的后端源码上传至网站根目录。 设置运行目录为 public,并配置 thinkphp 伪静态。 上传并导入数据库源码。 网站前台访问地址:域名/mobile 网站后台访问地址:域名/admin 账号:admin 密码:123456 若需要小程序端,可用 h5 进行封装。 b847add655.png图片 隐藏内容,请前往内页查看详情 -
晓翼易支付 PHP API 对接教程:支付退款代付全攻略 晓翼易支付全API对接实操教程:复制即用不踩坑,支付/退款/代付一套搞定 做PHP开发这么久,真心觉得找个好用的支付工具太难了——要么API文档写得云里雾里,示例代码跑起来一堆报错;要么只支持基础支付,退款、代付还得额外对接别的平台;更坑的是有些工具签名逻辑复杂,抠半天代码还验签失败。 mk2lq7ea.png图片 我们字节曜博客一直用晓翼易支付,从对接到底层稳定度都亲测过,现在把全套API的实操教程整理出来,涵盖统一下单、订单查询、退款、代付所有核心功能,每个接口都附可直接复制的PHP代码,替换密钥就能跑,新手半小时也能搞定,分享给有需要的朋友~ 笔者是在竞赛室偷偷写的教程,撰写不易,如有在所难免,文档在此:文档,以文档为准 一、对接前必做:3分钟搞定基础配置(所有API通用) 不管对接哪个接口,先把基础环境和密钥配置好,这步走对了,后面能少踩80%的坑: 1. 先拿核心信息(登录商户后台就能找到) 商户号(mch_id):你的专属标识,在「个人资料」→「基本信息」里一眼就能看到; RSA密钥对:去「API信息」→「密钥管理」生成,会拿到两个密钥——「商户私钥」和「平台公钥」,记好别搞混; 接口网关地址:固定是 https://pay.ziyeyao.com/api/v2,不用自己瞎找; 回调地址(notify_url):自己服务器上的接口地址,得能公网访问,还不能带参数,支付、退款、代付成功后平台会主动通知你。 2. 通用工具函数(必存!所有API都要靠它) 先把签名生成、验签、发送请求的通用函数写好,后续每个接口直接调用就行,不用重复写代码。 <?php /** * 晓翼易支付通用工具类 * 签名、请求、验签全靠它,直接复制用 */ class XyPayUtil { // 替换成你自己的商户私钥(PEM格式,带头尾) private static $privateKey = '-----BEGIN PRIVATE KEY----- 你的商户私钥内容,原样粘贴,别删任何字符 -----END PRIVATE KEY-----'; // 替换成你自己的平台公钥(PEM格式,带头尾) private static $publicKey = '-----BEGIN PUBLIC KEY----- 你的平台公钥内容,原样粘贴 -----END PUBLIC KEY-----'; /** * 生成SHA256WithRSA签名(对接核心,别改逻辑) * @param array $params 要签名的参数数组 * @return string 签名字符串 */ public static function createSign($params) { // 1. 参数按ASCII升序排序(必须这步,不然验签失败) ksort($params); // 2. 拼接成"key=value&key=value"格式 $signStr = http_build_query($params); // 3. 加载私钥 $privateKey = openssl_get_privatekey(self::$privateKey); if (!$privateKey) { throw new Exception("私钥加载失败!检查下私钥格式,是不是漏了头尾标识"); } // 4. 生成签名 openssl_sign($signStr, $sign, $privateKey, OPENSSL_ALGO_SHA256); openssl_free_key($privateKey); // 5. base64编码返回 return base64_encode($sign); } /** * 验证平台返回的签名(防止数据被篡改) * @param array $params 平台返回的参数(不含sign) * @param string $sign 平台返回的签名字符串 * @return bool 验证结果(true=通过) */ public static function verifySign($params, $sign) { ksort($params); $signStr = http_build_query($params); $publicKey = openssl_get_publickey(self::$publicKey); if (!$publicKey) { throw new Exception("公钥加载失败!检查公钥格式是否正确"); } $result = openssl_verify($signStr, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256); openssl_free_key($publicKey); return $result === 1; } /** * 发送POST请求(不用自己调curl,直接用) * @param string $url 接口地址 * @param array $params 请求参数 * @return array 解析后的JSON数据 */ public static function postRequest($url, $params) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 测试环境可以关闭SSL校验,生产环境一定要打开! curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $response = curl_exec($ch); if (curl_errno($ch)) { throw new Exception("请求失败:" . curl_error($ch)); } curl_close($ch); // 解析返回的JSON $result = json_decode($response, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new Exception("返回数据格式错了:" . $response); } return $result; } } ?>重点提醒: 私钥和公钥一定要原样粘贴,别删头尾的-----BEGIN PRIVATE KEY-----这些标识,也别多空格; 生产环境记得把CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST改成true,更安全; 密钥别直接写在代码里,生产环境建议存在单独的配置文件,权限设为只读,防止泄露。 二、核心API对接教程(PHP代码直接复制用) 1. 统一下单API——最常用!创建支付订单 不管是卖会员、卖产品,还是博客变现,第一步都是创建支付订单,这个接口会返回支付链接或二维码,用户付完钱会自动回调你的notify_url。 PHP调用代码(替换3处信息就能跑) <?php require_once 'XyPayUtil.php'; // 引入上面的工具类 // 1. 构造请求参数(按自己的业务改) $params = [ 'mch_id' => '123456', // 替换成你的商户号 'out_trade_no' => 'XY' . date('YmdHis') . rand(1000, 9999), // 订单号规则:前缀+时间戳+随机数,避免重复 'total_fee' => 0.01, // 金额(元),测试用0.01元,正式环境改实际金额 'body' => '字节曜博客VIP会员', // 商品描述,会显示在支付页面 'notify_url' => 'https://你的域名/pay/notify.php', // 支付成功后回调的地址 'timestamp' => time(), // 当前时间戳(秒),不用改 'pay_type' => 'wxpay' // 支付方式:wxpay=微信,alipay=支付宝,按需选 ]; // 2. 生成签名(不用动) $params['sign'] = XyPayUtil::createSign($params); // 3. 调用统一下单接口(固定地址,不用改) $url = 'https://pay.ziyeyao.com/api/v2/unified_order'; try { $result = XyPayUtil::postRequest($url, $params); // 4. 验证返回签名(防止数据被篡改) $sign = $result['sign']; unset($result['sign']); // 先移除sign字段再验签 if (!XyPayUtil::verifySign($result, $sign)) { die("签名验证失败!返回数据可能被篡改"); } // 5. 处理结果 if ($result['code'] == 0) { // 成功:拿到支付链接,可生成二维码让用户扫码 echo "订单创建成功!支付链接:" . $result['pay_url']; // 实际项目中可以跳转支付页面,或生成二维码 } else { die("订单创建失败:" . $result['msg']); } } catch (Exception $e) { die("调用失败:" . $e->getMessage()); } ?>成功返回示例(参考格式) { "code": 0, "msg": "success", "out_trade_no": "XY202601061530221234", "trade_no": "XYPay123456789", "pay_url": "https://pay.ziyeyao.com/pay?order=123456", "timestamp": 1735689622, "sign": "加密后的签名字符串" }2. 订单查询API——查支付状态(防止漏单) 有时候用户付了钱,但回调没收到,这时候就用这个接口主动查订单状态,避免漏单。 PHP调用代码 <?php require_once 'XyPayUtil.php'; // 1. 构造参数(只需要商户号和要查的订单号) $params = [ 'mch_id' => '123456', // 你的商户号 'out_trade_no' => 'XY202601061530221234', // 要查询的订单号(之前创建的) 'timestamp' => time() ]; // 2. 生成签名 $params['sign'] = XyPayUtil::createSign($params); // 3. 调用查询接口 $url = 'https://pay.ziyeyao.com/api/v2/order_query'; try { $result = XyPayUtil::postRequest($url, $params); // 验签 $sign = $result['sign']; unset($result['sign']); if (!XyPayUtil::verifySign($result, $sign)) { die("签名验证失败"); } // 处理结果 if ($result['code'] == 0) { echo "订单状态:" . $result['trade_state']; // SUCCESS=支付成功,NOTPAY=未支付 echo "支付金额:" . $result['total_fee'] . "元"; echo "支付时间:" . $result['pay_time']; } else { die("查询失败:" . $result['msg']); } } catch (Exception $e) { die("查询失败:" . $e->getMessage()); } ?>3. 退款申请API——订单退款(支持全额/部分退) 用户要退款、订单出错,都用这个接口,支持全额或部分退款,退款结果会回调你填的notify_url。 PHP调用代码 <?php require_once 'XyPayUtil.php'; // 1. 构造退款参数 $params = [ 'mch_id' => '123456', // 你的商户号 'out_trade_no' => 'XY202601061530221234', // 原支付订单号 'out_refund_no' => 'RF' . date('YmdHis') . rand(1000, 9999), // 退款单号,自己生成,唯一 'refund_fee' => 0.01, // 退款金额(不能超过原订单金额) 'refund_desc' => '用户主动申请退款', // 退款原因,会显示在后台 'notify_url' => 'https://你的域名/refund/notify.php', // 退款结果回调地址 'timestamp' => time() ]; // 2. 生成签名 $params['sign'] = XyPayUtil::createSign($params); // 3. 调用退款接口 $url = 'https://pay.ziyeyao.com/api/v2/refund'; try { $result = XyPayUtil::postRequest($url, $params); // 验签 $sign = $result['sign']; unset($result['sign']); if (!XyPayUtil::verifySign($result, $sign)) { die("签名验证失败"); } // 处理结果 if ($result['code'] == 0) { echo "退款申请提交成功!退款单号:" . $result['out_refund_no']; // 后续可以通过退款查询接口查进度 } else { die("退款申请失败:" . $result['msg']); } } catch (Exception $e) { die("退款调用失败:" . $e->getMessage()); } ?>4. 退款查询API——查退款进度(用户催单用) 用户问“退款什么时候到账”,用这个接口查状态,不用登录后台手动查。 PHP调用代码 <?php require_once 'XyPayUtil.php'; // 1. 构造参数 $params = [ 'mch_id' => '123456', // 你的商户号 'out_refund_no' => 'RF202601061535225678', // 要查的退款单号 'timestamp' => time() ]; // 2. 生成签名 $params['sign'] = XyPayUtil::createSign($params); // 3. 调用查询接口 $url = 'https://pay.ziyeyao.com/api/v2/refund_query'; try { $result = XyPayUtil::postRequest($url, $params); // 验签 $sign = $result['sign']; unset($result['sign']); if (!XyPayUtil::verifySign($result, $sign)) { die("签名验证失败"); } // 处理结果 if ($result['code'] == 0) { echo "退款状态:" . $result['refund_state']; // SUCCESS=到账,PROCESSING=处理中,FAIL=失败 echo "退款到账时间:" . $result['refund_time']; echo "退款金额:" . $result['refund_fee'] . "元"; } else { die("查询失败:" . $result['msg']); } } catch (Exception $e) { die("查询失败:" . $e->getMessage()); } ?>5. 代付申请API——给用户打款(佣金/提现用) 给合作方结佣金、用户提现,直接用这个接口打款到微信或支付宝,不用手动转账。 PHP调用代码 <?php require_once 'XyPayUtil.php'; // 1. 构造代付参数 $params = [ 'mch_id' => '123456', // 你的商户号 'out_transfer_no' => 'TF' . date('YmdHis') . rand(1000, 9999), // 代付单号,唯一 'payee_type' => 'wx', // 收款类型:wx=微信,alipay=支付宝 'payee_account' => '13800138000', // 收款账号(微信/支付宝手机号或账号) 'payee_name' => '张三', // 收款人姓名(必须和账号实名一致,否则打款失败) 'transfer_fee' => 1.00, // 代付金额(元) 'transfer_desc' => '字节曜博客佣金结算', // 备注,收款人能看到 'notify_url' => 'https://你的域名/transfer/notify.php', // 代付结果回调地址 'timestamp' => time() ]; // 2. 生成签名 $params['sign'] = XyPayUtil::createSign($params); // 3. 调用代付接口 $url = 'https://pay.ziyeyao.com/api/v2/transfer'; try { $result = XyPayUtil::postRequest($url, $params); // 验签 $sign = $result['sign']; unset($result['sign']); if (!XyPayUtil::verifySign($result, $sign)) { die("签名验证失败"); } // 处理结果 if ($result['code'] == 0) { echo "代付申请提交成功!代付单号:" . $result['out_transfer_no']; } else { die("代付申请失败:" . $result['msg']); } } catch (Exception $e) { die("代付调用失败:" . $e->getMessage()); } ?>6. 回调通知API——最容易踩坑!必看 支付、退款、代付成功后,平台会主动往你的notify_url发POST请求,这步处理不好会重复回调、漏单,分享我实测能用的通用回调代码: PHP回调处理代码(支付/退款/代付通用) <?php require_once 'XyPayUtil.php'; // 1. 获取平台回调的参数(兼容JSON和表单提交) $postData = file_get_contents('php://input'); $params = json_decode($postData, true); if (!$params) { $params = $_POST; // 表单格式兼容 } // 2. 验证签名(重中之重!防止伪造回调) try { $sign = $params['sign']; unset($params['sign']); // 移除sign再验签 if (!XyPayUtil::verifySign($params, $sign)) { die("sign error"); // 签名错,直接返回error } // 3. 按类型处理业务逻辑 $type = $params['type']; // trade=支付,refund=退款,transfer=代付 if ($type == 'trade') { // 支付成功:更新订单状态、开通会员等 $outTradeNo = $params['out_trade_no']; // 你的订单号 $tradeState = $params['trade_state']; if ($tradeState == 'SUCCESS') { // 这里写你的业务代码,比如: // update_order_status($outTradeNo, 'paid'); // 更新订单状态 // open_vip($outTradeNo); // 开通会员 echo "success"; // 必须返回纯字符串success,平台才停止回调 exit; } } elseif ($type == 'refund') { // 退款成功:更新退款订单状态 echo "success"; exit; } elseif ($type == 'transfer') { // 代付成功:更新代付订单状态 echo "success"; exit; } // 不管成功失败,都返回success避免重复回调 echo "success"; } catch (Exception $e) { die("fail"); } ?>回调踩坑提醒: 返回值必须是纯字符串success,不能带HTML、空格、换行,否则平台会每隔1分钟回调一次; 业务逻辑要加“幂等性处理”(比如判断订单是否已处理,避免重复开通会员); 回调接口别设超时,建议把业务逻辑异步处理(比如扔到消息队列),避免平台等待超时。 三、为啥推荐晓翼易支付?实测大半年的真心话 用过不少支付工具,晓翼易支付是我最省心的,尤其适合个人开发者和小团队: 代码真的能直接用!上面的代码我博客一直在用,替换商户号和密钥就能跑,不用自己抠签名、调curl; 功能全还稳定:支付、退款、代付一套接口全cover,不用对接多个平台;服务器是集群部署的,高峰期也没掉过链子,接口响应快; 无门槛+费率低:个人开发者不用营业执照就能开通,新用户还有专属费率(比行业平均低0.2%),省不少手续费; 客服靠谱:遇到问题加客服微信,基本秒回,甚至能远程帮你看代码,比自己查文档省太多时间。 现在我身边不少做博客、小电商、工具类产品的开发者都在用,核心就是“对接简单、用着放心”。如果是PHP开发者,不管是个人变现还是小团队业务,直接冲就行——半小时搞定对接,剩下的时间专心做核心业务,不比抠支付接口香? 最后总结几个实操重点 所有接口的核心是“签名和验签”,通用工具类别改逻辑,只换自己的密钥; 订单号、退款单号、代付单号必须唯一,按“前缀+时间戳+随机数”生成最稳妥; 回调一定要返回纯字符串success,业务逻辑加幂等性处理; 生产环境记得打开SSL校验,密钥别泄露,最好存在配置文件里。 如果对接中遇到问题,直接加我QQ2273962061,能远程帮你看一眼,比自己瞎琢磨省太多时间~ -
PyQt5信号与槽原理:从基础到进阶(附参数传递代码) 第11篇:PyQt5信号与槽原理:从基础到进阶(完整代码) 哈喽~ 欢迎来到PyQt5系列的第11篇!进入阶段三,我们将聚焦PyQt5的核心交互机制——信号与槽(Signal & Slot)。前两阶段我们已经用信号与槽实现了按钮点击、控件状态变化等基础交互,但很多同学可能只知其然不知其所以然。这一章我们从原理入手,深入讲解信号与槽的本质、多种绑定方式、参数传递、信号断开等进阶用法,帮你彻底掌握PyQt5交互的核心逻辑! mk237ph4.png图片 一、先搞懂:信号与槽的核心概念(事件驱动模型) 在学习进阶用法前,必须先明确信号与槽的本质,这是理解所有复杂交互的基础: 1. 核心定义 信号(Signal):控件的某个动作或状态变化(如按钮点击clicked、输入框内容变化textChanged、窗口关闭close),是“事件的触发者”; 槽(Slot):信号触发后执行的函数/方法(如按钮点击后执行on_btn_click),是“事件的响应者”; 绑定(connect):将信号与槽关联起来,形成“触发动作→执行响应”的逻辑链,是PyQt5事件驱动模型的核心。 2. 事件驱动模型图解 用户操作(如点击按钮)→ 控件发出信号(clicked)→ 信号触发绑定的槽函数 → 执行槽函数逻辑(如修改界面、处理数据) 特点:无需主动轮询事件,信号触发时自动执行槽函数,效率高、逻辑清晰; 优势:解耦——控件只负责发出信号,不关心哪个槽函数响应;槽函数只负责处理逻辑,不关心哪个信号触发,灵活度极高。 二、信号与槽的基础绑定方式(3种常用) 前两阶段我们主要用了控件.信号.connect(槽函数)的基础绑定方式,这一章我们拓展另外两种常用绑定方式,并对比各自的适用场景。 1. 方式1:代码手动绑定(最常用,灵活度最高) 这是我们之前一直使用的方式,直接通过代码将信号与槽函数关联,支持所有场景。 效果大概就是这样只 mk22u820.png图片 mk22u9e1.png图片 mk22uafw.png图片 完整代码:基础手动绑定 import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel class BasicSignalSlot(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("信号与槽基础绑定演示") self.resize(300, 200) layout = QVBoxLayout() self.label = QLabel("未点击按钮", alignment=1) self.btn = QPushButton("点击触发信号") layout.addWidget(self.label) layout.addWidget(self.btn) self.setLayout(layout) # 手动绑定:按钮clicked信号 → on_btn_click槽函数 self.btn.clicked.connect(self.on_btn_click) def on_btn_click(self): """槽函数:响应按钮点击信号""" self.label.setText("按钮被点击!信号触发成功") if __name__ == "__main__": app = QApplication(sys.argv) window = BasicSignalSlot() window.show() sys.exit(app.exec_())2. 方式2:Qt Designer可视化绑定(快速开发) 对于复杂界面,用Qt Designer(PyQt5-tools自带)拖拽控件后,可直接在界面中绑定信号与槽,无需手动写connect代码,适合快速开发。 操作步骤(核心流程): 打开Qt Designer:终端输入designer(Windows)/ 手动查找designer.exe(macOS/Linux需手动查找路径); 新建QWidget项目,拖拽一个QPushButton和QLabel; 点击菜单栏「Edit → Edit Signals/Slots」,进入信号槽编辑模式; 鼠标点击按钮并拖拽到标签上,松开后弹出绑定窗口; 左侧选择按钮的clicked()信号,右侧选择标签的setText(QString)槽函数,点击「OK」完成绑定; 保存为.ui文件,用pyuic5 -o ui_main.py main.ui转换为Python代码,直接运行即可。 优点:可视化操作,无需记忆信号/槽函数名称;缺点:灵活性不足,复杂逻辑仍需手动写代码。 3. 方式3:装饰器绑定(PyQt5.4+支持,简洁优雅) 用@pyqtSlot()装饰器标记槽函数,无需显式调用connect,代码更简洁,适合小型项目。 完整代码:装饰器绑定 import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel from PyQt5.QtCore import pyqtSlot class DecoratorSignalSlot(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("装饰器绑定信号与槽") self.resize(300, 200) layout = QVBoxLayout() self.label = QLabel("未点击按钮", alignment=1) self.btn = QPushButton("点击触发信号") layout.addWidget(self.label) layout.addWidget(self.btn) self.setLayout(layout) # 装饰器绑定:无需显式connect,通过@pyqtSlot关联 self.btn.clicked.connect(self.on_btn_click) @pyqtSlot() # 标记该函数为槽函数 def on_btn_click(self): self.label.setText("装饰器绑定:信号触发成功") if __name__ == "__main__": app = QApplication(sys.argv) window = DecoratorSignalSlot() window.show() sys.exit(app.exec_())三种绑定方式对比 绑定方式优点缺点适用场景代码手动绑定灵活度最高,支持复杂逻辑需手动写connect代码大多数项目(推荐)Qt Designer绑定可视化操作,快速开发复杂逻辑不支持,灵活性差简单界面、快速原型装饰器绑定代码简洁,无需显式connect功能有限,不支持动态绑定/解绑小型项目、简单交互三、进阶用法1:带参数的信号与槽(核心难点) 很多场景下,信号需要传递参数给槽函数(如输入框内容变化时传递文本、滑块拖动时传递数值),这是信号与槽的核心进阶用法,需掌握3种参数传递方式。 1. 方式1:信号自带参数(直接接收) PyQt5很多内置信号自带参数(如textChanged(str)、valueChanged(int)),槽函数直接定义对应参数即可接收。 完整代码:接收信号自带参数 mk233i3x.png图片 import sys from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QVBoxLayout, QLabel class SignalWithParam(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("带参数的信号与槽(自带参数)") self.resize(350, 200) layout = QVBoxLayout() self.edit = QLineEdit() self.edit.setPlaceholderText("输入文本,实时显示") self.label = QLabel("输入的文本:", alignment=1) layout.addWidget(self.edit) layout.addWidget(self.label) self.setLayout(layout) # 输入框textChanged信号(自带str参数)→ on_text_change槽函数 self.edit.textChanged.connect(self.on_text_change) def on_text_change(self, text): """槽函数:接收信号自带的text参数""" self.label.setText(f"输入的文本:{text}") if __name__ == "__main__": app = QApplication(sys.argv) window = SignalWithParam() window.show() sys.exit(app.exec_())2. 方式2:lambda表达式传递自定义参数 当需要给槽函数传递自定义参数(而非信号自带参数)时,用lambda表达式作为中间桥梁,灵活传递多参数。 完整代码:lambda传递自定义参数 import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QLabel class LambdaParamSignalSlot(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("lambda传递自定义参数") self.resize(400, 200) layout = QHBoxLayout() self.label = QLabel("未点击任何按钮", alignment=1) # 创建3个按钮,传递不同参数 btn1 = QPushButton("按钮1") btn2 = QPushButton("按钮2") btn3 = QPushButton("按钮3") # lambda传递自定义参数:信号触发时,将参数传递给槽函数 btn1.clicked.connect(lambda: self.on_btn_click(1, "按钮1被点击")) btn2.clicked.connect(lambda: self.on_btn_click(2, "按钮2被点击")) btn3.clicked.connect(lambda: self.on_btn_click(3, "按钮3被点击")) layout.addWidget(btn1) layout.addWidget(btn2) layout.addWidget(btn3) layout.addWidget(self.label) self.setLayout(layout) def on_btn_click(self, btn_id, msg): """槽函数:接收自定义参数(按钮ID和提示信息)""" self.label.setText(f"ID:{btn_id} | {msg}") if __name__ == "__main__": app = QApplication(sys.argv) window = LambdaParamSignalSlot() window.show() sys.exit(app.exec_())3. 方式3:functools.partial传递参数(多参数更优雅) 当需要传递多个参数且逻辑复杂时,用functools.partial比lambda更优雅,支持默认参数、关键字参数。 完整代码:partial传递参数 import sys from functools import partial from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel class PartialParamSignalSlot(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("partial传递多参数") self.resize(350, 250) layout = QVBoxLayout() self.label = QLabel("未点击按钮", alignment=1) # 创建按钮,用partial传递多参数(按钮ID、名称、颜色) btn1 = QPushButton("红色按钮") btn2 = QPushButton("蓝色按钮") btn3 = QPushButton("绿色按钮") # partial传递参数:第一个参数是槽函数,后续是自定义参数 btn1.clicked.connect(partial(self.on_btn_click, 1, "红色按钮", "#e74c3c")) btn2.clicked.connect(partial(self.on_btn_click, 2, "蓝色按钮", "#3498db")) btn3.clicked.connect(partial(self.on_btn_click, 3, "绿色按钮", "#2ecc71")) layout.addWidget(btn1) layout.addWidget(btn2) layout.addWidget(btn3) layout.addWidget(self.label) self.setLayout(layout) def on_btn_click(self, btn_id, btn_name, color): """槽函数:接收多个自定义参数""" self.label.setText(f"ID:{btn_id} | 选中:{btn_name}") self.label.setStyleSheet(f"color: {color}; font-size: 16px;") if __name__ == "__main__": app = QApplication(sys.argv) window = PartialParamSignalSlot() window.show() sys.exit(app.exec_())四、进阶用法2:信号与槽的断开(disconnect) 有时需要动态解除信号与槽的绑定(如按钮禁用时停止响应点击),用disconnect()方法实现,支持3种断开方式。 完整代码:信号与槽的断开 import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel class DisconnectSignalSlot(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("信号与槽的断开(disconnect)") self.resize(350, 250) layout = QVBoxLayout() self.label = QLabel("状态:未绑定信号", alignment=1) self.bind_btn = QPushButton("绑定信号") self.unbind_btn = QPushButton("断开信号") self.test_btn = QPushButton("测试信号(点击无响应)") # 绑定“绑定/断开”按钮的信号 self.bind_btn.clicked.connect(self.on_bind) self.unbind_btn.clicked.connect(self.on_unbind) layout.addWidget(self.label) layout.addWidget(self.bind_btn) layout.addWidget(self.unbind_btn) layout.addWidget(self.test_btn) self.setLayout(layout) # 记录信号是否绑定 self.is_bound = False def on_bind(self): """绑定信号""" if not self.is_bound: self.test_btn.clicked.connect(self.on_test_click) self.is_bound = True self.label.setText("状态:信号已绑定(点击测试按钮有响应)") self.test_btn.setText("测试信号(点击有响应)") def on_unbind(self): """断开信号""" if self.is_bound: self.test_btn.clicked.disconnect(self.on_test_click) self.is_bound = False self.label.setText("状态:信号已断开(点击测试按钮无响应)") self.test_btn.setText("测试信号(点击无响应)") def on_test_click(self): """测试信号的槽函数""" self.label.setText("测试信号触发成功!") if __name__ == "__main__": app = QApplication(sys.argv) window = DisconnectSignalSlot() window.show() sys.exit(app.exec_())三种断开方式说明 控件.信号.disconnect(槽函数):断开指定信号与指定槽函数的绑定(最常用); 控件.信号.disconnect():断开该信号的所有绑定槽函数; 控件.disconnect():断开该控件的所有信号与槽的绑定(慎用,可能误删必要绑定)。 五、进阶用法3:同一信号绑定多个槽函数 一个信号可以同时绑定多个槽函数,信号触发时,槽函数会按绑定顺序依次执行,适合复杂逻辑拆分。 完整代码:同一信号绑定多槽函数 import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel class MultiSlotSignal(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("同一信号绑定多个槽函数") self.resize(350, 250) layout = QVBoxLayout() self.label1 = QLabel("槽函数1:未执行", alignment=1) self.label2 = QLabel("槽函数2:未执行", alignment=1) self.label3 = QLabel("槽函数3:未执行", alignment=1) self.btn = QPushButton("点击触发所有槽函数") # 同一信号(btn.clicked)绑定3个槽函数 self.btn.clicked.connect(self.slot1) self.btn.clicked.connect(self.slot2) self.btn.clicked.connect(self.slot3) layout.addWidget(self.label1) layout.addWidget(self.label2) layout.addWidget(self.label3) layout.addWidget(self.btn) self.setLayout(layout) def slot1(self): self.label1.setText("槽函数1:执行成功(顺序1)") def slot2(self): self.label2.setText("槽函数2:执行成功(顺序2)") def slot3(self): self.label3.setText("槽函数3:执行成功(顺序3)") if __name__ == "__main__": app = QApplication(sys.argv) window = MultiSlotSignal() window.show() sys.exit(app.exec_())六、信号与槽的核心原理补充 1. 信号与槽的匹配规则 信号无参数 → 槽函数可无参数,或有默认参数; 信号有参数 → 槽函数必须接收所有参数(或用*args接收任意参数); 槽函数参数不能多于信号参数(否则会报错)。 2. 常用内置信号(高频汇总) 控件常用信号信号含义自带参数QPushButtonclicked()按钮点击无QLineEdittextChanged(str)文本内容变化str(当前文本)QSlidervalueChanged(int)滑块值变化int(当前值)QCheckBoxstateChanged(int)选中状态变化int(2=选中,0=未选中)QComboBoxcurrentIndexChanged(int)选中索引变化int(当前索引)QTableWidgetitemClicked(QTableWidgetItem)单元格点击QTableWidgetItem(单元格对象)七、常见问题排查 1. 信号绑定后槽函数不执行 问题原因1:信号/槽函数名称写错(如clicked写成click,on_btn_click写成on_btn_click1); 问题原因2:控件实例名错误(如self.btn写成btn,未绑定到当前对象); 问题原因3:槽函数参数不匹配(信号有参数但槽函数未接收); 问题原因4:控件被禁用(setDisabled(True)),无法发出信号。 2. 传递参数时报错(TypeError) 问题原因1:lambda表达式语法错误(如参数传递格式错误); 问题原因2:partial传递的参数数量与槽函数不匹配; 问题原因3:信号自带参数与自定义参数冲突(如同时接收信号参数和自定义参数,需用lambda整合)。 3. 断开信号时报错(RuntimeError) 问题原因1:信号未绑定该槽函数,强行断开; 问题原因2:同一信号绑定多次同一槽函数,断开时只需要断开一次; 解决方案:断开前先判断是否绑定(如用is_bound标记)。 总结 核心本质:信号与槽是PyQt5事件驱动模型的核心,实现“触发动作→响应逻辑”的解耦关联; 绑定方式:代码手动绑定(推荐)、Qt Designer可视化绑定(快速开发)、装饰器绑定(简洁); 进阶重点:带参数的信号与槽(lambda/partial传递参数)、信号断开、多槽函数绑定; 关键规则:槽函数参数数量不能多于信号参数,参数类型需匹配; 下一章我们将学习自定义信号——当内置信号无法满足需求时,如何自己定义信号并传递任意参数,实现更复杂的交互逻辑。 如果在实操中遇到信号与槽绑定、参数传递的问题,或者想了解某个复杂场景的信号槽用法,欢迎在评论区留言讨论~ -
PHP开源广告投放系统源码 - 短链接生成+广告效果跟踪工具 PHP开源广告投放系统源码:精准跟踪+高效管理的广告解决方案 给大家分享一款专为广告资源管理打造的开源工具——PHP开源广告投放系统源码!基于FastAdmin框架+MySQL数据库构建,聚焦广告投放全流程需求,集成短链接生成、效果跟踪、模板选择等核心功能,支持个性化定制与二次开发,免费开源且部署简单,妥妥的中小型广告主、个人开发者及需要管理广告资源的用户的实用选择! mk22ax54.png图片 一、核心功能模块:覆盖广告投放全需求 1. 短链接生成与管理:便捷传播无压力 支持长URL转短链接,系统自动生成唯一标识符,建立长URL与短码的关联映射,形成简洁易记的短链接。 短链接访问时自动重定向至目标页面,避免冗长URL带来的传播不便,适配微博、短信等字符限制场景。 支持自定义短链接后缀,满足个性化分享需求,便于品牌化传播或记忆。 2. 广告效果精准跟踪:数据驱动优化投放 全面记录广告页面访问数据,包括访问次数(PV)、独立访客数(UV)、访问来源、地理位置等关键信息。 生成详细数据分析报告,帮助用户直观了解广告投放效果,精准把握用户行为与偏好,为优化投放策略提供数据支撑。 3. 跳转页面与自定义功能:灵活适配场景 提供多套预设跳转页面模板,用户可根据广告风格、行业属性选择适配模板,无需单独设计页面。 支持个性化定制,除自定义短链接后缀外,可根据业务需求二次开发扩展功能,适配不同广告投放场景。 二、核心特色:广告管理系统的差异化优势 提升传播效率:短链接简洁易记、占用字符少,便于分享传播,有效降低用户访问门槛,提升点击转化。 数据可视化:完整的访问统计与数据分析功能,告别盲目投放,通过数据洞察优化广告策略,提升投放效果。 架构灵活易扩展:前端基于HTML开发,后端依托FastAdmin框架,代码结构清晰,支持二次开发与功能扩展,适配个性化需求。 部署门槛低:无需复杂技术配置,按指引完成环境搭建与基础设置即可上线,非专业开发者也能快速上手。 三、环境与部署要点 1. 基础环境要求 服务器:支持Nginx或Apache作为Web服务器,需具备稳定网络连接。 技术环境:PHP 7.3及以上版本,MySQL 5.7及以上版本(用于存储数据)。 2. 核心部署步骤 解压文件:将源码文件解压至站点目录。 数据库配置:导入系统提供的.sql文件,修改application\database.php中的数据库连接信息(主机名、数据库名、用户名、密码等)。 配置伪静态:根据使用的Web服务器(Nginx/Apache)设置对应伪静态规则,确保系统正常访问。 登录后台:通过域名/adminn.php/index/login访问后台,默认账号admin、密码123456,登录后可关闭调试模式(修改application\config.php中app_debug为false)。 四、适用场景:谁适合用这款源码? 中小型广告主:管理自有广告资源,通过短链接传播与效果跟踪,提升广告投放效率与转化效果。 个人开发者:搭建个人广告投放平台,或作为FastAdmin框架实战案例,学习PHP广告系统开发逻辑。 需要精准跟踪的用户:用于活动推广、产品宣传等场景,通过数据统计优化传播策略,降低投放成本。 源码下载 广告投放系统 下载地址:https://pan.quark.cn/s/22e5127448d8 提取码: -
Go+Vue开源软件授权管理系统源码 - 支持在线离线授权 Go+Vue开源软件授权管理系统源码:安全可控的授权解决方案 给大家分享一款专为软件授权管理打造的开源资源——Go+Vue开源软件授权管理系统源码!基于Go 1.23+(Gin框架)+ Vue3+技术栈+MySQL 8+数据库构建,聚焦授权全生命周期管理,支持在线/离线授权与硬件指纹绑定,提供完整的客户、授权、API服务模块,开源免费且可扩展,妥妥的商业软件开发者、技术团队的授权管理利器! mk22222m.png图片 一、核心功能模块:覆盖授权管理全场景 1. 客户与授权核心管理 客户管理:支持完整的客户信息录入、存储与状态控制,可精准管理授权对应的客户主体,方便后续追溯与维护。 授权生成:支持在线、离线两种授权模式,适配不同使用场景;内置硬件指纹绑定机制,确保授权与设备绑定,防止非法复制或滥用,提升授权安全性。 授权生命周期管理:实时监控授权状态(生效、过期、禁用等),可对授权进行创建、修改、撤销等操作,全流程可视化管控。 2. 部署与服务支持 部署包生成:自动生成包含授权配置的部署包,简化软件部署流程,确保授权配置与软件无缝衔接。 RESTful API服务:提供授权验证、激活、心跳监控等接口,方便与各类软件系统集成,快速实现授权功能对接。 跨平台工具:配套多平台硬件信息获取工具,支持采集不同设备的硬件指纹,为绑定授权提供数据支撑。 3. 系统运维管理 管理员认证:内置管理员权限控制,保障系统后台操作安全,防止未授权访问。 监控仪表盘:直观展示授权数据、客户信息等核心指标,方便管理员实时掌握系统运行状态。 二、核心特色:授权管理系统的差异化优势 技术栈稳定高效:后端基于Go语言Gin框架,性能强劲、并发处理能力强;前端采用Vue3+现代化UI组件,界面流畅、操作直观,架构清晰易维护。 授权安全可控:通过硬件指纹绑定机制,实现授权与设备强关联,搭配在线/离线双授权模式,兼顾灵活性与安全性,有效防范授权破解、非法传播。 扩展性强:作为开源项目,代码结构模块化,支持二次开发,可根据业务需求新增授权规则、扩展客户管理维度或集成更多第三方服务。 部署灵活多样:支持Docker部署、单机部署或系统服务部署,适配不同运维场景,无需复杂配置即可快速上线。 学习价值突出:完整展示Go+Vue技术栈的协同开发逻辑,包含权限管理、安全机制、部署流程等核心模块,是学习现代Web应用架构的优质实战案例。 三、技术栈与部署要点 1. 核心技术栈 前端:Vue.js 3+ + 现代化UI组件 后端:Go 1.23+(Gin框架)、GORM(ORM工具)、Viper(配置管理)、Logrus(日志管理) 数据库:MySQL 8+ 配置格式:YAML文件 部署方式:Docker、单机部署、系统服务部署 2. 部署核心逻辑 先配置YAML格式的系统配置文件,完成数据库连接、服务端口等基础设置; 部署后端服务与前端界面,确保API接口正常通信; 利用跨平台工具采集硬件信息,配置授权规则后即可生成与分发授权。 四、适用场景:谁适合用这款源码? 商业软件开发者:为自研商业软件搭建专属授权管理系统,通过在线/离线授权控制软件使用范围,防范盗版。 技术团队/企业:需要对内部软件、付费工具进行授权管控,实现客户分级授权、授权到期提醒等功能。 Go/Vue学习者:作为Go+Vue全栈开发实战案例,学习Gin框架使用、前后端分离架构、权限管理与安全机制设计。 下载源码 源码 下载地址:https://pan.quark.cn/s/b20744efedd4 提取码: