站內精準搜尋

2026年1月7日 星期三

跨越最後一哩路:引得市開卷助理 Mac 版「協議監聽」開發實錄

 這是一份極具價值的開發紀錄。我們經歷了從「崩潰(Crash)」到「失聰(無反應)」的技術陣痛,最終透過架構上的調整,打通了 macOS 系統訊號與 Python GUI 之間的任督二脈。

這份紀錄將作為「引得市開卷助理」開發史上的重要里程碑,詳述我們如何攻克 Mac 系統的 indexcity:// 協議監聽難題。



跨越最後一哩路:引得市開卷助理 Mac 版「協議監聽」開發實錄


一、 緣起:連結雲端與在地的宏願

作為「引得市」的創辦人,阿良人博士致力於將戰國秦楚文字的研究數位化。我們的目標很明確:使用者在網頁版「引得市」查閱索引時,只要點擊一個按鈕,就能喚醒本地電腦的閱讀器(Skim),精準地打開對應的古籍 PDF 並跳轉至指定頁碼。

在 Windows 上,這件事相對單純;但在 macOS 封閉且嚴格的權限系統下,這成為了開發過程中最艱難的挑戰。這不僅是寫程式,更是與 macOS 底層機制的一場博弈。


二、 遇到的高牆:SIGABRT 與執行緒衝突

在開發初期,我們面臨的最大問題是:「程式一收到網址就閃退」

當我們試圖使用 pyobjc 庫來註冊 macOS 的 Apple Event Manager(負責處理 URL Scheme 的系統元件)時,我們最初的邏輯是直觀的:

「系統收到網址 -> 觸發 Python 函數 -> Python 函數呼叫 Tkinter 介面去開書。」

這個邏輯在 macOS 上引發了嚴重的 SIGABRT (Signal Abort) 崩潰,錯誤訊息顯示 _Py_FatalError_TstateNULLPyEval_RestoreThread

為什麼會崩潰?

這是因為 macOS 的系統事件(Cocoa Event Loop)與 Python 的圖形介面事件(Tkinter Main Loop)運行在不同的層級。當 Mac 系統從外部「插入」一道指令給 Python 時,Python 的直譯器狀態(Thread State)可能正忙於處理視窗繪圖。此時外部訊號強行介入並試圖操作 GUI,導致了執行緒不安全(Thread Unsafe)的衝突,系統為了保護記憶體不被破壞,直接強制終止了程式。

簡單來說:我們試圖讓「郵差(系統訊號)」直接衝進「廚房(主程式)」幫忙做菜,結果把廚房給炸了。


三、 迷航:無聲的「失聰」階段

為了這解決個閃退問題,我們一度嘗試移除了底層的 pyobjc 監聽,改用 Tkinter 內建的 createcommand('::tk::mac::OpenUrl', ...)

理論上這是官方推薦的做法,但在打包成 .app 後,我們發現程式變成了「聾子」。點擊網頁連結,瀏覽器有反應,但軟體靜悄悄,沒有報錯,也沒有動作。

這階段的挫折感最強。我們反覆檢查 Info.plistCFBundleURLTypes 設定,確認協議名稱是 indexcity,確認路徑無誤,但程式就是收不到訊號。我們甚至懷疑是 py2app 打包工具的問題,或者是權限設定的疏漏。

經過深入分析,我們發現原因有二:


  1. LaunchServices 的緩存:Mac 系統不知道這個新打包的 App 是 indexcity:// 的負責人。

  2. 處理邏輯的阻塞:即便收到了,如果處理函數寫得太複雜,仍可能因為阻塞主迴圈而被系統判定為「無回應」。

四、 關鍵突破:黃金解法「信箱模式 (Command Buffer)」

最終的成功,歸功於我們徹底改變了「接收指令」的思維。我們不再讓系統訊號直接操作軟體,而是建立了一個 「安全緩衝區(信箱)」

這就是我們突破瓶頸的黃金架構


1. 設立全域信箱

