作者:Michael Turner

介紹

Erlang這門程式語言通常用於伺服器方面,雖然它也有類似Wings 3D這樣影象密集的應用。wxWidget是對Erlang支援最好的影象API,它為GUI程式設計提供一個大型,成熟,穩定的跨平臺API。

1。 怎樣閱讀該教程,為什麼要讀它

這個教程的教學方法是高度互動的:首先你將在Erlang shell中進行wxWidget相關呼叫。然後你可以立刻看到它的效果。當你新增一個東西后它能立即呈現。 這種方法是學習GUI程式設計更好的方式,原因如下:

更短更簡潔:大多數GUI教程依賴於一個GUI demo程式,但問題是即使最簡單的GUI demo程式碼也有一頁多。並且它們不僅僅是長,還寬和多:GUI呼叫通常有一大堆的引數。很難同時關注一兩行。Erlang shell幫助你解決這些問題,而且當你犯錯的時候很容易知道什麼做錯了。

快速反饋:當你瀏覽GUI程式碼時,你通常想知道:它到底發生了什麼,什麼時候發生的?在命令列中,你就能知道。或者說至少你能快速找出。

更少恐懼(或者說更少無聊):看看GUI程式設計中任何一個“hello world”程式,無論什麼語言都可以。如果你早就知道GUI APIs,你就會抱怨:有好多無關緊要的不同要學啊!如果你不知道,你就會顫抖:有好多要學啊!

1。1 讓手指動起來

你想透過這個教程獲得最大收益嗎?你是把這個教程直接複製到你執行的Erlang上的嗎?那麼你得記住一個最重要的事情:不要複製貼上到你的Erlang shell上。你想嘗試記住別人說的話然後複述一次這樣來練習口語嗎?教程說在哪寫程式碼,就在哪寫程式碼,

2。1 反對意見?駁回!

當然這種方法也有缺點。沒有任何事物是完美的。類似這樣的反對意見:

“純手打意味著犯一堆錯誤”——當然,那是好事。錯誤幫助你學習。

“要輸入很多內容”——可能你擔心手腕患病?考慮:壓力也是犯病因子。學一門新程式設計技術即使很有趣也可能會有壓力。這只是降低壓力的學習方法的一種。而且:你將在每個l小節結束後有停頓時間。

“浪費很多時間”——注意,你學到的GUI APIs一個也不會少。

說了這麼多為什麼不現在開始呢?

2。 準備工作

不像專門為特定系統設計的快速GUI原型程式,如Tcl/Tk,在從Erlang shell命令列開始圖形介面開發之前有一些準備工作要做。本節逐步引導你。在這個部分,假設你早已:

安裝了Erlang

已經知道怎麼使用Erlang shell

有用過合適的文字編輯器寫程式

在開始之前,將你的Erlang shell準備好會更容易理解。

2。1 尋找wx標頭檔案

眼見為實——但是前提是你得看清楚。並非所有wx呼叫都產生一個直觀的圖形顯示;在Erlang shell中,通常你只能看到返回值。這些值可能是神秘的,尤其是如果你過去沒有用過Erlang。因為Erlang是一門函數語言程式設計語言,所以每個wx呼叫都會返回一個值。這些值大多是tuple。而這些tuple又大多有記錄(record)的內容。在記錄格式下理解wx返回值會更容易。Erlang shell需要被告知wx的定義。那麼現在的問題是,這些記錄在哪定義的? 尋找在你係統上wx定義在哪,可以輸入這個:

My_wx_dir = code:lib_dir(wx)。

在Windows XP上,My_wx_dir可能看起來像這樣:

"c:/PROGRA~1/ERL57~1.2/lib/wx-0.98.2"

2。2 閱讀wx模組定

從剛剛獲取的那個目錄讀取wx定義:

rr

My_wx_dir

++

“/include/wx。hrl”

)。

rr

My_wx_dir

++

“/src/wxe。hrl”

)。

兩個rr呼叫都應該返回一系列模組,如果rr調用出錯,你將得到空列表。 開啟你的文字編輯器。把上面三行程式碼複製貼上到一個臨時檔案。然後,當你開始一個新小節,或者重新開始小節,或者因為崩潰不得不重啟,你可以把它們複製貼上到shell裡。現在就試試,確保它們能正常工作。退出Erlang,重啟,然後複製貼上這些程式碼到shell。

