Siyuan API 修改属性引用文本不更新

本贴最后更新于 548 天前,其中的信息可能已经物是人非

1. 公式编号

首先感谢思源笔记对公式编号的支持,通过 y=x^2\tag{1-1}z=i+A\cdot x\tag{1-2}E=m\cdot C^2\tag{1-3}y=A \cdot sin(x)\tag{1-4},可以很轻松实现公式 1-1 至 1-4,并进行编号。

image.png

图 1:公式编号

2. 公式交叉引用

如果对每个公式属性进行了赋值(图 3),那么后续在任意地方使用 (( eq: 1-1 或者 [[eq:1-1 可按照图 2 格式引用公式 1-1,实现对公式的交叉引用。后续因其他需要对属性进行修改,引用文本也会自动更新。通过这种思路,实际上就可以实现所有图、表、公式的交叉引用了。如果通过编写插件,实现整个过程的全自动,思源笔记就可真正意义上用于科研笔记,或者进行正规教材/学术论文的编写了。

image.png

图 2:公式交叉引用

image.png

图 3:对每个公式属性进行定义

3. 问题

现在遇到的问题是:通过鼠标手动更改属性,所有公式引用编号会自动更新,但通过 Siyuan API 函数 setBlockAttrs 修改属性,引用文本并不更新(尽管属性已经更新了)。重启或者按 F5 刷新都不好使。目前只有通过鼠标挨个点击一遍公式才能实现更新,但这样徒增太多不必要的操作了。

@88250 请问这是不是 Siyuan 的一个 bug,还是在调用 setBlockAttrs 修改属性后,还需要调用什么函数,激活所有引用文本的更新?

4 问题重现

下面是拟实现 Siyuan 公式交叉引用的 python 代码。该代码实现了增减公式时公式编号自动变更问题,实现了公式属性更新和交叉引用。现在唯一的问题是,增减公式时,需要通过鼠标挨个点击一遍公式,才能实现所有公式引用文本的更新。

如果公式交叉引用问题解决了,图表的交叉引用也就一并解决了。


import json
import requests
import win32con
import win32gui
import win32api
import win32process
import win32com
import win32com.client
import time
import pyperclip
import os
import sys
import re

# 要求当前文档中含有公式,否则会出错。

TokenID: str = ""  # 自己的TokenID


class SiYunNoteBook:
    def __init__(self, TokenID) -> None:
        self.TokenID = TokenID

    def Command(self, url: str, data=None, indent=False):
        headers = {
            "Content-Type": "application/javascript",
            "Authorization": f"Token { self.TokenID }"
        }
        Result = requests.post(f"http://localhost:6806{ url }",
                               headers=headers, data=json.dumps(data))
        if indent:
            return (json.dumps(json.loads(Result.text), indent=True, ensure_ascii=False))
        else:
            return json.loads(Result.text)


    def pushMsg(self, Msg: str):
        return self.Command("/api/notification/pushMsg",
                            data={"msg": Msg, "timeout": 7000}
                            )

    def pushErrMsg(self, Msg: str):
        return self.Command("/api/notification/pushErrMsg",
                            data={"msg": Msg, "timeout": 7000}
                            )

        return self.Command("/api/file/getFile",
                            data={"path": file}
                            )

    def SQL_query(self, SQL: str):
        return self.Command("/api/query/sql",
                            data={"stmt": SQL})

    def setBlockAttrs(self, block_id: str, dict: dict):
        return self.Command("/api/attr/setBlockAttrs",
                            data={"id": block_id,
                                  "attrs": dict
                                  })

    def updateBlock(self, doc_id: str, data, dataType: str = "markdown"):
        return self.Command("/api/block/updateBlock",
                            data={"id": doc_id,
                                  "dataType": dataType,
                                  "data": data
                                  }
                            )

    def getDocumentIDbyBlock(self, block_id):
        result = self.SQL_query(
            f"SELECT root_id FROM blocks where id= '{ block_id }'")
        return (result["data"][0]["root_id"])

    def getCurrentBlockID(self) -> str:
        def StopAndShowQuickerMessage(message: str):
            os.system(f'start quicker:showmessage:error:"{message}"')
            sys.exit()

        def find_process(exe_name):
            hwnd_list = []
            win32gui.EnumWindows(
                lambda _hwnd, _hwnd_list: _hwnd_list.append(_hwnd), hwnd_list)
            app_hwnd = None
            app_pid = None
            for hwnd in hwnd_list:
                app_pid, procname = get_process_name(hwnd)
                if procname.find(exe_name) > 0:
                    app_hwnd = hwnd
                    # print(procname)
                    break
            if not app_hwnd:
                print("没打开Siyuan笔记!")
            return app_pid, app_hwnd

        def get_process_name(hwnd):
            try:
                threadpid, procpid = win32process.GetWindowThreadProcessId(
                    hwnd)
                mypyproc = win32api.OpenProcess(
                    win32con.PROCESS_ALL_ACCESS, False, procpid)
                procname = win32process.GetModuleFileNameEx(mypyproc, 0)
                return procpid, procname
            except:
                return {0, "noprocname"}

        app_pid, app_hwnd = find_process("SiYuan.exe")
        if not app_hwnd:
            StopAndShowQuickerMessage("请确认Siyuan应用程序已打开! ")
        block_id: str = ""
        win32gui.SetForegroundWindow(app_hwnd)
        shell = win32com.client.Dispatch('WScript.Shell')
        shell.SendKeys("^+h", 0)
        time.sleep(0.3)
        siyuanLink: str = pyperclip.paste()
        if not siyuanLink.startswith("siyuan://blocks/"):
            StopAndShowQuickerMessage("没找到Block_ID")
        return (os.path.basename(siyuanLink))

    def getChildBlocks(self, block_id: str):
        return self.Command("/api/block/getChildBlocks", data={"id": block_id})

    def refreshBacklink(self, block_id: str):
        return self.Command("/api/ref/refreshBacklink", data={"id": block_id})


SY = SiYunNoteBook(TokenID)
block_id: str = SY.getCurrentBlockID()
doc_id: str = SY.getDocumentIDbyBlock(block_id)
result = SY.SQL_query(
    f"""
    Select id, markdown, name from blocks
    where root_id == '{doc_id}'
    and type=='m'
    """
)
blocklist = {item["id"]: item for item in result["data"]}
# 按照文档调整顺序
idx = [item["id"] for item in SY.getChildBlocks(
    doc_id)["data"] if item["id"] in blocklist]

result = [blocklist[i] for i in idx]
label: str = "1-"
seqID: int = 1
for item in result:
    id, mk, name = item.values()
    mk = mk.strip("$$").strip("\n").strip(" ")
    mk = re.sub(r'\\tag\{ ?(\d+-)\d+ ?\}', "", mk)
    NameLabel: str = f"{label}{seqID}"
    SY.updateBlock(id, rf"$${mk}\tag{{{label}{seqID}}}$$")
    SY.setBlockAttrs(id, {"name": NameLabel, "alias": f"eq:{NameLabel}"})
    seqID += 1

增减公式并运行代码后,引用文本直接变成图 4 所示了。鼠标挨个点击公式后,才会变成图 5。

image.png

图 4:增减公式并运行代码后的界面

image.png

图 5:增加公式并运行代码,再鼠标单击全部公式后的界面

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    23043 引用 • 92698 回帖 • 1 关注
  • 公式
    5 引用 • 24 回帖
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    77 引用 • 430 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • ttChen 3 评论

    image.png

    是动态文本啊

    哦,我注意到了,你是用 API 的,我回去有空试一下看看能不能复现。
    Frostime
    你可以试一下更新完了以后 F5 刷新一下页面看看行不行。
    Frostime
    @Frostime F5 刷新,重启 siyuan 都没有效果
    ttChen
  • 其他回帖
  • ttChen

    (1)是动态锚文本,但是并没有更新。需要鼠标点击一下公式后才会更新。

    (2) 按 F5 和重启思源笔记都无效果。

  • 2.9.3 已经包含了这项改进,调用以后界面可能需要等个几秒钟后再试试 F5 刷新。

    1 回复
  • ttChen

    建议增加一个 API 函数,能够使指定块或者指定文档内动态锚文本手动更新或者激活一下自动更新。

  • 查看全部回帖