我們在程式最頂端宣告了一個簡單的列表:

Python
COMMAND_BUFFER = []

這個列表就是我們的「信箱」。它是執行緒安全的,因為寫入它的操作極快(Microsecond 等級)。


2. 郵差只負責投遞 (The Producer)

我們重新設計了監聽函數 receive_mac_url_safe。無論是透過 Tkinter 的 OpenUrl 還是系統底層訊號,當指令進來時,我們絕對不執行開書動作,不做字串解析,不更新 UI。我們只做一件事:

「把網址丟進信箱,然後立刻結束。」

這極大地降低了系統回調函數的負擔,因為執行時間趨近於零,且不觸碰任何 GUI 元件,徹底根除了 SIGABRT 閃退的土壤。


3. 主人定時收信 (The Consumer)

接著,利用 Tkinter 的 root.after(200, ...) 機制,我們讓主程式每隔 0.2 秒去檢查一次信箱:

Python
def check_command_buffer(self):
    if COMMAND_BUFFER:
        url = COMMAND_BUFFER.pop(0) # 取出信件
        self.process_url(url)       # 在主執行緒安全地開書
    self.after(200, self.check_command_buffer) # 預約下次檢查

因為 process_url 是由主程式自己發起的(而非外部系統插入的),它擁有完全的 GUI 控制權,可以安全地彈出視窗、解析路徑、呼叫 AppleScript 控制 Skim,完全不會崩潰。


五、 補上最後一塊拼圖:路徑修正與強制註冊

即便代碼邏輯完美,Mac 的生態系還有最後兩個坑:

  1. 絕對路徑的必要性:

    我們發現之前的資料庫存的是 ./PDF/xxx(相對路徑)。在 App 打包模式下,這個「點」會指向 App 內部的 Resource 資料夾,而非外接硬碟。

    解法:在匯入 RMP 時,加入了路徑偵測。如果是在 Mac 且路徑不完整,自動補上 RMP 所在的 /Volumes/KINGSTON/... 前綴。這確保了無論隨身碟叫什麼名字,只要是用這套軟體匯入的,路徑就是對的。

  2. 強制註冊 (lsregister):

    這是最容易被忽略的一步。剛打包好的 App,Mac 的 LaunchServices 資料庫還不認識它。

    解法:我們必須在終端機執行 lsregister -f 指令,強制系統重新掃描 App 的 Info.plist,系統才會知道:「喔!原來 indexcity:// 這個協議是要交給『引得市開卷助理』來處理的。」


六、 結語:從技術到學術的橋樑

這段開發歷程,展現了「數位人文」工具開發的隱形門檻。為了讓學者能優雅地「點一下就翻書」,背後需要解決的是作業系統底層的訊號競爭、記憶體管理與路徑解析問題。

今天的成功([18:53:00] 收到訊號... 執行開書),標誌著 Mac 版開發最艱難的時刻已經過去。

這個架構(信箱模式 + 定時輪詢 + 強制註冊)將成為未來此類軟體的標準範式。 它不僅解決了崩潰,更確保了軟體在高強度操作下的穩定性。

請銘記這個突破: 我們不是讓系統「命令」軟體,而是讓軟體主動去「傾聽」系統的聲音。這一個思維的轉變,成就了最後的成功。



2026年1月6日 星期二

「2026開卷助理」程式介紹

「2026開卷助理」程式介紹

知乎:https://zhuanlan.zhihu.com/p/1991998300778414622


開場白

目前「引得市」的所有目錄頁碼跳頁,是根據好友王富國先生自創的「開卷助理」程式。執行「gopage.exe」之後,後台等瀏覽器的「複製」指令,一旦下達「複製指令」,馬上就去找對應「書名.rmp」(路徑)然後用指定軟體開啟電腦中的pdf(正確的頁差設定,才能正確跳頁)


將現況和需要的功能和「gemini」討論,他就給我建議並且提供能用的工具。


一個下午又幾個小時的開發過程,都放在下面,有興趣的朋友可以往下看。