很不幸,我們不能在shell中使用Erlang宏定義。不過這也是另一個需要定位

wx.hrl

檔案的理由:為了在shell中使用,我們得查詢需要的wx宏符號所對應的值。

2。3 初始化wx

要想啟動wx,先輸入:

Wx

=

wx

new

()。

它運行了嗎?可能有訊息說SMP(對稱多處理)沒有開啟,或者“SMP emulator required”。在一些Windows Erlang發行版中SMP沒有預設開啟。退出Erlang shell然後帶參

-smp

重啟,在DOS命令列中就像這樣:

"C:\Program Files\erl5.8.1.1\bin\werl.exe" -smp

如果wx:new()正常執行,將會返回一個記錄,類似下面:

#wx_ref

{

ref

=

0

type

=

wx

state

=

[]}

現在忽略它的值。

3。 Frame:開胃菜

在wxWidgets中,一個視窗相當於一個frame。讓我們寫一個簡單的程式,然後新增它。

3。1 生成一個frame

輸入下面的程式碼可以生成一個frame:

F=wxFrame:new(Wx, -1, “Hello, World!”)。

(第二個引數可以是一個整數ID,-1告訴wxWidgets給frame一個預設值) 現在,新frame在哪?我們得到一個類似這樣的記錄:

#wx_ref{ref = 35,type = wxFrame,state = []}

但是螢幕上沒有任何改變。為什麼?我們必須提出想看看frame的請求它才會出現。輸入:

wxFrame

show

F

)。

它會返回true。現在你就會看到類似這樣的東西:

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

3。2 從shell異常中恢復

只需要點選關閉按鈕就能關閉frame。但是別那麼做,先試試下面這個無意義的呼叫;

nothing

doint

()。

這會讓frame消失,隨之出現的還有異常錯誤訊息。這是因為wxWidgets在它(shell)的程序執行圖形程式,如果shell中出現異常又不捕獲,它就會當即被殺死。

僅僅鍵盤輸入錯誤就有可能導致GUI完全丟失,這種情況而且會經常發生。無論在哪隻要你引發了錯誤就要重新輸入一遍。沒人願意來上幾次吧?所以,在開始教程之前,輸入下面的程式碼:

catch_exception

true

)。

現在休息一下吧!這樣設定後可以讓GUI程式無視引發錯誤的地方繼續工作。把上面的程式碼全都放到一個臨時檔案,像這樣:

catch_exception

true

)。

My_wx_dir

=

code

lib_dir

wx

)。

rr

My_wx_dir

++

“/include/wx。hrl”

)。

rr

My_wx_dir

++

“/src/wxe。hrl”

)。

Wx

=

wx

new

()。

F

=

wxFrame

new

Wx

-

1

“Hello, World!”

)。

wxFrame

show

F

)。

3。3 Frames: 有創意的銷燬

當frame在螢幕上現實時,輸入:

wxFrame

destory

F

)。

它應該返回ok然後frame消失了。現在,就當是開心一下,建立多個frame:

建立一個標題為“Hey!”的wxFrame,變數名為F1

顯示F1

建立一個標題為“Boo!”的wxFrame,變數名為F2

顯示F2。

使用wxFrame:destroy將兩個frame銷燬。 別把這些程式碼放到臨時檔案,我們的lesson要從第一個destroy呼叫繼續。

4。狀態列

狀態列不僅方便程式功能,也便於除錯。

4。1 生成一個狀態列

輸入:

wxFrame

createStatusBar

F

)。

現在你的frame就會像這樣:

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

4。2 設定狀態列文字

將一些文字放到狀態列中:

wxFrame

setStatusText

F

“Quiet here。”

)。

現在看起來像這樣:

花一點時間把上面這些程式碼複製貼上到你的臨時檔案。嘗試向狀態列設定其他文字,然後恢復為“Quiet here”:

SB

=

wxFrame

getStatusBar

F

)。

wxStatusBar

pushStatusText

SB

“A LITTLE LOUDER NOW。”

)。

wxStatusBar

popStatusText

SB

)。

現在應該已經回到了之前你向狀態列新增文字的樣子。

5。 選單欄

