Обсуждение статьи "Язык MQL как средство разметки графического интерфейса MQL-программ (Часть 2)"

 

Опубликована статья Язык MQL как средство разметки графического интерфейса MQL-программ. Часть 2:

В статье продолжается проверка новой концепции описания оконного интерфейса MQL-программ с помощью конструкций языка MQL. Автоматическое создание GUI на основе MQL-разметки предоставляет дополнительный функционал для кэширования и динамического порождения элементов, управления стилями, новых схем обработки событий. Прилагается усовершенствованная версия стандартной библиотеки элементов управления.

Мы можем удалить любой элемент интерфейса, имеющийся в кэше, то есть не только те, что добавлены кнопкой Inject. Таким образом можно, например, удалить всю левую половину целиком или правый "радиобокс". Но самое интересное произойдет, если мы попытаемся удалить верхний контейнер с двумя кнопками. В результате этого кнопка Export потеряет привязку к диалогу и останется на графике.

Редактируемая форма: добавление и удаление элементов

Редактируемая форма: добавление и удаление элементов

Происходит так потому, что это единственный элемент, намеренно описанный как автоматическая, а не динамическая переменная (в классе формы есть экземпляр CButton m_button3).

Когда стандартная библиотека пытается удалить интерфейсные элементы, она делегирует это классу массивов CArrayObj, а он в свою очередь проверяет тип указателя и удаляет только объекты с POINTER_DYNAMIC. Таким образом, становится очевидно, что для построения адаптивного интерфейса, где элементы могут заменять друг друга или полностью удаляться, желательно использовать динамическое размещение, и кэш предлагает для этого готовое решение.

Автор: Stanislav Korotky

 
Прилагается усовершенствованная версия стандартной библиотеки элементов управления.

То есть все это будет работать только с усовершенствованной версией стандартной библиотеки

 
Не стану долго критиковать статью, потому как и так ясно, что автор не думает о читателе. Он разговаривает сам с собой и его поймет тот, кто в совершенстве знаком со стандартной библиотекой - ее классами, методами и переменными. Автор "галопом" скачет по известным ему кодам и чего то модифицирует...

Отсутствие изложения общих принципов устройства библиотеки, языка или редактора, нивелирует ценность статьи. "Копаться" во "внутренностях" с умными словами каждый может, а понятным языком обьяснить технологию - нет, ведь нужно действительно понимать ее суть. Боюсь, автор стал жертвой иллюзии, что он на "раз-два" из библиотеки (которою вызубрил) сделает язык разметки, а потом сразу студию. При этом, резиновость окон и добавление элемента в форму он наивно рассматривает как подверждение. 

Поскольку концепций от автора я не дождусь, наблюдать непонятное действо неинтересно. Жаль. Жаль, что автору придется нелегко в реализации амбиций и доказательстве "несмехотворности" своих результатов.

 Удачи!
 
Реter Konow:
Не стану долго критиковать статью, потому как и так ясно, что автор не думает о читателе. Он разговаривает сам с собой и его поймет тот, кто в совершенстве знаком со стандартной библиотекой - ее классами, методами и переменными. Автор "галопом" скачет по известным ему кодам и чего то модифицирует...

Отсутствие изложения общих принципов устройства библиотеки, языка или редактора, нивелирует ценность статьи. "Копаться" во "внутренностях" с умными словами каждый может, а понятным языком обьяснить технологию - нет, ведь нужно действительно понимать ее суть. Боюсь, автор стал жертвой иллюзии, что он на "раз-два" из библиотеки (которою вызубрил) сделает язык разметки, а потом сразу студию. При этом, резиновость окон и добавление элемента в форму он наивно рассматривает как подверждение. 

Поскольку концепций от автора я не дождусь, наблюдать непонятное действо неинтересно. Жаль. Жаль, что автору придется нелегко в реализации амбиций и доказательстве "несмехотворности" своих результатов.

 Удачи!

