Галерея UI написанных на MQL - страница 53

 
Реter Konow #:

И так, обновление...

...


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

В общем, если отладить все оставшиеся неоптимизированные решения - будет летать... ну, в пределах скоростей доступных MQL-программе, конечно. 


Берите релиз.

Уточню: здесь речь о скорости. Если исправить недочеты перерисовки окон на событии смены фокуса, то скорость работы интерфейса будет у верхней границы MQL-программы. Лаги таскбара частично можно исправить. Придумал хорошее решение - применить в механике таскбара принцип работы динамичного окна - оно не тормозит при изменении размеров, когда его тянут за рамку. Он будет быстрее и незаметнее подстраиваться. И конечно, нужно отменить лишнии перерисовки. Это обязательно. Но, если сами события CHARTEVENT_CHART_CHANGE приходят в программу с задержкой, то видимое отставание реакций таскбара неизбежно, хотя он сам непричем. 

В остальном - есть множество направлений развития и улучшения интерфейса. 

 

Еще несколько слов о скорости работы интерфейса.

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

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

И не только это. Поскольку массив изображения глобален (объявлен на глобальном уровне), в его памяти всегда сохраняется изменение последнего изображения. И если изменения продолжают происходить на том же канвасе, вместо того чтобы очищать его массив каждый раз, используется хранящееся изображение. Если на следующем событии имя ресурса не меняется, значит не нужно вызывать функцию ResourceReadImage(), и не нужно снова посылать массив канваса для заполнения. Блок продолжает работать с оставшимся данными без вызова функции ResourceReadImage(), и после изменения обновляет картинку с помощью ResourceCreate().

Получается приличная экономия времени на вызовах функции ResourceReadImage(). А также на очищении и заполнении массива. Удачное использование глобальной памяти, не так ли?

При перерисовке окон блок вообще не вызывается. Стираются и создаются составляющие окон и на них цепляются ранее сохраненные ресурсы. Отрисовки нет.


При всем этом, лаги все таки есть, и они неизбежны. Объсню в чем дело.

При первом открытии окна, или при первом открытии вкладки, или на событии древовидного списка, или при сворачивании/разворачивании больших пространств, происходит обязательная полная перерисовка канвасов. До момента создания ресурсов изображений которые потом можно многократно эффективно цеплять/менять, ОДИН раз ВСЕГДА нужно рисовать полное изображение с нуля. Первая отрисовка ВСЕГДА самая долгая. Экономить не на чем, сохраненного изображнения нет. Именно при первом открытии можно всегда видеть лаги. Это неизбежно.

Однако, и здесь применимо хорошее решение:  перенести лаги открытия окон в событие загрузки. Что имею ввиду: на этапе загрузки конструктора в фоновом режиме нарисовать все изображения и заранее сохранить в ресурсах, чтобы при открытии все было готово. Тогда пользователь не увидит задержек при первом открытии окон. Это конечно прекрасно, но есть обратная сторона. Лаг первого открытия перейдет в лаг загрузки и ее время увеличится. Трудно сказать насколько. Думаю, в среднем в пределах секунды. Зависит от конкретного интерфейса. 

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



Интересны ваши мнения.


Добавлено:

Возникла идея сохранять ресурсы интерфейса после первой загрузки в файл. Тогда последующие загрузки будут намного быстрее, ведь нужные ресурсы уже в распоряжении конструктора/движка. Нужно подумать.

 
Что-то не так Пётр
Лаг в одну секунду - это запредельный лаг. Формирование самого навороченного интерфейса на все окно не должно превышать 50 миллисекунд.. 
Реально похоже, что логика отрисовки перегружена функциями Update (фактически ChartRedraw). 
Для проверки этого предположения поставь статический счётчик в функцию Update в классе CCanvas (если его используешь) и принтуй этот счётчик, например когда count%100 == 0
 

У меня тоже канвас на полный экран перерисовывается полностью при каждом изменении, но это занимает не дольше 50 мс...

Самое затратное это текст рисовать. Поэтому, чтобы не юзать TextOut каждый раз, то я их храню в массивах. Получается значительно быстрее.

 

Основная задача сейчас - предоставить пользователю программный контроль элементами управления интерфейса. 

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

Но есть нюанс. Названия функций получаются слишком длинные. Ведь каждая функциональная обертка интерактивного элемента должна включать наименования элемента и окна. Это необходимо для ориентирования среди названий. Иначе пользователь легко запутается и не разберется в своих функциях-обертках. Также, имена могут совпадать, а это совсем плохо. Вот и получается, - нужно генерировать названия из двух составляющих: имени элемента и названия окна. Тогда путаницы не возникает, но имена оберток становятся слишком длинные. Особенно со сторокой передаваемых параметров. К тому же к функциям нужно добавлять префиксы, чтобы быстро находить через интеллисенс. Это позволяет эффективно фильтровать всплывающую выборку. Удобно. НО ДЛИННО!

