Wlad писал(а):
Не для того, что бы "моё слово было последним" ! Просто - вдогонку, хотя и говорят, что "не делай добра - не будет зла" ...
Ну что уж прямо за отношение! Форум на то и форум, чтобы обсуждать, и отнюдь не факт, что вот прямо все должны иметь одинаковую точку зрения, одинаковый набор навыков, одинаковую специфику и прочее. Тогда все сведется к редкой унылой констатации того, что "DUP дублирует число на вершине стека", потому что больше-то обсуждать нечего. А продуктивное обсуждение может возникнуть практически на любой основе. Как в той самой шутке/притче, что спросить на форуме, как надо делать, обычно означает не получить ничего, но вот написать первый попавшийся, даже неправильный, вариант - хороший способ получить вал ответов, среди которых будут и правильные.
Wlad писал(а):
Обратите внимание ещё на такой класс, как TAction.
Не знаю, есть ли он в Lazarus-е и, если есть, то также он реализован, как в VCL, но в Дельфе он позволяет получать иногда очень красивые и рациональные решения.
Ну вот это как раз и пример того, как предметы разговора "не состыковались". И мне тут интересно в первую очередь довести точку зрения, потому что я понимаю, что рано или поздно подобные вопросы возникнут от кого-либо в RL. А раз так, нужно корректировать изложение, заранее поясняя неочевидные вещи. Я понимаю, что бывают случаи, когда человек агрессивно потакает своим комплексам и на форумы ходит только за "энергетической подпиткой", изощряясь в оскорбительных эпитетах для всех подряд. Но поскольку тут явно не такой случай, я для себя считаю, что нужно нагляднее доносить идею.
Конкретно здесь проблема программирования лежит в другой плоскости. И явно необходимо обозначить переход от поколения "перед Delphi" к "Delphi, C++ Builder и другие RAD tools". Итак, что у нас было. Был цикл GetMessage - TranslateMessage - DispatchMessage. Был обработчик сообщений, у которого самый редактируемый пункт был фрагмент, относящийся к wm_command. Почему так - потому что написать функцию было недостаточно. Функция могла вызываться только в обработчике wm_command, который принимал сообщение со своим id, а для этого нужно было описать еще и элемент, который это сообщение генерировал. Поэтому добавление новой функции сопровождалось рутинными действиями "copy-paste кода в обработчике с заменой id и имени вызываемой функции, далее copy-paste кнопки с заменой генерируемого id, а заодно координат и надписи". Чревато ошибками из-за невнимательности.
Дельфи-подобные инструменты уже лучше, поскольку визуальный конструктор после перетаскивания кнопки уже генерирует весь обработчик, так что нажатие на кнопку и сообщение создаст, и вызовет обработчик. Вот только обработчик пустой, его надо написать, но это как раз то, что и хотел бы сделать программист.
Что получается в Форте при решении "в лоб". Получается обычно откат на первый шаг и попытка подключить LoadLibrary, RegisterClassEx и тонны констант. Эти тонны сводят на нет усилия, тем более что результат, который только брезжит впереди - это программы примерно как до эпохи Дельфи, "только на Форте".
Заходим с другой стороны. Вот есть форма и кнопка. Кнопка что-то делает, но хотелось бы поменять ее действие без перекомпиляции всего. Решение "лобовое в стиле Дельфи" - рядом с кнопкой положить выпадающие списки, чекбоксы, поля и прочее, что будет ее настраивать. Это ситуация, когда лечение хуже болезни - на один виджет нужно еще 5-10. Что можно от Форта? А пусть кнопка запускает на выполнение строку на Форте. Тогда будет достаточно отредактировать эту строку, чтобы изменить функции кнопки в очень широких пределах. Нужно, конечно, где-то держать эту строку, и естественно, нужна Форт-машина.
Теперь переходим к сути проекта. Допустим, у нас 10 кнопок, и, очевидно, 10 строк. Нажали button1 - выполнилась str1. Нажали button2 - соответственно, str2. В тексте программы... хмм, ладно, 10 кусков кода с именами button1clicked, button2clicked... button10clicked. Во всех почти одно и то же, только меняются имена строковых переменных. Оно может быть сделано и через TAction, но все равно - куда деваться от нарастающего кома обработчиков? А если кнопок 100? А если еще выпадающие списки? А поля редактирования?
Напрашивается решение - массив кнопок. Массив можно инициализировать в цикле... правда, есть одна объективная тонкость - там нужно указать обработчик, скажем, onClick. Каждой кнопке. Это что, для 100 кнопок нужно написать 100 обработчиков? Которые будут одинаковые, за исключением той строки, которые они будут запускать. Ну явно не то.
Проблема с генерированием в рантайме нескольких компонентов не нова. Например, в Qt 4.x еще был такой компонент signal mapper (в 5.x его почему-то объявили устаревшим, хотя все равно собирается). Он обеспечивал простую вещь - если создано N виджетов одного типа, то каждый виджет подключался к этому signal mapper, который сигнал от него преобразовывал в пару "сигнал + индекс". Поэтому signal mapper обеспечивает подключение всех кнопок к одному обработчику, сообщая ему при этом, какая именно кнопка сделала onClick. В руководствах оно описано достаточно подробно.
Для Delphi/Lazarus существует подобное по сути решение, только signal mapper там отсутствует. Зато примеры с генерированием вполне однозначны. Например, если нужно создать 10 кнопок с цифрами для калькулятора, то не нужно писать 10 обработчиков. Достаточно написать один, но.... обеспечив ему идентификацию конкретного объекта, который его вызвал. Раньше такой проблемы не было, потому что связь виджет-кнопка была явной. Ну и рекомендации на этот случай именно те, которые я описал - унаследовать класс, добавив ему поле "id экземпляра aka индекс в массиве". В переопределенном конструкторе довольно легко сделать Object.id = i;, при этом обработчик у всех один и тот же. А уже внутри обработчика все вытаскивается, поскольку ему передается (Sender : TObject), из которого легко все вытащить как (Sender as TMyButton).id.
В итоге полученное приложение вообще не перекомпилируется для изменения интерфейса. Кнопки появляются так:
0 BUTTON.SHOW
" 2 2 + . " 0 BUTTON.ACTION
Все, после этих двух строк в консоли на форме появится кнопка (она уже давно есть, просто спрятана, но при старте запускался конструктор, настроен обработчик и т.д.). А если ее нажать, то обработчик пошлет форт-машине строку 2 2 + . В форт-консоли можно набрать другую строку для кнопки, спрятать кнопку и т.д.
Что в итоге получается. Большой размер программы, избыточное количество созданных (и спрятанных) виджетов - это минус. Однако все обработчики, TAction, wm_command и прочее уже не имеют смысла - они написаны, скомпилированы, привязаны к обработчикам и уже давно крутятся "под капотом", просто заранее настроены только на запуск кусочков Форта и ждут, пока эти кусочки им назначат. Так что в программе, условно, уже есть 1000 кнопок, 1000 чекбоксов, 100 выпадающих списков и т.д. и т.д. (и одна трехмерная сцена OpenGL).