利用AppleScript讀取PD下Win程序文檔信息

此事的前戲說來話長。

前戲一:我用 SWP 5.5

我一直使用 Scientific WorkPlace 5.5 (後文簡稱 SWP 55)這款神級軟體來寫論文作演算和記手稿。在 portable 模式下,它相當於一個 LaTeX 前端,可以清晰直觀地寫文檔看文檔。而於我而言最重要的特性是,SWP 55 可以利用快捷鍵很快速地打出各種複雜的數學公式,且公式是即時渲染的,查錯也方便。附加驚喜是,這貨還能直接調用 MAPLE 的計算引擎,像什麼分解因式、三角函數和差化積、洛必達法則求極限、解微分方程、求矩陣特徵根特徵向量、求矩陣約旦型哈密爾頓型等等運算需求,它都能一鍵出結果。對於我這種在研究工作中需要運用大量數學推導運算且需要寫 LaTeX 的人來說,這簡直就是上帝賜予的禮物,而且到目前為止,我沒有找到與之比肩的軟體。

強大的 SWP 55

可惜的是,SWP 55 只能運行在 Windows 下,而且只在 XP 下運行得最好,Win 7 下會顯著抬高 CPU 佔用,Win 10 下乾脆安裝都出問題。雖然 SWP 團隊已推出跨平台的 version 6,但軟體穩定性極差,目前(2017.12.4)版本號 6.0.27,推出了兩年半,每天都在拚命修 bug,自己都不好意思把版本號升到 6.1,無法成為合格的生產力工具。

所以,在 macOS 下只能採用 5.5 版本 + Win XP 虛擬機組合。考慮到 Parallels Desktop (後文簡稱 PD)的融合模式 (Coherence) 會極大減少切屏的次數,因此虛擬機最終選擇了 PD。

現在前端軟體的運行環境問題算是有了答案,下面該解決 .tex 文檔的編譯問題了。

前戲二:.tex 文檔的編譯

由於 SWP 5 系列軟體的開發年代過於久遠,其自帶的編譯器已很難滿足當下的使用需求,尤其對中文的支持很差,因此只能將 SWP 55 用作純粹的 LaTeX 前端,即只生成 .tex,不參與編譯,而編譯環境可直接採用每年一大更的 TeXLive (macOS 下使用 MacTeX),即整個操作流程如下圖所示:

先用 SWP 55 生成 .tex文件,再用 TeXLive 編譯成 .pdf 文件

編譯命令的執行過程很簡單,終端進 .tex 所在目錄:

xelatex FileName.tex

即可。如果想順便把生成的 .pdf 文件也直接打開,可以:

xelatex FileName.tex && start FileName.pdf

為了避免每次編譯都要敲命令,我當時寫了一個 AutoHotKey (後文簡稱 AHK) 的快捷鍵腳本,讀取 SWP 55 標題欄中顯示的文檔路徑與文件名,做好相應的截取,再組合成命令,送到 cmd 中執行:

;==================================================================;Compile the tex using XeLaTeX;------------------------------------------------------------------^!p::WinGetTitle, swptitle, ahk_class Scientific WorkPlace550StringMid, fullname, swptitle, 25,StrLen(swptitle)-1subpart = StringGetPos, index, fullname, %subpart%, Rif index >= 0{ pathname := SubStr(fullname, 1,index+1) diskname := SubStr(fullname, 1,2) filename := SubStr(fullname, index+2) filename := SubStr(filename, 1,StrLen(filename)-5)}IfWinNotExist, ahk_class ConsoleWindowClass Run cmdWinActivate ahk_class ConsoleWindowClassWinWaitActive ahk_class ConsoleWindowClass, , 2Send %diskname% && cd "%pathname%"{enter}Send xelatex %filename% && start %filename%.pdf{enter}return

在 PD 下,本可以把上面的方法照搬,但會存在這樣一個讓人心裡不爽的事:那麼大一個 TeXLive 要全塞到 XP 的虛擬機下執行——在磁碟空間上是浪費,在執行效率上也是浪費。所以基於上述考慮,更佳的編譯方案應該是在 macOS 下用 MacTeX 執行,這樣避免了 macOS 下和虛擬機下重複安裝 TeX 環境的問題,也能讓編譯效率達到最大化。

編輯和編譯操作在兩個平台上

TeX 環境一旦被拿到 macOS 下,整個操作流程一下子變成了跨平台的,AHK 腳本在這種跨平台任務面前完全廢掉了。既然 AHK 不行,那 AppleScript 能行么?

如何實現跨平台操作的自動化執行

前戲結束,正文開始!

問題來了:AppleScript 能否讀取 PD 虛擬機下 SWP 55 軟體標題欄上顯示的信息?