И не только это. Проблема в передаваемых параметрах. Встает выбор: либо писать обертки под каждые предустановленные get/set-свойства элементов и окон, либо каждая обертка принимает полный список параметров от пользовательского вызова. Жутко неудобно. А главное, - трудно объяснить.


Выход есть и он прост. И вот какой: группа абстрактных свойств. Глобальные переменные одновременно видимые со стороны юзерской программы и движка.

Как это будет работать:

1. Все обертки элементов и окон будут обращаться к центральной функции и передавать свои индексы. По ним она определит целевой элемент/окно обращения.

2. После этого вызова, пользователь установит выбранному набору абстрактных свойств требуемые значения. 

3. Вызовет центральную функцию и передаст к.слово "Set". 

4. Центральная установит значения абстрактных свойств из их глобальных переменных в целевые свойства конкретного элемента или окна.

5. Обновит элемент/окно и обнулит абстрактные свойства.

Всё.

По моему, простое и эффективное решение которое обеспечит: 

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

в) Вольное комбинирование набора свойств элементов и окон при установке/получении значений, в любом месте программы


Если кто то видит недостатки - высказывайтесь. Неплохо бы согласовать этот вопрос перед релизом.  

 
Nikolai Semko #:
Что-то не так Пётр
Лаг в одну секунду - это запредельный лаг. Формирование самого навороченного интерфейса на все окно не должно превышать 50 миллисекунд.. 
Реально похоже, что логика отрисовки перегружена функциями Update (фактически ChartRedraw). 
Для проверки этого предположения поставь статический счётчик в функцию Update в классе CCanvas (если его используешь) и принтуй этот счётчик, например когда count%100 == 0

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

Если брать среднее число окон 10 (Папков, помню, заказывал интерфейс из 11-ти окон.),и представить что каждое имеет множество элементов или таблицу, тогда становится ясно почему полная отрисовка ВСЕГО интерфейса берет столько времени. Напомню, что в интерфейсе есть иконки, тени, градиент поверхностей, разнообразные рамки... то в сумме полное рисование ВСЕГО интерфейса будет не менее 500 мс. С этим ничего не поделать.

Можно быстрее, если уменьшить количество окон или упростить графику. 

Насчет перерисовки, - чистых вызовов ChartRedraw() у меня почти нет. Везде используется флаг _ChartRedraw. При установке этого флага вызывается функция ChartRedraw() на следующей итерации таймера, через 25мс. То есть - один раз. Таким образом я избегаю лишних перерисовок. Только в единичных случаях делаю прямой вызов  ChartRedraw() .

 
Andrey Barinov #:

У меня тоже канвас на полный экран перерисовывается полностью при каждом изменении, но это занимает не дольше 50 мс...

Самое затратное это текст рисовать. Поэтому, чтобы не юзать TextOut каждый раз, то я их храню в массивах. Получается значительно быстрее.

Ну, здесь работает простая арифметика: сумма площадей 10 - 17 окон гораздо больше полного экрана. Согласитесь. Плюс вторичные дорисовки необходимые для создания теней, иконок, рамок... 

А насчет  TextOut проверю и напишу. Интересная мысль. 

 

Провел тест:

Зашел в файл Demo project 1.mqh и поставил всем окнам флаг OOI. (открытие на инициализации).

Всего - 15 окон разных размеров и разного содержания. 2 окна с полосами прокрутки и таблицами (значит их канвас частично спрятан и на самом деле в 2-3 раза длиннее. О полной длинне можно судить по соотношению ползунка к полосе прокрутки). Общая площадь рисования (минимум) 2000*1000 пикселей. Но думаю, больше. Всего нарисовано 1158 деталей (проверил после распечатки ядра). Время полной отрисовки всех канвасов с нуля от 1600 - до 1900 мс.

Еще раз обратите внимание на количество деталей которые требовалось нарисовать. Тени, иконки, градиенты, рамки, тексты. 


Время рисования на скриншоте:


 
Возможно, есть способ ускорить рисование. Убрать нижнее основание платформ окон. Это большие канвасы за лицевой стороной где расположены элементы. Если их убрать - будет раза в 2 быстрее. Надо подумать.
 
Можно ли рисовать определенные окна только тогда, когда они открыты? Редко бывает так, что одновременно открыты десятки окон. В этом нет необходимости.