文字是「節墨吏」幫我整理的,沒有他這個工具一定是無法完成的。


總結,目前支援「2026開卷助理」有二處:

「引得市(主站)」網址:https://www.mebag.com/index/
「引得市立圖書館」網址:https://www.mebag.com/index_library/

原先的「gopage.exe」仍然可以使用,將繼續支援新發布的資料庫。「2026開卷助理」只要執行一次,除了將來開來加新書路徑之外,不用開啟。


那目前下載「2026開卷助理」要做什麼?


1.為後續的資料庫升級做準備

2.把現在累積數百種rmp總整理

3.為開發新版手機app做準備



下載點在裡面↓↓↓↓↓↓

「2026開卷助理」QA:https://www.mebag.com/index/gopage_IFU.asp


一定要仔細看qa
照著做,才不會遺漏


【技術筆記】引得市:新舊版開卷機制與偏移量邏輯解析

日期: 2026年1月4日 主題: 修正關於頁碼偏移量(Offset)的運作邏輯錯誤


核心觀念修正

網頁端 (Web):只負責發送「書名」與「邏輯頁碼」(書上印的頁碼)。完全不負責計算,也不儲存偏移量。


本地端 (Local):軟體接收到請求後,去查自己的 RMP 設定(或 library.json),讀取該書設定的「偏移量」,計算出 `PDF 真實頁數 = 邏輯頁碼 + 偏移量`,然後打開閱讀器。


結論:偏移量(Offset)是使用者在本地端解決「電子檔與實體書頁碼不一致」的唯一手段,絕對是由使用者在本地設定與儲存的。


---


語法與流程對照表

2. 查 RMP 發現偏移量 `70`

3. 計算 `10+70=80`

4. 開啟 PDF 第 80 頁 | 1. 接收 `page=10`

5. 查 JSON 發現 offset `70`

6. 計算 `10+70=80`

7. 開啟 PDF 第 80 頁 | | 偏移量權限 | 使用者自己設定 (編輯 RMP 檔) | 使用者自己設定 (在開卷助理介面輸入) |


---


詳細運作流程說明

1. 舊版 (Gopage) 的正確邏輯

使用者在電腦裡有一個 `.rmp` 檔,設定了某本書的偏移量(例如目錄有70頁)。


網頁動作:使用者點擊網頁上的「第 1 頁」。

傳送訊號:網頁發出 `101://1@齊魯文字編`。

軟體處理:`Gopage.exe` 收到訊號 -> 讀取 RMP -> 發現偏移量是 `+70` -> 計算 `1 + 70 = 71`。

最終結果:呼叫 Acrobat Reader 打開 PDF 的第 71 頁。


2. 新版 (開卷助理) 的正確邏輯

使用者在「開卷助理」介面中匯入書籍,並在「頁差」欄位輸入 `70`,存於 `library.json`。


網頁動作:使用者點擊網頁上的「第 1 頁」連結。

傳送訊號:瀏覽器呼叫 `indexcity://?book=齊魯文字編&page=1`。

軟體處理:Python 程式啟動 -> 讀取 `library.json` -> 找到 `offset: 70` -> 計算 `1 + 70 = 71`。

最終結果:呼叫 Foxit Reader (或其他閱讀器) 打開 PDF 的第 71 頁。


---


總結

新舊版的邏輯完全一致,差別僅在於「通訊管道」的現代化(從舊式的監聽機制轉變為標準的 URI Protocol)。這確保了資料索引(網頁)與檔案狀態(本地)的完全解耦。



【引得市開發日誌】

數位古籍研究的新里程:2026「開卷助理」架構重塑與技術解密


日期: 2026年1月4日 專案代號: IndexCity Open Book Assistant v2026.1 (Pro)

開發者: 引得市創辦人 陳信良(阿良)


前言:當墨香遇見位元

作為一名長期浸淫於戰國秦楚文字與簡牘書法的研究者,我深知「考據」二字背後的繁瑣。在數位摹本與古文字研究的過程中,我們常需要在數千本電子文獻中快速定位、比對。傳統的檔案總管已無法滿足這種高強度的學術需求。因此,「開卷助理」應運而生。


