Español Português
preview
От начального до среднего уровня: События мыши

От начального до среднего уровня: События мыши

MetaTrader 5Примеры |
78 0
CODE X
CODE X

Введение

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

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


События мыши

Итак, начнем с понимания следующего момента: мышь — штука глупая, раздражающая, ужасная и будто вылезшая из самой преисподней. Она совершает абсолютно случайные действия, которые не учитывают особенности работы как кода, так и самого программиста. Несмотря на то, что MetaTrader 5 очень полезен и обеспечивает значительную гибкость, по умолчанию он не генерирует события мыши для каких-либо приложений, работающих на графике. Но почему? Причина заключается именно в том, что MetaTrader 5 ориентирован на обеспечение отличной производительности и эффективности. И мышь, в свою очередь, оказывает негативное влияние на данные показатели. Тем более, что размещенные на графике приложения, как правило, очень плохо оптимизированы для событий, связанных с мышью.

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

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

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

Следующий пункт: поскольку MetaTrader 5 по умолчанию не генерирует события мыши для запущенных на графике приложений, нам приходится писать код, который сообщает MetaTrader 5, что мы хотим получать события мыши. И как только будет установлено, что график должен получать события мыши, нам, как программистам, при реализации кода, запускающего эти события в MetaTrader 5, необходимо указать, что при закрытии приложения события мыши должны быть отключены.

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

Следующий момент: не пытайтесь включить или использовать больше ресурсов, чем необходимо. Между событиями, генерируемыми MetaTrader 5 при срабатывании событий мыши, существуют небольшие различия. Если приложение не требует использования определенных ресурсов, не следует их включать. Всё должно быть просто и незамысловато. Включите только то, что действительно необходимо.

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

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

Итак, начнем с учетом того, что события мыши могут быть перехвачены только обработчиком OnChartEvent. Следовательно, только два типа приложений могут перехватывать события мыши. Один из них — это советник, о котором мы поговорим позже. А вторым будет индикатор. Но всё сказанное здесь также относится к советнику, поскольку все данные будут направляться обработчику события OnChartEvent. Важно это понять, уважаемые читатели, чтобы у вас не осталось вопросов: "Ух ты, вы объяснили, как работать с мышью на индикаторе. Однако вы так и не объяснили, как это сделать в советнике".

Всё начинается с кода, показанного ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
09. 
10.     return INIT_SUCCEEDED;
11. };
12. //+------------------------------------------------------------------+
13. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
14. {
15.     return rates_total;
16. };
17. //+------------------------------------------------------------------+
18. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
19. {
20.     switch (id)
21.     {
22.         case CHARTEVENT_MOUSE_MOVE:
23.             break;
24.         case CHARTEVENT_MOUSE_WHEEL:
25.             break;
26.     }
27. };
28. //+------------------------------------------------------------------+
29. void OnDeinit(const int reason)
30. {
31. };
32. //+------------------------------------------------------------------+

Код 01

Здесь представлено то, что мы можем назвать базовым каркасом индикатора, который будет использовать мышь в своих операциях. Прошу заметить следующее: в обработчике события OnChartEvent можно перехватывать два типа событий мыши. Первый пример находится в строке 22, а второй — в строке 24. Использование того или иного события не влияет на выполнение или компиляцию кода. Однако в текущем виде код MetaTrader 5 не будет генерировать ни одно из этих двух событий. Это происходит так, потому что обе функции по умолчанию отключены. Для того, чтобы событие OnChartEvent получало одно из этих двух событий, нам необходимо включить именно тот тип события, который мы хотим получить.

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

Для возможности наблюдения за работой мыши в действии и, следовательно, понимания различных других аспектов, которые становятся более доступными для восприятия при визуализации процессов, включим одно из событий мыши с одновременным выводом определенной информации непосредственно на график. Таким образом, мы на практике узнаем, как это работает на самом деле. Для этого мы изменим код 01, преобразовав его в код 02, который показываем ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
09.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
10. 
11.     return INIT_SUCCEEDED;
12. };
13. //+------------------------------------------------------------------+
14. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
15. {
16.     return rates_total;
17. };
18. //+------------------------------------------------------------------+
19. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
20. {
21.     string sz = "";
22. 
23.     switch (id)
24.     {
25.         case CHARTEVENT_MOUSE_MOVE:
26.             Comment(sz);
27.             sz += "Mouse position X: " + (string)(short)lparam;
28.             sz += "\nMouse position Y: " + (string)(short)dparam;
29.             Comment(sz);
30.             break;
31.         case CHARTEVENT_MOUSE_WHEEL:
32.             break;
33.     }
34. };
35. //+------------------------------------------------------------------+
36. void OnDeinit(const int reason)
37. {
38.     Comment("");
39.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
40. };
41. //+------------------------------------------------------------------+

