代码之家  ›  专栏  ›  技术社区  ›  nmichaels

有必要为wxErlang小部件发明id吗?

  •  3
  • nmichaels  · 技术社区  · 13 年前

    我一直在找厄兰的 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 回复  |  直到 8 年前
        1
  •  8
  •   VZ.    13 年前

    我不知道关于Erlang,但在C++中(以及在我知道的其他绑定中),使用它通常是更好的。 wxID_ANY 作为widget id,意味着您不关心它的特定值,然后使用 Connect() 从小部件处理事件。如果以后需要按其id查找小部件(尽管也可以为此使用小部件名称),或者如果需要id的连续范围(例如,使用IDS100、101、。。。,109对于一个计算器的按钮,你可以很容易地从它的id中推断出每个按钮的值),但是没有必要总是使用它们。

    至于命名,当然,不需要使用这种奇怪的约定(快速浏览教程可以发现,这是作者个人的偏好——不用说,我不愿意分享)。

        2
  •  5
  •   3lectrologos    13 年前

    像上面提到的VZ一样,如果以后不需要通过widget的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公司 文件夹。

        3
  •  1
  •   zxq9    8 年前

    不,你想通过ID查找东西的情况大致相同,在C++中你想用ID查找一些东西。这适用于我能想到的任何小部件库——每当你对一个编码的信号做出反应时 some_standard_button_name 匹配一个标签 ?wxID_OK 您正在等待由宏隐藏的标签表示的数字标识。大多数GUI库都会做大量的预处理来清除这个问题,所以通常你不会注意到它(对于像Qt这样的sooper-dooper库,它仍然在运行,只是在后台,而你的所有代码 run through a precompiler 在它变成“真正的”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 definition ,和 docs )

    一次你真的想通过它的ID引用对象而不是它的对象引用与C++是一样的:当你在听一个特定的点击事件:

    {AddressPicker, _, _, AddressSz} =
        zxw:list_picker(Frame,
                        ?widgetADDRESS, ?addADDRESS, ?delADDRESS,
                        AddressHeader, Addresses, j(J, address)),
    

    然后在泛型wx_对象的消息处理循环中:

    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{} 事件记录在视觉上不是很吸引人,使用匹配in子句的形式使得这个GUI代码 许多的 在维护过程中比庞大的检查、异常捕获和后续操作更容易理解 if elif elif elif elif... , switch case..break ,等。键入缺少匹配的语言所需的代码。

    在上述情况下,所有特定的id都标记为宏, 确切地 同样的方式,这是在WX在C++和其他C++部件工具包中完成的。 大多数 在时间上,只需使用标准预定义的WX按钮类型,并相应地对它们作出反应;上面的示例是由于某些特定的接口需求而被迫低于某一点的代码(和等效C++代码基本上是相同的,但要完成同样的任务却要更加冗长)。

    一些稍高级别语言的平台有不同的方法来处理身份问题。iOS和Android小部件包(以及QtQuick)隐藏了这个细节,比如一个更通用的对象引用,而不是依赖于id。也就是说,这些小部件包实际上存储了在{ID=>ObjReference}散列中创建的所有小部件,从每个信号中选择ID,在将控制权传递给处理回调之前检索对象引用,并返回散列中存储的引用,而不是直接传递ID。

    这很巧妙,但这不是旧的小部件包绑定到C样式枚举作为标签代码工作的方式。当它的所有说和做的电脑仍然只有一个真正的类型:整数——我们发明了各种其他的东西在上面,享受类型和其他乐趣的幻想。

    我们也可以在Erlang上做这个引用,但是WxErlang代码通常写的方式是遵循C++的传统,在宏标签后面使用对象ID,以避免无法识别的事件,以及对象引用和其他东西的标准标签。

    上面使用的zx_widgets库是一组预定义的元widgets,它涵盖了样板字段构造的一些最常见情况,并返回易于功能处理的数据结构。Wx的OOP风格在某些方面不太适合Erlang(因此,在Erlang中编写的loooooongest函数很可能是GUI代码),因此有时需要额外的一层来使承载逻辑的代码与Erlang的其余部分保持一致。但是,在任何语言和环境中,GUI代码都是非常烦人的。

    [注1: 有一些奇怪的、不舒服的案例,其中一些C++风格的神秘通过绑定进入你的Erlang代码,比如使用2D图形DC CouvAsAs和WHATNOT的神奇环境创建过程。 ]