English Deutsch 日本語
preview
在 MQL5 中创建交易管理员面板(第十部分):基于外部资源的界面

在 MQL5 中创建交易管理员面板(第十部分):基于外部资源的界面

MetaTrader 5示例 |
60 0
Clemence Benjamin
Clemence Benjamin

内容:


引言

我深有感触,找到一个切入点总是令人倍感困难。然而,一旦完成了初稿,在其基础上构建和探索新的可能性就会变得容易许多。许多成功的设计师和开发者都是从简单的草图开始,逐步打磨自己的作品,直到它真正引人注目。即使你认为自己已经达到了产品的终极版本,总还有进一步提升的空间。演进是一个持续的过程,每一个新版本都必须与前一版本相比有所突破。

在这个系列的开发中,我们从一个包含多个界面的单体管理员面板程序起步。当代码变得冗长且难以维护时,我们通过利用 MQL5 标准库、类继承和自定义头文件,引入了更优的代码组织方式。这种方法让我们能够专注于程序的各个独立方面,而不会影响其他部分,大大降低了出错的可能性,并为未来的扩展铺平了道路——唯一受限于开发者的想象力。今天,我们开启一个新的循环,从主面板开始。 

在我看来,符号是最有效的视觉沟通形式之一,能够在最小的空间内瞬间传递丰富的信息。在人类历史上,符号一直扮演着至关重要的沟通角色——早在文字发明之前,早期文明就通过图画和象形符号来表达思想、情感和指令。从古老的埃及象形文字到现代的表情符号,符号始终是一种超越语言障碍的通用视觉速记。

在数字时代,这一传统得以延续。以 Wingdings 字体为例——它是一系列嵌入文本的符号集合,无需文字就能传递含义。在 MQL5 开发中,类似的符号非常有用,因为它们文档完善且易于实现。使用这些符号可以显著减少图表上显示的视觉对象数量,同时依然清晰优雅地传达关键信息。例如,"买入"信号可以用一个简单的向上箭头来表示,而不是使用完整的文字标签,从而节省屏幕空间和处理能力。

我的重点在于界面的设计与管理。通过引入符号元素,我们可以减少视觉杂乱,提升易用性。Wingdings 是一个可用的工具,但我也想强调自定义位图图像的应用。这些图像可以替代大型按钮,同时保持功能完整性,提供一种简洁高效的替代方案,使界面保持整洁且富有吸引力。

虽然 New_Admin_Panel 的主界面在功能上已经让我感到满意,但在视觉吸引力方面仍有提升空间。现在基础架构已经到位,是时候优化和完善界面设计了。在下一节中,我将概述今天的开发目标,以及我们计划如何推进这一演进。


概览

今天的目标聚焦于主界面,我们将重新设计和组织界面元素,打造一个更紧凑、视觉效率更高的面板。我们将首先勾勒出新主界面及其组件的概念设计,然后使用 MQL5 进行实现——集成新的资源并编译改进版的管理员面板。下面提供了一张参考图片,帮助大家直观地了解我们的设计方向。

之前,我提到 Wingdings 是符号化沟通的优秀范例——它提供视觉提示,能够快速简单地传递含义。虽然它们作为简约图标的理想参考很有价值,但我并不打算直接使用它们。相反,我考虑使用自定义设计的位图图像,以更好地匹配新界面的美学和功能目标。像 GIMP 这样的开源图像编辑工具,非常适合创建这些定制图形。

一旦自定义图像准备就绪,我们将开始编写功能代码,并将其与现有程序的功能进行集成。这些视觉资源将使我们能够用轻量级的图标替代笨重的按钮,在保持功能完整性的同时减少视觉杂乱。

作为这个开发周期的一部分,我们还将暂时禁用用户身份验证,以简化测试流程,避免频繁的登录提示。虽然在这个阶段对面板的安全要求并不严格,但如果要在未授权访问可能危及敏感操作的环境中使用,安全就显得尤为重要。尽管之前的实现使用硬编码值进行密钥加密,但开发者应根据需要额外采取措施,实现安全的访问管理。

本次教程将引导你完成在 MQL5 中使用自定义视觉资源的全过程,从概念到实现。到结束时,我们的目标是打造一个精良、响应灵敏且可扩展的界面,不仅功能完善,而且外观简洁现代。

HomeInterfaceSketch

新的最小化主界面草图(56 像素 × 400 像素)


概念设计

基于这个概念——并且在可用资源中没有找到令人满意的符号选项——我决定创建自己的符号,利用开源工具以获得灵活性和可定制性。我使用 Inkscape 快速布局我的设计,因为它是最强大高效的矢量图形编辑器之一。然而,Inkscape 在导出某些文件格式时确实存在局限性。

