Нужно ли придумывать идентификаторы для виджетов wxErlang?

Я возился с Эрлангом 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),

Люди действительно должны назначать идентификаторы виджетов виджетам при их создании? Нормально ли называть переменную, которая указывает на виджет, после идентификатора виджета?

3 ответа

Решение

Я не знаю об Erlang, но в C++ (и в других привязках, о которых я знаю) часто предпочтительнее использовать wxID_ANY в качестве идентификатора виджета, что означает, что вы не заботитесь о его конкретном значении, а затем использовать Connect() обрабатывать события из виджета. Явные идентификаторы могут быть удобны, если вам нужно найти виджет по его идентификатору позже (хотя вы также можете использовать имена виджетов для этого) или если вам нужен последовательный диапазон для идентификаторов (например, имеет смысл использовать идентификаторы 100, 101, ...., 109 для кнопок калькулятора, поскольку вы можете легко определить значение каждой кнопки из ее идентификатора), но нет необходимости всегда использовать их.

Что касается именования, то, разумеется, нет необходимости использовать это странное соглашение (и быстрый просмотр учебника показывает, что это личное предпочтение автора, которое, разумеется, я не разделяю).

Как и VZ, упомянутый выше, вы можете использовать wxID_ANY, если вам не нужно искать виджет по его идентификатору позже.

Тем не менее, я считаю, что не только ненормально называть переменные после идентификаторов, но это очень плохая идея. Назовите ваши переменные в соответствии с их значением, а не с использованием какого-то непонятного идентификатора.

Кроме того, вам лучше определить нужные вам идентификаторы и дать им правильные (семантические) имена, чтобы они определялись только в одном месте, а позже вы сможете легко изменить идентификаторы, не влияя на вашу программу вообще, например так:

-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 файл, который вы должны будете включить во все ваши графические интерфейсы .erl файлы.

Нет. Случаи, когда вы хотите искать что-то по идентификатору, - это примерно те же случаи, когда вам нужно искать что-то по идентификатору в C++. Это применимо ко всем библиотекам виджетов, о которых я могу подумать - каждый раз, когда вы реагируете на закодированный сигнал some_standard_button_name и соответствует метке как ?wxID_OK вы ждете числовой идентификатор, представленный меткой, скрытой макросом. Большинство GUI-библиотек выполняют большую часть предварительной обработки, чтобы вымыть это, поэтому часто вы этого не замечаете (в случае библиотек sooper-dooper, таких как Qt, они все еще работают, только в фоновом режиме, и весь ваш код проходит через прекомпилятор, прежде чем он станет "настоящим" C++...).

Итак, как вы получаете wx вещь, которая была создана? Используя возвращенную ссылку.

Почти каждый wx*:new() call возвращает ссылку на объект [note1]. Это абстрактная ссылка (внутренне кортеж, но не рассчитывайте на это) с достаточным количеством информации для привязок Эрланга и системных процессов 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 и документы)

Один раз, когда вы действительно хотите ссылаться на объект по его идентификатору, а не по ссылке на объект, это то же самое, что и в 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и т. д. введите код, необходимый для языков, в которых нет соответствия.

В приведенном выше случае все конкретные идентификаторы помечены как макросы, точно так же, как это делается в Wx в C++ и других наборах виджетов C++. Большую часть времени ваши потребности удовлетворяются просто с помощью стандартных предопределенных типов кнопок Wx и соответствующей реакции на них; приведенный выше пример взят из кода, который вынужден нырнуть немного ниже, из-за некоторых специфических требований к интерфейсу (и эквивалентный код C++ оказывается практически таким же, но значительно более многословным для выполнения той же задачи).

Некоторые платформы на языках чуть более высокого уровня имеют разные способы решения проблемы идентичности. Наборы виджетов для iOS и Android (и QtQuick, в этом отношении) скрывают эту деталь за чем-то вроде чуть более универсально полезной ссылки на объект, а не в зависимости от идентификаторов. То есть эти наборы виджетов по существу хранят все виджеты, созданные в хэше {ID => ObjReference}, выбирают идентификатор из каждого сигнала, извлекают ссылку на объект перед передачей управления в ваши обратные вызовы обработки и возвращают ссылку, сохраненную в хэш вместо того, чтобы просто передать идентификатор напрямую.

Это гладко, но это не то, как работают старые наборы виджетов, привязанные к коду C-style enums-as-label. Когда все сказанное и выполненное на компьютерах все еще имеет только один реальный тип: целые числа - мы изобретаем всевозможные другие вещи поверх этого и наслаждаемся иллюзией типов и прочего веселья.

Мы могли бы сделать эту функцию ID-to-reference также в Erlang, но способ, которым обычно пишется код WxErlang, состоит в том, чтобы следовать традиции C++, заключающейся в использовании идентификаторов объектов за метками макросов для событий, которые вы не можете избежать уникальной идентификации, и ссылок на объекты и стандартных этикетки для всего остального.

Использованная выше библиотека zx_widgets представляет собой набор предопределенных мета-виджетов, которые охватывают некоторые из наиболее распространенных случаев построения шаблонного поля и возвращают структуры данных, которые просты в обращении функционально. ООП-стиль Wx в некоторых отношениях не совсем подходит для Erlang (по этой причине самые длинные функции, которые вы когда-либо будете писать в Erlang, скорее всего, будут кодом GUI), поэтому для создания логики иногда необходим дополнительный слой Носящий код вяжется с остальной частью Эрланга. Код GUI довольно универсален, но на любом языке и в любой среде.

[примечание 1: Есть несколько странных, неудобных случаев, когда некоторые тайны в стиле C++ просачиваются через привязки в ваш код Erlang, такие как процедура создания магической среды, используемая при использовании 2D-графики DC-холсты и еще много чего.]

Другие вопросы по тегам