书砚 插件开发指南 适配 v1.0.0 · PySide2 返回首页
书砚 / 插件开发指南 / 快速上手

快速上手

从零写一个书砚插件

本教程带你一步一步写出自己的第一个书砚扩展——「每日一句」侧边栏插件,会在侧边栏显示一句随机金句,鼓励码字的自己。

第 1 步:创建文件

在书砚安装目录的 plugins/ 下新建 daily_quote.py:

plugins/daily_quote.py
def get_info():
    return {
        'name': 'daily_quote',
        'display_name': '📅 每日一句',
        'version': '1.0.0',
        'author': '你的名字',
        'description': '侧边栏显示一句随机金句',
    }

def activate(api):
    # TODO: 在这里往侧边栏挂面板
    pass

保存后重启书砚,打开任意项目 → 编辑器页面右下角的控制台应该会打印 [插件] ✓ daily_quote 已激活。

第 2 步:往侧边栏挂面板

侧边栏是一个 QTabWidget(就是「👤 人物 / 📍 地点 / ⚔️ 物品 / ⚠️ 敏感词」那一排),用 api.add_side_tab(widget, label) 就能加自己的 tab:

import random
from PySide2.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton

QUOTES = [
    "写作是把自己摊开的过程。",
    "每天写一千字,坚持一年就是三十六万字。",
    "不要等灵感,要等习惯。",
    "写完比写好更重要。",
    "读者要的不是完美,是真实。",
]

def activate(api):
    # 1. 造面板
    panel = QWidget()
    layout = QVBoxLayout(panel)

    quote_label = QLabel("点下面按钮抽一句~")
    quote_label.setWordWrap(True)
    quote_label.setStyleSheet("font-size: 16px; padding: 12px; color: #555;")

    btn = QPushButton("🎲 换一句")

    layout.addWidget(quote_label)
    layout.addWidget(btn)
    layout.addStretch()

    # 2. 挂到侧边栏
    api.add_side_tab(panel, "📅 金句")

    # 3. 按钮逻辑(可以直接访问 api.db / api.project_id 等)
    def refresh():
        quote_label.setText(random.choice(QUOTES))

    btn.clicked.connect(refresh)
    refresh()  # 初始化先显示一句

重启书砚,侧边栏最后一个 tab 就是「📅 金句」了!

第 3 步:加一个工具栏按钮

工具栏按钮走 api.add_toolbar_btn(label, slot);如果想加在已有菜单里,用 api.add_menu(parent, label, slot, shortcut=None)。parent 可以是字符串("文件" / "编辑" / "工具"),也可以是 QMenu 对象。

def activate(api):
    # ... 上面的代码 ...

    # 加工具栏按钮
    api.add_toolbar_btn("🎲 金句", lambda: random.choice(QUOTES))

    # 加菜单项(带快捷键)
    api.add_menu("工具", "📅 显示金句",
                 lambda: api.toast(random.choice(QUOTES)),
                 shortcut="Ctrl+Shift+Q")

第 4 步:注册 Hook

Hook 让你的插件在某些事件发生时自动被通知,比如章节保存后、切换 tab 时、文本变化时。

def activate(api):
    # ...

    # 章节保存时弹个鼓励 toast
    def on_saved(editor):
        api.toast("本章保存啦!✅ " + random.choice(QUOTES))

    api.on_chapter_saved(on_saved)

第 5 步:读写数据库和配置

插件通过 api.db 拿到完整的 Database 实例,可以用 api.project_id 拿到当前项目 ID。

def activate(api):
    # 列出所有章节
    chapters = api.list_chapters()
    api.toast(f"当前项目共 {len(chapters)} 章")

    # 搜索某个关键词
    hits = api.search_text("张三")
    for h in hits[:3]:
        print(h['chapter_title'], h['context'])

    # 存插件自己的配置(key 要加前缀防冲突)
    api.set_config(f'daily_quote_fav_{api.project_id}',
                   "写作是把自己摊开的过程")

    # 读回来
    fav = api.get_config(f'daily_quote_fav_{api.project_id}', '')

第 6 步:打开章节 / 跳转位置

如果插件想主动让书砚打开某个章节或者跳到某个位置:

# 打开第 3 章
api.open_chapter(3)

# 打开第 3 章并跳到第 120 个字符
api.jump_to_offset(3, 120)

第 7 步:调用编辑器的 Markdown 方法

书砚的 NovelEditor 内置了 Markdown 快捷插入方法,插件可以直接调用:

def activate(api):
    # 当章节保存时自动在开头加一条分割线
    def on_saved(editor):
        # 编辑器可能为 None
        if not api.editor:
            return
        api.editor.md_hr()   # 插入 --- 分割线
        api.editor.md_h3()   # 插入 ###

    api.on_chapter_saved(on_saved)

第 8 步:禁用 / 卸载

如果你的插件提供了 deactivate(api) 函数,禁用或卸载时会被调用。适合做清理工作,比如从侧边栏移除自己的 tab:

def deactivate(api):
    # 侧边栏 tab 会自动随 widget 销毁而关闭
    # 这里主要清理 Hook、定时器等
    print("[插件] daily_quote 已停用")

禁用 / 卸载 / 刷新都可以在 工具 → 扩展管理 对话框里完成。

安全警告:插件有完整的 Python 执行权限,可以读写任意文件、发起网络请求、执行系统命令。只从可信来源安装扩展!