为了克服这个问题,我使用 GIMP 进一步优化图像,并导出为适合 MQL5 集成的所需位图格式。下面,你将看到界面的概念布局。审阅设计后,我们将分解每个按钮的功能,然后进入代码实现阶段。

HomePanelConceptual design

AdminHome 界面的概念布局

主界面按钮及其功能说明

展开/收起按钮:

这个按钮有两种状态:一种是展开状态,显示所有界面按钮;另一种是收起状态,隐藏所有按钮。

 展开

收起收起

交易管理面板按钮:

我们使用两个箭头图标来直观地表示买入和卖出的概念。这让用户能够一眼就能直观地理解按钮的用途。

 交易管理面板按钮

TradeManagementPanelButton 按下状态 交易管理面板按钮按下状态

通讯面板按钮:

对于这个按钮,我设计了一个信封符号来代表通讯的概念。这个熟悉的图标使用户能够快速识别其用途,并在第一眼就理解其功能。

 通讯面板按钮

CommunicationPanelButtonPressed  通讯面板按钮按下状态

分析面板按钮:

这个按钮以图表符号为特色,有效地传达了分析的概念。由于图表是进行市场分析的重要工具,图标能立即向用户表明其用途。

 分析面板按钮

AnalyticsPanelButtonPressed 分析面板按钮按下状态

显示全部/隐藏全部面板按钮:

这个按钮作为一个便捷的快捷方式,可以立即展开或收起所有面板。它的设计采用了发散箭头,象征着展开或扩展的视野,在视觉上向用户传达其功能。

  显示/隐藏全部按钮

ShowAllHideAllButtonPressed  显示/隐藏全部按钮按下状态

小贴士

为了让图像在 MetaTrader 5 中正常显示,我必须使用 GIMP 将其导出为 BMP 格式,并采用 24 位色设置。任何其他格式都无法在终端中正确显示。上图中的 PNG 格式图像仅用于演示说明。你可以在本文末尾的附件文件夹中找到实际的 BMP 文件。



代码实现

现在我们开始使用 MQL5 代码,将上述概念付诸实现。这标志着界面的转型——从以往的结构转向更精致、更富创意的设计——我们持续推进并完善我们的管理员面板。到结束时,我们将呈现出如下图所示的新主界面。

Inferace

从旧的主界面向新的基于外部资源的界面的迁移

正如之前提到的,我们暂时禁用了身份验证功能,以避免测试过程中登录提示的干扰。重要的是,这并不意味着要完全移除身份验证逻辑。相反,我们只是将相关代码注释掉。请记住,注释中的代码不会被执行;它们存在的目的是帮助开发者用通俗易懂的语言沟通和解释逻辑。

在这一步中,我们还将演示如何将准备好的自定义图像作为资源使用,在主界面上显示为浮动按钮。这标志着不再使用 Dialog 类来构建主面板界面。新设计更加简约,采用了更极简的风格。

现在让我们来分解主程序的结构。

1. 概述

在本节中,我们的目标是将一个基础的管理员面板转换为现代化的、使用 BMP 图像按钮的浮动界面。目标是打造这样一个界面:按钮以图像形式显示,并能够切换状态(例如在"展开"和"收起"之间),提供视觉上吸引人且富有交互性的设计体验。这些按钮浮动在其他图表元素(如 K 线)之上,确保用户交互流畅。

我们引入了身份验证模块(以及通讯、Telegram、对话框控件、交易管理和分析的外部模块),尽管身份验证逻辑本身目前通过注释已暂时禁用。这个决定是暂时的,因为该功能仍在开发中,它使我们能够专注于开发和测试浮动按钮界面,而不会受到密码提示的干扰。稍后我们可以通过最少的调整重新启用身份验证代码,实现向完全安全系统的无缝过渡。

//+------------------------------------------------------------------+
//|                                               New_Admin_Panel.mq5|
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.00"

// Authentication inputs (unused; authentication disabled).
input string AuthPassword = "2024";
input string TwoFactorChatID = "YOUR_CHAT_ID";
input string TwoFactorBotToken = "YOUR_BOT_TOKEN";

#include <Authentication.mqh>
#include <CommunicationsDialog.mqh>
#include <Telegram.mqh>
#include <Controls\Dialog.mqh>
#include <TradeManagementPanel.mqh>
#include <AnalyticsPanel.mqh>

2. MQL5 中的文件资源

