是否有必要为 wxErlang 小部件发明 ID?

发布于 2024-10-03 23:29:04 字数 610 浏览 2 评论 0原文

我一直在研究 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 吗?在widget的ID之后命名指向widget的变量是否正常?

I've been poking around with Erlang's wx module and this tutorial. I haven't used wxwidgets before, so maybe this is just how it's done, but this code seems really terrible to me:

%% 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),

Do people really have to assign widget IDs to widgets when creating them? Is it normal to name the variable that points to the widget after the widget's ID?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

来世叙缘 2024-10-10 23:29:04

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

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

I don't know about Erlang but in C++ (and in the other bindings I know about) it's often preferable to use wxID_ANY as the widget id, meaning that you don't care about its specific value, and then use Connect() to handle events from the widget. Explicit ids may be convenient if you need to find the widget by its id later (although you can also use widget names for this) or if you need a consecutive range for ids (e.g. it would make sense to use ids 100, 101, ..., 109 for the buttons of a calculator as you could then easily deduce each buttons value from its id) but there is no need to always use them.

As for the naming, there is, of course, no need to use this strange convention (and a quick look at the tutorial shows that it's a personal preference of the author -- which, needless to say, I don't share).

夏花。依旧 2024-10-10 23:29:04

与上面提到的 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 相关的 .erl 文件。

Like VZ mentioned above, you can use wxID_ANY if you won't need to lookup a widget by its id later.

However, I believe that not only it's not normal to name the variables after the ids, but rather it's a very bad idea to do so. Name your variables according to their meaning and not using some obscure id.

Also, you'd better define the ids you need and give them proper (semantic) names, so that they are defined in only one place and you can later easily change the ids without affecting your program at all, like this:

-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"}])

You may put the definitions either in your .erl file, if it's only one, or in an .hrl file, which you'll have to include in all your GUI-related .erl files.

神仙妹妹 2024-10-10 23:29:04

不。您想要通过 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 定义,以及docs)

当你确实想通过对象的 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...switchcase..break 等。键入必要的代码在缺乏匹配的语言中。

在上述情况下,所有特定 ID 都被标记为宏,完全相同,这与 C++ 和其他 C++ 小部件套件中的 Wx 中完成的方式相同。 大多数,只需使用标准的预定义 Wx 按钮类型并对它们做出相应的反应即可满足您的需求;上面的示例来自由于某些特定接口要求而被迫低于该值的代码(等效的 C++ 代码最终基本上相同,但完成相同任务时要冗长得多)。

一些使用稍微高级语言的平台有不同的方式来处理身份问题。 iOS 和 Android 小部件套件(以及 QtQuick)将此细节隐藏在稍微更普遍有用的对象引用后面,而不是依赖于 ID。也就是说,这些小部件套件本质上存储了在 {ID =>; 的哈希值中创建的所有小部件。 ObjReference},从每个信号中选取 ID,在将控制传递给处理回调之前检索对象引用,并返回存储在哈希中的引用,而不是直接传递 ID。

这很巧妙,但它不是旧版小部件套件绑定到 C 样式枚举作为标签代码的工作方式。当这一切都说完了之后,计算机仍然只有一种真实类型:整数——我们在此基础上发明了各种其他东西,并享受类型的幻觉和其他乐趣。

我们也可以在 Erlang 中执行 ID 到引用的操作,但是 WxErlang 代码通常编写的方式是遵循 C++ 传统,即在宏标签后面使用对象 ID 来表示无法避免唯一标识的事件,并且对象引用和标准其他一切的标签。

上面使用的 zx_widgets 库是一组预定义的元小部件,涵盖了样板字段构造和返回易于功能处理的数据结构的一些最常见情况。 Wx 的 OOP 风格在某些方面不太适合 Erlang(由于这个原因,你在 Erlang 中编写的最棒的函数很可能是 GUI 代码),因此有时需要额外的层来制作逻辑-轴承代码与 Erlang 的其余部分一致。然而,无论在任何语言和任何环境中,GUI 代码都非常烦人。