按照慣例wxWidgets中的frame都會偶一個選單欄。這樣看起來狀態列選單欄沒什麼區別。然而,選單欄通常由其他東西組成:它們需要被組合到一起。

在wxWidgets中,複雜的東西通常都是由簡單的東西開始一步步構建的。wxWidgets的API不假設新建的複雜的東西包含任何簡單的東西。對於越複雜的東西,所需的構建步驟就越多。

讓我們儘快生成一個可見的選單欄。當你完成後記得複製下面的程式碼到你的臨時檔案,

5。1 生成一個選單欄

輸入:

MenuBar

=

wxMenuBar

new

()。

你將會看到shell輸出:

#wx_ref{ref = 37,type = wxMenuBar,state = []}

但是frame仍然沒有選單欄吧?視窗上沒有任何改變。這是什麼問題導致的呢?

我們有看到選單欄關聯frame嗎?你知道它在哪(譯註:就是指MenuBar),但是wx不能讀取你的腦子。是的,F是到目前為止你僅有的frame。但是wx不假設你想把MenuBar放到F裡面去。

5。1 關聯選單欄與Frame

嘗試一下將MenuBar設定為F的一部分:

wxFrame

setMenuBar

F

MenuBar

)。

它可能會返回ok,但是。。。視窗還是沒有任何東西!到底發生了什麼?有一個方法可以問問frame的選單欄。輸入:

wxFrame

getMenuBar

F

)。

你在shell看到它返回了什麼?和之前setMenuBar返回了一樣的wx_ref。的確發生了選單欄和frame的關聯。問題是:frame沒有顯示這個已經關聯的選單欄。我們得做點什麼。

6。 選單

下面幾步將新增選單項到選單欄,然後顯示它。

6。1 生成選單

大多數GUI應用程式都有一個File(檔案)選單。輸入這個:

FileMn

=

wxMenu

new

()。

你會看到:

#wx_ref{ref = 37,type = wxMenu,state = []}

又是這樣,wxWidgets不知道你想把這個選單新增到哪所以不會顯示。你必須告訴FileMn它應該被放到哪個選單欄。現在我們把FileMn放到F的選單欄MenuBar裡:

wxMenuBar

append

MenuBar

FileMn

“&File”

)。

(“File”前面的“&”符號表示你可以輸入快捷鍵Alt-F使用它)

你會看到視窗有一些改變,就像這樣:

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

但是一個好的選單沒有選單項怎麼行。點選File選單(或者Alt-F) 好吧,沒有任何東西,那是怎麼回事?

6。2 新增一個選單項

每個File選單都有一個Quit選單項,讓我們也新增一個,輸入:

Quit

=

wxMenuItem

new

([{

id

400

},{

text

“&Quit”

}])。

還是之前那樣,你不會看到任何圖形上的改變,只在shell裡面有一個返回值。你需要把它新增到一個選單裡面:

wxMenu

append

FileMn

Quit

)。

點選File選單,現在可以看到:

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

回顧上述設定的選單的所有程式碼,你會看到:

MenuBar

=

wxMenuBar

new

()。

wxFrame

setMenuBar

F

MenuBar

)。

FileMn

=

wxMenu

new

()。

wxMenuBar

append

MenuBar

FileMn

“&File”

)。

Quit

=

wxMenuItem

new

([{

id

400

},{

text

“&Quit”

}])。

wxMenu

append

FileMn

Quit

)。

我們還可以新增什麼呢?每一個得體的應用程式都有一個Help選單。然後Help選單通常有一個About選單項。

6。3 新增一個Help選單

重複你之前新增File選單所用的

new append

命令:

HelpMn

=

wxMenu

new

()。

wxMenuBar

append

MenuBar

HelpMn

“&Help”

)。

你可以看到這個:

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

現在新增一個About選單項。同樣重複之前將Quit選單項新增到File選單的步驟。你會輸入這樣的程式碼:

About

=

wxMenuItem

new

([{

id

500

},{

text

“About”

}])。

wxMenu

append

HelpMn

About

)。

點一下Help選單,你會看到這個:

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

花一點時間把程式碼複製貼上到你的臨時檔案。

7。事件

到目前為止,我們所做的都沒有涉及事件。你可能認為Erlang wxWidgets沒有事件。如果你現在輸入

