Завершающая статья из мини-цикла про разработку GUI на tcl/tk
Для общего понимания о чём тут, настоятельно рекомендую прочесть первую и вторую часть
Итак, мы пишем скрипт form3_embed.tcl и в нём процеду Embed. До того как добавлять непосредственно в советник запускаем его в консоли.
В торговый советник добавляем только работающий и стабильный код
Изучаем полезную документацию :
- toplevel http://www.tcl-lang.org/man/tcl8.6/TkCmd/toplevel.htm - окна верхнего уровня Tk (их настройки)
В первой итерации наша процедура Embed пусть выводит на печать все что можно вывести:
# встроить в чарт окошко # chart - handle окна чарта # w - встраиваемое окно # level - уровень (передаваемый далее в reparent) 0-как виджет 1-как окно package require reparent proc noop {} {} proc Embed { chart w level } { # убираем у него всякие границы $w configure -borderwidth 0 $w configure -padx 0 $w configure -pady 0 # забираем окно у wm (он теперь не отвечает за декор) wm overrideredirect $w 1 update if { [ winfo exists .tkcon ] } { # если ведётся отладка в консоли # будем печатать события # протольное событие (see wm) wm protocol $w WM_TAKE_FOCUS [ list puts "WM_TAKE_FOCUS" ] wm protocol $w WM_DELETE_WINDOW [ list noop ] # оконные события (see bind) bind $w <Activate> [ list puts "%W Activate" ] bind $w <Deactivate> [ list puts "%W Deactivate" ] bind $w <MouseWheel> [ list puts "%W MouseWheel" ] bind $w <ButtonPress> [ list puts "%W ButtonPress" ] bind $w <ButtonRelease> [ list puts "%W ButtonRelease" ] bind $w <FocusOut> [ list puts "%W FocusOut" ] bind $w <FocusIn> [ list puts "%W FocusIn" ] bind $w <KeyPress> [ list puts "%W KeyPress" ] bind $w <Configure> [ list puts "%W Configure" ] bind $w <ResizeRequest> [ list puts "%W ResizeRequest" ] bind $w <ConfigureRequest> [ list puts "%W ConfigureRequest" ] bind $w <MapRequest> [ list puts "%W MapRequest" ] bind $w <Map> [ list puts "%W Map" ] } reparent $chart $w $level update }
смотрим, что происходит и как можно преодолеть проблемы с фокусом ввода и отрисовками окна. В консоли загружаем скрипт source mql5/experts/form2_embed.tcl и запускаем процедуру Embed $chartWin .win 1 (и с параметром 0 тоже) :
проблема с фокусом ввода становится понятной: когда наше окно отдаёт фокус ввода родительскому окну (чарту), обратно он уже не возвращается. Но мы можем его забрать по клику на элемент формы.
а вот с режимом окно-в-окне, несколько сложнее - события windows move resize не перетраслируются в tk. Явно что на уровне скриптов tcl или mql это не исправить, поэтому обратимся в автору ATcl (или дабавим тикет в проект :https://chiselapp.com/user/nektomk/repository/atcl-lib/rptview?rn=1) - «необходимо чтобы после reparent 1 транслировались события виндовс move/resize и неплохо-бы запрещать пользователю менять размеры окна»;
до разрешения этих проблем, фичу окно-в-окне пока не будем демонстрировать конечному пользователю
доделываем form2_embed.tcl добавив обработку фокуса ввода:
package require reparent proc noop {} {} # признак, что фокус ввода у нашей формы set hasFocus 0 # обработка оконных сообщений # w - окно tk (toplevel формы или дочерний виджет) proc OnWindowEvent { w ev } { # если включена консоль - напечатаем if { [ winfo exists .tkcon ] } { puts "$w $ev" } global hasFocus if { $hasFocus && $ev == "FocusOut" } { # потеряли фокус ввода set hasFocus 0 } if { !$hasFocus && ( $ev in { "Activate" "FocusIn" "MouseWheel" "ButtonPress" "KeyPress" } ) } { # произошло событие когда фокус ввода стоит забрать # сообщаем windows что хотим забрать фокус winfocus .win # сообщаем tk куда его поместить focus $w set hasFocus 1 } } # встроить в чарт окошко # chart - handle окна чарта # w - встраиваемое окно # level - уровень (передаваемый далее в reparent) 0-как виджет 1-как окно proc Embed { chart w level } { # убираем у него всякие границы $w configure -borderwidth 0 $w configure -padx 0 $w configure -pady 0 # забираем окно у wm (он теперь не отвечает за декор) wm overrideredirect $w 1 # если ведётся отладка в консоли # будем печатать события # протольное событие (see wm) #wm protocol $w WM_TAKE_FOCUS [ list puts "WM_TAKE_FOCUS" ] wm protocol $w WM_DELETE_WINDOW [ list noop ] # оконные события (see bind) bind $w <Activate> +[ list OnWindowEvent %W Activate ] bind $w <Deactivate> +[ list OnWindowEvent %W Deactivate ] bind $w <MouseWheel> +[ list OnWindowEvent %W MouseWheel ] bind $w <ButtonPress> +[ list OnWindowEvent %W ButtonPress ] bind $w <ButtonRelease> +[ list OnWindowEvent %W ButtonRelease ] bind $w <FocusOut> +[ list OnWindowEvent %W FocusOut ] bind $w <FocusIn> +[ list OnWindowEvent %W FocusIn ] bind $w <KeyPress> +[ list OnWindowEvent %W KeyPress ] bind $w <Configure> +[ list OnWindowEvent %W Configure ] bind $w <ResizeRequest> +[ list OnWindowEvent %W ResizeRequest ] bind $w <ConfigureRequest> +[ list OnWindowEvent %W ConfigureRequest ] bind $w <MapRequest> +[ list OnWindowEvent %W MapRequest ] bind $w <Map> +[ list OnWindowEvent %W Map ] # меняем родителя wm withdraw .win reparent $chart $w $level catch { wm deiconify .win } raise .win }
Из консоли работает, теперь добавляем в советник.
Изменения в советнике
Как и планировали - добавим параметр про встраивание формы, и если указано то вызовем Embed
enum ENUM_FORM_MODE { FORM_SYSTEM, // системное окно // FORM_CHART_WINDOW // окно в чарте FORM_CHART_WIDGET // элемент чарта }; input ENUM_FORM_MODE FORM_MODE=FORM_SYSTEM; // Расположение формы: #resource "form2.tcl" as string Form_tcl; // заберём скрипт в виде ресурса #resource "form2_embed.tcl" as string Form2_embed_tcl; // встраивание окна #include <ATcl/ATcl.mqh> ATcl *tcl=NULL; long flags[1]={0}; // флаги Tcl_Obj var_BOX=0; // имя переменной BOX int OnInit() { // инициализуем библиотеку if (ATcl_OnInit()!=INIT_SUCCEEDED) { return INIT_FAILED; } // создаём интерпретатор tcl=new ATcl(); if (tcl==NULL || tcl.OnInit()!=INIT_SUCCEEDED) { return INIT_FAILED; } tcl.Eval("set FLAGS 0"); // создаём переменную tcl.Link(flags,0,"FLAGS"); // связываем с элементом массива var_BOX=tcl.Obj("BOX"); // литерал для переменной BOX tcl.Ref(var_BOX); // и делаем себе консоль для отладки if (tcl.Eval("package require tkcon")==TCL_OK) { tcl.Eval("tkcon show"); } // передаём идентификатор окна tcl.Set("chartWin",ChartGetInteger(0,CHART_WINDOW_HANDLE)); // исполняем скрипт из ресурса if (tcl.Eval(Form_tcl)!=TCL_OK) { PrintFormat("error in script:%s",tcl.StringResult()); return INIT_FAILED; } // скрипт встраивания if (tcl.Eval(Form2_embed_tcl)!=TCL_OK) { PrintFormat("error in script:%s",tcl.StringResult()); return INIT_FAILED; } // встраивание формы в чарт if (FORM_MODE==FORM_CHART_WIDGET) { if (tcl.Eval("Embed $chartWin .win 0")!=TCL_OK) { PrintFormat("error in embed:%s",tcl.StringResult()); return INIT_FAILED; } // и сразу передвинем tcl.Eval("winmove .win 10 15"); } // задаём таймер EventSetMillisecondTimer(50); return(INIT_SUCCEEDED); }
включаем, всё вроде хорошо, но что-то опять фокус подключивает
тут уже надо «подыграть» со стороны советника:
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { // если TAB получен как кнопка // занчит закончен переход фокуса по контролам чарта // отдадим фокус форме if (id == CHARTEVENT_KEYDOWN && lparam==9) { tcl.Eval("winfocus .win"); } tcl.Update(); }
вот и закончили c GUI:
форма отображается, советник может реагировать на ввод в форму. Дополнительно сделали что форма может быть элементом чарта или окном виндовс.
Более развёрнутый пример и использования разных виджетов в интерфейсе, посмотрите в TradePanel в соcтаве библиотеки:
PS/ несколько часов назад веб-мастера mql начали блокировать переход на инсталлятор.
Скачать можно по резервному адресу https://www.filefactory.com/file/6zj49q87vhkk/atclsetup.exe
ещё вариант https://dfiles.eu/files/9muo987ad
или через эскизный сайт проекта http://atcl.unaux.com/download/
приношу свои извинения