Код 02

При выполнении кода 02 на графике мы увидим результат, аналогичный показанному в следующей анимации.

Анимация 01

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

И да, графическое окно разделено на области. Подобные аспекты рассматриваются при изучении программирования для графических сред, например, при разработке приложений Windows. Это процесс, включающий в себя множество деталей. Однако я постараюсь максимально упростить объяснение. Таким образом, можно понять, что действительно важно в программировании на MQL5.

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

Для повышения наглядности можно рассмотреть следующий пример: предположим, что используется экран с разрешением 1920 x 1080 пикселей, то есть экран Full HD. Если область панели задач занимает 48 пикселей, то полезная область вашего экрана составит 1920 x 1032 пикселя. Таким образом, любое приложение, находящееся в развернутом на весь экран окне при отображаемой панели задач, сможет занимать указанные ранее размеры, то есть меньше разрешения экрана Full HD. Эффект практически незаметен. Однако в качестве примечания можно указать, что при просмотре изображения размером 1920 x 1080 пикселей на экране чуть меньшего размера отображение будет немного отличаться, так как потребуется компенсировать недостающие 48 пикселей.

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

Как и на рабочем столе, эти окна становятся всё меньше и меньше, поскольку каждая область, не входящая в клиентскую область, уменьшает доступное полезное пространство. Область, которую мы можем использовать, — это именно та, которая отвечает за изменение значения положения мыши, и она варьируется от 0 x 0, что находится в верхнем левом углу, до любого максимального значения, которое находится в нижнем правом углу. Это происходит при использовании стандартной модели построения графиков, которая расположена в четвертом квадранте.

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

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

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

Думаю, теперь это ясно. А что насчет кнопок? Как определить, когда была нажата та или иная кнопка? Проблема с кнопками решается с помощью маски. Самый простой способ — использовать макрос в коде. Это упрощает планирование и фильтрацию различных событий, поскольку мы можем одновременно щелкать по элементу и перетаскивать его.

Перед тем, как мы перейдем к данному вопросу, обратите внимание, что в строке 09 из кода 02 мы активировали событие мыши. Однако в строке 39 мы его выключили. А поскольку информация представлена в различных форматах данных, нам необходимо преобразовать эти данные таким образом, чтобы они отображали значения, соответствующие нашим ожиданиям. Именно поэтому мы используем явное преобразование типов, как можно видеть в строках 27 и 28. Всё просто.


Кнопки мыши

Вопрос о кнопках мыши является довольно спорной темой, также как и причина этой спорности. Некоторые пользователи могут считать, что реализовать функции, связанные с мышью, довольно просто. Однако на практике всё несколько сложнее, чем кажется. Причина этого на первый взгляд кажется чем- то странным. В большинстве приложений кнопки мыши используются для выполнения определенных функций. Или даже сама операционная система. В случае с MetaTrader 5 средняя кнопка мыши отвечает за вызов обычного перекрестия для анализа. Правая кнопка, наоборот, открывает меню для различных манипуляций и настроек графика.

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

Наше приложение возьмет с собой эти заимствованные кнопки. Это не позволит пользователю получить к ним доступ в течение указанного периода использования MetaTrader 5 на этом конкретном графике. Чтобы восстановить работу кнопок, которые наше приложение использовало, но не вернуло из-за неисправности, пользователю придется закрыть график символа и открыть его заново. Только таким образом MetaTrader 5 сможет вернуть себе контроль над этими кнопками. Поэтому нам следует быть внимательными при планировании подобных моментов. В противном случае мы создадим крайне неприятный опыт для пользователя нашего приложения.