MQL5 允许使用资源指令将外部文件(如图像和声音)直接嵌入到程序中。本项目使用 BMP 图像文件作为创建自定义按钮的资源。通过资源语句嵌入这些图像文件后,图像会与可执行文件打包在一起,确保程序运行时它们始终可用。对于构建可靠且可移植的界面而言,这一技术至关重要,因为它保证了无论外部文件系统如何,都能稳定访问必要的视觉资源。

#resource "\\Images\\expand.bmp"
#resource "\\Images\\collapse.bmp"
#resource "\\Images\\TradeManagementPanelButton.bmp"
#resource "\\Images\\TradeManagementPanelButtonPressed.bmp"
#resource "\\Images\\CommunicationPanelButton.bmp"
#resource "\\Images\\CommunicationPanelButtonPressed.bmp"
#resource "\\Images\\AnalyticsPanelButton.bmp"
#resource "\\Images\\AnalyticsPanelButtonPressed.bmp"
#resource "\\Images\\ShowAllHideAllButton.bmp"
#resource "\\Images\\ShowAllHideAllButtonPressed.bmp"

3. 界面创建与按钮设计

该界面使用显示 BMP 图像的"位图标签"对象构建。界面中的每个按钮都由其中一个图像对象表示,它们在图表上的位置由预定义的坐标确定。这些对象被分配了较高的 Z 轴顺序值,使其渲染在其他图表元素之上,从而能够高效响应用户的点击操作。这种设置不仅确保了浮动按钮的视觉吸引力,还保证了功能的稳健性。

// Button names
string toggleButtonName    = "ToggleButton";
string tradeButtonName     = "TradeButton";
string commButtonName      = "CommButton";
string analyticsButtonName = "AnalyticsButton";
string showAllButtonName   = "ShowAllButton";

// Button original positions
const int BUTTON_TOGGLE_X    = 10;
const int BUTTON_TOGGLE_Y    = 30;
const int BUTTON_TRADE_X     = 10;
const int BUTTON_TRADE_Y     = 100;
const int BUTTON_COMM_X      = 10;
const int BUTTON_COMM_Y      = 170;
const int BUTTON_ANALYTICS_X = 10;
const int BUTTON_ANALYTICS_Y = 240;
const int BUTTON_SHOWALL_X   = 10;
const int BUTTON_SHOWALL_Y   = 310;

4. 按钮隐藏与显示技术

我们使用辅助函数将按钮移动到屏幕外的特定坐标,从而模拟出折叠效果。当界面展开时,另一组辅助函数会将按钮恢复到其原始的屏幕位置。这种技术用于管理界面元素的可见性,既高效又响应迅速。

// Off-screen coordinate for hiding buttons
const int HIDDEN_X = -50;

// Hide a button by moving it off-screen.
void HideButton(string buttonName)
{
   ObjectSetInteger(0, buttonName, OBJPROP_XDISTANCE, HIDDEN_X);
}

// Restore a button to its original position.
void ShowButton(string buttonName, int X, int Y)
{
   ObjectSetInteger(0, buttonName, OBJPROP_XDISTANCE, X);
   ObjectSetInteger(0, buttonName, OBJPROP_YDISTANCE, Y);
}

5. 处理子面板交

界面上的每个按钮都关联到一个子面板(例如交易管理、通讯或分析面板)。当点击按钮时,如果关联的子面板不存在,则会创建它;如果已存在,则会在显示和隐藏状态之间切换。这种按需创建机制最大限度地减少了资源占用,并保持界面整洁。每个子面板独立管理,允许用户一次专注于一个部分,同时仍能在需要时访问其他部分。

// Handle the Communications Panel button.
void HandleCommunications()
{
   if(g_commPanel != NULL && g_commPanel.IsVisible())
   {
      g_commPanel.Hide();
      ChartRedraw();
      return;
   }
   if(g_commPanel == NULL)
   {
      g_commPanel = new CCommunicationDialog(TwoFactorChatID, TwoFactorBotToken);
      if(!g_commPanel.Create(g_chart_id, "CommPanel", g_subwin, 80, 100, 380, 300))
      {
         delete g_commPanel;
         g_commPanel = NULL;
         return;
      }
      ObjectSetInteger(0, "CommPanel", OBJPROP_ZORDER, 10);
   }
   g_commPanel.Toggle();
   ChartRedraw();
}

// Handle the Trade Management Panel button.
void HandleTradeManagement()
{
   if(g_tradePanel != NULL && g_tradePanel.IsVisible())
   {
      g_tradePanel.Hide();
      ChartRedraw();
      return;
   }
   if(g_tradePanel == NULL)
   {
      g_tradePanel = new CTradeManagementPanel();
      if(!g_tradePanel.Create(g_chart_id, "TradeManagementPanel", g_subwin, 390, 20, 900, 530))
      {
         delete g_tradePanel;
         g_tradePanel = NULL;
         return;
      }
      ObjectSetInteger(0, "TradeManagementPanel", OBJPROP_ZORDER, 10);
   }
   g_tradePanel.Toggle();
   ChartRedraw();
}

// Handle the Analytics Panel button.
void HandleAnalytics()
{
   if(g_analyticsPanel != NULL && g_analyticsPanel.IsVisible())
   {
      g_analyticsPanel.Hide();
      ChartRedraw();
      return;
   }
   if(g_analyticsPanel == NULL)
   {
      g_analyticsPanel = new CAnalyticsPanel();
      if(!g_analyticsPanel.CreatePanel(g_chart_id, "AnalyticsPanel", g_subwin, 980, 20, 1480, 480))
      {
         delete g_analyticsPanel;
         g_analyticsPanel = NULL;
         return;
      }
      ObjectSetInteger(0, "AnalyticsPanel", OBJPROP_ZORDER, 10);
   }
   g_analyticsPanel.Toggle();
   ChartRedraw();
}

6. 折叠/展开逻辑的封装

为了简化用户界面管理,折叠和展开的逻辑被封装在单个函数中。该函数读取主切换按钮的当前状态,翻转其状态,然后隐藏或恢复其他按钮的位置。将此逻辑封装在一个函数中,避免了事件处理代码中的冗余,使整体设计更加简洁且易于维护。

// Toggle collapse/expand of interface buttons.
void ToggleInterface()
{
   // Toggle the state of the toggle button.
   bool currentState = ObjectGetInteger(0, toggleButtonName, OBJPROP_STATE);
   bool newState = !currentState;
   ObjectSetInteger(0, toggleButtonName, OBJPROP_STATE, newState);
   
   if(newState)
      UpdateButtonVisibility(false);  // Collapse: hide buttons and minimize sub-panels.
   else
      UpdateButtonVisibility(true);   // Expand: restore button positions.
   
   ChartRedraw();
}

7. 程序初始化

彻底的测试对于确保界面行为符合预期至关重要。初始化函数负责设置按钮,并确保它们从一开始就可见。在测试过程中,应验证按钮是否保持响应,子面板是否正确切换,以及得益于较高的 Z 轴顺序值,按钮是否始终显示在图表元素之上。此外,虽然目前身份验证功能已被禁用,但相关代码以注释形式保留,以便在需要时轻松重新启用访问限制。未来的改进可能包括集成音效或动画,以进一步丰富用户体验。

// Expert initialization function
int OnInit()
{
   g_chart_id = ChartID();
   g_subwin   = 0;

   // For potential future use, authentication can be re-enabled by uncommenting these:
   // if(!g_authManager.Initialize())
   //    return INIT_FAILED;

   // Create main toggle and sub-panel buttons using their original positions.
   CreateObjectBITMAP_LABEL(toggleButtonName, BUTTON_TOGGLE_X, BUTTON_TOGGLE_Y, "::Images\\expand.bmp", "::Images\\collapse.bmp");
   CreateObjectBITMAP_LABEL(tradeButtonName, BUTTON_TRADE_X, BUTTON_TRADE_Y, "::Images\\TradeManagementPanelButtonPressed.bmp", "::Images\\TradeManagementPanelButton.bmp");
   CreateObjectBITMAP_LABEL(commButtonName, BUTTON_COMM_X, BUTTON_COMM_Y, "::Images\\CommunicationPanelButtonPressed.bmp", "::Images\\CommunicationPanelButton.bmp");
   CreateObjectBITMAP_LABEL(analyticsButtonName, BUTTON_ANALYTICS_X, BUTTON_ANALYTICS_Y, "::Images\\AnalyticsPanelButtonPressed.bmp", "::Images\\AnalyticsPanelButton.bmp");
   CreateObjectBITMAP_LABEL(showAllButtonName, BUTTON_SHOWALL_X, BUTTON_SHOWALL_Y, "::Images\\ShowAllHideAllButtonPressed.bmp", "::Images\\ShowAllHideAllButton.bmp");

   // Always show the interface initially.
   UpdateButtonVisibility(true);
   ChartRedraw();
   return INIT_SUCCEEDED;
}

