寫程式最怕碰到的,就是信心滿滿地寫好程式後,發現結果不如預期,而且完全看不出問題出在哪裡。這種慘況可以分成兩種:一種是程式很長、很複雜,想要把心機很重,躲在幽暗深處的臭蟲給抓出來,即使有功能強大的除錯工具,都不是件簡單的事;另一種是程式沒幾行,一切看起來都很清楚正常,臭蟲根本沒地方躲藏,可是結果就是不對。不管是哪一種,最讓人傻眼翻白眼的是,焚膏繼晷、殫精竭慮地奮戰多時,搞到開始懷疑人生之後,卻赫然發現,根本沒有臭蟲!根本就不是程式的問題!
寫好畫fractal tree的程式之後,邊看著turtle在螢幕上畫圖邊在想:應該把畫圖的過程做成動畫才對,看著一棵樹慢慢長出來,應該會比只看最後的完成圖有趣多了。可惜turtle並沒有支援把畫面存成動畫的功能,只能另外想辦法。
要把fractal tree的生長過程做成動畫,最簡單的方式,就是用螢幕錄影軟體直接錄下來。不過這樣做的話,檔案會挺大的,而且如果放在網頁裡頭,還要靠播放器才能看。比較好的方式,應該是做成GIF檔,既輕巧又好處理,使用方式也可以比較多樣化。至於顏色方面,雖然GIF支援的顏色不多,不過fractal tree的顏色就只有黑、綠、紅,不會有失真的問題。想要這麼做,就得把畫面一張一張的塞進GIF檔。所以最關鍵的問題是:怎樣把畫面一張一張地擷取下來?
嘿!turtle有支援畫面存檔的功能耶!剛看到時,心想這就好辦了,可省去很多功夫。可是再仔細一看,心裡涼了半截,居然只能存成ps檔。想要用PIL這個module來處理ps檔,還得額外裝Ghostscript。這可不是個好主意,電腦裝了一堆不常用的東西,就好像房子塞滿了不常用的雜物,搞到後來整個亂七八糟,想整理都無從整理起。所以能不裝就不裝,應該有其他辦法。
網路爬文的結果,可以用PIL的ImageGrab.grab().crop((x0, y0, x1, y1))把左上角和右下角分別是(x0, y0)及(x1, y1)這個長方形的畫面擷取下來。程式就簡單的幾行而已:
screen = turtle.Screen()
cv = turtle.getcanvas()
x0, y0 = cv.winfo_rootx(), cv.winfo_rooty()
x1, y1 = x0+screen.window_width(), y0+screen.window_height()
img = ImageGrab.grab().crop((x0, y0, x1, y1))
程式是夠簡單的了,可是……為什麼擷取下來的畫面不是原先想的區域?那個長方形明顯框錯了範圍,而且框到的turtle畫面的部分,還有透明的效果,居然看得到原本應該被蓋住的Spyder畫面。
天啊!這究竟是怎麼回事?程式就這麼幾行,到底是哪裡的問題?莫非是搞錯那幾個設定長方形範圍參數的意思了?一堆問號啊!
要解決這個問題,當然就是在網路上找資料。不過在網路上爬文時,最讓人頭大的一點是,一大堆名詞攪和在一起,有時實在是很難分辨到底所指為何。最主要的原因,是因為turtle使用tkinter來當作畫圖的介面,所以網路上的討論文章,有些會把這兩個東西混雜在一起討論。糟糕的是,因為要擷取螢幕畫面,所以就必須清楚知道畫面的座標位置。這時候,就又牽涉到相對座標和絕對座標的問題。既然牽涉到座標,那就得先搞清楚screen、widget、window、canvas這些是指什麼東西,而winfo_rootx()、winfo_rootx()、winfo_x()、winfo_y()傳回來的,又是哪個東西的哪個座標……
爬文了幾天,費盡千辛萬苦,橫看、豎看、左看、右看,不管怎麼看,都看不出為什麼那個長方形會框錯範圍。這時候,心裡開始浮現是不是要使出最終極那一招的想法。這招絕對有效,只是效率奇差無比,而且就只能用那麼一次,下次當狀況改變時,就得又花上不少時間重新再來。
最終極那一招,招式簡單但有效,就像神鵰俠侶中的傻姑,面對武功高強的赤練仙子李莫愁,就只一支火叉呆板毫無變化的刺、刺、刺,便讓李莫愁左支右絀,幾十手變化全被封死,最後落荒而逃。那招就是:手動調整。慢慢地試、慢慢地調,直到那個長方形能準確框住要擷取的區域為止。
這終極的一招,施展起來實在是無聊透頂,頗讓人躊躇要不要這麼做,所以就在網路上東逛逛、西逛逛,實在是下不了決心。突然間,「咦?!莫非……」
火速調整Windows的設定,然後執行程式。居然……成功了!
天啊!原來這一切都是Windows的設定搞的鬼,程式根本就沒問題。因為現在電腦螢幕的解析度都很高,所以Windows的字體會變得非常小。這麼小的字體,如果不調整一下,實在是在虐待眼睛,所以習慣把Windows的「變更文字、應用程式與其他項目的大小」調大。把這個設定值調回建議的100%之後,就可以準確地擷取到需要的範圍了。以前在錄製螢幕畫面時,也曾因為這個原因,錄好的畫面都被切掉一塊,花了不少時間才搞清楚原因。因為時日已久,早就忘了這麼回事,沒想到現在居然又被同樣的設定問題整了一次。還好這次在使出終極一招之前,靈光乍現,發現了問題所在,免去了浪費大把時間做無聊事之苦。
這一切的一切,都跟程式無關,是非戰之罪啊!