А тот, кто знаком с этой библиотекой в совершенстве, вряд ли будет нуждаться в каких-то дополнениях.

 
Dmitry Fedoseev:

А тот, кто знаком с этой библиотекой в совершенстве, вряд ли будет нуждаться в каких-то дополнениях.

Если внимательно прочесть заключение статьи, то можно понять (спорные) выводы автора:

//------------------------------------------------------------------------------------------------------------------------

1. Автор утверждает, что проверил "осуществимость концепции описания раскладки графического интерфейса MQL-программ на самом языке MQL" (c).

  • Из за специфики изложения материала, не могу ничего сказать в за/против этого вывода. Неочевидно.

//------------------------------------------------------------------------------------------------------------------------

2. "Использование динамической генерации элементов с централизованным хранением в кэше позволяет упростить создание и управление иерархией компонентов. На базе кэша можно реализовать большинство задач, связанных с проектированием интерфейса, в частности, единообразное изменение стиля, обработка событий, редактирование раскладки на лету и её сохранение в формате, пригодном для последующего использования." (c)

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

//------------------------------------------------------------------------------------------------------------------------

3. "Если сложить эти функции вместе, окажется, что в наличии есть практически всё для простого визуального редактора форм. Он мог бы поддерживать лишь самые важные свойства, общие для многих "контролов", но тем не менее позволил бы формировать заготовки интерфейса."(c)

  • Опять же, - без обобщенного описания технологических нюансов, читатель не владеющий в совершенстве системой производства виз.редакторов из граф.библиотек, НЕ может ни согласится, ни поспорить с автором. Неочевидно.

//------------------------------------------------------------------------------------------------------------------------

4. "Однако мы видим, что даже начальный этап оценки новой концепции потребовал много усилий. Поэтому реализация полноценного редактора на практике представляет собой довольно сложную задачу. И это уже другая история." (c)

  • На мой взгляд - это САМЫЙ объективный вывод. То есть - "реализация полноценного редактора - это уже другая история".  С таким выводом, я бы согласился. Нельзя просто "нахрапом" его сделать (сам, в течении нескольких лет пытался).

//------------------------------------------------------------------------------------------------------------------------

Мой итог: в Заключении статьи (а именно в последним выводе), автор наиболее близок к реальности. Но, статья расчитана на читателей, которые должны придти к тем же выводам по мере развития темы, а для этого, нужно постепенно раскрывать технологию. Главный недостаток двух статей - нет раскрытия технологий. Их контент - обсуждение реализации неочевидных авторских решений без предисловий к ним. Надеюсь, автор учтёт.

Документация по MQL5: Основы языка / Функции / Функции обработки событий
Документация по MQL5: Основы языка / Функции / Функции обработки событий
  • www.mql5.com
В языке MQL5 предусмотрена обработка некоторых предопределенных событий. Функции для обработки этих событий должны быть определены в программе MQL5: имя функции, тип возвращаемого значения, состав параметров (если они есть) и их типы должны строго соответствовать описанию функции-обработчика события. Именно по типу возвращаемого значения и по...
 
Реter Konow:

Если внимательно прочесть заключение статьи, то можно понять (спорные) выводы автора:

//------------------------------------------------------------------------------------------------------------------------

1. Автор утверждает, что проверил "осуществимость концепции описания раскладки графического интерфейса MQL-программ на самом языке MQL" (c).

  • Из за специфики изложения материала, не могу ничего сказать в за/против этого вывода. Неочевидно.

//------------------------------------------------------------------------------------------------------------------------

2. "Использование динамической генерации элементов с централизованным хранением в кэше позволяет упростить создание и управление иерархией компонентов. На базе кэша можно реализовать большинство задач, связанных с проектированием интерфейса, в частности, единообразное изменение стиля, обработка событий, редактирование раскладки на лету и её сохранение в формате, пригодном для последующего использования." (c)

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

//------------------------------------------------------------------------------------------------------------------------