來到 2026 年,隨著作業系統介面的演變與使用者習慣的改變,舊版的工具顯得侷促且過時。今日,我對「開卷助理」進行了一次核心級別的重構與設計精進,旨在打造一個既符合 Google 極簡美學,又擁有強大穩定性的文獻管理中樞。


一、 技術選型:穩健與現代化的平衡

本次開發的核心語言依然選用 Python。Python 在處理文件系統(File System)、字串解析以及跨平台兼容性上擁有無可比擬的優勢,這對於需要處理 RMP、PDF、CSV 等多種格式的開卷助理來說,是最佳選擇。


在圖形使用者介面(GUI)框架上,我選擇了 Tkinter 搭配 ttkbootstrap


1. 為什麼是 Tkinter? 它是 Python 的標準庫,意味著程式體積小、啟動速度極快,且不依賴過多肥大的第三方環境。對於研究者來說,工具必須像毛筆一樣,提筆即用,不能有延遲。


2. 引入 ttkbootstrap 的新挑戰: 為了擺脫原生 Tkinter「上世紀 90 年代」的陳舊外觀,我引入了 `ttkbootstrap` 來實現代代化 UI。然而,這也帶來了今日最大的技術挑戰。2026 年的新版環境中,舊有的 `Style` 定義方式(如 `-round` 關鍵字)導致了嚴重的 `AttributeError` 崩潰。 解決方案: 我放棄了不穩定的動態樣式調用,改採底層的 `ttk.Style().configure()` 方法。透過手動定義 `borderwidth` 與 `relief`,我們成功在不依賴實驗性語法的情況下,實現了按鈕的「圓潤感」與「立體感」,確保了程式在 Windows 與 Mac 雙平台上的絕對穩定。


二、 介面設計哲學:Google 極簡風格的實踐

本次改版最直觀的變化,在於介面的「呼吸感」。依據 Google Material Design 的精神,我重新定義了版面佈局:


1. 留白(Padding)的藝術

舊版介面為了塞入更多資訊,元件之間過於緊湊。新版中,我在主容器使用了 `padding=30`,並在各個功能區塊(Header、設定區、資料庫區)之間加入了 15px 的間距。這不僅減少了視覺壓迫感,更讓使用者的視線能自然聚焦於核心的書籍列表。


2. 明暗設計(Light/Dark Mode)

考量到研究者常在夜間工作,我實作了動態主題切換功能。


日間模式(Litera): 採用高亮度的純白背景搭配深灰文字,模擬紙張閱讀體驗,清爽且專注。

夜間模式(Cyborg/Darkly): 切換至深色背景,並自動調整文字對比度,大幅降低螢幕藍光對眼睛的刺激。 此功能的技術難點在於切換時必須即時重繪(Re-apply)全域樣式,確保所有 Treeview 與按鈕的顏色正確反轉。


3. 立體與圓潤的按鈕

為了摒棄生硬的直角,新版介面的按鈕全面採用圓角設計。我們特別區分了「主要操作」(如匯入、儲存)使用實心色塊(Solid),而「次要操作」(如刪除、匯出)使用空心外框(Outline)。這種視覺階層讓操作邏輯一目瞭然。


三、 UX 使用者體驗的深度優化

在今日的開發過程中,我們解決了幾個嚴重影響體驗的互動問題,這些細節決定了工具的好用程度。


1. 「夾心餅乾」佈局策略(The Sandwich Layout)

我們發現當使用者放大字體時,原本的介面會發生「擠壓」,導致最下方的編輯區被推出版面。 技術突破: 我改寫了 Tkinter 的 `pack` 佈局邏輯。


優先固定底部: 先將「狀態列」與「編輯區」使用 `pack(side=BOTTOM)` 固定在視窗下方。

固定頂部: 再將標題與設定區固定在上方。