flush().

,你就不會那樣想了。 事實上,在wxWidgets中每個滑鼠點選都會觸發事件。它們被wx以預設的一些方式處理。通常,wx的預設處理方式是忽略它們。讓我們捕獲事件,看看它到底是什麼樣的。

7。1 使用connect檢視事件

輸入:

wxFrame

connect

F

close_window

)。

點選frame上的關閉按鈕,然後輸入:

flush

()。

你會看到這樣的輸出:

Shell

got

{

wx

-

202

,{

wx_ref

35

wxFrame

,[]},[],{

wxClose

close_window

}}

注意,現在點選關閉按鈕不會真正的關閉一個frame。你將重寫這個預設行為。 多點幾次關閉按鈕,然後點選最大化視窗按鈕,最小化視窗按鈕。然後再次輸入

flush().

。你會看到

close_window

事件,但是沒有最大化最小化事件。

同樣請注意shell怎樣輸出它收到的事件:它不會使用之前讀取的wx定義。你只會看到原始tuple。這使得我們知曉這些wx事件是什麼的難度增加。

有一個使用記錄定義檢視事件的方法。下面的

fun

返回一個事件。輸入:

Ev

=

fun

()

->

receive

E

->

E

after

0

->

empty

end

end

點選關閉按鈕,然後呼叫事件讀取器:

Ev

()。

你會看到類似這樣的東西:

#wx

{

id

=

-

202

obj

=

#wx_ref

{

ref

=

35

type

=

wxFrame

state

=

[]},

userData

=

[],

event

=

#wxClose

{

type

=

close_window

}}

7。2 選單選擇事件

讓我們嘗試關聯(connect)更多事件。輸入:

wxFrame

connect

F

command_menu_selected

)。

嘗試選擇選單。選擇

File->Quit

,然後選擇

File->About

。然後輸入

Ev().

看看生成了哪些事件。除了id外,返回的事件應該都是一樣的。

#wx

{

id

=

400

obj

=

#wx_ref

{

ref

=

35

type

=

wxFrame

state

=

[]},

userData

=

[],

event

=

#wxCommand

{

type

=

command_menu_selected

cmdString

=

[],

commandInt

=

0

extraLong

=

0

}}

7。3 回撥

知道發生了什麼事件很有用。有時它有助於觀察細節。但是大多數時候,我們只想當事件發生時做出我們希望的動作。所以我們必須捕獲事件,然後搞懂怎樣給它新增一個動作。

wx中的事件由回撥函式處理。首先,生成一個回撥函式。輸入:

Ding

=

fun

(_,_)

->

wx_misc

bell

()

end

試試,給它傳入正確的引數。

Ding

#wx

{},

#wx_ref

{})。

它會響鈴嗎?會的。

現在將它關聯到你的frame的close_windows事件上:

wxFrame

connect

F

close_window

[{

callback

Ding

}])。

再試試點選關閉按鈕。就會有嗶嗶聲。試試呼叫

Ev().

它不再返回close_window事件。

因為簡單的嗶嗶聲對於這個關閉視窗事件是沒有實際意義的,你可能想解除關聯

wxFrame

disconnect

F

close_window

)。

8。對話方塊

一個“About”選單項應該給我們顯示一個模態對話方塊。但是怎樣生成這個對話方塊?這裡是最簡單的方法。

8。1 生成一個模態對話方塊

輸入下面這行程式碼:

D

=

wxMessageDialog

new

F

“Let‘s talk。”

)。

它應該返回一個類似這樣的指:

#wx_ref

{

ref

=

43

type

=

wxMessageDialog

state

=

[]}

但是螢幕上不會有任何顯示。

8。2 顯示模態對話方塊

要想顯示對話方塊並與之互動,輸入:

wxMessageDialog

showModal

D

)。

在你的螢幕上就會看到類似這個:

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

Erlang GUI程式設計 - wxWidgets教程

因為對話方塊是模態的,所以直到你點OK之前shell都不會有任何返回值。返回值應該是5100。如果你看看

wx.hrl

,你就會知道它代表wxID_OK。

全文完

(但我怎麼感覺還有後續內容。。。可能是我沒找全,如果後續內容找到了我會更新:) )