3. "Если сложить эти функции вместе, окажется, что в наличии есть практически всё для простого визуального редактора форм. Он мог бы поддерживать лишь самые важные свойства, общие для многих "контролов", но тем не менее позволил бы формировать заготовки интерфейса."(c)

  • Опять же, - без обобщенного описания технологических нюансов, читатель не владеющий в совершенстве системой производства виз.редакторов из граф.библиотек, НЕ может ни согласится, ни поспорить с автором. Неочевидно.

//------------------------------------------------------------------------------------------------------------------------

4. "Однако мы видим, что даже начальный этап оценки новой концепции потребовал много усилий. Поэтому реализация полноценного редактора на практике представляет собой довольно сложную задачу. И это уже другая история." (c)

  • На мой взгляд - это САМЫЙ объективный вывод. То есть - "реализация полноценного редактора - это уже другая история".  С таким выводом, я бы согласился. Нельзя просто "нахрапом" его сделать (сам, в течении нескольких лет пытался).

//------------------------------------------------------------------------------------------------------------------------

Мой итог: в Заключении статьи (а именно в последним выводе), автор наиболее близок к реальности. Но, статья расчитана на читателей, которые должны придти к тем же выводам по мере развития темы, а для этого, нужно постепенно раскрывать технологию. Главный недостаток двух статей - нет раскрытия технологий. Их контент - обсуждение реализации неочевидных авторских решений без предисловий к ним. Надеюсь, автор учтёт.

Воздержусь, ибо запредельно.

 

Провел тщательный анализ содержания статьи и понял, что "частицы" искомой технологии имеются, но они произвольно разбросаны по материалу. То есть, автор не отделил теорию от реализации и все перемешал, попутно разделив обильными примерами кода. Я решил собрать основы его концепции в одном месте, дабы получить о них полное представление.

Копии фрагментов статьи поясняющие технологию (последовательность сохранена, пропущены примеры кода):

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

1. Общая структура библиотеки стандартных элементов, с учетом адаптированных версий с поддержкой "резиновости" и сторонних контейнеров, приведена на диаграмме классов. (мое добавление: приведена схема).

2. Порождение элементов и их кэширование

До сих пор элементы конструировались как автоматические экземпляры внутри объекта окна. По сути, это "пустышки", которые затем инициализируются методами вроде Create. Система раскладки элементов GUI может сама создавать эти элементы, а не получать их из окна. Для этого достаточно иметь некое хранилище. Назовем его LayoutCache. По сути, это массив указателей базового класса (общего для всех элементов), куда их можно помещать с помощью метода save. Также в интерфейсе реализуются (если возможно на данном абстрактном уровне) или декларируются (для последующего переопределения) методы для поиска элементов по номеру, имени, ссылке или факту "родительских" отношений (обратная связь от вложенных элементов к контейнеру).

Добавим кэш как статический член в класс LayoutBase. Каждое окно должно будет создать для себя экземпляр кэша и установить его в качестве рабочего с помощью setCache в самом начале метода вроде CreateLayout. Поскольку программы MQL однопоточные, у нас есть гарантия, что окна (если их нужно несколько) не будут формироваться параллельно и конкурировать за указатель cacher. Очистку указателя будем делать автоматически в деструкторе LayoutBase, когда стек закончился — это означает, что мы вышли из последнего внешнего контейнера в описании раскладки и сохранять больше ничего не потребуется. Обнуление ссылки не означает, что мы очищаем кэш. Просто таким образом обеспечивается, что потенциальная следующая раскладка не допишет туда по ошибке "контролы" другого окна. Для заполнения кэша добавим новую разновидность метода init в LayoutBase — на этот раз без указателя или ссылки на "сторонний" элемент GUI в параметрах. За счет шаблона мы имеем возможность написать new T и порождать объекты в процессе раскладки (по умолчанию, 1 объект за раз, но опционально можем и несколько).

Для элементов стандартной библиотеки написана конкретная реализация кэша — StdLayoutCache (здесь приведен с сокращениями, полный код — в приложении). 

