按下 NVDA+Shift+V 時,外掛會把剪貼簿裡的花俏內容「洗乾淨」變成純文字,並自動幫你按 Ctrl+V 貼上!
當我們的外掛涉及到「修改系統資料(如剪貼簿)」與「模擬使用者動作(如貼上)」時,需求設計必須非常謹慎。我們來進行三步驟拆解:
NVDA+Shift+V 觸發。Ctrl+V。為了避免 AI 使用不安全的方法,我們必須在 Prompt 中指定以下 NVDA 專用 API:
* 「讀取與寫入剪貼簿」 ➔ 必須指定 NVDA 官方提供的 api.getClipData() 與 api.copyToClip(),這能防止 Windows 剪貼簿鎖死崩潰。
* 「模擬鍵盤按鍵」 ➔ 指定使用 KeyboardInputGesture.fromName("control+v").send(),這是 NVDA 2026.1 最標準且安全的按鍵注入方式。
請將以下 Prompt 貼給你的 AI 工程師:
「你現在是專業的 NVDA 視障輔助軟體外掛工程師。請幫我寫一個全域外掛(GlobalPlugin),檔案名為
purePaste.py。1. 開發需求描述: - 類型:全域外掛(GlobalPlugin),請綁定快捷鍵
kb:NVDA+shift+v。 - 剪貼簿處理:請使用 NVDA 官方的api.getClipData()讀取剪貼簿。如果剪貼簿沒有文字,請呼叫ui.message("剪貼簿是空的")並結束。 - 格式淨化:將讀取出來的純文字,使用api.copyToClip()重新寫入剪貼簿,以清除 HTML 等格式資訊。 - 模擬貼上:請呼叫keyboardHandler.KeyboardInputGesture.fromName("control+v").send()模擬按下 Ctrl+V 完成貼上。 - 播報提示:最後以ui.message("已淨化並貼上")提示使用者。2. 嚴格規範限制: - 嚴禁使用 Python 的
win32clipboard庫,必須完全採用上述 NVDA 官方的api模組,以維持相容性。 - 程式碼必須採用標準縮排,且必須有給一般人看得懂的詳細中文註釋,逐行解釋其運作原理,讓不懂寫程式代碼的人也能完全了解。請直接輸出完整程式碼。」
AI 產出的 purePaste.py 程式碼結構如下,我們一起來讀懂它:
# 引入全域外掛範本
import globalPluginHandler
# 引入快捷鍵裝飾器
import scriptHandler
# 引入 NVDA 官方 API 控制庫(負責剪貼簿讀寫)
import api
# 引入語音/點字播報模組
import ui
# 引入處理鍵盤模擬的模組
import keyboardHandler
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
# 綁定快捷鍵為 NVDA+Shift+V
@scriptHandler.script(
description="Purifies clipboard to plain text and pastes it",
gestures=["kb:NVDA+shift+v"]
)
def script_purePaste(self, gesture):
# 1. 安全地從系統剪貼簿讀取文字內容
text = api.getClipData()
# 2. 檢查剪貼簿是不是空的
if not text:
# 如果是空的,報讀提示,並立刻結束函數
ui.message("剪貼簿是空的")
return
# 3. 將純文字重新寫回剪貼簿(這會自動清除所有字型、顏色與表格格式)
api.copyToClip(text)
# 4. 建立一個「按下 Control+V」的鍵盤虛擬手勢,並發送出去(模擬貼上)
keyboardHandler.KeyboardInputGesture.fromName("control+v").send()
# 5. 發送完成提示,用語音和點字告訴使用者
ui.message("已淨化並貼上")
api.getClipData():這是 NVDA 寫好的防摔網,能保證抓出來的一定是純文字字串,把所有花俏的樣式丟掉。KeyboardInputGesture.fromName("control+v").send():這行程式碼就像是外掛長出了一隻無形的手,在你的鍵盤上按下 Ctrl+V。purePaste.py 放進 globalPlugins。NVDA+Ctrl+F3 重啟。NVDA+Shift+V。字會被以純文字自動貼上,大功告成!以下題目專為尋求真正硬核觀念與專家級實戰能力的學員設計。題目無法從講義表面抄寫解答,請嘗試獨立思考後再點擊展開解析。
【實戰情境問題】:在編寫 NVDA 外掛時,呼叫 `ui.message("提示訊息")` 與直接呼叫 `speech.speakText("提示訊息")` 或 `braille.handler.message("點字訊息")` 有何本質上的無障礙架構差異?
【鑑別點說明】:深剖 NVDA 輸出層(Speech / Braille Output Manager)的抽象封裝與多模態輸出。
【專家級解答與深度剖析】: `speech.speakText()` 只會單純觸發語音合成器發聲,不會將訊息傳送到視障者的點字觸摸顯示器 (Braille Display) 上;而 `ui.message()` 是 NVDA 提供的高階通用 API,它會**同時向語音合成器與點字顯示器發送訊息**,並且會處理點字訊息的暫留顯示時間與語音佇列的優先級搶占(Preemption)。因此,撰寫符合規範的外掛應優先使用 `ui.message()` 以確保點字使用者享有同等權益。
【實戰情境問題】:當使用者在 Windows 系統中快速切換視窗(如按 `Alt + Tab`)時,外掛呼叫 `obj = api.getFocusObject()` 時經常會回傳 `None` 或無效物件。如果不做非同步判空保護直接存取 `obj.name`,會引發什麼災難?
【鑑別點說明】:測試學員對 Win32 視窗焦點切換期間的 race condition 與 Python 例外防禦機制。
【專家級解答與深度剖析】: 當作業系統正在銷毀舊視窗並建立新視窗的微秒級間隙中,NVDA 的焦點物件可能暫時處於未初始化狀態,`getFocusObject()` 會回傳 `None` 或已失效的 `DeadNVDAObject`。若代碼直接呼叫 `obj.name`,會觸發 `AttributeError: 'NoneType' object has no attribute 'name'`,導致 NVDA 外掛腳本崩潰在中途,使用者失去語音反饋。防禦作法:必須進行嚴格的判空與 `try-except` 包裹:`if obj and not obj.isDead:`。