這問題乍一看根本就是無解,PD 沒有對任何功能進行腳本化,連個最基本的 suspend 都不得不去調用 System Events 的 menu bar 指令,逞論闖進虛擬系統里搞事情。這就好比讓你用 Google Map 查一下你家煤氣表讀數,簡直就是開玩笑。

我曾經一度老老實實親自 cd 進文件目錄下親手敲命令編譯,自我安慰說這樣做有使命感……

後來我驚喜地發現,在融合模式下,Win 下打開的窗口會在 Dock 欄中以應用程序圖標佔位,右鍵該圖標竟會顯示程序標題欄的信息!

融合模式下 Windows 程序窗口的標題欄信息會出現在 Dock 圖標的右鍵菜單中

接下來只要能 get 到 Dock 欄圖標右鍵菜單上的文本信息就能完成任務突破。本著外事不決問 Google 的思想,我在一則 stackoverflow 貼子上找到了解決思路:Accessing dock icon right-click menu items with AppleScript 。

於是問題迎刃而解,先上腳本代碼:

-- 獲取 SWP 55 右鍵菜單第一項的文字信息tell application "Dock" activateend telltell application "System Events" tell process "Dock" set frontmost to true activate tell list 1 perform action "AXShowMenu" of UI element "Scientific Workplace" set orgPath to name of menu item 1 of menu 1 of UI element "Scientific Workplace" key code 53 end tell end tellend tell-- 剔除非路徑信息set thePreLenth to length of "Scientific WorkPlace - [Y:"set orgFileWithPath to characters (thePreLenth + 1) thru -2 of orgPath as string-- 按分割符將文本劃分成若干項set AppleScript"s text item delimiters to ""set theTeXFileWithPathItems to every text item of orgFileWithPath-- 分別獲得 tex 文件名、pdf 文件名及絕對路徑set theTeXFile to the last item of theTeXFileWithPathItemsset thePDFFile to findAndReplaceInText(theTeXFile, ".tex", ".pdf")set thePath to getAbsolutePath(theTeXFileWithPathItems)-- 構成命令set cdCommand to "cd " & thePathset xelatexCommand to "xelatex " & theTeXFile & " && qlmanage -p " & thePDFFileset myCommand to cdCommand & " && " & xelatexCommand-- 送入終端執行tell application "Terminal" if index of windows is {} then do script myCommand else do script myCommand in front tab of front window end if activateend tell-- 文本替換函數on findAndReplaceInText(theText, theSearchString, theReplacementString) set AppleScript"s text item delimiters to theSearchString set theTextItems to every text item of theText set AppleScript"s text item delimiters to theReplacementString set theText to theTextItems as string set AppleScript"s text item delimiters to "" return theTextend findAndReplaceInText-- 路徑提取函數on getAbsolutePath(thePathWithFileNameItems) set thePathItems to thePathWithFileNameItems set the last item of thePathItems to "" set AppleScript"s text item delimiters to "/" set thePath to thePathItems as string set AppleScript"s text item delimiters to "" set thePath to "~/" & thePath return thePathend getAbsolutePath

AppleScript 語法很接近自然語言,讀、學、寫都很容易。下面對代碼稍做一點解釋。

  1. perform action "AXShowMenu" 用於虛擬右鍵圖標,以產生 menu items,否則系統無法獲取菜單信息,為了不讓菜單一直顯示,在取得菜單信息後再虛擬送 key code 53 即 esc 鍵讓菜單消失;
  2. 對取得的菜單信息需要做一點掐頭去尾的工作,即把前面的 "Scientific WorkPlace - [Y:" 和後面的 "]" 號去掉,其中 "Y:" 是上游 macOS 系統中的 HOME 目錄,後面需要將其替換為 "~";
  3. 在提取路徑信息和文件名信息的過程中利用到了兩個函數:findAndReplaceInText 和 getAbsolutePath,前者參考了蘋果官方手冊的常式,用於將 .tex 替換成 .pdf,後者是我自己編寫的,用於刪掉文件名並補充 mac 下的 HOME 標識 "~";
  4. 為了實現 QuickLook 的預覽效果而非直接打開 Preview.app,在終端執行完編譯操作後使用了 qlmanage 指令。

以後再寫 LaTeX 文檔時,只需要融合模式打開 SWP 55,需要生成 pdf 時,直接執行腳本,就可立即調出 macOS 下的終端,編譯並打開快速預覽。


推薦閱讀:

Kindle for mac兼容OS X EI Capitan Public Beta 3嗎?
EI Capitan 是指哪個風景名勝?
OS X 10.11.1 中的 Silent Clicking 功能是如何實現的?

TAG:AppleScript | macOS | 虚拟机 |