Обратите внимание, что метод get ищет "контрол" или по порядковому номеру (если входной параметр положителен), или по идентификатору (он передается со знаком минус). Здесь под идентификатором имеется в виду уникальный номер, присваиваемый библиотекой стандартных компонентов для диспетчеризации событий. В событиях он передается в параметре lparam.

3. Стилизатор

Поскольку кэш представляет собой объект, централизованно обрабатывающий элементы, с помощью него удобно выполнять многие другие задачи помимо раскладки. В частности, для элементов можно унифицирированно применять правила единого стиля (цвет, шрифт, отступы). При этом достаточно настраивать этот стиль в одном месте, а не прописывать одни и те же свойства для каждого "контрола" отдельно. Кроме того, кэш может взять на себя задачу обработки сообщений для кэшированных элементов. Потенциально мы можем динамически конструировать, помещать в кэш и интерактивно взаимодействовать абсолютно со всеми элементами. Тогда в окне вообще не нужно декларировать никаких "явных" элементов. Чуть позже мы увидим, чем динамически создаваемые элементы выгодно отличаются от автоматических.

Для поддержки централизованных стилей в классе StdLayoutCache предусмотрен метод-заглушка: Метод apply будет вызываться для каждого "контрола" дважды: на стадии инициализации (STYLE_PHASE_BEFORE_INIT) и на стадии регистрации в контейнере (STYLE_PHASE_AFTER_INIT). Так, в методах LayoutBase::init добавляется вызов на первой стадии: а в деструктор — похожие строки, но с STYLE_PHASE_AFTER_INIT для второй стадии.

Две фазы нужны из-за того, что цели стилизации бывают разными. У некоторых элементов иногда нужно установить индивидуальные свойства, имеющие больший приоритет над теми общими, что заданы в стилизаторе. На стадии инициализации "контрол" еще пустой — без настроек, сделанных в раскладке. На стадии регистрации в нем уже установлены все свойства и на их основе можно дополнительно менять стиль. Самый очевидный пример такой. Все поля ввода с флагом "только чтение" желательно отобразить серым цветом. Но свойство "только чтение" присваивается "контролу" в процессе раскладки, после инициализации, и потому первая стадия здесь не подходит и требуется вторая. С другой стороны, обычно не все поля ввода будут иметь данный флаг и во всех остальных случаях необходимо установить цвет по-умолчанию, прежде чем язык раскладки выполнит выборочную кастомизацию.

Между прочим, похожую технологию можно применить для централизованной локализации интерфейса MQL-программ на разные национальные языки.

4. Обработка событий

Второй функцией, которую логично поручить кэшу, является обработка событий. Для них в классе LayoutCache добавлен метод-заглушка (C — параметр шаблона класса): Опять же, его можно реализовать в производном классе, но не обязательно. Коды событий определяются конкретной библиотекой.

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

5. Пример 2. Диалог с элементами управления Controls

Демо-проект содержит класс CControlsDialog с основными типами "контролов" Стандартной библиотеки. По аналогии с первым примером удалим все методы по их созданию и заменим на единственный CreateLayout. Кстати говоря, методов этих было в старом проекте аж 17, и они вызывались друг из друга с помощью сложносочиненных условных операторов. В классе кэша объявлен обработчик событий onEvent, который мы подключим посредством карты событий. Здесь обработчик отравляет сообщение в родительское окно, где оно как и в предыдущих версиях примера выводится в информационное поле.

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

Ссылка на кэш хранится в окне, он создается и удаляется, соответственно, в конструкторе и деструкторе, причем при создании в качестве параметра передается ссылка на окно для обеспечения обратной связи.


