是否有必要为wxErlang小部件创建ID?

时间:2010-11-18 22:52:18

标签: erlang wxwidgets wxerlang

我一直在探索Erlang的wx模块和this教程。我之前没有使用过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之后命名指向窗口小部件的变量是否正常?

3 个答案:

答案 0 :(得分:8)

我不知道Erlang但是在C ++中(以及我所知道的其他绑定),通常最好使用wxID_ANY作为小部件ID,这意味着你不关心它的特定值,然后使用Connect()处理来自窗口小部件的事件。如果您需要稍后通过其ID找到窗口小部件(尽管您也可以使用窗口小部件名称),或者如果您需要连续的ID范围(例如使用ID 100,101,...,那么显式ID)可能很方便。 ..,109用于计算器的按钮,因为您可以轻松地从其ID中推导出每个按钮值,但不需要总是使用它们。

至于命名,当然,没有必要使用这个奇怪的约定(快速查看教程表明它是作者的个人偏好 - 不用说,我不共享)。

答案 1 :(得分:5)

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

但是,我认为不仅在ids之后命名变量是不正常的,而且这样做是一个非常糟糕的主意。 根据其含义命名您的变量,而不是使用一些模糊的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相关的文件中{{1}文件。

答案 2 :(得分: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),

等等。 (run through a precompilerzxw:text_input_grid/3 definition

有一次你真的想通过它的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代码很多比巨大的检查级联,异常捕获和跟随更容易理解-on if elif elif elif elif...switchcase..break等等。缺少匹配语言所需的类型代码。

在上面的例子中,所有特定的ID都标记为宏,完全与在C ++和其他C ++小部件工具包中的Wx完成相同。只需使用标准的预定义Wx按钮类型并相应地对它们做出反应,大多数的时间就可以满足您的需求;上面的例子来自于代码,由于某些特定的接口要求(而且等效的C ++代码基本上相同,但实现同样的任务的速度更加冗长),因此需要在代码之下稍微下潜一段时间。

稍高级语言的某些平台有不同的处理身份问题的方法。 iOS和Android小部件工具包(以及QtQuick,就此而言)隐藏了这个细节背后的东西,比如一个更普遍有用的对象引用,而不是依赖于ID。也就是说,那些小部件工具包基本上存储了以{ID =&gt;的散列形式创建的所有小部件。 ObjReference},从每个信号中选择ID,在将控制传递给处理回调之前检索对象引用,并返回存储在哈希中的引用,而不是直接传递ID。

这很光滑,但它不是旧的小部件工具包绑定到C风格的枚举 - 标签代码工作的方式。当它所有的说和完成的计算机仍然只有一个真正的类型:整数 - 我们发明了各种其他的东西,并享受类型和其他乐趣的幻觉。

我们也可以在Erlang中执行此ID到引用的事情,但WxErlang代码通常编写的方式是遵循C ++传统,即使用宏标签后面的对象ID来处理无法识别唯一的事件,以及对象其他一切的参考和标准标签。

上面使用的zx_widgets库是一组预定义的元小部件,它们涵盖了一些最常见的样板字段构造和返回数据结构,这些结构易于在功能上处理。在某些方面,Wx的OOP风格并不是非常适合Erlang(因为这个原因,您在Erlang中编写的looooooongest函数可能是GUI代码),因此有时需要额外的层来构建逻辑 - 与Erlang的其余部分相关的代码。但是,在任何语言和任何环境中,GUI代码都非常烦人。

[注1:有一些奇怪的,令人不舒服的情况,一些C ++风格的谜团通过绑定到你的Erlang代码中泄漏,例如使用2D图形DC画布和诸如此类的神奇环境创建过程。< / EM>