"Хорошо, думаю, мы разобрались с проблемой. Но как же тогда это делается? Если мне по какой-либо причине необходимо взять под контроль правую и центральную кнопки, как мне следует поступить? И чтобы вернуть управление этими же кнопками в MetaTrader 5, как лучше всего это сделать?" Что ж, это очень хорошие вопросы, уважаемые читатели. И, поскольку это имеет отношение к делу, мы объясним ещё кое-что довольно интересное. В статьях, где мы говорили о событиях клавиатуры, мы не упомянули тот факт, что мы тоже можем получить ещё больший контроль над клавиатурой. Но поскольку мы собираемся взять под контроль кнопки мыши, а также другие элементы, я думаю, это хорошая возможность показать, как полностью управлять элементами взаимодействия с пользователем. Это делается с помощью следующего кода:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
09.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
10. //+----------------+    
11.     ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
12.     ChartSetInteger(0, CHART_CONTEXT_MENU, false);
13.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
14. //+----------------+
15.     ChartSetInteger(0, CHART_KEYBOARD_CONTROL, false);
16.     ChartSetInteger(0, CHART_QUICK_NAVIGATION, false);
17. //+----------------+
18. 
19.     return INIT_SUCCEEDED;
20. };
21. //+------------------------------------------------------------------+
22. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
23. {
24.     return rates_total;
25. };
26. //+------------------------------------------------------------------+
27. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
28. {
29.     string sz = "";
30. 
31.     switch (id)
32.     {
33.         case CHARTEVENT_MOUSE_MOVE:
34.             Comment(sz);
35.             sz += "Mouse position X: " + (string)(short)lparam;
36.             sz += "\nMouse position Y: " + (string)(short)dparam;
37.             Comment(sz);
38.             break;
39.         case CHARTEVENT_MOUSE_WHEEL:
40.             break;
41.     }
42. };
43. //+------------------------------------------------------------------+
44. void OnDeinit(const int reason)
45. {
46.     Comment("");
47.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
48. //+----------------+    
49.     ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
50.     ChartSetInteger(0, CHART_CONTEXT_MENU, true);
51.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
52. //+----------------+
53.     ChartSetInteger(0, CHART_KEYBOARD_CONTROL, true);
54.     ChartSetInteger(0, CHART_QUICK_NAVIGATION, true);
55. //+----------------+
56. };
57. //+------------------------------------------------------------------+

Код 03

Этот код 03 во время своего выполнения будет блокировать использование стандартных элементов и элементов управления MetaTrader 5 — таких как, например, обычное перекрестие анализа или даже возможность нажатия клавиши ENTER для смены текущего символа графика Итак, давайте посмотрим, как мы добились этого: во-первых, в строке 11 мы сообщаем MetaTrader 5, что пользователь больше не сможет перетаскивать график для отображения баров, которые не отображаются. Для повторной активации этого стандартного поведения MetaTrader 5 мы используем строку 49.

В строке 12 мы отключаем контекстное меню графика, на котором запущен код. Это меню открывается нажатием правой кнопки мыши. Для повторного включения этой стандартной функции MetaTrader 5 мы используем строку 50. В строке 13 мы отключаем обычное перекрестие анализа, которое вызывается нажатием средней кнопки мыши. Для повторного включения этого ресурса мы используем строку 51.

Теперь, в строке 15, мы отключаем ресурсы, связанные с прокруткой и масштабированием с помощью клавиатуры. Для их повторной активации мы используем строку 53. И, наконец, в строке 16 мы отключаем функцию, которая позволяет пользователю нажимать клавишу ENTER и вводить название символа или временного интервала. Для повторного включения этого же ресурса мы используем строку 54.

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
09.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
10. //+----------------+    
11.     ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
12.     ChartSetInteger(0, CHART_CONTEXT_MENU, false);
13.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
14. //+----------------+
15.     ChartSetInteger(0, CHART_KEYBOARD_CONTROL, false);
16.     ChartSetInteger(0, CHART_QUICK_NAVIGATION, false);
17. //+----------------+
18. 
19.     return INIT_SUCCEEDED;
20. };
21. //+------------------------------------------------------------------+
22. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
23. {
24.     return rates_total;
25. };
26. //+------------------------------------------------------------------+
27. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
28. {
29.     string sz = "";
30. 
31.     switch (id)
32.     {
33.         case CHARTEVENT_KEYDOWN:
34.             Comment(sz);
35.             sz += "Key press: " + (string)lparam;
36.             Comment(sz);
37.             break;
38.         case CHARTEVENT_MOUSE_MOVE:
39.             Comment(sz);
40.             sz += "Mouse position X: " + (string)(short)lparam;
41.             sz += "\nMouse position Y: " + (string)(short)dparam;
42.             sz += StringFormat("\nHexadecimal mask of mouse buttons is: 0x%02X", (uchar)sparam);
43.             Comment(sz);
44.             break;
45.         case CHARTEVENT_MOUSE_WHEEL:
46.             break;
47.     }
48. };
49. //+------------------------------------------------------------------+
50. void OnDeinit(const int reason)
51. {
52.     Comment("");
53.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
54. //+----------------+    
55.     ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
56.     ChartSetInteger(0, CHART_CONTEXT_MENU, true);
57.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
58. //+----------------+
59.     ChartSetInteger(0, CHART_KEYBOARD_CONTROL, true);
60.     ChartSetInteger(0, CHART_QUICK_NAVIGATION, true);
61. //+----------------+
62. };
63. //+------------------------------------------------------------------+

Код 04

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

Изображение 01

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

Итак, теперь, когда всё это объяснено, давайте посмотрим, что делает код 04.

Анимация 02

Важно, чтобы вы обратили внимание на следующее: анимация 02 имеет единственную цель — продемонстрировать, как MQL5 интерпретирует действия мыши и клавиатуры. Крайне важно протестировать этот индикатор, который мы видим в коде 04, чтобы понять, на что способна комбинация кнопок и клавиатуры. Это объясняется тем, что некоторые вещи довольно сложно объяснить, какими бы простыми они ни казались. В идеале, нам следует попробовать это на практике и понять, как эти элементы можно комбинировать.

Однако, если посмотреть на комбинацию кнопок и клавиатуры, то видно, что строка 42 из кода 04 иногда будет показывать один результат, а в других случаях — другой. Это кажется странным. Но это происходит при нажатии клавиш SHIFT и/или CTRL. Эти две клавиши обладают интересной особенностью. При нажатии без перемещения мыши, вы заметите появление результата работы строки 35.

Однако, если переместить мышь, MetaTrader 5 вызовет событие, которое активирует строку 42. Почему? Причина в том, что эти две клавиши, SHIFT и CTRL, связаны с мышью и являются частью битовой маски. Понимание этого позволит вам создавать вещи, которые в ином случае было бы гораздо сложнее сделать. Поэтому очень важно: если вы действительно хотите понять, как работают мышь и клавиатура в MQL5, вы должны изучить результаты, представленные кодом 04.

Однако нам ещё нужно поговорить о другом типе событий. А именно то, что включается в строке 45 из кода 04. Но это событие прокрутки колёсика мыши, мягко говоря, довольно странное. По крайней мере, здесь, в MQL5. Оно срабатывает и возвращает структуру данных, которая немного отличается от того, что можно было ожидать. Возможно, именно поэтому оно не так часто используется в приложениях, написанных на MQL5.

Чтобы это понять, мы добавим необходимый код в обработчик событий, чтобы иметь возможность визуализировать разницу, существующую между CHARTEVENT_MOUSE_MOVE и CHARTEVENT_MOUSE_WHEEL. И поверьте, это действительно очень интересно наблюдать на практике. Таким образом, используемый код показан ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
09.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
10.     ChartSetInteger(0, CHART_EVENT_MOUSE_WHEEL, true);
11. //+----------------+    
12.     ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
13.     ChartSetInteger(0, CHART_CONTEXT_MENU, false);
14.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
15. //+----------------+
16.     ChartSetInteger(0, CHART_KEYBOARD_CONTROL, false);
17.     ChartSetInteger(0, CHART_QUICK_NAVIGATION, false);
18. //+----------------+
19. 
20.     return INIT_SUCCEEDED;
21. };
22. //+------------------------------------------------------------------+
23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
24. {
25.     return rates_total;
26. };
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30.     string sz = "";
31. 
32.     switch (id)
33.     {
34.         case CHARTEVENT_KEYDOWN:
35.             Comment(sz);
36.             sz += "Key press: " + (string)lparam;
37.             Comment(sz);
38.             break;
39.         case CHARTEVENT_MOUSE_MOVE:
40.             Comment(sz);
41.             sz += " **** Chart Event Mouse Move ****";
42.             sz += "\nMouse position X: " + (string)(short)lparam;
43.             sz += "\nMouse position Y: " + (string)(short)dparam;
44.             sz += StringFormat("\nHexadecimal mask of mouse buttons is: 0x%02X", (uchar)sparam);
45.             Comment(sz);
46.             break;
47.         case CHARTEVENT_MOUSE_WHEEL:
48.             Comment(sz);
49.             sz += " **** Chart Event Mouse Wheel ****";
50.             sz += "\nMouse position X: " + (string)(short)lparam;
51.             sz += "\nMouse position Y: " + (string)(short)(lparam >> 16);
52.             sz += "\nMouse Delta: " + (string) dparam;
53.             sz += StringFormat("\nHexadecimal mask of mouse buttons is: 0x%02X", (uchar)(lparam >> 32));
54.             Comment(sz);
55.             break;
56.     }
57. };
58. //+------------------------------------------------------------------+
59. void OnDeinit(const int reason)
60. {
61.     Comment("");
62.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
63.     ChartSetInteger(0, CHART_EVENT_MOUSE_WHEEL, false);
64. //+----------------+    
65.     ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
66.     ChartSetInteger(0, CHART_CONTEXT_MENU, true);
67.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
68. //+----------------+
69.     ChartSetInteger(0, CHART_KEYBOARD_CONTROL, true);
70.     ChartSetInteger(0, CHART_QUICK_NAVIGATION, true);
71. //+----------------+
72. };
73. //+------------------------------------------------------------------+