В класс CBox добавлен алгоритм масштабирования вложенных элементов под размер контейнера. Он выполняется в методе AdjustFlexControls и вступает в силу, только если во флагах выравнивания контейнера указано специальное значение WND_ALIGN_CONTENT. Оно не является частью стандартного перечисления ENUM_WND_ALIGN_FLAGS. Контейнер анализирует "контролы" на предмет того, какие из них имеют фиксированный размер, а какие — нет. "Контролы" с фиксированным размером это те, для которых не указано выравнивание по сторонам контейнера (в конкретном измерении). Для всех таких "контролов" контейнер подсчитывает сумму их размеров, вычитает её из общего размера контейнера и остаток пропорционально делит между всеми оставшимися "контролами". Например, если в контейнере два "контрола", и ни один из них не имеет привязки, то они будут пополам делить между собой всю область контейнера.

Это очень удобный режим, но не следует им злоупотреблять на множестве вложенных друг в друга контейнеров — из-за однопроходного алгоритма расчета размеров выравнивание внутренних элементов по области контейнера, который в свою очередь подстраивается под содержимое порождает неопределенность (по этой причине в классах раскладки заведено специальное событие ON_LAYOUT_REFRESH, которое окно может отослать самому себе для того, чтобы повторить вычисление размеров).

Макрос ON_EVENT_LAYOUT_CTRL_DLG подключает уведомления о нажатиях мышью для любых кнопок класса NotifiableButton (в нашем случае, она одна). Макрос ON_EVENT_LAYOUT_INDEX отправляет то же событие в кнопку с указанным индексом в кэше. Но этот макрос можно было и не писать, потому что последней строкой макрос ON_EVENT_LAYOUT_ARRAY перешлет нажатие мыши любому элементу в кэше при совпадении его идентификатора lparam.

В принципе можно было все элементы перенести в кэш и обрабатывать их события новым способом, но старый тоже работает и их можно комбинировать.

 Пример 3. Динамические раскладки DynamicForm

В этом последнем примере мы рассмотрим форму, в которой все элементы будут динамически создаваться в кэше. Это даст нам пару новых важных возможностей.

Как и в предыдущем примере, кэш будет поддерживать стилизацию элементов. Единственная настройка стиля — одинаковые заметные поля, которые позволят видеть вложенность контейнеров друг в друга и выделять их по желанию с помощью мыши.

Внутри метода CreateLayout описывается следующая простая структура интерфейса. Главный контейнер, как обычно, занимает всю клиентскую область окна. В верхней части расположен блок с двумя кнопками: Inject и Export. Всё место под ним занимает контейнер, разделенный на левую и правую колонки.

Контейнер, куда должны вставляться новые элементы, сперва ищется в кэше по имени "column1". Этот контейнер идет первым параметром при создании объекта injectionPanel. Тот факт, что передаваемый элемент уже находится в кэше, особым образом учтен в алгоритме раскладки — повторно он в кэш не добавляется, но как обычно кладется в стек контейнеров. Таким образом обеспечивается возможность добавлять элементы в "старые" контейнеры.

На основе выбора пользователя объект необходимого типа создается с помощью оператора new во вспомогательном методе getPtr. Для того, чтобы добавленные "контролы" нормально работали, для них случайным образом генерируются уникальные идентификаторы. Удаление указателя при выходе из блока кода обеспечивает специальный класс AutoPtr.

Если добавить слишком много элементов, они выйдут за границы контейнера. Это происходит потому, что имеющиеся у нас классы контейнеров пока не умеют правильно реагировать на переполнение. В этом случае можно было бы, например, показывать полосу прокрутки, а выступающие за границы элементы скрывать.

Но это и не важно. Суть примера в том, что мы можем генерировать динамическое содержимое, настраивая форму, и обеспечить необходимое наполнение и размер контейнеров.

Помимо добавления элементов, данный диалог умеет и удалять их. Любой элемент на форме можно выделить щелчком мыши. При этом класс и имя элемента выводятся в лог, а сам элемент выделяется красной рамкой. Если щелкнуть по уже выделенному элементу, диалог выведет запрос на подтверждение удаления и при согласии удалит элемент. Все это реализуется в нашем классе кэша.