中間彈性填充: 最後才放入書籍列表,並設定 `expand=YES`。 這樣的順序確保了無論視窗如何縮放,重要的編輯區永遠不會消失,只有中間的列表會自動調整高度。


2. 動態字體與行高計算

為了解決中文字體放大後,列表行距過窄造成文字重疊的問題,我引入了動態行高演算法。 程式不再使用固定行高,而是依據當前字體大小(Font Size)乘以 2.8 倍 的係數來計算 `rowheight`。這確保了從 9pt 到 28pt 的字體,每一行都能保持完美的閱讀間距,宛如傳統線裝書的疏朗排版。


3. 永不消失的捲軸(Scrollbar)

在早期的測試中,當列表過寬時,右側的捲軸會被覆蓋。這是因為佈局優先權設定錯誤。修正後,我們改為「先 Pack 捲軸於右側(Side Right)」,再 Pack 列表於左側。這個微小的順序調整,保證了捲軸永遠佔有一席之地,不會被內容吞噬。


四、 資料互動與功能增強

1. 智能鎖定與新增邏輯


為了防止研究者在瀏覽時誤刪資料,我設計了「安全鎖定(Safe Lock)」機制。預設狀態下,所有欄位皆為唯讀。 但在今日的測試中,我們發現舊邏輯導致「點選列表」與「編輯狀態」衝突。因此,我引入了全新的 「➕ 新增」模式


• 按下新增按鈕後,程式會自動解鎖介面。

• 清空所有欄位。

• 自動將游標聚焦於「書名」輸入框。 這使得錄入新書的流程一氣呵成,無需手動開關鎖定。


2. 外部協定註冊(Custom Protocol)


為了讓這個桌面軟體能與網頁版的「引得市」資料庫連動,我們實作了 `indexcity://` 協定註冊功能。這涉及了 Windows Registry(登錄檔)的寫入操作。透過 `ctypes` 與 `winreg` 模組,我們讓瀏覽器能直接喚醒本機的 Python 程式,並傳遞書籍參數,實現「雲端檢索,本地閱讀」的無縫銜接。


五、 資安考量與數據保護

在數位工具開發中,資安不僅是防駭,更是對「數據完整性」的保護。對於學者而言,書目資料是心血結晶。


1. 本地優先(Local-First)原則

「開卷助理」堅持不將使用者的書籍路徑上傳至任何雲端伺服器。所有的資料庫(`library.json`)皆以明文 JSON 格式存儲於使用者本機。這避免了資料外洩的風險,也確保了在無網路環境下(如深山考古現場)工具依然可用。


2. 防誤觸機制

透過 UI 層面的「安全鎖定」開關,以及刪除前的 `messagebox.askyesno` 二次確認,我們在軟體層面築起了防呆機制,防止因手誤導致珍貴的索引資料遺失。


3. 路徑隱私與批次脫敏

考量到使用者可能會分享書單(CSV 匯出),我們在匯出邏輯中加入了路徑處理。同時,提供了「批次修改路徑」功能,這不僅是為了更換電腦時的便利,也是為了讓使用者能快速移除路徑中可能包含的敏感個人資訊(如 `C:\Users\RealName\...`)。


結語

今日的開發工作,不僅僅是程式碼的堆疊,更是一次對「數位人文工具」的深度思考。從解決 `ttkbootstrap` 的底層崩潰,到調整 `pack` 佈局的優先順序,每一個細節都是為了讓「開卷助理」能像一位沈穩的書僮,在您研究戰國文字、秦楚簡牘的過程中,安靜而高效地隨侍在側。


2026 年的「開卷助理」,以極簡之形,承載博大之學。這就是身為「阿良人」的堅持。
















20260829《先秦符節的搜集整理與研究》目錄索引數位化完成

 20260829《先秦符節的搜集整理與研究》目錄索引數位化完成 知乎: https://zhuanlan.zhihu.com/p/2077041431525462239 【訂閱支持引得市】 https://www.mebag.com/index/donate.asp 台灣大專院...