1111- [ 三、理解插件发现机制] ( #三理解插件发现机制 )
1212- [ 四、编写第一个插件(Hello World)] ( #四编写第一个插件hello-world )
1313- [ 五、核心 API 详解] ( #五核心-api-详解 )
14- - [ 六、实战:带 GUI 的完整插件示例] ( #六实战带-gui-的完整插件示例 )
15- - [ 七、VS Code 调试指南] ( #七vs-code-调试指南 )
16- - [ 八、常见问题与最佳实践] ( #八常见问题与最佳实践 )
14+ - [ 六、插件自定义配置系统] ( #六插件自定义配置系统 )
15+ - [ 七、实战:带 GUI 的完整插件示例] ( #七实战带-gui-的完整插件示例 )
16+ - [ 八、VS Code 调试指南] ( #八vs-code-调试指南 )
17+ - [ 九、常见问题与最佳实践] ( #九常见问题与最佳实践 )
1718
1819---
1920
@@ -344,6 +345,8 @@ class HelloPlugin(BasePlugin):
344345| ` self.log_level ` | ` LogLevel ` | 当前的日志级别 |
345346| ` self.plugin_icon ` | ` QIcon ` | 插件图标 |
346347| ` self.logger ` | ` loguru.Logger ` | ** 已绑定插件名称的日志器** (直接用!) |
348+ | ` self.other_info ` | ` OtherInfoBase \| None ` | 插件自定义配置对象 |
349+ | ` self.config_changed ` | ` pyqtSignal ` | 配置变化信号,参数 ` (name, value) ` |
347350
348351### 5.2 事件订阅 API
349352
@@ -523,6 +526,7 @@ class PluginInfo:
523526 log_level: LogLevel = " DEBUG" # 默认日志级别
524527 icon: QIcon | None = None # 图标
525528 log_config: LogConfig | None = None # 高级日志配置
529+ other_info: type[OtherInfoBase] | None = None # 👈 自定义配置类
526530```
527531
528532** WindowMode 含义:**
@@ -535,7 +539,265 @@ class PluginInfo:
535539
536540---
537541
538- ## 六、实战:带 GUI 的完整插件示例
542+ ## 六、插件自定义配置系统
543+
544+ 插件可以定义自己的配置项,这些配置会:
545+ - 自动生成 UI 控件(在设置对话框中)
546+ - 自动持久化到 ` data/plugin_data/<plugin_name>/config.json `
547+ - 支持配置变化事件通知
548+
549+ ### 6.1 配置类型一览
550+
551+ | 类型 | UI 控件 | 用途示例 |
552+ | ------| ---------| ----------|
553+ | ` BoolConfig ` | QCheckBox | 开关选项(启用/禁用功能) |
554+ | ` IntConfig ` | QSpinBox / QSlider | 整数设置(数量、超时时间) |
555+ | ` FloatConfig ` | QDoubleSpinBox | 浮点数设置(阈值、系数) |
556+ | ` ChoiceConfig ` | QComboBox | 下拉选择(主题、模式) |
557+ | ` TextConfig ` | QLineEdit | 文本输入(名称、路径、密码) |
558+ | ` ColorConfig ` | 颜色按钮 + QColorDialog | 颜色选择(主题颜色) |
559+ | ` FileConfig ` | QLineEdit + 文件对话框 | 文件路径选择 |
560+ | ` PathConfig ` | QLineEdit + 目录对话框 | 目录路径选择 |
561+ | ` LongTextConfig ` | QTextEdit | 多行文本(脚本、描述) |
562+ | ` RangeConfig ` | 两个 QSpinBox | 数值范围(最小/最大值) |
563+
564+ ### 6.2 定义配置类
565+
566+ 继承 ` OtherInfoBase ` 并声明配置字段:
567+
568+ ``` python
569+ from plugin_manager.config_types import (
570+ OtherInfoBase, BoolConfig, IntConfig, FloatConfig,
571+ ChoiceConfig, TextConfig, ColorConfig, FileConfig,
572+ PathConfig, LongTextConfig, RangeConfig,
573+ )
574+
575+ class MyPluginConfig (OtherInfoBase ):
576+ """ 我的插件配置"""
577+
578+ # ── 基础类型 ─────────────────────────
579+ enable_auto_save = BoolConfig(
580+ default = True ,
581+ label = " 自动保存" ,
582+ description = " 游戏结束后自动保存录像" ,
583+ )
584+
585+ max_records = IntConfig(
586+ default = 100 ,
587+ label = " 最大记录数" ,
588+ min_value = 10 ,
589+ max_value = 10000 ,
590+ step = 10 ,
591+ )
592+
593+ min_rtime = FloatConfig(
594+ default = 0.0 ,
595+ label = " 最小用时筛选" ,
596+ min_value = 0.0 ,
597+ max_value = 999.0 ,
598+ decimals = 2 ,
599+ )
600+
601+ theme = ChoiceConfig(
602+ default = " dark" ,
603+ label = " 主题" ,
604+ choices = [
605+ (" light" , " 明亮" ),
606+ (" dark" , " 暗黑" ),
607+ (" auto" , " 跟随系统" ),
608+ ],
609+ )
610+
611+ player_name = TextConfig(
612+ default = " " ,
613+ label = " 玩家名称" ,
614+ placeholder = " 输入名称..." ,
615+ )
616+
617+ api_token = TextConfig(
618+ default = " " ,
619+ label = " API Token" ,
620+ password = True , # 密码模式
621+ placeholder = " 输入密钥..." ,
622+ )
623+
624+ # ── 高级类型 ─────────────────────────
625+ theme_color = ColorConfig(
626+ default = " #1976d2" ,
627+ label = " 主题颜色" ,
628+ )
629+
630+ export_file = FileConfig(
631+ default = " " ,
632+ label = " 导出文件" ,
633+ filter = " JSON (*.json)" , # 文件过滤器
634+ save_mode = True , # 保存文件模式
635+ )
636+
637+ log_directory = PathConfig(
638+ default = " " ,
639+ label = " 日志目录" ,
640+ )
641+
642+ description = LongTextConfig(
643+ default = " " ,
644+ label = " 描述" ,
645+ placeholder = " 输入描述..." ,
646+ max_height = 100 ,
647+ )
648+
649+ time_range = RangeConfig(
650+ default = (0 , 300 ),
651+ label = " 时间范围(秒)" ,
652+ min_value = 0 ,
653+ max_value = 999 ,
654+ )
655+ ```
656+
657+ ### 6.3 绑定配置到插件
658+
659+ 在 ` PluginInfo ` 中通过 ` other_info ` 属性绑定:
660+
661+ ``` python
662+ class MyPlugin (BasePlugin ):
663+
664+ @ classmethod
665+ def plugin_info (cls ) -> PluginInfo:
666+ return PluginInfo(
667+ name = " my_plugin" ,
668+ version = " 1.0.0" ,
669+ description = " 我的插件" ,
670+ other_info = MyPluginConfig, # 👈 绑定配置类
671+ )
672+ ```
673+
674+ ### 6.4 访问配置值
675+
676+ ``` python
677+ class MyPlugin (BasePlugin ):
678+
679+ def on_initialized (self ):
680+ # 访问配置值
681+ if self .other_info:
682+ max_records = self .other_info.max_records
683+ theme = self .other_info.theme
684+ self .logger.info(f " 配置: max_records= { max_records} , theme= { theme} " )
685+
686+ def _handle_event (self , event ):
687+ # 使用配置
688+ if self .other_info and self .other_info.enable_auto_save:
689+ self ._save_record(event)
690+ ```
691+
692+ ### 6.5 监听配置变化
693+
694+ ``` python
695+ class MyPlugin (BasePlugin ):
696+
697+ def on_initialized (self ):
698+ # 连接配置变化信号
699+ self .config_changed.connect(self ._on_config_changed)
700+
701+ def _on_config_changed (self , name : str , value : Any):
702+ """ 配置变化时调用(在主线程执行)"""
703+ self .logger.info(f " 配置变化: { name} = { value} " )
704+
705+ if name == " theme" :
706+ self ._apply_theme(value)
707+ elif name == " max_records" :
708+ self ._resize_buffer(value)
709+ ```
710+
711+ ### 6.6 配置相关属性和方法
712+
713+ | 属性/方法 | 说明 |
714+ | -----------| ------|
715+ | ` self.other_info ` | 配置对象实例(可能为 None) |
716+ | ` self.config_changed ` | 配置变化信号,参数 ` (name, value) ` |
717+ | ` self.save_config() ` | 手动保存配置到文件 |
718+ | ` self.other_info.to_dict() ` | 导出配置为字典 |
719+ | ` self.other_info.from_dict(data) ` | 从字典加载配置 |
720+ | ` self.other_info.reset_to_defaults() ` | 重置为默认值 |
721+
722+ ### 6.7 配置存储位置
723+
724+ 配置自动保存到:
725+ ```
726+ data/plugin_data/<plugin_name>/config.json
727+ ```
728+
729+ 示例:
730+ ``` json
731+ {
732+ "enable_auto_save" : true ,
733+ "max_records" : 100 ,
734+ "theme" : " dark" ,
735+ "player_name" : " Player1" ,
736+ "theme_color" : " #1976d2"
737+ }
738+ ```
739+
740+ ### 6.8 自定义配置类型
741+
742+ 如果预定义的配置类型不满足需求,可以继承 ` BaseConfig ` 创建自定义类型:
743+
744+ ``` python
745+ from plugin_manager.config_types.base_config import BaseConfig
746+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QDial
747+ from PyQt5.QtCore import Qt
748+
749+ class DialConfig (BaseConfig[int ]):
750+ """ 旋钮配置 → QDial 控件"""
751+ widget_type = " dial"
752+
753+ def __init__ (
754+ self ,
755+ default : int = 0 ,
756+ label : str = " " ,
757+ min_value : int = 0 ,
758+ max_value : int = 100 ,
759+ ** kwargs ,
760+ ):
761+ super ().__init__ (default, label, ** kwargs)
762+ self .min_value = min_value
763+ self .max_value = max_value
764+
765+ def create_widget (self ):
766+ """ 创建自定义 UI 控件,返回 (控件, 获取值函数, 设置值函数)"""
767+ widget = QDial()
768+ widget.setRange(self .min_value, self .max_value)
769+ widget.setValue(int (self .default))
770+ widget.setNotchesVisible(True )
771+
772+ if self .description:
773+ widget.setToolTip(self .description)
774+
775+ return widget, widget.value, widget.setValue
776+
777+ def to_storage (self , value : int ) -> int :
778+ return int (value)
779+
780+ def from_storage (self , data ) -> int :
781+ return int (data)
782+
783+ # 使用自定义配置类型
784+ class MyConfig (OtherInfoBase ):
785+ volume = DialConfig(50 , " 音量" , min_value = 0 , max_value = 100 )
786+ sensitivity = DialConfig(5 , " 灵敏度" , min_value = 1 , max_value = 10 )
787+ ```
788+
789+ ** 自定义配置类型要点:**
790+
791+ | 方法/属性 | 说明 |
792+ | -----------| ------|
793+ | ` widget_type ` | 控件类型标识 |
794+ | ` create_widget() ` | 返回 ` (控件, getter, setter) ` 三元组 |
795+ | ` to_storage(value) ` | 将值转换为 JSON 可序列化格式 |
796+ | ` from_storage(data) ` | 从 JSON 数据恢复值 |
797+
798+ ---
799+
800+ ## 七、实战:带 GUI 的完整插件示例
539801
540802下面是一个更完整的示例——** 实时统计面板插件** ,展示计数器、表格等常见 UI 元素的用法:
541803
@@ -712,7 +974,7 @@ class StatsPlugin(BasePlugin):
712974```
713975---
714976
715- ## 七 、VS Code 调试指南
977+ ## 八 、VS Code 调试指南
716978
717979### 最简开发方式(推荐)
718980
@@ -749,7 +1011,7 @@ code <安装目录>
7491011
7501012---
7511013
752- ## 八 、常见问题与最佳实践
1014+ ## 九 、常见问题与最佳实践
7531015
7541016### Q1: 我的插件为什么没有被加载?
7551017
@@ -775,6 +1037,33 @@ code <安装目录>
7751037
7761038### Q3: 如何存储插件的持久化数据?
7771039
1040+ ** 方式一:使用配置系统(推荐)**
1041+
1042+ 定义配置类并绑定到插件,配置会自动保存和加载:
1043+
1044+ ``` python
1045+ class MyConfig (OtherInfoBase ):
1046+ setting1 = BoolConfig(True , " 设置1" )
1047+ setting2 = IntConfig(100 , " 设置2" )
1048+
1049+ class MyPlugin (BasePlugin ):
1050+ @ classmethod
1051+ def plugin_info (cls ) -> PluginInfo:
1052+ return PluginInfo(name = " my_plugin" , other_info = MyConfig)
1053+
1054+ def on_initialized (self ):
1055+ # 访问配置
1056+ if self .other_info:
1057+ value = self .other_info.setting1
1058+
1059+ def on_shutdown (self ):
1060+ # 配置在设置对话框确认时自动保存
1061+ # 也可以手动保存
1062+ self .save_config()
1063+ ```
1064+
1065+ ** 方式二:使用 self.data_dir**
1066+
7781067使用 ` self.data_dir ` —— 它指向 ` <exe_dir>/data/plugin_data/<PluginClassName>/ ` :
7791068
7801069``` python
@@ -911,9 +1200,15 @@ class ConsumerPlugin(BasePlugin):
9111200# ═══ 最小可行插件模板 ═══
9121201
9131202from plugin_manager import BasePlugin, PluginInfo, make_plugin_icon, WindowMode
1203+ from plugin_manager.config_types import OtherInfoBase, BoolConfig, IntConfig # 可选
9141204from shared_types.events import VideoSaveEvent # 按需导入
9151205from shared_types.services.my_service import MyService # 服务接口(可选)
9161206
1207+ # ═══ 配置类定义(可选) ═══
1208+ class MyConfig (OtherInfoBase ):
1209+ enable_feature = BoolConfig(True , " 启用功能" )
1210+ max_count = IntConfig(100 , " 最大数量" , min_value = 1 , max_value = 1000 )
1211+
9171212class MyPlugin (BasePlugin ):
9181213
9191214 @ classmethod
@@ -923,6 +1218,7 @@ class MyPlugin(BasePlugin):
9231218 description = " 插件描述" ,
9241219 window_mode = WindowMode.TAB , # TAB / DETACHED / CLOSED
9251220 icon = make_plugin_icon(" #1976D2" , " M" ),
1221+ other_info = MyConfig, # 👈 绑定配置类(可选)
9261222 )
9271223
9281224 def _setup_subscriptions (self ) -> None :
@@ -940,6 +1236,13 @@ class MyPlugin(BasePlugin):
9401236 # 获取服务代理(如果是服务使用者)
9411237 # if self.has_service(MyService):
9421238 # self._service = self.get_service_proxy(MyService)
1239+
1240+ # 连接配置变化信号(可选)
1241+ # self.config_changed.connect(self._on_config_changed)
1242+
1243+ # 访问配置值(可选)
1244+ # if self.other_info:
1245+ # max_count = self.other_info.max_count
9431246
9441247 def on_shutdown (self ): # 可选:资源清理
9451248 pass
0 commit comments