Мы можем удалить любой элемент интерфейса, имеющийся в кэше, то есть не только те, что добавлены кнопкой Inject. Таким образом можно, например, удалить всю левую половину целиком или правый "радиобокс". Но самое интересное произойдет, если мы попытаемся удалить верхний контейнер с двумя кнопками. В результате этого кнопка Export потеряет привязку к диалогу и останется на графике.

Происходит так потому, что это единственный элемент, намеренно описанный как автоматическая, а не динамическая переменная (в классе формы есть экземпляр CButton m_button3).

Когда стандартная библиотека пытается удалить интерфейсные элементы, она делегирует это классу массивов CArrayObj, а он в свою очередь проверяет тип указателя и удаляет только объекты с POINTER_DYNAMIC. Таким образом, становится очевидно, что для построения адаптивного интерфейса, где элементы могут заменять друг друга или полностью удаляться, желательно использовать динамическое размещение, и кэш предлагает для этого готовое решение.

Напоследок обратимся ко второй кнопке диалога — Export. Как можно догадаться по её названию, она предназначена для сохранения текущего состояния диалога в виде текстового файла в рассмотренном синтаксисе MQL-раскладок. Конечно, форма позволяет настроить свой вид лишь в ограниченном объеме, в демонстрационных целях, но сама возможность выгрузить внешний вид в готовый MQL-код, который затем легко скопировать в программу и получить тот же интерфейс, потенциально представляет собой довольно ценную технологию. Разумеется, переносится только интерфейс, а код обработки событий или общие настройки стилизатора необходимо подключить самостоятельно.

Экспорт обеспечивается классом LayoutExporter, мы не будем его подробно рассматривать, исходные коды прилагаются.



Документация по MQL5: Стандартная библиотека
Документация по MQL5: Стандартная библиотека
  • www.mql5.com
Стандартная библиотека MQL5 написана на языке MQL5 и предназначена для облегчения написания программ (индикаторов, скриптов, экспертов) конечным пользователям. Библиотека обеспечивает удобный доступ к большинству внутренних функций MQL5.
 
Пытался найти в вышеприведенном тексте систему, концепцию, технологию, - не нашел. Это даже не "эскиз", и больше напоминает поток сознания. Однако, если постоянно переключаться на примеры кодов - это незаметно. А жаль.
 
Реter Konow:
Пытался найти в вышеприведенном тексте систему, концепцию, технологию, - не нашел. Это даже не "эскиз", и больше напоминает поток сознания. Однако, если постоянно переключаться на примеры кодов - это незаметно. А жаль.

Вообще-то там всё понятно для того, кто знает ООП.

Кто не знает, сам виноват.

 
Koldun Zloy:

Вообще-то там всё понятно для того, кто знает ООП.

Кто не знает, сам виноват.

Понимаю желание вступиться за автора, но делать это нужно на уровне профессионала, а не дет.сада. 

Речь идет о структуре изложения концепции. По факту, в тексте статьи нет ни структуры, ни концепции. Есть крупицы, хаотично разбросанные по разным частям. Впрочем, понять это может тот, кто сам разрабатывал сложные системы. Для остальных и так сойдет...

С первой статьи была заявлена цель: проверка реализуемости КОНЦЕПЦИИ раскладки GUI средствами MQL, в виде языковой обертки или виз.редактора. 

Так вот, КОНЦЕПЦИИ нет, а вместо нее спонтанные рассуждения разбавленные кодами. 

Пример: автор сначала говорит о кеше, как о фундаментальном решении для порождения/уничтожения контроллов, и сразу переходит на стилизатор, минуя все остальные вопросы. Далее, изменения в стандартной событийной модели он ставит после темы стилей. Что за странные приоритеты? 
 

Смысл этого языка разметки в том, что он не требует отдельного интерпретатора. Он встраивается непосредственно в код программы.

Но без знания ООП, Вы очень многого в статьях просто не видите.

А поскольку изучать ООП Вы не планируете, зачем Вы здесь вообще?

Причина обращения: