FF14 挖宝会计 - V4插件开发手册
FF14 挖宝会计 - 插件开发手册目录 插件是什么 插件必需声明 目录结构规范 插件生命周期 插件方法说明 插件可用的 API 数据库接口 主窗口公开 API WebSocket 服务
FF14 挖宝会计 - 插件开发手册
目录
-
插件是什么
-
插件必需声明
-
目录结构规范
-
插件生命周期
-
插件方法说明
-
插件可用的 API
-
数据库接口
-
主窗口公开 API
-
WebSocket 服务器
-
创建自定义窗口
-
跨插件协作
-
打包与分发
-
性能与稳定性
-
注意事项
-
完整示例
-
依赖清单
一、插件是什么?
一个插件就是一个独立的 .py 文件或文件夹,放在 %APPDATA%\FF14TreasureHunter\plugin\plugins\ 目录下(没有则新建)。程序启动时会自动扫描并加载其中的所有插件。
每个插件必须定义一个类,继承自 src.core.tool_plugin.ToolPlugin,并实现或声明一些必要的属性和方法。
目录结构规范
方式一:单文件插件
textplugin/plugins/ ├── door_chooser.py # 插件主文件 ├── another_plugin.py # 另一个插件
方式二:文件夹插件(推荐)
textplugin/plugins/ ├── door_chooser/ # 插件文件夹(与 plugin_id 同名) │ ├── main.py # 插件入口文件(必须) │ ├── helper.pyd # 私有依赖(可选) │ ├── icon.png # 资源文件(可选) │ └── overlay.html # 悬浮窗页面(可选) ├── summary_display/ │ └── main.py
配置文件目录
textplugin/ ├── plugins/ # 插件代码目录 ├── config/ # 插件配置目录 │ ├── door_chooser/ │ │ └── settings.json # 插件配置 │ └── summary_display/ │ └── settings.json └── pluginlist.json # 所有插件的元数据缓存
二、插件必需声明
在你的插件类中,必须直接声明以下类属性(不需要实例化),它们是插件的身份证:
| 属性 | 类型 | 说明 |
|---|---|---|
plugin_id |
str | 全局唯一标识,建议使用英文下划线命名,如 "door_chooser"。后续配置和更新都依赖此 ID。 |
plugin_name |
str | 显示在管理界面的名称,如 "选门工具"。 |
plugin_description |
str | 简要描述插件功能,会出现在管理界面和鼠标悬停提示中。 |
plugin_author |
str | 作者名。 |
plugin_version |
str | 版本号,如 "1.0.0"。用于更新检测。 |
update_url |
str | 可选。更新信息 JSON 文件的 URL。若不需要更新功能,留空即可。 |
示例
python
from src.core.tool_plugin import ToolPlugin
class DoorChooser(ToolPlugin):
plugin_id = "door_chooser"
plugin_name = "选门工具"
plugin_description = "发送指令随机选择左/中/右门"
plugin_author = "陆行鸟"
plugin_version = "1.0.0"
update_url = "" # 暂不提供更新
三、目录结构规范
插件文件夹结构
text
plugin/plugins/{plugin_id}/
├── main.py # 插件主文件(必须)
├── icon.png # 可选资源
├── helper.pyd # 私有依赖库
└── overlay.html # 悬浮窗页面
配置文件路径
插件配置自动存储在 plugin/config/{plugin_id}/settings.json,通过 self.load_config() 和 self.save_config() 操作。
四、插件生命周期
生命周期流程图
text
加载插件 → on_load() → 正常运行 → on_unload() → 卸载插件
↓
on_permission_changed() (权限变更时)
生命周期方法
| 方法 | 说明 |
|---|---|
on_load() |
插件被加载/启用时调用。在此创建窗口、启动服务、初始化资源。 |
on_unload() |
插件被卸载/禁用时调用。在此销毁窗口、释放资源、停止 WebSocket 服务器。 |
on_permission_changed(has_full_access: bool) |
当用户在管理界面切换本插件的数据库权限时调用。 |
示例
python
def on_load(self):
"""插件加载时初始化"""
config = self.load_config()
self.debug = config.get('debug', False)
self.start_ws_server(9765) # 启动 WebSocket
self.create_overlay() # 创建 UI
self.debug_log("插件已加载")
def on_unload(self):
"""插件卸载时清理资源"""
self.stop_ws_server() # 停止 WebSocket
self.destroy_overlay() # 销毁 UI
self.debug_log("插件已卸载")
def on_permission_changed(self, has_full_access: bool):
"""数据库权限变更时调用"""
self.debug_log(f"数据库权限已变更: {has_full_access}")
五、插件方法说明
1. 构造方法 __init__(self, manager, plugin_id)
插件实例化时调用,manager 是工具箱管理器,你可以通过它访问各种能力。通常在这里加载本插件的自定义配置。
def __init__(self, manager, plugin_id):
super().__init__(manager, plugin_id)
config = self.load_config() # 读取 plugin/config/{plugin_id}/settings.json
self.keyword = config.get('keyword', '!选门')
2. 日志匹配条件 get_match_conditions(self)
如果插件需要监听特定的日志事件,实现此方法,返回一个条件列表。每个条件是一个字典,可以包含 message_code、type_code、keyword(大小写不敏感子串匹配)。所有条件为"与"关系,列表内条件为"或"关系。
def get_match_conditions(self):
# 匹配 00|0038 的默语消息,且内容包含 self.keyword
return [{'message_code': '00', 'type_code': '0038', 'keyword': self.keyword}]
如果不需监听日志,可以返回空列表或直接删除该方法。
#更新支持列表 OR 语义:
只要事件满足任意一个条件,就视为匹配。列表中的多个条件,只要满足其中一个,就会触发插件的 on_log_event
实例:
def get_match_conditions(self): # 监听11(队伍), 03/38/39(角色名), 21(挖掘动作), 00+0839(任务流程)
return [
{'message_code': '11'},
{'message_code': '03'},
{'message_code': '38'},
{'message_code': '39'},
{'message_code': '21'},
{'message_code': '00', 'type_code': '0839'},
]
3. 事件处理 on_log_event(self, event)
当有日志匹配你的条件时,此方法被调用。event 是一个字典,包含以下字段:
-
timestamp:时间戳 -
message_code:消息代码 -
type_code:类型代码 -
event_type:事件类型 -
raw_log:原始日志行
def on_log_event(self, event):
# 随机选门
result = random.choice(["左", "中", "右"])
self.send_message(f"/e {result}门 <se.6>")
4. 数据库权限声明 get_full_db_permission_state(self) -> bool
返回 True 表示你的插件需要完整数据库访问权限(可读写所有数据)。管理界面会根据此返回值决定是否允许用户勾选"开放"权限。若返回 False,则插件只拥有隔离权限(只能读写自己的记录)。
def get_full_db_permission_state(self):
return False # 选门工具无需数据库
5. 设置界面 get_settings_widget(self) -> QWidget or None
如果插件需要自定义设置界面,实现此方法,返回一个 QWidget。用户可在管理界面点击"打开设置"来弹出这个窗口。没有设置界面则返回 None。
def get_settings_widget(self):
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton
widget = QWidget()
layout = QVBoxLayout(widget)
layout.addWidget(QLabel("关键词:"))
self.keyword_edit = QLineEdit(self.keyword)
layout.addWidget(self.keyword_edit)
save_btn = QPushButton("保存")
save_btn.clicked.connect(self.save_settings)
layout.addWidget(save_btn)
return widget
def save_settings(self):
self.keyword = self.keyword_edit.text()
self.save_config({'keyword': self.keyword})
6. 依赖声明 get_dependencies(self)
如果插件依赖其他插件,实现此方法返回依赖的插件 ID 列表。
python
def get_dependencies(self):
return ["data_provider", "overlay_manager"]
六、插件可用的 API
插件基类提供的便利方法(通过 self 调用)
| 方法 | 说明 |
|---|---|
self.send_message(text) |
通过鲶鱼精邮差发送聊天消息。text 可以是 /e 消息 或 /p 消息 等格式。 |
self.get_config_dir() |
返回本插件的配置目录路径(Path 对象)。 |
self.load_config() |
加载插件配置(settings.json),返回字典。 |
self.save_config(data) |
保存插件配置(字典)。 |
self.debug_log(msg) |
输出调试日志到 plugin/DEBUG.log(需 self.debug = True)。 |
self.plugin_db |
数据库访问代理。根据权限,可能是隔离的 PluginDB 或完整的 Database 实例。 |
self.main_window |
访问主窗口实例(只读属性)。 |
self.ws_server |
获取当前插件的 WebSocket 服务器实例(可能为 None)。 |
self.start_ws_server(port) |
为当前插件启动 WebSocket 服务器。 |
self.stop_ws_server() |
停止当前插件的 WebSocket 服务器。 |
self.get_plugin(plugin_id) |
获取另一个已加载的插件实例。 |
调试日志使用
python
def on_load(self):
config = self.load_config()
self.debug = config.get('debug', False) # 从配置读取是否启用调试
self.debug_log("插件加载完成")
七、数据库接口
插件通过 self.plugin_db 访问数据库。根据用户在管理界面设置的权限,该对象可能是:
-
受限模式:
PluginDB实例(默认) -
完全模式:原始
Database实例
1. 受限模式 (PluginDB)
受限模式下,插件可读取全部数据,但写入操作自动附加 plugin_id,且只能修改/删除自己创建的记录。
写入方法(仅操作自己的记录)
| 方法 | 说明 |
|---|---|
log_event(timestamp, event_type, raw_log=None, extra=None, run_label='') |
写入一条事件,自动附加当前 plugin_id。返回 event_id。 |
add_loot(event_id, item_name, quantity=1, map_name=None, vault_segment_id=None) |
记录一件物品(与指定事件关联)。 |
add_custom_record(event_id, record_name, value, vault_segment_id=None) |
添加一条自定义记录。 |
update_my_record(record_id, new_value) |
修改自己创建的自定义记录的值。 |
delete_my_record(record_id) |
删除自己创建的自定义记录。 |
读取方法(全部数据可读)
| 方法 | 说明 |
|---|---|
get_my_custom_records(since=None, run_label=None) |
获取本插件创建的所有自定义记录。 |
get_total_maps(run_label=None) |
总地图数。 |
get_total_magic_seals(run_label=None) |
总魔纹数。 |
get_loot_summary(run_label=None) |
物品汇总。 |
get_layer_summary(run_label=None) |
层级汇总。 |
get_custom_record_summary(run_label=None) |
所有自定义记录汇总。 |
get_run_summary(run_label) |
当前运行标签的盈亏汇总。 |
示例:记录自定义数据
python
def on_log_event(self, event):
# 创建一个事件
eid = self.plugin_db.log_event(
timestamp=event['timestamp'],
event_type='custom',
raw_log=event.get('raw_log'),
run_label=self.get_current_run_label()
)
# 添加自定义记录
self.plugin_db.add_custom_record(eid, "选门次数", 1)
# 读取自己插件的所有记录
records = self.plugin_db.get_my_custom_records()
for r in records:
print(r['record_name'], r['value'])
2. 完全模式 (Database)
当用户授予"开放"权限后,插件直接获得完整的 Database 实例,拥有与主程序相同的读写权限,无任何限制。请谨慎使用,避免破坏用户数据。
可用方法包括 log_event(不带自动 plugin_id)、直接调用 execute 执行自定义 SQL 等,但需自行保证数据安全。
3. 如何申请完整权限
在插件类中重写 get_full_db_permission_state 方法并返回 True:
def get_full_db_permission_state(self) -> bool:
return True
用户在管理界面即可将"数据权限"切换为"开放"。
八、主窗口公开 API
插件可通过 self.main_window 直接获取主程序的所有实时数据与状态,无需数据库查询。
| 方法 | 返回类型 | 说明 |
|---|---|---|
get_summary_data() |
dict | 当前全部汇总数据(地图数、魔纹数、系统金、总成本、总收入、盈亏、物品列表、层级列表) |
get_current_segment_data() |
dict | 当前分段(增量)数据 |
get_segment_snapshots() |
list | 所有历史分段快照 |
get_game_status() |
str | 游戏状态:"启动" 或 "等待游戏数据..." |
get_postnamazu_status() |
bool | 鲶鱼精邮差是否已连接 |
is_advanced_output_enabled() |
bool | 高级功能输出是否启用 |
is_prediction_enabled() |
bool | 预测功能是否启用 |
is_detail_output_enabled() |
bool | 详细信息通知是否启用 |
is_global_actions_enabled() |
bool | 全局高级功能是否启用 |
is_map_actions_enabled() |
bool | 地图高级功能是否启用 |
is_single_loot_enabled() |
bool | 单人物品获取模式是否启用 |
get_current_run_label() |
str | 当前运行标签(日期) |
get_item_map_short_name(item_name) |
str | 获取物品所属地图的简称(如 "G18") |
get_layer_map_short_name(layer_name) |
str | 获取层级所属地图的简称 |
示例
python
# 获取汇总数据
summary = self.main_window.get_summary_data()
print(f"地图数: {summary['total_maps']}, 盈亏: {summary['profit']}")
# 获取游戏状态
game_status = self.main_window.get_game_status()
# 获取物品所属地图
short_name = self.main_window.get_item_map_short_name("羊角笛")
# 获取当前运行标签
run_label = self.main_window.get_current_run_label()
九、WebSocket 服务器
启动 WebSocket 服务器
插件可以创建独立的 WebSocket 服务器,用于与本地网页前端通信。
python
def on_load(self):
# 启动 WebSocket 服务器,监听指定端口
self.start_ws_server(9765)
self.debug_log(f"WebSocket 服务器已启动,端口: 9765")
广播消息
python
def refresh_data(self):
summary = self.main_window.get_summary_data()
segment = self.main_window.get_current_segment_data()
if self.ws_server:
self.ws_server.broadcast({
"type": "dual_update",
"summary": summary,
"segment": segment,
"timestamp": datetime.now().isoformat()
})
停止 WebSocket 服务器
重要:卸载时必须停止 WebSocket 服务器以释放端口。
python
def on_unload(self):
self.stop_ws_server()
self.debug_log("WebSocket 服务器已停止")
工具箱管理器 WebSocket 方法(通过 self.manager)
| 方法 | 说明 |
|---|---|
create_ws_server(plugin_id, port) |
为插件创建独立端口的 WebSocket 服务器(自动停止旧服务) |
stop_ws_server(plugin_id) |
停止插件的 WebSocket 服务器并释放端口 |
十、创建自定义窗口
插件可以在事件触发时创建并显示独立的 QWidget 窗口。为确保主窗口不受影响,请遵循以下规则:
-
使用
show()而非exec_()显示窗口。 -
在窗口的
__init__中只构建 UI,不执行耗时操作。 -
如需加载数据,使用
QTimer.singleShot(0, self.load_data)延迟执行。
示例:非模态工具窗口
python
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
from PyQt5.QtCore import Qt, QTimer
class MyToolWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("我的工具")
self.setWindowFlags(Qt.Window) # 独立窗口
layout = QVBoxLayout(self)
layout.addWidget(QLabel("工具内容"))
# 延迟加载数据
QTimer.singleShot(0, self.load_data)
self.show()
def load_data(self):
# 执行耗时操作
pass
# 在插件事件中调用
def on_log_event(self, event):
self.window = MyToolWindow()
使用 Overlay Manager 创建悬浮窗
程序内置了 overlay_manager 插件,可用于创建可拖拽、半透明的悬浮窗。
def init_overlay(self):
import time
from pathlib import Path
# 等待 overlay_manager 就绪(最多3秒)
for _ in range(30):
mgr = self.get_plugin("overlay_manager")
if mgr and getattr(mgr, 'enabled', False):
self.overlay_manager = mgr
break
time.sleep(0.1)
else:
self.debug_log("overlay_manager 插件未找到或未启用")
return
# 检查窗口是否已被注册
existing_windows = self.overlay_manager._windows_config.get('windows', [])
if any(w['window_id'] == self.overlay_window_id for w in existing_windows):
self.debug_log("悬浮窗已存在,跳过注册")
return
plugin_dir = Path(__file__).resolve().parent
html_path = plugin_dir / "overlay.html"
if not html_path.exists():
self.debug_log(f"缺少 overlay.html: {html_path}")
return
self.overlay_manager.register_overlay(
window_id=self.overlay_window_id,
url=html_path.as_uri(),
width=800, height=600,
x=100, y=100,
draggable=True,
click_through=False,
auto_resize=True,
scrollable=True
)
self.debug_log(f"已请求创建悬浮窗: {self.overlay_window_id}")
如果希望保留“持续更新窗口属性”的能力
如果你以后需要通过插件更新窗口属性(比如修改默认大小),可以改为只更新 url,而不动已有的位置和大小:
# 更新已有窗口,但只修改 URL,保留位置和大小
for w in existing_windows:
if w['window_id'] == self.overlay_window_id:
w['url'] = html_path.as_uri()
self.overlay_manager.save_windows_config()
# 如果需要实时更新窗口,可调用宿主指令(但谨慎)
return
十一、跨插件协作
获取其他插件实例
通过 self.get_plugin(plugin_id) 获取另一个已加载的插件实例。
def on_load(self):
# 获取数据提供者插件
data_provider = self.get_plugin("data_provider")
if data_provider:
self.data = data_provider.get_data()
声明依赖
在插件类中实现 get_dependencies() 方法,确保依赖插件先加载。
def get_dependencies(self):
return ["data_provider", "overlay_manager"]
工具箱管理器方法(通过 self.manager)
| 方法 | 说明 |
|---|---|
get_plugin(plugin_id) |
获取另一个已加载的插件实例 |
save_plugin_state() |
将插件元数据(启用状态、权限)保存到 pluginlist.json |
delete_plugin(plugin_id) |
永久删除插件及其所有数据(文件、配置、记录) |
install_from_zip(zip_path) |
从 ZIP 文件导入插件包 |
download_file(url, dest) |
下载文件到指定路径 |
十二、打包与分发
ZIP 包格式
若要通过 ZIP 分发,ZIP 内必须包含 manifest.json,结构如下:
{
"plugins": [
{
"plugin_id": "door_chooser",
"version": "1.0.1",
"entry_file": "main.py",
"dependencies": []
}
]
}
ZIP 内其他文件将全部被解压到插件文件夹。
ZIP 包目录结构示例
textdoor_chooser_v1.0.1.zip ├── manifest.json ├── main.py ├── icon.png └── overlay.html
十三、性能与稳定性
执行限制
您的插件在主线程中执行,但为了避免阻塞 UI 和核心功能,插件事件处理被推迟到 Qt 事件队列的下一轮。这意味着即使您的 on_log_event 执行了耗时操作,主界面也不会立即卡死,但极长时间(>数秒)的阻塞仍会导致界面失去响应。
注意:任何执行超过 3 秒的插件都会被自动禁用。
最佳实践
-
耗时操作使用后台线程:
import threading
def on_log_event(self, event):
thread = threading.Thread(target=self.do_heavy_work)
thread.daemon = True
thread.start()
def do_heavy_work(self):
# 执行网络请求、文件 I/O 等耗时操作
result = self.fetch_data()
# 通过信号或 invokeMethod 更新 UI
-
避免在
on_log_event中使用无限循环 -
使用
self.debug_log()记录关键信息,结合plugin/DEBUG.log快速定位问题
调试建议
python
def on_load(self):
config = self.load_config()
self.debug = config.get('debug', False)
self.debug_log("插件加载完成,调试模式: {}".format(self.debug))
def on_log_event(self, event):
self.debug_log(f"收到事件: {event.get('raw_log', '')}")
# 插件逻辑...
十四、注意事项
-
唯一 ID 不可变:
plugin_id确定后不要随意更改,否则用户的配置和数据库记录会丢失关联。 -
谨慎申请完整数据库权限:如非必要,请使用隔离模式,确保用户数据安全。
-
设置文件格式:建议使用 JSON,通过
self.load_config()和self.save_config()操作。 -
卸载时务必释放资源:
-
调用
self.stop_ws_server()停止 WebSocket -
关闭所有自定义窗口
-
保存配置
-
-
隔离模式下,所有写入操作会自动附上
plugin_id,不会影响主程序或其他插件的数据。 -
读取操作不受限,但请勿缓存敏感用户信息。
-
如需执行复杂查询,可在完全权限下使用
self.plugin_db.execute("SELECT ...")(受限模式未暴露)。 -
数据库字段增删请在主程序更新时留意兼容性。
十五、完整示例
示例:选门工具
完整代码可在 plugin/plugins/door_chooser.py 找到。
import random
from src.core.tool_plugin import ToolPlugin
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton
class DoorChooser(ToolPlugin):
plugin_id = "door_chooser"
plugin_name = "选门工具"
plugin_description = "发送指令(默认 !选门),随机选择左/中/右门"
plugin_author = "陆行鸟"
plugin_version = "1.0.0"
update_url = ""
def __init__(self, manager, plugin_id):
super().__init__(manager, plugin_id)
config = self.load_config()
self.keyword = config.get('keyword', '!选门')
self.debug = config.get('debug', False)
def get_match_conditions(self):
return [{'message_code': '00', 'type_code': '0038', 'keyword': self.keyword}]
def on_log_event(self, event):
result = random.choice(["左", "中", "右"])
self.send_message(f"/e {result}门 <se.6>")
self.debug_log(f"选门结果: {result}")
def get_full_db_permission_state(self):
return False
def get_settings_widget(self):
widget = QWidget()
layout = QVBoxLayout(widget)
layout.addWidget(QLabel("触发关键词:"))
self.keyword_edit = QLineEdit(self.keyword)
layout.addWidget(self.keyword_edit)
save_btn = QPushButton("保存")
save_btn.clicked.connect(self.save_settings)
layout.addWidget(save_btn)
return widget
def save_settings(self):
self.keyword = self.keyword_edit.text()
self.save_config({'keyword': self.keyword, 'debug': self.debug})
def on_load(self):
self.debug_log("选门工具已加载")
def on_unload(self):
self.debug_log("选门工具已卸载")
示例:数据展示插件(使用 WebSocket)
python
import json
from datetime import datetime
from src.core.tool_plugin import ToolPlugin
from PyQt5.QtCore import QTimer
class SummaryDisplay(ToolPlugin):
plugin_id = "summary_display"
plugin_name = "数据展示"
plugin_description = "通过 WebSocket 推送挖宝数据到前端"
plugin_author = "陆行鸟"
plugin_version = "1.0.0"
update_url = ""
def on_load(self):
config = self.load_config()
self.refresh_interval = config.get('refresh_interval', 5)
self.start_ws_server(9765)
# 定时推送数据
self.timer = QTimer()
self.timer.timeout.connect(self.push_data)
self.timer.start(self.refresh_interval * 1000)
self.debug_log("数据展示插件已加载")
def on_unload(self):
if self.timer:
self.timer.stop()
self.stop_ws_server()
self.debug_log("数据展示插件已卸载")
def push_data(self):
if not self.ws_server:
return
summary = self.main_window.get_summary_data()
segment = self.main_window.get_current_segment_data()
self.ws_server.broadcast({
"type": "data_update",
"summary": summary,
"segment": segment,
"timestamp": datetime.now().isoformat()
})
def get_full_db_permission_state(self):
return False # 只读数据,不需要完整权限
十六、依赖清单
以下是程序打包时已包含的全部第三方依赖库,插件开发者可放心使用这些库而无需用户额外安装。若插件需要使用清单外的库,请务必在插件文档中明确说明,并提供安装指导。
核心依赖
| 库名 | 版本 (参考) | 用途 |
|---|---|---|
| PyQt5 | 5.15.x | 图形用户界面框架 |
| requests | 2.x | HTTP 网络请求(API 调用、版本检查、市场数据) |
| beautifulsoup4 | 4.x | HTML/XML 解析 |
| winotify | 1.x | Windows 原生 Toast 通知 |
| sqlite3 | (内置) | 本地数据库 |
| win32file | - | 网络管道(Deucalion 预测) |
Qt 子系统(由 PyQt5 附带)
| 模块 | 说明 |
|---|---|
| PyQt5.QtWidgets | 窗口、控件、布局 |
| PyQt5.QtCore | 信号槽、定时器、事件循环 |
| PyQt5.QtGui | 字体、图标、颜色 |
| PyQt5.QtNetwork | 网络功能 |
Python 标准库(无需额外安装)
| 库名 | 用途 |
|---|---|
| json | 配置文件读写、数据序列化 |
| threading | 日志监控后台线程 |
| queue | 事件队列 |
| hashlib | 日志行去重 |
| re | 正则表达式匹配 |
| time / datetime | 时间处理 |
| pathlib | 文件路径操作 |
| sqlite3 | 数据库操作 |
| importlib | 插件动态加载 |
| zipfile | 插件 ZIP 包导入 |
| tempfile | 临时文件 |
| shutil | 文件复制、目录删除 |
| os | 系统操作 |
| sys | 系统参数 |
| ctypes | Windows API 调用 |
| collections | 数据结构(deque、defaultdict) |
| urllib.parse | URL 编码 |
| typing | 类型注解 |
插件开发注意事项
-
优先使用清单内库:避免引入额外依赖,保证用户开箱即用。
-
如需特殊库:在插件文档中明确列出,并提供
.pyd文件或安装说明。 -
打包时不会自动包含新库:如果您为插件引入了
numpy等大型库,需要用户自行安装 Python 环境或由您提供预编译文件。
版本历史
| 版本 | 日期 | 说明 |
|---|---|---|
| 1.0 | - | 初始版本 |
| 2.0 | - | 新增主窗口 API、WebSocket、生命周期方法 |
| 2.1 | - | 新增 Overlay Manager 悬浮窗支持 |
| 3.0 | 2026-01-15 | 完整整合所有文档,重组织结构 |