[注1:有一些奇怪的、令人不舒服的情况,其中一些 C++ 风格的秘密通过绑定泄漏到您的 Erlang 代码中,例如使用 2D 图形 DC 画布等涉及的神奇环境创建过程。 ]

No. The cases in which you would want to look something up by ID are roughly the same cases in which you would want to look something up by ID in C++. This applies across any widget library I can think of -- every time you react to a signal coded some_standard_button_name and matches a label like ?wxID_OK you are waiting for a numeric ID that is represented by a label hidden by a macro. Most GUI libraries do a lot of preprocessing to wash this out, so often you don't notice it (in the case of sooper-dooper libraries like Qt its still going on, just in the background, and all of your code is run through a precompiler before it becomes "real" C++...).

So how do you get a hold of a wx thingy that's been created? By using its returned reference.

Almost every wx*:new() call returns an object reference[note1]. This is an abstract reference (internally a tuple, but don't count on that) with enough information for the Erlang bindings and the Wx system processes to talk unambiguously about specific Wx objects that have been created. Passing these references around is the typical way of accessing Wx objects later on:

GridSz = wxFlexGridSizer:new(2, 2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),

A less obvious case, though, is when you want something like a grid of input fields that you can cycle through or pull by key value:

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),

And so on. (zxw:text_input_grid/3 definition, and docs)

The one time you really do want to reference an object by its ID and not its object reference is the same as in C++: when you are listening for a specific click event:

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

And then later in the message handling loop of the generic 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;

The first clause deals specifically with clicks on non-standard buttons, and the second with list-control widget selection events to do some arbitrary things in the interface. While unwrapping the #wx{} event record isn't very visually appealing, using matching in clause formation makes this GUI code much easier to understand during maintenance than the gigantic cascade of checks, exception catches and follow-on if elif elif elif elif..., switch or case..break, etc. type code necessary in languages that lack matching.

In the above case all the specific IDs are labeled as macros, exactly the same way this is done in Wx in C++ and other C++ widget kits. Most of the time your needs are served simply by using the standard pre-defined Wx button types and reacting to them accordingly; the example above is from code that is forced to dive a bit below that because of some specific interface requirements (and the equivalent C++ code winds up essentially the same, but dramatically more verbose to accomplish the same task).

Some platforms in slightly higher level languages have different ways of dealing with the identity issue. iOS and Android widget kits (and QtQuick, for that matter) hide this detail behind something like a slightly more universally useful object reference rather than depending on IDs. That is to say those widget kits essentially store all widgets created in a hash of {ID => ObjReference}, pick the ID out of every signal, retrieve the object reference before passing control to your handling callbacks, and return the reference stored in the hash instead of just passing the ID through directly.

That's slick, but its not the way older widget kits bound to C-style enums-as-labels code work. When its all said and done computers still have only one real type: integers -- we invent all sorts of other stuff on top of this and enjoy the illusion of types and other fun.

We could do this ID-to-reference thing in Erlang also, but the way WxErlang code is typically written is to follow the C++ tradition of using object IDs behind macro labels for events you can't avoid identifying uniquely, and object references and standard labels for everything else.

The zx_widgets library employed above is a set of pre-defined meta-widgets that cover some of the most common cases of boilerplate field construction and return data structures that are easy to handle functionally. The OOP style of Wx isn't a very natural fit for Erlang in some ways (the looooooongest functions you'll ever write in Erlang are likely to be GUI code for this reason), so an extra layer is sometimes necessary to make the logic-bearing code jibe with the rest of Erlang. GUI code is pretty universally annoying, though, in any language and in any environment.

[note1: There are some weird, uncomfortable cases where a few C++-style mysteries leak through the bindings into your Erlang code, such as the magical environment creation procedure involved in using 2D graphics DC canvasses and whatnot.]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文