不。您希望通过 ID 查找内容的情况与您希望在 C++ 中通过 ID 查找内容的情况大致相同。这适用于我能想到的任何小部件库 - 每次您对编码的信号做出反应some_standard_button_name
并匹配标签时,就像?wxID_OK
您在等待由宏隐藏的标签表示的数字 ID 一样。大多数 GUI 库都会进行大量预处理来清除它,因此您通常不会注意到它(在 Qt 之类的 sooper-dooper 库的情况下,它仍在运行,只是在后台运行,并且您的所有代码都在运行在它成为“真正的”C++之前的预编译器......)。
那么如何获得一个已创建的 wx 事物呢?通过使用其返回的引用。
几乎每个wx*:new()
调用都返回一个对象引用[note1]。这是一个抽象引用(内部是一个元组,但不要指望它),其中包含足够的信息供 Erlang 绑定和 Wx 系统进程明确地谈论已创建的特定 Wx 对象。传递这些引用是以后访问 Wx 对象的典型方式:
GridSz = wxFlexGridSizer:new(2, 2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
不过,一个不太明显的情况是,当您想要一个输入字段网格之类的东西时,您可以循环浏览或按键值拉取:
Scripts = [katakana, hiragana, kanji, latin],
Ilks = [family, given, middle, maiden],
Rows = [{Tag, j(J, Tag)} || Tag <- Scripts],
Cols = [{Tag, j(J, Tag)} || Tag <- Ilks],
{GridSz, Fields} = zxw:text_input_grid(Dialog, Rows, Cols),
% Later on, extracting only present values as
Keys = [{S, I} || S <- Scripts, I <- Ilks],
Extract =
fun(Key, Acc) ->
case wxTextCtrl:getValue(proplists:get_value(Key, Fields)) of
"" -> Acc;
Val -> [{Key, Val} | Acc]
end
end,
NewParts = lists:foldl(Extract, [], Keys),
等等。(zxw:text_input_grid/3 定义和文档)
有一次你真的想通过它的 ID 引用一个对象,而不是它的对象引用,这与 C++ 中的相同:当你正在监听一个特定的点击事件时:
{AddressPicker, _, _, AddressSz} =
zxw:list_picker(Frame,
?widgetADDRESS, ?addADDRESS, ?delADDRESS,
AddressHeader, Addresses, j(J, address)),
然后在通用 wx_object 的消息处理循环中:
handle_event(Wx = #wx{id = Id,
event = #wxCommand{type = command_button_clicked}},
State) ->
case Id of
?editNAME -> {noreply, edit_name(State)};
?editDOB -> {noreply, edit_dob(State)};
?editPORTRAIT -> {noreply, edit_portrait(State)};
?addCONTACT -> {noreply, add_contact_info(State)};
?delCONTACT -> {noreply, del_contact_info(State)};
?addADDRESS -> {noreply, add_address_info(State)};
?delADDRESS -> {noreply, del_address_info(State)};
_ ->
ok = unexpected(Wx),
{noreply, State}
end;
handle_event(Wx = #wx{id = Id,
event = #wxList{type = command_list_item_selected,
itemIndex = Index}},
State) ->
case Id of
?widgetCONTACT -> {noreply, update_selection(contact, Index, State)};
?widgetADDRESS -> {noreply, update_selection(address, Index, State)};
_ ->
ok = unexpected(Wx),
{noreply, State}
end;
第一个子句专门处理对非标准按钮的单击,第二个子句处理列表控件小部件选择事件,以在界面中执行一些任意操作。虽然展开#wx{}
事件记录在视觉上不是很吸引人,但在子句格式中使用匹配使得这个 GUI 代码在维护期间比检查、异常捕获和后续的巨大级联代码更if elif elif elif elif...
容易理解,switch
或者case..break
等等。类型代码在语言中是必需的缺乏匹配。
在上述情况下,所有特定的 ID 都被标记为宏,这与在 C++ 中的 Wx 和其他 C++ 小部件工具包中完成的方式完全相同。大多数情况下,您只需使用标准预定义的 Wx 按钮类型并相应地对其做出反应即可满足您的需求;上面的示例来自由于某些特定的接口要求而被迫略低于该代码的代码(等效的 C++ 代码基本上相同,但完成相同任务时要冗长得多)。
一些稍高级语言的平台有不同的方式来处理身份问题。iOS 和 Android 小部件工具包(以及 QtQuick,就此而言)隐藏了这些细节,例如稍微更普遍有用的对象引用,而不是依赖于 ID。也就是说,这些小部件工具包本质上将所有创建的小部件存储在 {ID => ObjReference} 的哈希中,从每个信号中选择 ID,在将控制权传递给处理回调之前检索对象引用,并返回存储在散列而不是直接传递 ID。
这很巧妙,但它不是绑定到 C 样式枚举作为标签代码的旧小部件工具包的工作方式。话虽如此,计算机仍然只有一种真正的类型:整数——我们在此基础上发明了各种其他东西,并享受类型的错觉和其他乐趣。
我们也可以在 Erlang 中做这个 ID-to-reference 的事情,但是 WxErlang 代码的典型编写方式是遵循 C++ 的传统,即在宏标签后面使用对象 ID 来处理您无法避免唯一标识的事件,以及对象引用和标准其他一切的标签。
上面使用的 zx_widgets 库是一组预定义的元小部件,涵盖了一些最常见的样板字段构造情况,并返回易于在功能上处理的数据结构。Wx 的 OOP 风格在某些方面并不适合 Erlang(出于这个原因,您在 Erlang 中编写的最冗长的函数很可能是 GUI 代码),因此有时需要额外的层来制作逻辑- 与 Erlang 的其余部分兼容的代码。然而,在任何语言和任何环境中,GUI 代码都非常令人讨厌。
[注 1:有一些奇怪的、不舒服的情况,一些 C++ 风格的谜团通过绑定泄漏到你的 Erlang 代码中,例如使用 2D 图形 DC 画布所涉及的神奇环境创建过程等等。]