// Expert deinitialization function
void OnDeinit(const int reason)
{
   ObjectDelete(0, toggleButtonName);
   ObjectDelete(0, tradeButtonName);
   ObjectDelete(0, commButtonName);
   ObjectDelete(0, analyticsButtonName);
   ObjectDelete(0, showAllButtonName);

   if(g_commPanel != NULL)
   {
      g_commPanel.Destroy(reason);
      delete g_commPanel;
      g_commPanel = NULL;
   }
   if(g_tradePanel != NULL)
   {
      g_tradePanel.Destroy(reason);
      delete g_tradePanel;
      g_tradePanel = NULL;
   }
   if(g_analyticsPanel != NULL)
   {
      g_analyticsPanel.Destroy(reason);
      delete g_analyticsPanel;
      g_analyticsPanel = NULL;
   }
}


测试

与 MetaTrader 5 中的往常一样,你可以通过从 Expert Advisors 列表中将程序拖拽到图表上来启动它。为了确保编译和执行顺畅,必须确保所有外部模块都正确放置在各自的文件夹中,并且所有必需的资源(如图像)都组织得当。

下面,我附上了一张图片,展示我们重新设计后的界面效果。通过这些视觉效果,你可以清楚地了解我们的设计方向,并发现可以进一步改进的地方。

悬浮主界面

测试 New_Admin_Home 界面

小贴士

当程序添加到图表后第一次使用新按钮时,首次点击会初始化相应的面板,但面板不会立即显示。只有在第二次点击后,面板才会变为可见。


结论

今天,我们探讨了如何有效地利用外部资源在 MQL5 中构建强大且可定制的用户界面。在这个项目中,我们从基础的主界面演进为一个更具动态感和视觉吸引力的浮动面板系统——在保留核心功能的同时,引入了新特性以提升易用性和可访问性。

其中一个关键收获是,这次开发加深了我们对 MQL5 功能的理解,特别是关于 BMP 图像等图形资源的使用。浮动按钮设计提供了对管理员面板功能的集中访问,同时保持图表整洁,为交易洞察保留了更多的可见图表空间(像素)。

值得一提的是,没有哪个解决方案是真正完美的。然而,每一次迭代——每一次进步——都让我们离更好、更高效的版本更近了一步。早期版本仍然非常重要,因为它们为创新和改进提供了参考基准。

为了支持其他人能够顺利跟进,我考虑将今天的源代码与之前的模块一起打包到一个统一的 ZIP 文件中。这样可以确保包含所有必要的文件,尤其是对于那些可能跳过或错过了之前设置的用户。缺少文件往往会导致编译错误,所以这将帮助用户顺利开始。

特别感谢 MetaQuotes 提供的关于在 MQL5 中使用资源的详实文章,这是宝贵的参考资料。在这样一个真实项目中实现这些想法,是一次富有启发性和收获的体验。


文件名 说明
New_Admin_Panel.mq5 具有新外观的新主程序。
New_Admin_Panel_BMP images.zip 
这是包含所有使用的位图图像的文件夹。请将这些图像添加到 MQL5\Images 目录中,以确保界面能够正确加载和显示它们。
All Modules.zip 这是包含第 (X)部分中使用的所有头文件的文件夹。请将这些文件解压到 MetaTrader 安装目录的 MQL5\Include 目录中,以确保正确编译和功能运行。


本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/17780

附加的文件 |
All_Modules.zip (15.92 KB)
基于MQL5中表模型的表类和表头类:应用MVC概念 基于MQL5中表模型的表类和表头类:应用MVC概念
本文是致力于使用MVC(模型-视图-控制器)架构范式在MQL5中实现表模型系列文章的第二部分。本文基于先前创建的表模型来开发表类和表头。已经开发的类将构成进一步实现视图和控制器组件的基础,这些内容将在随后的文章中讨论。
MQL5 简介(第 13 部分):构建自定义指标的初学者指南(二) MQL5 简介(第 13 部分):构建自定义指标的初学者指南(二)
本文将指导您从头开始构建自定义 Heikin Ashi 指标,并演示如何将自定义指标集成到 EA 中。它涵盖了指标计算、交易执行逻辑和风险管理技术,以增强自动化交易策略。
在MQL5中构建自定义市场状态检测系统(第一部分):指标 在MQL5中构建自定义市场状态检测系统(第一部分):指标
本文详细介绍了如何使用自相关和波动性等统计方法,在MQL5中创建一个市场状态检测系统。文中提供了用于分类趋势、盘整和波动行情的类代码,以及一个自定义指标。
纯 MQL5 货币对强弱指标 纯 MQL5 货币对强弱指标
我们将在 MQL5 中开发货币强势分析的专业指标。这本分步指南将向你展示如何为 MetaTrader 5 开发一款功能强大的交易工具,该工具带有可视化仪表板。您将学习如何计算多个时间周期(H1、H4、D1)内货币对的强度,实现动态数据更新,并创建用户友好的界面。