Код 05

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

Анимация 03

Обратите внимание, что между кодом 04 и кодом 05 практически нет разницы. Но это небольшое различие означает, что оба кода имеют совершенно разные цели. В строке 10 из кода 05 мы включаем событие для получения любого движения колёсика мыши, которое совершает пользователь. Аналогичным образом, в строке 63 мы отключили получение тех же самых событий. Однако я хочу обратить ваше внимание на то, что отображается в обработчике событий. Потому что здесь мы находим принципиальное различие.

Прошу заметить, что обработчик для CHARTEVENT_MOUSE_WHEEL очень похож на обработчик для CHARTEVENT_MOUSE_MOVE. Обратите внимание на то, откуда берутся эти значения. Расположение значений позиции и нажатых кнопок в обоих случаях различно. Но именно здесь разница становится действительно огромной. МЫ ПОЛУЧИМ СОБЫТИЕ CHARTEVENT_MOUSE_WHEEL ТОЛЬКО в том случае, если произойдет какое-либо событие при прокрутке колёсика мыши. Помимо этого, все остальные события мыши будут направляться в событие CHARTEVENT_MOUSE_MOVE. Именно поэтому так редко можно встретить приложения или код, использующие событие CHARTEVENT_MOUSE_WHEEL. Это происходит так, потому что мы не получаем обновления о других состояниях мыши, таких как движение или нажатия кнопок, если только не произойдет событие на колёсике мыши.

Итак, как уже упоминалось, мышь — отличный союзник. Однако, в зависимости от того, что мы хотим реализовать, это может быть что-то прямо из глубин ада.


Заключительные идеи

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

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

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

файл MQL5 Описание
Code 01 Демонстрация мыши


Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/16000

Прикрепленные файлы |
Code_01.mq5 (2.88 KB)
Разработка инструментария для анализа Price Action (Часть 61): Структурные пробои наклонных трендовых линий с подтверждением по трем свингам Разработка инструментария для анализа Price Action (Часть 61): Структурные пробои наклонных трендовых линий с подтверждением по трем свингам
Представлен инструмент для анализа пробоев наклонных трендовых линий, который использует проверку по трем свингам для генерации объективных сигналов Price Action. Система автоматизирует выявление свингов, построение трендовых линий и подтверждение пробоев, используя логику пересечения цены с линией, чтобы снизить шум и стандартизировать исполнение сигналов. В статье изложены правила стратегии, показана реализация на языке MQL5 и рассмотрены результаты тестирования; инструмент предназначен для анализа и подтверждения сигналов, а не для автоматической торговли.
Разработка инструментария для анализа Price Action (Часть 60): Объективное построение трендовых линий по свингам для структурного анализа Разработка инструментария для анализа Price Action (Часть 60): Объективное построение трендовых линий по свингам для структурного анализа
Мы предлагаем подход к трендовым линиям на основе четких правил, который не опирается на опорные точки индикаторов и использует упорядоченные свинги, полученные непосредственно из ценовых данных. В статье разбираются выявление свингов, проверка их размера по ATR или фиксированным порогам, а также подтверждение восходящих и нисходящих структур, после чего эти правила реализуются на языке MQL5 без перерисовки и с избирательным выводом. Вы получаете четкий и воспроизводимый способ отслеживать структурные уровни поддержки и сопротивления, который надежно работает в разных рыночных условиях.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Разработка торговой стратегии: Метод Triple Sine для возврата к среднему Разработка торговой стратегии: Метод Triple Sine для возврата к среднему
В этой статье представлен метод Triple Sine (тройного синуса) для возврата к среднему — торговая стратегия, опирающаяся на новый математический индикатор Triple Sine Oscillator (TSO). Индикатор TSO выводится из функции куба синуса, которая колеблется между –1 и +1, что делает его подходящим для выявления условий перекупленности и перепроданности на рынке. В целом, данное исследование демонстрирует, как математические функции можно преобразовать в практические инструменты для торговли.