3

我一直在研究 Erlang 的wx模块和教程。我以前没有使用过 wxwidgets,所以也许这就是它的完成方式,但是这段代码对我来说似乎真的很糟糕:

%% create widgets
    T1001 = wxTextCtrl:new(Panel, 1001,[]),
    ST2001 = wxStaticText:new(Panel, 2001,"Output Area", []),
    B101  = wxButton:new(Panel, 101, [{label, "&Countdown"}]),
    B102  = wxButton:new(Panel, ?wxID_EXIT, [{label, "E&xit"}]),
    wxFrame:show(Frame),

人们真的必须在创建小部件时为其分配小部件 ID 吗?用小部件的ID命名指向小部件的变量是否正常?

4

3 回答 3

8

我不了解 Erlang,但在 C++(以及我知道的其他绑定)中,通常最好将其wxID_ANY用作小部件 id,这意味着您不关心它的具体值,然后用于Connect()处理来自小部件。如果您稍后需要通过其 id 查找小部件(尽管您也可以为此使用小部件名称)或者如果您需要连续的 id 范围(例如使用 id 100、101、. .., 109 用于计算器的按钮,因为您可以轻松地从其 id 中推断出每个按钮的值),但无需总是使用它们。

至于命名,当然没有必要使用这种奇怪的约定(快速浏览一下教程就会发现这是作者的个人偏好——不用说,我不分享)。

于 2010-11-19T17:15:02.710 回答
5

与上面提到的 VZ 一样,如果您以后不需要通过其 id 查找小部件,则可以使用 wxID_ANY。

但是,我认为不仅在 id 之后命名变量是不正常的,而且这样做是一个非常糟糕的主意。根据变量的含义命名变量,而不是使用一些晦涩的 id

此外,您最好定义您需要的 id 并为它们提供正确的(语义)名称,以便它们只在一个地方定义,您以后可以轻松地更改 id 而不会影响您的程序,如下所示:

-define(ID_TEXT_CTRL, 1001).
-define(ID_OUTPUT_AREA, 2001).
-define(ID_COUNTDOWN_BUTTON, 101).
-define(ID_EXIT_BUTTON, ?wxID_EXIT).

TextCtrl = wxTextCtrl:new(Panel, ?ID_TEXT_CTRL,[]),
OutputArea = wxStaticText:new(Panel, ?ID_OUTPUT_AREA,"Output Area", []),
CountdownButton  = wxButton:new(Panel, ?ID_COUNTDOWN_BUTTON, [{label, "&Countdown"}]),
ExitButton  = wxButton:new(Panel, ?ID_EXIT_BUTTON, [{label, "E&xit"}])

您可以将定义放在您的.erl文件中,如果它只有一个,或者放在一个.hrl文件中,您必须将其包含在所有与 GUI 相关的.erl文件中。

于 2010-11-21T23:03:32.650 回答
1

不。您希望通过 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 画布所涉及的神奇环境创建过程等等。]

于 2015-12-31T06:53:25.423 回答