我看书有个习惯,先看图和代码,然后代码中不懂的再去前后文去找说明,有人说这太浪费时间了,我觉得这样才0距离接触代码。Armstrong的代码看得我十分的不爽,可是,学erlang就是像受戒律一样,忍啊忍,悟啊悟,之后,如修炼得道般豁然开朗。
ok,废话少说,读programming erlang 读到 IRC Lite的时候发现在自己面前横着一道槛了。io_widget是什么?lib_chan是什么?mm是什么?太多东西复杂东西了呢。我下载来老头子的源代码,编译运行一下,看了下前面的说明,知道人与这个IRCLite交互的界面大概是什么样子的了,模仿着老头的代码边读练习写IRC Lite,看下我们的需求列表(我喜欢做一点事然后喘口气,把事情分成一小段一小段来做,完成一个小段,奖励下自己,比如喝口茶,需求列表也是我的备忘录,指导我下一步该做什么,它是不断变化,动态增长的一张表):
- 一个500×200的窗口
- 在这个窗口里面放入一个frame的布局2行1列,上面的作为历史记录显示框,下面的输入框
- 历史记录框是一个editor
- 输入框是一个entry
- 输入框有一个提示符
%%在io_widget应该封装好gs,所以应该这么写,
start(Pid)->
io_widget_init().
io_widget_init()->
GS = gs:start(),
Size = [{width,500,{height,200}],
Win = gs:window(Gs,
[{map,true},
{configure,true},
{title,"chat windows"}|Size]),
State=nil,
loop(Win,state).
loop(Win,State)->
receive
after 5000->
true
end.
编译运行一下

恩不错,顺心的开始是成功的一半。
再来看下我们的需求列表:
- 在这个窗口里面放入一个frame的布局2行1列,上面的作为历史记录显示框,下面的输入框
- 历史记录框是一个editor
- 输入框是一个entry
- 输入框有一个提示符
io_widget_init()->
GS = gs:start(),
Size = [{width,500,{height,200}],
Win = gs:window(Gs,
[{map,true},
{configure,true},
{title,"chat windows"}|Size]),
gs:config(IdOrName, Option).
%%Frame options:%%{packer_x,PackList} where PackList is list() of PackOption, and%%{packer_y,PackList} where PackList is list() of PackOption.
%%PackOption is:%%{stretch, Weight} where Weight is integer() > 0, or%%{stretch, Weight, MinPixelSize, or}%%{stretch, Weight, MinPixelSize, MaxPixelSize}, or%%{fixed, PixelSize}
gs:frame(packer,Win,[{packer_x,[{stretch,1,500}]},
{packer_y,[{stretch,10,120,100},
{stretch,1,15,15}]}]),
%%这里我有个不明白的地方, {packer_y,[{stretch,10,120,100},为什么MinPixerlSize会比
%%MaxPixerlSize难道它们会自动交换?
%%gs:create(Obttype, Parent).
%%gs:create(Objtype, Parent, Options).
%%gs:create(Objtype, Parent, Option).
%%gs:create(Objtype, Name, Parent, Options).
%%gs:create(Objtype, Name, Parent, Option).
gs:create(editor,editor,packer,
[{pack_x,1},{pack_y,1},{vscroll,right}]),
gs:create(entry ,entry ,packer,
[{pack_x,1},{pack_y,2},{keypress,true}]),
gs:config(packer,Size),
State=nil,
loop(Win,state).

- 输入框有一个提示符
- 能用标题右边的红叉来关闭整个应用。
io_widget_init()->
GS = gs:start(),
Size = [{width,500,{height,200}],
Win = gs:window(Gs,
[{map,true},
{configure,true},
{title,"chat windows"}|Size]),
gs:frame(packer,Win,[{packer_x,[{stretch,1,500}]},
{stretch,1,15,15}]}]),
gs:create(editor,editor,packer,
[{pack_x,1},{pack_y,1},{vscroll,right}]),
gs:create(entry ,entry ,packer,
[{pack_x,1},{pack_y,2},{keypress,true}]),
gs:config(packer,Size),
State=nil,
loop(Win,state).

ps:这里所有红色边框是因为截屏软件引起的,不要误解~:)
- 能用标题右边的红叉来关闭整个应用。
- 能接受从entry输入的文字信息。
- 把entry输入的文字信息显示在editor上面。
- 把entry输入在editor上显示的提示符去掉。
loop(Win,State)->
receive
{gs,_,destroy,_,_}->
io:format("Destroyed ~n ",[]),
exit(windowDestroyed)
end.
%%当这个循环接收到来自gs的窗口关闭信息的时候,作出退出整个应用的系统的反应。这里发出一个windowDestroy
%%的消亡信号,所以与这个loop进程相联系的进程都会收到这个信号。我们当前代码中与这个loop进程相关联的进程是
%%我们shell的进程。

- 能接受从entry输入的文字信息(在entry中输入文字,当按下enter键就接收一次entry中的数据)。
- 把entry输入的文字信息显示在editor上面。
- 把entry输入在editor上显示的提示符去掉。
- reset下entry


- 把entry输入的文字信息显示在editor上面。
- 把entry输入在editor上显示的提示符去掉。
- reset下entry
widget(Gs)->
%%这里省略一段代码...
%%原先的gs:config(entry,{insert,{0,Prompt}}).删掉改成如下,算是DRY吧-_-!!!
reset_prompt(Prompt),
loop(Win,State,Prompt).
loop(Win,State,Prompt)->
receive
{gs,entry,keypress,_,['Return'|_]}->
Text = gs:read(entry,text),
Parse_Text = parse(Text),
io:format("Read:~p ~n",[Parse_Text]),
gs:config(entry,{delete,{0,last}}),
reset_prompt(Prompt),
loop(Win,State,Prompt);
{gs,_,destroy,_,_}->
io:format("Destroyed ~n ",[]),
exit(windowDestroyed)
end.
reset_prompt(Prompt)->
gs:config(entry,{insert,{0,Prompt}}).
parse([$>|T])->T;
parse([_|T])->parse(T);
parse([])->exit("no >").

- 把entry输入的文字信息显示在editor上面。
- 当editor中文字信息满掉的时候,能向下滚动。
loop(Win,State,Prompt)->
receive
{gs,entry,keypress,_,['Return'|_]}->
Text = gs:read(entry,text),
Parse_Text = parse(Text),
gs:config(editor,{insert,{'end',Parse_Text++"\n"}}),
%%这里我本想把最后收到信息放在最上面一行的,因为editor的默认vscrollpos一直定位在0的,把
%%最后收到的信息放在最后一行不能进行自动的滚动,想要查看还得使用滚动条,麻烦!
%%尝试过gs:config(editor,{insert,{'0',Parse_Text++"\n"}}),
%%gs:config(editor,{insert,{0,Parse_Text++"\n"}}),
%%gs:config(editor,{insert,{'begin',Parse_Text++"\n"}}),
%%都出错,gs的api上面也没写着!这里知道的朋友请告之下!
scroll_to_show_last_line(),
gs:config(entry,{delete,{0,last}}),
reset_prompt(Prompt),
loop(Win,State,Prompt);
{gs,_,destroy,_,_}->
io:format("Destroyed ~n ",[]),
exit(windowDestroyed)
end.
scroll_to_show_last_line()->
Size = gs:read(editor,size),
Height = gs:read(editor,height),
CharHeight = gs:read(editor,char_height),
TopRow = Size - Height/CharHeight,
if
TopRow >0 -> gs:config(editor,{vscrollpos,TopRow});
true -> gs:config(editor,{vscrollpos,0})
end.

本文分享了使用Erlang编程语言及图形界面库gs开发IRC客户端GUI的过程,作者逐步实现了窗口创建、组件布局、文本输入与显示等功能。
1121

被折叠的 条评论
为什么被折叠?



