SCIM輸入選框無法自動跟隨光標?
2009-03-29
| 張貼者:
M-OSCAR
Ubuntu下的中文輸入法真係多災多難,之前出現詞頻無法調整的問題,貌似換用繁体中文後就沒事了,今天又發現輸入選框沒有自動跟隨光標的位置,而係傻傻的呆在屏幕上....Google一番得出如下解決方法:
打開/etc/X11/xinit/xinput.d/scim文件,找到如下內容:
GTK_IM_MODULE=xim
QT_IM_MODULE=xim
將它們該成:
GTK_IM_MODULE=scim
QT_IM_MODULE=scim
注銷或重啟X即可~~
打開/etc/X11/xinit/xinput.d/scim文件,找到如下內容:
GTK_IM_MODULE=xim
QT_IM_MODULE=xim
將它們該成:
GTK_IM_MODULE=scim
QT_IM_MODULE=scim
注銷或重啟X即可~~
Posted In
scim,
ubuntu
|
0
意見
|
Erlang 查看文件屬性
| 張貼者:
M-OSCAR
要查看某文件F的屬性,我們可以調用file:read_file_info(F),如果F係一個有效的文件或目錄名,則函數返回{ok, Info},Info係一個#file_info類型的record,包含如下項:
size:文件的大小(字節);
type:類型,可能為:device, directory, regular, other;
access:讀寫模式,可能為:read, write, read_write, none;
atime:文件最后一次被讀的本地時間:{{Year, Mon, Day}, {Hour, Min, Sec}};
mtime:文件最后一次被寫入的本地時間;
ctime:此項對不同的操作系統有不同的意義,在Unix下為文件或inode最后一次更改的時間,在Windows下為文件的創建時間;
mode:(整數)文件權限;
links:連接到該文件的鏈接個數(如果文件系統不支持連接則為1);
major_device:(整數)用于標識文件系統(Unix)或設備號(A:=0, B:=1)(Windows)。
mode和access的功能其實係一樣的,不過你可以用mode來一次過設置多個文件的屬性。下例演示如何查看文件的大小和類型(注意需要包含file.hrl文件,其包含了#file_info的定義):
還有一种更為方便的方式,filelib模塊里包含了一些諸如file_size(File)和is_dir(X)的函數,如果你僅需得到文件的大小信息,直接調用file_size(File)就可以了,不用使用file:read_file_info那么囉嗦~~
譯自《Programming Erlang》- Chapter 13 - Programming with Files
size:文件的大小(字節);
type:類型,可能為:device, directory, regular, other;
access:讀寫模式,可能為:read, write, read_write, none;
atime:文件最后一次被讀的本地時間:{{Year, Mon, Day}, {Hour, Min, Sec}};
mtime:文件最后一次被寫入的本地時間;
ctime:此項對不同的操作系統有不同的意義,在Unix下為文件或inode最后一次更改的時間,在Windows下為文件的創建時間;
mode:(整數)文件權限;
links:連接到該文件的鏈接個數(如果文件系統不支持連接則為1);
major_device:(整數)用于標識文件系統(Unix)或設備號(A:=0, B:=1)(Windows)。
mode和access的功能其實係一樣的,不過你可以用mode來一次過設置多個文件的屬性。下例演示如何查看文件的大小和類型(注意需要包含file.hrl文件,其包含了#file_info的定義):
-include_lib("kernel/include/file.hrl").
file_size_and_type(File) ->
case file:read_file_info(File) of
{ok, Facts} ->
{Facts#file_info.type, Facts#file_info.size};
_ ->
error
end.還有一种更為方便的方式,filelib模塊里包含了一些諸如file_size(File)和is_dir(X)的函數,如果你僅需得到文件的大小信息,直接調用file_size(File)就可以了,不用使用file:read_file_info那么囉嗦~~
譯自《Programming Erlang》- Chapter 13 - Programming with Files
Erlang Directory Operations
| 張貼者:
M-OSCAR
file模塊里有三個函數用于目錄操作:list_dir(Dir)用于列舉目錄Dir里的所有文件;make_dir(Dir)用于創建一個新目錄;del_dir(Dir)用于刪除目錄,例如:
大家可以發現,輸出的文件列表貌似沒有什么順序,也沒有信息說明某項是文件還是子目錄,更沒有文件大小等信息....想要查看上述這些信息,我們可以使用函數file:read_file_info,詳細介紹見下一篇文章~~
譯自《Programming Erlang》- Chapter 13 - Programming with Files
1> cd("/home/joe/book/erlang/Book/code").
/home/joe/book/erlang/Book/code
ok
2> file:list_dir(".").
{ok, ["id3_v1.erl~",
"update_binary_file.beam",
"benchmark_assoc.beam",
....
]}大家可以發現,輸出的文件列表貌似沒有什么順序,也沒有信息說明某項是文件還是子目錄,更沒有文件大小等信息....想要查看上述這些信息,我們可以使用函數file:read_file_info,詳細介紹見下一篇文章~~
譯自《Programming Erlang》- Chapter 13 - Programming with Files
Erlang文件讀寫
| 張貼者:
M-OSCAR
Organization of the Libraries
操作文件的函數被組織成如下4個模塊:
file:打開、關閉、讀寫文件、列取目錄等;
filename:跨平台的文件名操作模塊;
filelib:係file的擴展模塊,包含一系列用于列取文件、檢查文件類型等的函數;
io:用于操作已經打開的文件,例如格式化輸出數据到文件等。
The Different Ways of Reading a File
例如我們要操作包含如下內容的一個文件data1.dat:
下面我們用几种方式來讀取上述文件:
Reading All the Terms in the File
函數file:consult可讀取文件里包含的所有Erlang terms:
file:consult(File)假設文件由一堆Erlang terms組成,如成功讀取文件里的所有terms則返回{ok, [Term]},否則返回{error, Reason}。
Reading the Terms in the File One at a Time
如果想逐個讀取文件里的erlang term,可首先用file:open來打開文件,然后用io:read讀取一個erlang term,直到去到文件尾,最后用file:close關閉文件:
上例用到的函數用法如下:
@spec file:open(File, read) -> {ok, IoDevice} | {error, Why}
嘗試以讀取方式打開文件,如果成功打開則返回{ok, IoDevice},否則返回{error, Reason},IoDevice係一個用于操作文件的標識符。
@spec io:read(IoDevice, Prompt) -> {ok, Term} | {error, Why} | eof
從IoDevice處讀取一個erlang term,如果係讀取文件,參數Prompt會被省略,當我們用io:read從標准輸入處獲取用戶輸入時,Prompt負責輸出一句提示輸入的信息。
@spec file:close(IoDevice) -> ok | {error, Why}
關閉文件。
使用上述的几個函數我們就可以自己實現file:consult了:
BUT file:consult其實并非這樣定義的,它有更好的錯誤報告功能,如果你明白上面的代碼的話,可以去看看真正的file:consult係怎樣的。不過還有一個小問題,我們去哪里找file.erl的源文件?Well,我們可以使用函數code:which,該函數可以定位任何已加載模塊的源文件所在路徑:
在標准發布(standard release)里,每個庫都包含有兩個子目錄,一個叫做src,包含源文件,另一個叫ebin,包含已編譯的Erlang代碼,所以根据上面的返回結果,我們可以知道file.erl的源文件應該在:
/usr/local/lib/erlang/lib/kernel-2.11.2/src/file.erl
Reading the Lines in a File One at a Time
如果我們將io:read換成io:get_line,我們就可以每次讀取文件的一行數据,例如:
Reading the Entire File into a Binary
你可以用函數file:read_file(File)將整個文件當作一個二進製數据來讀取:
如果讀取成功的話,file:read_file(File)返回{ok, Bin},否則返回{error, Why}。這種方式係至今讀取文件最高效的方式。
Reading a File with Random Access
如果我們想要讀取的文件非常大不能一次過進行讀取,那么我們可以打開raw模式用file:pread來讀取文件的任何部分,例如:
file:pread(IoDevice, Start, Len)從IoDevice處讀取以Start為開頭讀取Len個字節的數据(第一個字節的位置為1,不是0),返回{ok, Bin}或{error, Why}。
The Different Ways of Writing to a File
寫文件的過程跟讀文件差不多~~
Write a List of Terms to a File
假設我們想要創建一個能用file:consult來讀取的文件,標准庫里并沒有提供這樣的一個函數,所以我們要自己實現,看下面的例子:
然后在shell里運行上述代碼:
看看是否寫入成功并且可以正常打開:
unconsult首先以write模式來打開文件,然后用io:format(S, "~p\n", [X])來將erlang terms輸出到文件里。
Writing Lines to a File
上述代碼會生成一個名為test2.dat的文件,并寫入如下內容到文件里:
Writing an Entire File in One Operation
用函數file:write_file(Filename, Bytes)一次過寫入整個文件,係最有效率的寫文件操作,如果指定的文件不存在則自動創建一個。
Writing to a Random-Access File
首先我們要先把文件以write模式打開,然后用函數file:pwrite(Position, Bin)來寫入文件,例如:
上述代碼在文件的第10個字節位置處寫入字符串"new",如果原先已經有舊的內容,則舊內容會被覆蓋掉~~
譯自《Programming Erlang》- Chapter 13 - Programming with Files
操作文件的函數被組織成如下4個模塊:
file:打開、關閉、讀寫文件、列取目錄等;
filename:跨平台的文件名操作模塊;
filelib:係file的擴展模塊,包含一系列用于列取文件、檢查文件類型等的函數;
io:用于操作已經打開的文件,例如格式化輸出數据到文件等。
The Different Ways of Reading a File
例如我們要操作包含如下內容的一個文件data1.dat:
{person, "oscar", "tang",
[{occupation, programmer}, {favoriteLanguage, erlang}]}.
{cat, {name, "ml"},{owner, "oscar"}}.下面我們用几种方式來讀取上述文件:
Reading All the Terms in the File
函數file:consult可讀取文件里包含的所有Erlang terms:
1> file:consult("data1.dat").
{ok, [{person, "oscar", "tang",
[{occupation, programmer}, {favoriteLanguage, erlang}]},
{cat, {name, "ml"}, {owner, "oscar"}}]}file:consult(File)假設文件由一堆Erlang terms組成,如成功讀取文件里的所有terms則返回{ok, [Term]},否則返回{error, Reason}。
Reading the Terms in the File One at a Time
如果想逐個讀取文件里的erlang term,可首先用file:open來打開文件,然后用io:read讀取一個erlang term,直到去到文件尾,最后用file:close關閉文件:
1> {ok, S} = file:open("data1.dat", read).
{ok, <0.36.0>}
2> io:read(S, '').
{ok, {person, "oscar", "tang",
[{occupation, programmer}, {favoriteLanguage, erlang}]}}
3> io:read(S, '').
{ok, {cat, {name, "ml"}, {owner, "oscar"}}}
4> io:read(S, '').
eof
5> file:close(S)上例用到的函數用法如下:
@spec file:open(File, read) -> {ok, IoDevice} | {error, Why}
嘗試以讀取方式打開文件,如果成功打開則返回{ok, IoDevice},否則返回{error, Reason},IoDevice係一個用于操作文件的標識符。
@spec io:read(IoDevice, Prompt) -> {ok, Term} | {error, Why} | eof
從IoDevice處讀取一個erlang term,如果係讀取文件,參數Prompt會被省略,當我們用io:read從標准輸入處獲取用戶輸入時,Prompt負責輸出一句提示輸入的信息。
@spec file:close(IoDevice) -> ok | {error, Why}
關閉文件。
使用上述的几個函數我們就可以自己實現file:consult了:
consult(File) ->
case file:open(File, read) of
{ok, S} ->
Val = consult1(S),
file:close(S),
{ok, Val};
{error, Why} ->
{error, Why}
end.
consult1(S) ->
case io:read(S, '') of
{ok, Term} -> [Term | consult1(S)];
eof -> [];
Error -> Error
end.
BUT file:consult其實并非這樣定義的,它有更好的錯誤報告功能,如果你明白上面的代碼的話,可以去看看真正的file:consult係怎樣的。不過還有一個小問題,我們去哪里找file.erl的源文件?Well,我們可以使用函數code:which,該函數可以定位任何已加載模塊的源文件所在路徑:
1> code:which(file).
"/usr/local/lib/erlang/lib/kernel-2.11.2/ebin/file.beam"
在標准發布(standard release)里,每個庫都包含有兩個子目錄,一個叫做src,包含源文件,另一個叫ebin,包含已編譯的Erlang代碼,所以根据上面的返回結果,我們可以知道file.erl的源文件應該在:
/usr/local/lib/erlang/lib/kernel-2.11.2/src/file.erl
Reading the Lines in a File One at a Time
如果我們將io:read換成io:get_line,我們就可以每次讀取文件的一行數据,例如:
1> {ok, S} = file:open("data1.dat", read).
{ok, <0.43.0>}
2> io:get_line(S, '').
"{person, \"oscar\", \"tang\", \n"
3> io:get_line(S, '').
"\t[{occupation, programmer}, {favoriteLanguage, erlang}]}.\n"
....
6> io:get_line(S, '').
eof
7> file:close(S).
okReading the Entire File into a Binary
你可以用函數file:read_file(File)將整個文件當作一個二進製數据來讀取:
1> file:read_file("data1.dat").
{ok, <<"{person, \"oscar\", \"tang\"" ....>>}
如果讀取成功的話,file:read_file(File)返回{ok, Bin},否則返回{error, Why}。這種方式係至今讀取文件最高效的方式。
Reading a File with Random Access
如果我們想要讀取的文件非常大不能一次過進行讀取,那么我們可以打開raw模式用file:pread來讀取文件的任何部分,例如:
1> file:open("data1.dat",[read, binary, raw]).
{ok, {file_descriptor, prim_file, {#Port<0.106>, 5}}}
2> file:pread(S, 22, 46).
{ok, <<"ng\", \n\t[{occupation, progr ....>>}
3> file:pread(S, 1, 10).
{ok, <<"person, \"o">>}
4> file:pread(S, 2, 10).
{ok, <<"erson, \"os">>}
5> file:close(S).
file:pread(IoDevice, Start, Len)從IoDevice處讀取以Start為開頭讀取Len個字節的數据(第一個字節的位置為1,不是0),返回{ok, Bin}或{error, Why}。
The Different Ways of Writing to a File
寫文件的過程跟讀文件差不多~~
Write a List of Terms to a File
假設我們想要創建一個能用file:consult來讀取的文件,標准庫里并沒有提供這樣的一個函數,所以我們要自己實現,看下面的例子:
unconsult(File, L) ->
{ok, S} = file:open(File, write),
lists:foreach(fun(X) -> io:format(S, "~p.\n", [X] end, L),
file:close(S).
然后在shell里運行上述代碼:
1> lib_misc:unconsult("test1.dat",
[{cats, ["zorrow", "daisy"]},
{weather, snowing}]).
ok看看是否寫入成功并且可以正常打開:
2> file:consult("test1.dat").
{ok, [{cats, ["zorrow", "daisy"]}, {weather, snowing}]}unconsult首先以write模式來打開文件,然后用io:format(S, "~p\n", [X])來將erlang terms輸出到文件里。
Writing Lines to a File
1> {ok, S} = file:open("test2.dat", write).
{ok, <0.62.0>}
2> io:format(S, "~s\n", ["Hello readers"]).
ok
3> io:format(S, "~w\n", [123]).
ok
4> io:format(S, "~s\n", ["that's it"]).
ok
5> file:close(S).上述代碼會生成一個名為test2.dat的文件,并寫入如下內容到文件里:
Hello readers
123
that's it
Writing an Entire File in One Operation
用函數file:write_file(Filename, Bytes)一次過寫入整個文件,係最有效率的寫文件操作,如果指定的文件不存在則自動創建一個。
Writing to a Random-Access File
首先我們要先把文件以write模式打開,然后用函數file:pwrite(Position, Bin)來寫入文件,例如:
1> {ok, S} = file:open("....", [raw, write, binary]).
{ok, ....}
2> file:pwrite(S, 10, <<"new">>).
ok
3> file:close(S).
ok上述代碼在文件的第10個字節位置處寫入字符串"new",如果原先已經有舊的內容,則舊內容會被覆蓋掉~~
譯自《Programming Erlang》- Chapter 13 - Programming with Files
Erlang Gen_Event Behaviour
| 張貼者:
M-OSCAR
Event Handling Principles
在Erlang OTP里,事件管理器(event manager)係一個用于處理事件的模塊,這些事件可能係錯誤、警報、或其他需要被記錄的信息。
在事件管理器里可能會有0個、1個或多個事件處理器(event handlers),當事件管理器接收到事件時,所有這些事件處理器都會對事件進行處理。比如有一個處理錯誤的事件管理器,它有一個默認的事件處理器將錯誤信息輸出到終端顯示;你可以為其添加另一個事件處理器,在特定情況下將錯誤信息寫入文件;當不再需要將信息寫入文件時,可以將這個事件處理器移除。
事件管理器實質上係一個接收事件的進程,而事件處理器則為一個回調模塊(callback module)。
Example
下例演示如何實現一個將錯誤信息輸出到終端的事件處理器:
而將錯誤信息寫入文件的事件處理器可以這樣寫:
下面會詳細講解上述代碼。
Starting an Event Manager
要啟動事件管理器來處理錯誤,可使用如下函數:
這函數會新生并連接一個新進程,即事件管理器進程。參數{local, error_man}指定了該事件管理器的注冊名,此例中其注冊名為error_man,僅在本節點有效。如果不注冊一個名字,則必需通過事件管理器進程的進程標識PID來向其發送信息,注冊名也可以設置為{global, Name}。
如果事件管理器係屬於監督樹的一部分,則必需通過gen_event:start_link來啟動。另外有一個gen_event:start函數來啟動一個獨立的事件管理器~~
Adding an Event Handler
下例在shell里演示如何啟動一個事件處理器并為其添加一個事件處理器:
上述函數向名為error_man的事件管理器發送一個事件,告訴它添加一個事件處理器terminal_logger,事件管理器會調用terminal_logger模塊的初始化函數terminal_logger:init([]),其中[]即add_handler函數里的第三個參數。init應該返回{ok, State},State即事件處理器的內部狀態。
Notifying About Events
error_man係事件管理器的名字,no_reply係事件。事件會作為信息發送給事件管理器進程,接收到事件後,事件管理器會按照添加順序調用它每個事件處理器的handle_event(Event, State)函數來處理事件,這些函數都應返回{ok, State1},State1即事件處理器的新內部狀態。
Deleting an Event Handler
上述函數向名為error_man的事件管理器發送一個信息,告知它刪除事件處理器terminal_logger,事件管理器會調用terminal_logger模塊的terminate([], State)函數來終止該處理器。terminate函數應進行一些必要的清理工作等,其返回值會被忽略~~
Stopping
當事件管理器停止時,它會給机會給它的每一個事件處理器進行清理工作。如果該事件處理器係監督樹的一部分,事件管理器在必要的時候會被其監督者關閉;如果是一個獨立的事件管理器,則可調用如下函數去停止它:
在Erlang OTP里,事件管理器(event manager)係一個用于處理事件的模塊,這些事件可能係錯誤、警報、或其他需要被記錄的信息。
在事件管理器里可能會有0個、1個或多個事件處理器(event handlers),當事件管理器接收到事件時,所有這些事件處理器都會對事件進行處理。比如有一個處理錯誤的事件管理器,它有一個默認的事件處理器將錯誤信息輸出到終端顯示;你可以為其添加另一個事件處理器,在特定情況下將錯誤信息寫入文件;當不再需要將信息寫入文件時,可以將這個事件處理器移除。
事件管理器實質上係一個接收事件的進程,而事件處理器則為一個回調模塊(callback module)。
Example
下例演示如何實現一個將錯誤信息輸出到終端的事件處理器:
-module(terminal_logger).
-behaviour(gen_event).
-export([init/1, handle_event/2, terminate/2]).
init(_Args) ->
{ok, []}.
handle_event(ErrorMsg, State) ->
io:format("***ERROR*** ~p\n", [ErrorMsg]),
{ok, State}.
terminate(_Args, _State) ->
ok.
而將錯誤信息寫入文件的事件處理器可以這樣寫:
-module(file_logger).
-behaviour(gen_event).
-export([init/1, handle_event/2, terminate/2]).
init(File) ->
{ok, Fd} = file:open(File, read),
%原文的打開方式係read,但我覺得應該是write或append吧
{ok, Fd}.
handle_event(ErrorMsg, Fd) ->
io:format(Fd, "***ERROR*** ~p\n", [ErrorMsg]),
{ok, Fd}.
terminate(_Args, Fd) ->
file:close(Fd).
下面會詳細講解上述代碼。
Starting an Event Manager
要啟動事件管理器來處理錯誤,可使用如下函數:
gen_event:start_link({local, error_man}).這函數會新生并連接一個新進程,即事件管理器進程。參數{local, error_man}指定了該事件管理器的注冊名,此例中其注冊名為error_man,僅在本節點有效。如果不注冊一個名字,則必需通過事件管理器進程的進程標識PID來向其發送信息,注冊名也可以設置為{global, Name}。
如果事件管理器係屬於監督樹的一部分,則必需通過gen_event:start_link來啟動。另外有一個gen_event:start函數來啟動一個獨立的事件管理器~~
Adding an Event Handler
下例在shell里演示如何啟動一個事件處理器并為其添加一個事件處理器:
1> gen_event:start({local, error_man}).
{ok, <0.31.0>}
2> gen_event:add_handler(error_man, terminal_logger, []).
ok上述函數向名為error_man的事件管理器發送一個事件,告訴它添加一個事件處理器terminal_logger,事件管理器會調用terminal_logger模塊的初始化函數terminal_logger:init([]),其中[]即add_handler函數里的第三個參數。init應該返回{ok, State},State即事件處理器的內部狀態。
Notifying About Events
3> gen_event:notify(error_man, no_reply).
***Error*** no_reply
ok
error_man係事件管理器的名字,no_reply係事件。事件會作為信息發送給事件管理器進程,接收到事件後,事件管理器會按照添加順序調用它每個事件處理器的handle_event(Event, State)函數來處理事件,這些函數都應返回{ok, State1},State1即事件處理器的新內部狀態。
Deleting an Event Handler
4> gen_event:delete_handler(error_man, terminal_logger, []).
ok
上述函數向名為error_man的事件管理器發送一個信息,告知它刪除事件處理器terminal_logger,事件管理器會調用terminal_logger模塊的terminate([], State)函數來終止該處理器。terminate函數應進行一些必要的清理工作等,其返回值會被忽略~~
Stopping
當事件管理器停止時,它會給机會給它的每一個事件處理器進行清理工作。如果該事件處理器係監督樹的一部分,事件管理器在必要的時候會被其監督者關閉;如果是一個獨立的事件管理器,則可調用如下函數去停止它:
>gen_event:stop(error_man).
ok
Erlang Generic Event Handling
2009-03-28
| 張貼者:
M-OSCAR
當我們編程去處理事件的時候,只需將事件作為一個信息發送給負責處理事件的進程:
E就係一個事件(可以係任何Erlang數据類型),RegProcName即處理進程的注冊名。事件發送出去後,發送方就毋需關心該事件會被如何處理了~~
現在我們看看處理事件的進程(事件處理器 - event handler),看個例子:
上例的事件處理器有如下功能:
event_handler:make(Name):創建一個名為Name的事件處理器;
event_handler:event(Name, X):向名為Name的事件處理器發送事件X;
event_handler:add_handler(Name, Fun):將事件處理器的處理函數(最初係一個什么都不幹的函數no_op/1)替換為Fun,替換後當接收到事件時,就會調用新的處理函數Fun來處理該事件。
運行試試效果如何:
Well,沒咩特別事發生,因為現在的處理函數什么都不幹~~我們接著寫個回調模塊(callback module)來設立一個會做實際工作的事件處理函數:
上述代碼編譯之後就會添加一個事件處理函數了:
現在向事件處理器發送信息,將由函數motor_controller:controller/1來處理:
好了,上面寫了一大堆,到底都做了些什么呢?首先我們提供了一個接收事件的事件處理器(這里係一個注冊名為errors的進程),然后定義了如何發送事件給該處理器(發送方毋需知道處理器會對事件作什么處理),最后我們看到怎么在運行過程中動態的更新事件處理函數。
譯自《Programming Erlang》
RegProcName ! {event, E}E就係一個事件(可以係任何Erlang數据類型),RegProcName即處理進程的注冊名。事件發送出去後,發送方就毋需關心該事件會被如何處理了~~
現在我們看看處理事件的進程(事件處理器 - event handler),看個例子:
-module(event_handler).
-export([make/1, add_handler/2, event/2]).
%創建一個名為Name的新事件處理器
%其接收到事件後,會執行函數no_op/1
make(Name) ->
register(Name, spawn(fun() -> my_handler(fun no_op/1) end)).
add_handler(Name, Fun) -> Name ! {add, Fun}.
%產生一個事件
event(Name, X) -> Name ! {event, X}.
my_handler(Fun) ->
receive
{add, Fun1} ->
my_handler(Fun1);
{event, Any} ->
(catch Fun(Any)),
my_handler(Fun)
end.
no_op(_) -> void.
上例的事件處理器有如下功能:
event_handler:make(Name):創建一個名為Name的事件處理器;
event_handler:event(Name, X):向名為Name的事件處理器發送事件X;
event_handler:add_handler(Name, Fun):將事件處理器的處理函數(最初係一個什么都不幹的函數no_op/1)替換為Fun,替換後當接收到事件時,就會調用新的處理函數Fun來處理該事件。
運行試試效果如何:
1> event_handler:make(errors).
true
2> event_handler:event(errors, hi).
{event, hi}
Well,沒咩特別事發生,因為現在的處理函數什么都不幹~~我們接著寫個回調模塊(callback module)來設立一個會做實際工作的事件處理函數:
-module(motor_controller).
-export([add_event_handler/0]).
add_event_handler() ->
event_handler:add_handler(errors, fun controller/1).
controller(too_hot) -> io:format("Turn off the motor!n");
controller(X) -> io:format("~w ignored event: ~p~n", [?MODULE, X]).
上述代碼編譯之後就會添加一個事件處理函數了:
3> c(motor_controller).
{ok, motor_controller}
4> motor_controller:add_event_handler().
{add, #Fun}
現在向事件處理器發送信息,將由函數motor_controller:controller/1來處理:
5> event_handler:event(errors, cool).
motor_controller ignored event: cool
{event, cool}
6> event_handler:event(errors, too_hot).
Turn off the motor
{event, too_hot}
好了,上面寫了一大堆,到底都做了些什么呢?首先我們提供了一個接收事件的事件處理器(這里係一個注冊名為errors的進程),然后定義了如何發送事件給該處理器(發送方毋需知道處理器會對事件作什么處理),最后我們看到怎么在運行過程中動態的更新事件處理函數。
譯自《Programming Erlang》
訂閱:
文章 (Atom)