檢視原始碼 wx,wxWidgets 的 Erlang 綁定
wx 應用程式是 wxWidgets 的 Erlang 綁定。本文檔描述了 Erlang 對 wxWidgets 的映射及其實現。它不是 wxWidgets 的完整使用者指南。如果您需要,則必須閱讀 wxWidgets 文件。wx 嘗試與原始 API 保持一對一的映射,以便盡可能輕鬆地使用原始文件和範例。
可以在 Erlang src 發行版中找到 Wx 範例和測試套件。它們還可以提供有關如何使用 API 的一些幫助。
目前這只是對 wx 的一個非常簡短的介紹。該應用程式仍在開發中,這表示介面可能會發生變化,並且測試套件目前的覆蓋率很低。
目錄
簡介
原始的 wxWidgets 是一個物件導向 (C++) API,這反映在 Erlang 映射中。在大多數情況下,wxWidgets 中的每個類別都表示為 Erlang 中的一個模組。這使得 wx 應用程式具有一個龐大的介面,分佈在多個模組中,並且所有這些都從 wx 模組開始。wx 模組包含用於建立和銷毀 GUI 的函數,即 wx:new/0
、wx:destroy/0
和一些其他有用的函數。
wx 中的物件或物件參考應被視為 Erlang 進程,而不是 Erlang 項。當您對它們進行操作時,它們可能會改變狀態,例如,它們不是像 Erlang 項那樣的函數式物件。每個物件都有一個類型或更確切地說是一個類別,它通過相應的模組或該物件的子類別來操作。會進行類型檢查,以便模組僅對其物件或繼承的類別進行操作。
使用 new 建立物件,並使用 destroy 銷毀物件。類別中的大多數函數都與其 C++ 對應項同名,只是為了方便起見,在 Erlang 中它們以小寫字母開頭,第一個參數是物件參考。可選參數放在最後,並以任何順序表示為標記的元組。
例如,wxWindow C++ 類別是在 wxWindow Erlang 模組中實現的,因此成員 wxWindow::CenterOnParent 為 wxWindow:centerOnParent。以下 C++ 程式碼
wxWindow MyWin = new wxWindow();
MyWin.CenterOnParent(wxVERTICAL);
...
delete MyWin;
在 Erlang 中看起來會像
MyWin = wxWindow:new(),
wxWindow:centerOnParent(MyWin, [{dir,?wxVERTICAL}]),
...
wxWindow:destroy(MyWin),
當您閱讀 wxWidgets 文件或範例時,您會注意到 wx 中缺少一些最基本的類別,它們會直接映射到對應的 Erlang 項
wxPoint 由 {X坐標,Y坐標} 表示
wxSize 由 {寬度,高度} 表示
wxRect 由 {X坐標,Y坐標,寬度,高度} 表示
wxColour 由 {紅色,綠色,藍色[,Alpha]} 表示
wxString 由 unicode:charlist() 表示
wxGBPosition 由 {列,欄} 表示
wxGBSpan 由 {列跨度,欄跨度} 表示
wxGridCellCoords 由 {列,欄} 表示
在 Erlang API 與原始 API 不同之處,Erlang 文件應該會明確指出使用了哪種表示形式。例如,C++ 陣列和/或清單有時表示為 Erlang 清單,有時表示為元組。
顏色用 {紅色,綠色,藍色[,Alpha]} 表示,當用作函數的引數時,Alpha 值是可選的,但它始終會從 wx 函數傳回。
定義、列舉和全域變數以定義的形式存在於 wx.hrl
中。這些定義大多數是常數,但並非全部。有些是與平台相關的,因此全域變數必須在執行階段進行實例化。這些將通過調用從驅動程式取得,因此並非所有定義都可以在匹配語句中使用。類別局部列舉將以類別名稱和底線作為字首,例如 ClassName_Enum
。
此外,一些全域函數(即非類別函數)存在於 wx_misc
模組中。
Wx 是作為一個(多執行緒)驅動程式實現的,並且是 C++ API 的相當直接的介面,缺點是如果 Erlang 程式設計師發生錯誤,可能會導致模擬器崩潰。
由於驅動程式是多執行緒的,因此它需要啟用 smp 的模擬器,該模擬器為驅動程式提供執行緒安全介面。
多個進程和記憶體處理
目的是每個 Erlang 應用程式都調用一次 wx:new() 來設定其 GUI,這會建立一個環境和一個記憶體映射。為了能夠在應用程式中的多個進程中使用 wx,您必須共享環境。您可以使用 wx:get_env/0
取得活動環境,並使用 wx:set_env/1
在新進程中設定它。兩個都調用過 wx:new() 的進程或應用程式將無法使用彼此的物件。
wx:new(),
MyWin = wxFrame:new(wx:null(), 42, "Example", []),
Env = wx:get_env(),
spawn(fun() ->
wx:set_env(Env),
%% Here you can do wx calls from your helper process.
...
end),
...
當調用 wx:destroy/0
時,或當應用程式中的所有進程都已結束時,記憶體將被刪除,並且該應用程式建立的所有視窗都將被關閉。
只要使用者應用程式處於活動狀態,wx 應用程式永遠不會清除或垃圾回收記憶體。當視窗關閉時,或至少當所有具有非空父引數的物件關閉時,大部分物件都會被刪除。在可能的情況下使用 wxCLASS:destroy/1
可以避免記憶體使用量增加。當 wxWidgets 假設或建議您(或更確切地說 C++ 程式設計師)已在堆疊上分配物件時,這一點尤其重要,因為這永遠不會在 Erlang 綁定中完成。例如 wxDC
類別或其子類別或 wxSizerFlags
。
目前,顯示模式對話方塊會凍結 wxWidgets,直到對話方塊關閉。這是預期的,但在 Erlang 中,您可以同時執行多個 GUI 應用程式,這會導致問題。希望這會在未來的 wxWidgets 版本中得到修正。
事件處理
wx 中的事件處理與原始 API 的差異最大。您必須在 wxWidgets 中指定您要處理的每個事件,這在 Erlang 綁定中是相同的,但您可以選擇將事件接收為訊息或使用回呼 funs 來處理它們。
否則,事件訂閱將作為 wxWidgets 動態事件處理程式連線處理。您可以訂閱來自具有 ID 或在 ID 範圍內的物件的特定類型的事件。回呼 fun 是可選的,如果未提供,則事件將發送到調用 connect/2 的進程。因此,處理程式是一個回呼 fun 或一個將接收事件訊息的進程。
事件會按照由下往上、在小工具層次結構中的順序,由最後訂閱的處理程式優先處理。根據是否調用了 wxEvent:skip()
,其他處理程式之後將處理該事件。大多數事件都已安裝了預設事件處理程式。
訊息事件看起來像 #wx{id=integer(), obj=wx:wxObject(), userData=term(), event=Rec }。id 是接收事件的物件的識別碼。obj 欄位包含您使用 connect 的物件。userData 欄位包含使用者提供的項,這是 connect 的一個選項。event 欄位包含具有事件類型相關資訊的記錄。事件記錄中的第一個元素始終是您訂閱的類型。例如,如果您訂閱了 key_up 事件,您將收到 #wx{event=Event}
,其中 Event 將是一個 wxKey 事件記錄,其中 Event#wxKey.type = key_up
。
在 wxWidgets 中,開發人員必須調用 wxEvent:skip()
,如果他希望其他處理程式處理該事件。如果您使用回呼,您可以在 wx 中執行相同的操作。如果您想要將事件作為訊息,則只需不提供回呼,並且您可以在 connect 調用中將 skip 選項設定為 true 或 false,預設值為 false。True 表示您收到訊息,但也讓後續處理程式處理該事件。如果您想要動態變更此行為,您必須使用回呼並調用 wxEvent:skip()
。
回呼事件處理是通過在附加處理程式時使用可選回呼 fun/2 來完成的。fun(#wx{},wxObject() 必須採用兩個引數,其中第一個引數與上述訊息事件中的相同,第二個引數是實際事件物件的物件參考。使用事件物件,您可以調用 wxEvent:skip()
並存取所有資料。使用回呼時,如果您希望將任何事件轉發到後續處理程式,則必須自行調用 wxEvent:skip()
。實際的事件物件會在 fun 傳回後刪除。
回呼始終由另一個進程調用,並且在調用時具有 GUI 的獨佔使用權。這表示回呼 fun 無法使用進程字典,並且不應調用其他進程。如果其他進程正在等待其對 GUI 的調用完成,則在回呼 fun 內調用另一個進程可能會導致死鎖。
致謝
Mats-Ola Persson 編寫了最初的 wxWidgets 綁定,作為他碩士論文的一部分。目前版本是完全重新編寫的,但許多想法已被重複使用。重新編寫的原因主要是由於我們給予他的限制要求。
還要感謝 wxWidgets 團隊開發和支援它,以便我們可以使用它。