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

 

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

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

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

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

На инциализации программы мы должны вызывать некую функцию, устанавливающую исходные значения в наши собственные окна, а не штатное окно. Как вариант, это можно сделать в KIB-конструкторе, на этапе сборки интерфейса, когда задаются значения V_CURRENT или состояния ON/OFF и и.д., но сейчас появилась возможность инициализировать элементы программым образом. Теперь можно совмещать инициализацию элементов в конструкторе и в программе.

Поэтому нужна специальная функция вызываемая из OnInit() и выполняющая эту работу. 

Что конкретно эта функция будет делать:

  • Открывать нужные окна на запуске программы.
  • Устанавливать значения в целевые элементы управления.

Как фунция будет называться?

Я бы назвал ее Initialize(), но каждый может придумать свой вариант.

Главное: эта функция обязана быть в любом интерфейсном советнике. Ее можно сравнить с функцией OnTick() эксперта, или OnCalculate() индикатора. Это важно понимать.


Какое значение будет возвращать функция?

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

*Важно учесть, что на данный момент концепт интерфейсных советников только формируется и многие открытия впереди.


Приведу пример инициализирующей функции интерфейсного советника в контексте текущего демо-проекта:

1. Вызов функции:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int _OnInit()
  {
   //------------------------------------------------
   //Initializing program interface:
   //------------------------------------------------
   Initialize();
   //------------------------------------------------


2.  Реализация функции:

void Initialize()   
{
   //------------------------------------------------
   //Opening the windows:
   //------------------------------------------------
   //1. "Main Parameters". 

    w_14_Main_Parameters();
   //------------------------------------------------

   //------------------------------------------------
   //2. "Input parameters".

   w_10_Input_parameters();
   //------------------------------------------------

   //------------------------------------------------
   //3. "Setting example 1"

   w_6_Settings_example_1();
   //------------------------------------------------


  //------------------------------------------------
  //Initializing the elements:
  //------------------------------------------------
  
   w6_d_p5_S_EDIT_Spin_the_value(55);

   w6_i_p3_H_SLIDER_Roll_the_value(55);

   w14_i_p3_V_SLIDER_Vertical_slider(55);

   w14_s_p4_EDIT_Max("100");

   w14_s_p4_EDIT_Min("0");

   w14_s_p4_EDIT_Selected("55");

   w15_i_p2_P_BAR_P1(55);

   w15_s_p4_VALUE_V1("55");
  //------------------------------------------------
}


Пока что, структура функции мне видится такой. Очень простая функция. Открывает окна и посылает нужные значения в параметры элементов. Можно поменять инциализацию элементов и открытие окон местами, потому что открывающиеся окна сразу явят нужные значения элементов без дополнительной перерисовки. Впрочем, это мелочи.


Далее перейдем к основной теме: реализация ступенчатой защиты параметров.

 

14. Реализация ступенчатой защиты границ параметров:

  • 1. Написание логики контроля настроек в предустановленных границах и создание системы предупреждений.
  • 2. Первое предупреждение:    пользователь получает сигнал в виде изменение цвета частей элементов отвечающих за настройки. (Установим связь цвета текста, оснований, рамок и полосы слайдера с границами значений.)
  • 3. Второе предупрежение:      открытие диалогового окна с уведомлением о рисках и предложением вернуться к исходным настройкам. (Протестируем фактическую возможность вернуться к прежним настройкам при нажатии на кнопку "Сancel".)
  • 4. Третье предупреждение:    открытие окна предупреждения блокирующего дальнейшее изменение параметров настроек с требованием ручного подтверждения пользователя. (Проверим блокировку окон и элементов при появлении окна предупреждения.)

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

Написание логики контроля настроек в предустановленных границах, и создание системы предупреждений:

  • Первая часть реализации - установим начальные значения параметров выбранных элементов и откроем нужные окна. Для этого напишем функцию Initialize() и поставим ее вызов в функции _OnInit(). 
  • Вторая часть реализации - откроем файл API и пропишем соединение элементов. В кейсе каждого элемента в общей цепочке пропишем вызовы и передачу значения остальным целевым элементам. Например: на событии поля с кнопками вызываем горизонтальный слайдер и передаем ему значение. Потом, когда событие приходит в кейс слайдера, пересылаем его обратно в поле с кнопками, и дополнительно в вертикальный слайдер. Последний, в свою очередь, передает значение полям ввода около себя и в окно прогресс бара... Всего в цепочке будет семь основных элементов передающих значение в нескольких направлениях. Вот они: "Settings example 1" с элементами "Spin the value" и "Roll the value", "Main parameters" с элементами "V_SLIDER", полем "Selected" и "Percent", и "Processing the data..." с элементами VALUE и P_BAR 
  • Третья часть: Протестируем соединение.
  • Четвертая часть: Напишем функцию контроля парамеров c условным названием: void Risk_management_group_1(). Ее задача - сигнализировать пользователю о приближении к границам опасных значений внутри этой группы элементов, и предупреждать при выходе за разрешенные пределы. Функция будет принимать значения от связанных элементов и пропускать их через установленные фильтры внутри условий. Предупреждение реализуем через соответствующие степени риска изменения цвета текста и рамок и будем посылать его во все элементы цепочки. Параллельно, будем вызывать блокирующие окна, не позволяющие пользователю продолжать действия без дополнительного подтверждения.
  • Пятая часть: протестируем работу функции.

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

Приступим:

1.  Установим начальные значения параметров выбранных элементов и откроем нужные окна. Для этого напишем функцию Initialize() и поставим ее вызов в функции _OnInit()

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int _OnInit()
  {
   //------------------------------------------------
   //Initializing program interface:
   //------------------------------------------------
   Initialize();
   //------------------------------------------------
void Initialize()   
{
   //------------------------------------------------
   //Opening the windows:
   //------------------------------------------------
   //1. "Main Parameters". 
    w_14_Main_Parameters();

   //2. "Input parameters".
   w_10_Input_parameters();

   //3. "Setting example 1"
   w_6_Settings_example_1();

   //------------------------------------------------
  //Initializing the elements:
  //------------------------------------------------
   w6_d_p5_S_EDIT_Spin_the_value(55);
   w6_i_p3_H_SLIDER_Roll_the_value(55);
   w14_i_p3_V_SLIDER_Vertical_slider(55);
   w14_s_p4_EDIT_Max("100");
   w14_s_p4_EDIT_Min("0");
   w14_s_p4_EDIT_Selected("55");
   w15_i_p2_P_BAR_P1(55);
   w15_s_p4_VALUE_V1("55");
  //------------------------------------------------
}

Результат: Открыты нужные окна и установлены исходные значения в целевые элементы.


2.  Откроем файл API и пропишем соединение элементов. В кейсе каждого элемента пропишем вызовы и передачу значения остальным целевым элементам в цепочке:

case Settings_example_1___Spin_the_value:
  
               //------------------------------------------------------------------------------------------------------
               //What to do when the value is set?
               //------------------------------------------------------------------------------------------------------
               //Min value:  NOT SET  |   Max value:  NOT SET  |   V_step:  1.7  |   Default value:  468.99  |  Digits: 3
               //------------------------------------------------------------------------------------------------------
               w6_i_p3_H_SLIDER_Roll_the_value((int)value);

               //------------------------------------------------------------------------------------------------------
               //Your comment:
               //------------------------------------------------------------------------------------------------------
               break;
case Settings_example_1___Roll_the_value:
  
               //------------------------------------------------------------------------------------------------------
               //What to do when the slider's handle is moved?
               //------------------------------------------------------------------------------------------------------
               //Min value:  0  |   Max value:  100  |   V_step:  1  |   Default value:  55  |  Digits: Integer value
               //------------------------------------------------------------------------------------------------------
               //Transferring the value:
               w14_i_p3_V_SLIDER_Vertical_slider((int)value);

               w6_d_p5_S_EDIT_Spin_the_value((double)value);
               //------------------------------------------------------------------------------------------------------
               //Your comment:
               //------------------------------------------------------------------------------------------------------
               break;
case Main_Parameters___Vertical_slider:
  
               //------------------------------------------------------------------------------------------------------
               //What to do when the slider's handle is moved?
               //------------------------------------------------------------------------------------------------------
               //Min value:  0  |   Max value:  100  |   V_step:  1  |   Default value:  50  |  Digits: Integer value
               //------------------------------------------------------------------------------------------------------
               {
                //----------------------------------------------------- 
                //Transferring value to other destinations:
                //----------------------------------------------------- 
                w14_s_p4_EDIT_Percent(value);
                //-----------------------------------------------------
                w14_s_p4_EDIT_Selected(value);
                //-----------------------------------------------------
                w15_i_p2_P_BAR_P1((int)value);
                //-----------------------------------------------------
                w15_s_p4_VALUE_V1(value);
                //-----------------------------------------------------
                w6_i_p3_H_SLIDER_Roll_the_value((int)value);
                //-----------------------------------------------------
                w6_d_p5_S_EDIT_Spin_the_value((double)value);
                //-----------------------------------------------------
                w8_s_p4_CELL_Account_balance__Value(value);
                //------------------------------------------------------------------------------------------------------
                //Your comment:
                //------------------------------------------------------------------------------------------------------
               }
               break;


3. Протестируем соединение:

Результат: значения элементов связаны как задумано.



4. Пишем функцию контроля параметров нашей группы элементов: void Risk_management_group_1().

void Risk_management_group_1(string value)
{
 uint Color = 0;
 //--------------------
 static uint This_color;
 static bool User_warned, Last_warning;
 //------------------------------------------------------------
 //Setting limit colors:
 //------------------------------------------------------------
 if((int)value < 25)                      Color = clrLightGreen;
 //------------------------------------------------------------
 if((int)value >= 25 && (int)value < 50)  Color = clrLimeGreen;
 //------------------------------------------------------------
 if((int)value >= 50 && (int)value < 70)  Color = clrGreen;
 //------------------------------------------------------------
 if((int)value >= 70 && (int)value < 85)  Color = clrDarkGreen;
 //------------------------------------------------------------
 if((int)value >= 85 && (int)value < 90)  Color = clrBrown;
 //------------------------------------------------------------
 if((int)value >= 90 && (int)value < 95)  Color = C'170,0,0';
 //------------------------------------------------------------
 if((int)value >= 95 && (int)value <=100) Color = clrRed;
 //------------------------------------------------------------  

 //------------------------------------------------------------ 
 //Changing colors when the limits are passed:
 //------------------------------------------------------------
 if(This_color != Color)
   {
    w14_s_p4_EDIT_Percent((string)Color, p4_COLOR_base); 
    //-----------------------------------------------------
    w14_s_p4_EDIT_Selected((string)Color, p4_COLOR_base); 
    //-----------------------------------------------------
    w15_i_p2_P_BAR_P1(Color, p2_COLOR_bar);
    w15_i_p2_P_BAR_P1(Color, p2_COLOR_frame);
    w15_s_p4_VALUE_V1((string)Color, p4_COLOR_frame);
                   
    w8_s_p4_CELL_Account_balance__Value((string)Color, p4_COLOR_text);
    w8_s_p4_CELL_Account_balance__Value((string)Color, p4_COLOR_frame);
    //-----------------------------------------------------
    w14_i_p3_V_SLIDER_Vertical_slider(Color,p3_COLOR_bar);
    //-----------------------------------------------------
    w15_s_p4_VALUE_V1((string)Color, p4_COLOR_text);
    //-----------------------------------------------------
    w6_i_p3_H_SLIDER_Roll_the_value(Color,p3_COLOR_bar);
    //-----------------------------------------------------
    w6_d_p5_S_EDIT_Spin_the_value((double)Color, p5_COLOR_text);
    //-----------------------------------------------------
    w6_d_p5_S_EDIT_Spin_the_value((double)Color, p5_COLOR_frame);
    //-----------------------------------------------------
    w14_i_p1_BUTTON_BUY_OFF(Color, p1_N_COLOR_frame);
    w14_i_p1_BUTTON_BUY_OFF(Color, p1_A_COLOR_frame);
    //-----------------------------------------------------
    w14_i_p1_BUTTON_SELL_OFF(Color, p1_N_COLOR_frame);
    w14_i_p1_BUTTON_SELL_OFF(Color, p1_A_COLOR_frame);
    //-----------------------------------------------------
    w7_s_p4_EDIT_Comment_1(Color, p4_COLOR_frame);
    //-----------------------------------------------------
    This_color = Color;
    //-----------------------------------------------------
   }   
 //-----------------------------------------------------
 //Opening warning window 1:
 //-----------------------------------------------------
 if((int)value >= 85 && (int)value < 95 && !User_warned)
   { 
    //---------------------------------
    //Opening dialog window:
    //---------------------------------
    w_13_Risky_managment(); 
    //---------------------------------
    //Setting flag of warning 1:
    //---------------------------------
    User_warned = true;
    //---------------------------------
   }
 //-----------------------------------------------------
 if((int)value < 85)User_warned = false;
 //-----------------------------------------------------
 //Opening warning window 2:
 //-----------------------------------------------------
 if((int)value >= 96 && !Last_warning)
   { 
    //---------------------------------
    //Calling blocking window:
    //---------------------------------
    w_17_Last_warning();
    //---------------------------------
    //Setting flag of warning 2:
    //---------------------------------
    Last_warning = true;
    //---------------------------------
   }
 //-----------------------------------------------------
 if((int)value < 95)Last_warning = false;                
 //-----------------------------------------------------                 
}

Вызываем функцию Risk_management_group_1() из функции _OnInit():

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int _OnInit()
  {
   //------------------------------------------------
   //Initializing program interface:
   //------------------------------------------------
   Initialize();
   //----------------------------------------------------------
   //Checking default value of the parameters in the group:  
   //----------------------------------------------------------
   Risk_management_group_1();
   //----------------------------------------------------------
 

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

(*Также установка цвета рамок добавлена в обновление, но в текущей версии отсутствует.)


 

Cледующая задача - рассмотреть отмену введенных параметров нажатием на кнопку "Cancel". 

Это очень непростая задача, но уже была мною частично реализована. Попробую восстановить прежний функционал.

 
В догонку к цветовой индикации, сейчас реализую мигание рамок, текстов и оснований. Думаю, завтра уже будет готово. Далее, отмена введенных значений кнопкой Cancel.

 Проблема в том, что при вводе значений в элементы, они сразу поступают в ядро и передаются в файл API, что логично вызывает отработку пользовательского кода. Необходимо задержать принятие значений в окнах с кнопками подтверждения перед поступлением в параметры и связать его с нажатием на "Ок" или "Confirm" (или "Apply"). 

Но также, нужно возвращать элементам предыдущие значения предшествующие изменениям, если пользователь нажимает на кнопку отмены (Cancel).

Это очень интересная задача и ее решение сразу поднимает практическую полезность интерфейса на новый уровень. К счастью, в большей степени цель была давно достигнута. Просто удивительно, как много интересного уже было реализовано 4 года назад.

Думаю, за неделю справлюсь с задачей и далее, серьезно займусь таблицами.
 

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

1. Функция рискменеджмента открывает первое окно предупреждения при пересечении опасного уровня, но если в этот момент не отрывать нажатый курсор от элемента, значение продолжает расти и доходит до следующего условного уровня. - Критического. 

2. При пересечении критического значения открывается окно последнего предупреждения, однако, оно тоже не останавливает изменения значения если пользователь продолжает держать левую кнопку мышки. 

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

На изображении ниже показано как это происходило:

 

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


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

 
Сегодня опубликую продолжение темы цветовой индикации значений параметров - мигание элементов. Задача оказалась несколько сложнее чем полагал, поэтому задержался на ней. В итоге полученное решение оправдало потраченное время. На мой взгляд, во всяком случае.
[Удален]  
Вы можете это понять.
 
Jin Rong Xia #:
Вы можете это понять.
Да, я вас понимаю. Переводчик работает хорошо.