English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Concevoir et implémenter de nouveaux widgets GUI axés sur la classe CChartObject

Concevoir et implémenter de nouveaux widgets GUI axés sur la classe CChartObject

MetaTrader 5Indicateurs | 12 janvier 2022, 14:14
99 0
investeo
investeo

Introduction

Après avoir écrit unarticle sur Expert Advisor automatique avec interface GUI , il s'est avéré qu'il serait souhaitable d'améliorer l'interface avec de nouvelles fonctionnalités pour les indicateurs et les Expert Advisors plus complexes. Après m'être familiarisé avec les classes de la bibliothèque standard MQL5, j'ai implémenté de nouveaux widgets.

Dans cet article je vais décrire un processus d’utilisation des classes deMQL5 Standard Library pour les objets GUI et comment implémenter de nouvelles classes dérivées depuis la classeCChartObjectEdit: CChartObjectProgressBar, CChartObjectSpinner et CChartEditTable. La classe CChartEditTable utilise un tableau d'objets dynamique bi-dimensionnel, il s'agit d'un exemple pratique sur la façon d'implémenter un tableau d'objets 2D dynamique dans MQL5.

 

1. CChartObject et ses descendants

Si nous n'utilisons pas la classe de bibliothèque MQL5 standard, nous devons utiliser les Fonctions d’objet pour créer et gérer tout objet sur le graphique.

Les objets sont créés avec la fonction ObjectCreate() et le type d’objet est transmis vers la fonctionObjectCreate() en tant que valeur ENUM_OBJECT. Tous les objets sur le graphique disposent de leurs propres caractéristiques qui peuvent être de typeInteger, Double, ou String Toutes les propriétés sont définies et récupérées via des fonctions dédiées : ObjectGetInteger(), ObjectSetInteger(), ObjectGetDouble(), ObjectSetDouble(), ObjectGetString(), ObjectSetString(). Il y a également des fonctions poursupprimer, déplacer et compter les objets sur tout graphique donné. 

En raison du paradigme de la POO dans MQL5, la gestion de divers objets graphiques peut être effectuée à l'aide de la classeCChartObject et de ses descendants.

La classeCChartObject est une classe de base pour tous les objets graphiques pouvant être placés sur un graphique. Veuillez observer le diagramme d'héritage de base pour CChartObject ci-dessous :

 

 Diagramme d'héritage pour la classe CChartObject 

Figure 1. Diagramme d'héritage pour la classe CChartObject


Comme nous pouvons le constater, quelques classes sont marquées d'un petit triangle dans le coin inférieur droit.

Ce sont des classes qui sont parentes d'autres classes.  Fondamentalement, les classes descendantes améliorent les possibilités d'une classe de base en ajoutant de nouvelles variables et méthodes qui agissent sur l'objet. EIles peuvent également différer dans les méthodes Create() et Type() pour créer un objet dérivé et renvoyer son type.

Permettez-moi de montrer ceci par exemple: CChartObjectTrend est une classe parente à CChartObjectTrendByAngle, CChartObjectChannel, CChartObjectStdDevChannel, CChartObjectRegression et CChartObjectPitchfork classes.

La CChartObjectTrendest une classe de base pour les objets qui disposent de propriétés OBJPROP_RAY_RIGHT et OBJPROP_RAY_LEFTet définie comme suit:

class CChartObjectTrend : public CChartObject
  {
public:
   //--- methods of access to properties of the object
   bool              RayLeft() const;
   bool              RayLeft(bool new_sel);
   bool              RayRight() const;
   bool              RayRight(bool new_sel);
   //--- method of creating the object
   bool              Create(long chart_id,string name,int window,
                                datetime time1,double price1,datetime time2,double price2);
   //--- method of identifying the object
   virtual int       Type() const { return(OBJ_TREND); }
   //--- methods for working with files
   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
  };

Il y a des commentaires dans la définition qui permettent de distinguer différents types de méthodes.

Les méthodes d'accès aux propriétés de l'objet sont RayLeft() et RayRight(). Leur implémentation est d’appeler les méthodesObjectGetInteger() et ObjectSetInteger() qui agissent sur l’objet CChartObjectTrend.

bool CChartObjectTrend::RayLeft(bool new_ray)
  {
//--- checking
   if(m_chart_id==-1) return(false);
//---
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_LEFT,new_ray));
  }

La méthode Create () est chargée de créer et d’attacher l’objet sur le graphique

Il appelle la méthode ObjectCreate()avec OBJ_TREND comme un des paramètres:

bool CChartObjectTrend::Create(long chart_id,string name,int window,
                                   datetime time1,double price1,datetime time2,double price2)
  {
   bool result=ObjectCreate(chart_id,name,OBJ_TREND,window,time1,price1,time2,price2);
   if(result) result&=Attach(chart_id,name,window,2);
//---
   return(result);
  }

Les méthodes Save() et Load() stockent et téléchargent les données de l’objet sur un disque dur à l’aide des fonctions FileWriteInteger() et FileLoadInteger():

bool CChartObjectTrend::Save(int file_handle)
  {
   bool result;
//--- checking
   if(file_handle<=0) return(false);
   if(m_chart_id==-1) return(false);
//--- writing
   result=CChartObject::Save(file_handle);
   if(result)
     {
      //--- writing value of the "Ray left" property
      if(FileWriteInteger(file_handle,(int) ObjectGetInteger(m_chart_id,m_name,
                                                        OBJPROP_RAY_LEFT),CHAR_VALUE)!=sizeof(char))
      return(false);
      //--- writing value of the "Ray right" property
      if(FileWriteInteger(file_handle,(int) ObjectGetInteger(m_chart_id,m_name, 
                                                                OBJPROP_RAY_RIGHT),CHAR_VALUE)!=sizeof(char))
       return(false);
     }
//---
   return(result);
  }

bool CChartObjectTrend::Load(int file_handle)
  {
   bool result;
//--- checking
   if(file_handle<=0) return(false);
   if(m_chart_id==-1) return(false);
//--- reading
   result=CChartObject::Load(file_handle);
   if(result)
     {
      //--- reading value of the "Ray left" property
      if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_LEFT,
                                                 FileReadInteger(file_handle,CHAR_VALUE)))return(false);
      //--- reading value of the "Ray right" property
      if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_RIGHT,
                                                 FileReadInteger(file_handle,CHAR_VALUE))) return(false);
     }
//---
   return(result);
  }

Examinons rapidement les définitions des classes descendantes deCChartObjectTrend

La classe CChartObjectTrendByAngle ajoute Angle() modificateur de propriété et renvoie le type d’objet OBJ_TRENDBYANGLE:

class CChartObjectTrendByAngle : public CChartObjectTrend
  {
public:
   //--- methods of access to properties of the object
   double            Angle() const;
   bool              Angle(double angle);
   //--- method of creating the object
   bool              Create(long chart_id,string name,int window,datetime time1,double price1,
                               datetime time2,double price2);
   //--- method of identifying the object
   virtual int       Type() { return(OBJ_TRENDBYANGLE); }
  };

La classe CChartObjectChannelrenvoie OBJ_CHANNEL le type d’objet et puisque elle gère les canaux, trois paires de paramètres de prix/date sont transmises vers la méthode to Create():

class CChartObjectChannel : public CChartObjectTrend
  {
public:
   //--- method of creating the object
   bool              Create(long chart_id,string name,int window,datetime time1,double price1,
                               datetime time2,double price2,datetime time3,double price3);
   //--- method of identifying the object
   virtual int       Type() const { return(OBJ_CHANNEL); }
  };

La classeCChartObjectStdDevChannel ajoute un modificateur de propriété Deviations() et un paramètre de déviation supplémentaire dans la méthode Create() :

class CChartObjectStdDevChannel : public CChartObjectTrend
  {
public:
   //--- methods of access to properties of the object
   double            Deviations() const;
   bool              Deviations(double deviation);
   //--- method of creating the object
   bool              Create(long chart_id,string name,int window,
                           datetime time1,datetime time2,double deviation);
   //--- method of identifying the object
   virtual int       Type() const { return(OBJ_STDDEVCHANNEL); }
   //--- methods for working with files
   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
  };

La classeCChartObjectRegression crée une ligne de tendance de régression, seules les méthodes Create() et Type() sont remplacées par celles de la classeCChartObjectTrend

class CChartObjectRegression : public CChartObjectTrend
  {
public:
   //--- method of creating the object
   bool              Create(long chart_id,string name,int window,datetime time1,datetime time2);
   //--- method of identifying the object
   virtual int       Type() const { return(OBJ_REGRESSION); }
  };

La classeCChartObjectPitchfork gère le type de fourche, seules les méthodes Create () et Type () sont également modifiées :

class CChartObjectPitchfork : public CChartObjectTrend
  {
public:
   //--- method of creating the object
   bool              Create(long chart_id,string name,int window,datetime time1,double price1,
                               datetime time2,double price2,datetime time3,double price3);
   //--- method of identifying the object
   virtual int       Type() const { return(OBJ_CHANNEL); }
  };

 Cette analyse rapide a indiqué les règles de base appliquées lors de l'écriture d'une nouvelle classe d'objets graphiques axée sur une autre classe :

  • changer la méthode Create () pour la création d'objets
  • changer la méthode Type() pour renvoyer le type d'objet
  • ajout de modificateurs d'accès aux propriétés 

Pas toutes les règles doivent être appliquées, on peut seulement ajouter de nouveaux modificateurs d'accès ou ajouter de nouvelles variables et/ou objets dans la classe.

Avant d'aller plus loin, permettez-moi de vous expliquer comment utiliser les méthodes CChartObject sur des objets graphiques.

Au lieu d'utiliser la famille de méthodes ObjectSet et ObjectGet et d'utiliser des propriétés d'objet, il suffit de déclarer CChartObject ou un objet descendant et faire appel à des méthodes qui modifient les propriétés souhaitées. Afin de faciliter les choses, j’offre un exemple d'étiquette ordinaire. 

Au lieu d'écrire :

void OnStart()
  {
//---
   string label_name="my_OBJ_LABEL_object";
   if(ObjectFind(0,label_name)<0)
     {
      Print("Object ",label_name," not found. Error code = ",GetLastError());
      ObjectCreate(0,label_name,OBJ_LABEL,0,0,0);           
      ObjectSetInteger(0,label_name,OBJPROP_XDISTANCE,200);
      ObjectSetInteger(0,label_name,OBJPROP_YDISTANCE,300);
      ObjectSetInteger(0,label_name,OBJPROP_COLOR,White);
      ObjectSetString(0,label_name,OBJPROP_TEXT,UP);
      ObjectSetString(0,label_name,OBJPROP_FONT,"Wingdings");
      ObjectSetInteger(0,label_name,OBJPROP_FONTSIZE,10);
      ObjectSetDouble(0,label_name,OBJPROP_ANGLE,-45);
      ObjectSetInteger(0,label_name,OBJPROP_SELECTABLE,false);
      ChartRedraw(0);                                      
     }
  }

Nous pouvons l'implémenter en utilisant le paradigme OOP :

1. Déclarez l'objetCChartObjectLabel:

CChartObjectLabel label;

2. Agir sur l'objet : 

int OnInit()
  {
//---
   label.Create(0, label_name, 0, 0);
   label.X_Distance(200);
   label.Y_Distance(300);
   label.Color(White);
   label.Description(UP);
   label.Font("Wingdings");
   label.FontSize(10);
   label.Angle(-45);
   label.Selectable(false);
//---
   return(0);
  }

Comme vous pouvez le constater, la principale différence est que nous n’agissons plus sur une chaîne label_name : 

string label_name="my_OBJ_LABEL_object";

et faites appel à ObjectSetInteger(), ObjectGetInteger(), ObjectSetDouble(), ObjectGetDouble() fonctions avec des noms de labels comme paramètres mais nous déclarons CChartObjectLabel objet et utilisons ses méthodes C'est non seulement plus simple à retenir et logique à implémenter mais aussi plus rapide à écrire.

L'éditeur de code MQL5 nous offre une fonctionnalité de complétion de code lorsque vous placez un point (.) après l'instance d'objet. Il n’est pas nécessaire de parcourir MQL5 documentation dans tous les sens pour voir quelle propriété OBJPROP à mettre ou obtenir une propriété donnée.

Analogiquement pour la classeCChartObjectTrend qui a été décrite précédemment, pour obtenir ou définir un rayon à gauche ou à droite, il suffit de déclarer l'objet CChartObjectTrend et d'appeler la méthode RayRight() ou RayLeft() :

CChartObjectTrend trendline;
trendline.RayRight(true); 


2. ProgressBar

Le premier widget que nous allons implémenter est ProgressBar. Les barres de progression indiquent la progression de certaines opérations, de 0 à x pour cent.

Pour le rendre plus robuste, ne contraignons pas la valeur maximale à 100, mais à n'importe quelle valeur entière positive. Nous avons besoin d'une bande de couleur qui changera de taille en fonction de la valeur de progression. La première chose qui me vient à l'esprit est d'utiliser deux rectangles, mais j'ai pris une autre voie : utiliser deux objetsCChartObjectEdit, l'un dans l'autre, avec des couleurs de fond différentes.

Il simplifie le codage et ajoute du texte qui peut être placé dans la barre de progression pour montrer sa valeur. Ce serait idéal si notre barre de progression pouvait être horizontale ou verticale selon les besoins de chacun.


2.1. Implémentation de ProgressBar

La classe CChartObjectProgress est dérivée de la classeCChartObjectEdit .

J'ai ajouté des variables internes privées pour contenir la valeur et les contraintes sur la valeur : m_value, m_min, m_max.

La direction de la barre de progression est définie comme une valeur entière et maintenue par la variable m_direction. La couleur est détenue par la variable m_color. La méthode Type() renvoie la valeurOBJ_EDIT , car de toute façon, aucune valeur n'est reconnue pour notre besoin. On peut remarquer la variable m_bar deCChartObjectEdità l'intérieur de la définition de classe - c'est la barre intérieure qui change de taille en fonction de la m_value. Les variables supplémentaires m_name et m_chart comportent en interne des valeurs pour la variable m_bar.

class CChartObjectProgressBar : public CChartObjectEdit
  {
private:
   int               m_value;
   int               m_min;
   int               m_max;
   int               m_direction;
   color             m_color;
   CChartObjectEdit  m_bar;
   string            m_name;
   long              m_chart_id;

public:
   int               GetValue();
   int               GetMin();
   int               GetMax();

   void              SetValue(int val);
   void              SetMin(int val);
   void              SetMax(int val);

   void              SetColor(color bgcol,color fgcol);
   bool              Create(long chart_id,string name,int window,int X,int Y,
                           int sizeX,int sizeY,int direction);

   //--- method of identifying the object
   virtual int       Type() const { return(OBJ_EDIT); }
};

La méthode Create() crée l'objet ProgressBar et l'attache au graphique.

Vous remarquerez peut-être que la variable Y est soustraite de la variable sizeY au cas où la barre verticale est dessinée, car normalementCChartObjectEdit est dessiné de haut en bas, et je voulais dessiner le rectangle intérieur de bas en haut :

bool CChartObjectProgressBar::Create(long chart_id,string name,int window,int X,int Y,
                                          int sizeX,int sizeY,int direction=0)
  {
   bool result=ObjectCreate(chart_id,name,(ENUM_OBJECT)Type(),window,0,0,0);

   m_name=name;
   m_chart_id=chart_id;
   m_direction=direction;

   if(direction!=0)
     {
      Y=Y-sizeY;
     }

   ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,White);
   ObjectSetInteger(chart_id,name,OBJPROP_COLOR,White);
   ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(chart_id,name,OBJPROP_READONLY,true);

   result&=m_bar.Create(chart_id,name+"m_bar",window,X,Y,sizeX,sizeY);
   m_bar.Color(White);
   m_bar.ReadOnly(true);
   m_bar.Selectable(false);

//---
   if(result) result&=Attach(chart_id,name,window,1);
   result&=X_Distance(X);
   result&=Y_Distance(Y);
   result&=X_Size(sizeX);
   result&=Y_Size(sizeY);
//---
   return(result);
  }

La méthode SetColor() définit les couleurs d'arrière-plan et de premier plan sur les deux rectangles :

void CChartObjectProgressBar::SetColor(color bgCol,color fgCol=White)
  {
   m_color=bgCol;
   m_bar.BackColor(m_color);
   m_bar.Color(fgCol);
  }

La méthode SetValue() est responsable à la fois de la définition de la valeur m_val et du recalcul de la taille de l'objet du rectangle intérieur.

La taille est calculée différemment pour les barres horizontales et verticales :

void CChartObjectProgressBar::SetValue(int val)
  {
   if(m_direction==0) // horizontal ProgressBar
     {
      double sizex=(double)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XSIZE,0);

      double stepSize=sizex/(m_max-m_min);

      m_value=val;
      m_bar.Create(m_bar.ChartId(),m_bar.Name(),m_bar.Window(),
                   m_bar.X_Distance(),m_bar.Y_Distance(),(int)MathFloor(stepSize*m_value),m_bar.Y_Size());
        } else {
      double sizey=(double)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YSIZE,0);

      double stepSize=sizey/(m_max-m_min);
      m_value=val;
      m_bar.Create(m_bar.ChartId(),m_bar.Name(),m_bar.Window(),
                   m_bar.X_Distance(),(int)(this.Y_Distance()+sizey-MathFloor(stepSize*m_value)),
                   m_bar.X_Size(),(int)MathFloor(stepSize*m_value));

     }

   m_bar.Description(IntegerToString(m_value));
  }


2.2. Démo ProgressBar

Puisque nous avons déjà implémenté la classe CChartObjectProgressBar, il est temps de la voir à l’œuvre

Pour placer une nouvelle barre de progression sur le graphique, il suffit de déclarer l'objet CChartObjectProgressBar et d'utiliser Create() et les méthodes de propriété appropriées :

progressBar.Create(0, "progressBar1", 0, 10, 10, 200, 40);
progressBar.SetColor(YellowGreen);
progressBar.SetMin(0);
progressBar.SetMax(100);
progressBar.SetValue(0);

J'ai écrit une démo Expert Advisor qui place six barres de progression différentes sur le graphique et modifie leur valeur après avoir cliqué sur un objet à l'écran.

Le code source complet de cette démo et d'autres se trouvent dans les pièces jointes, veuillez suivre la présentation ci-dessous : 

 


3. Spinner

Le widget Spinner est un widget qui comporte un champ et deux boutons. Il permet d'incrémenter ou de décrémenter une valeur dans le champ d'édition en cliquant sur l'un des boutons.

Lors de la conception de l'objet, je ne voulais pas agir uniquement sur des valeurs entières, donc Spinner a été conçu pour fonctionner sur un type double. Spinner offre également la possibilité de définir le pas de traceur, c'est-à-dire la valeur pour incrémenter ou décrémenter la valeur actuelle. Il doit également avoir une valeur minimale et maximale qui ne peut pas être franchie.


3.1. Implémentation du spinner

Dans MQL5 nous avons les classes CChartObjectEdit et CChartObjectButton qui peuvent être combinées en une seule classe CChartObjectSpinner. CChartObjectSpinner hérite deCChartObjectEdit et comporte deux objets CChartObjectButton membres privés.

Il existe des contraintes pour la m_value minimale et maximale stockées dans les variables membres m_min et m_max et la variable m_precision stocke la précision du calcul jusqu'à la valeur du n-ième chiffre. Les méthodes nécessaires donnent accès à la valeur, le pas de traceur d'incrémentation et de décrémentation et la valeur de réglage.

class CChartObjectSpinner: public CChartObjectEdit
  {

private:
   double            m_value;
   double            m_stepSize;
   double            m_min;
   double            m_max;
   int               m_precision;
   string            m_name;
   long              m_chart_id;
   CChartObjectButton m_up,m_down;

public:
   double            GetValue();
   double            GetMin();
   double            GetMax();

   void              SetValue(double val);
   void              SetMin(double val);
   void              SetMax(double val);

   double            Inc();
   double            Dec();

   bool              Create(long chart_id,string name,int window,int X,int Y,
                               int sizeX,int sizeY,double val,double stepSize,int precision);

   //--- method of identifying the object
   virtual int       Type() const { return(OBJ_EDIT); }
   
  };

La méthode Create () crée un nouveau CChartObjectSpinner et l'attache au graphique.

Il y a deux CChartObjectButtons créés sur le côté droit deCChartObjectEdit, chacun ayant une hauteur de la moitié de la hauteur de CChartObjectEdit.

Le bouton d'incrémentation a le signe '+' et le bouton de décrémentation '-'.

bool CChartObjectSpinner::Create(long chart_id,string name,int window,int X,int Y,
                                     int sizeX,int sizeY,double val=0.0,double stepSize=1.0,int precision=8)
  {
   bool result=ObjectCreate(chart_id,name,(ENUM_OBJECT)Type(),window,0,0,0);

   m_name=name;
   m_chart_id=chart_id;
   m_value=val;
   m_stepSize=stepSize;
   m_precision=precision;

   ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,White);
   ObjectSetInteger(chart_id,name,OBJPROP_COLOR,Black);
   ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(chart_id,name,OBJPROP_READONLY,true);

   result&=m_up.Create(chart_id, name+"_up", window, X+sizeX, Y, 15, sizeY/2);
   result&=m_down.Create(chart_id, name+"_down", window, X+sizeX, Y+sizeY/2, 15, sizeY/2);
   m_up.Description("+");
   m_down.Description("-");
   ObjectSetString(chart_id,name,OBJPROP_TEXT,0,(DoubleToString(m_value,precision)));

//---
   if(result) result&=Attach(chart_id,name,window,1);
   result&=X_Distance(X);
   result&=Y_Distance(Y);
   result&=X_Size(sizeX);
   result&=Y_Size(sizeY);
//---
   return(result);
  }

La méthode SetValue() définit la variable privée m_value sur une valeur double à condition qu'elle se trouve dans la plage <m_min, m_max>.

void CChartObjectSpinner::SetValue(double val)
  {
   if(val>=m_min && val<=m_max) m_value=val;
   this.Description(DoubleToString(m_value));
  }

La méthode Inc() incrémente la valeur d'un pas de traceur donnée, mais pas plus de la valeur m_max.

Veuillez noter que j'ai dû utiliser la fonctionNormalizeDouble()pour comparer des valeurs doubles à une précision donnée.

double CChartObjectSpinner::Inc(void)
  {
   if(NormalizeDouble(m_max-m_value-m_stepSize,m_precision)>0.0) m_value+=m_stepSize;
   else m_value=m_max;
   this.Description(DoubleToString(m_value, m_precision));
   m_up.State(false);
   return m_value;
  }

La méthode Dec() décrémente la valeur d'un pas de traceur donnée, mais pas moins que la valeur m_min.

double CChartObjectSpinner::Dec(void)
  {
   if(NormalizeDouble(m_value-m_stepSize-m_min,m_precision)>0.0)
      m_value-=m_stepSize; else m_value=m_min;
   this.Description(DoubleToString(m_value,m_precision));
   m_down.State(false);

   return m_value;
  }


3.2. Démo de Spinner

Il est temps de tester les objets Spinner. Pour les utiliser, il suffit de déclarer l'objet CChartObjectSpinner et d'utiliser les méthodes Create(), SetMin() et SetMax().

   spinner.Create(0, "spinner1", 0, 10, 10, 200, 40, 0.0, 0.4);
   spinner.SetMin(0);
   spinner.SetMax(100);

J'ai préparé une démo qui utilise trois widgets Spinner et ajoute toutes les valeurs après avoir cliqué sur n’importe quel bouton Spinner.

Cela se fait dans la fonction OnChartEvent() ;

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Check the event by pressing a mouse button
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
     
     if (sparam=="spinner1_up") spinner.Inc();
     if (sparam=="spinner1_down") spinner.Dec();
     if (sparam=="spinner2_up") spinner2.Inc();
     if (sparam=="spinner2_down") spinner2.Dec();
     if (sparam=="spinner3_up") spinner3.Inc();
     if (sparam=="spinner3_down") spinner3.Dec();
     
     label.Description(DoubleToString(NormalizeDouble(spinner.GetValue()+spinner2.GetValue()+spinner3.GetValue(),10),10));
   
   ChartRedraw();
     }
  }

 Veuillez voir la démo ci-jointe :

 

 

4. CChartObjectEditTable

Dans de nombreux Expert Advisors multi-temps (MTF), des valeurs d'indicateur sont affichées pour chaque intervalle de temps séparément.

Parfois, chaque intervalle de temps a des paramètres d'indicateur différents affichés sous la forme d'un tableau 2D de rectangles ou de carrés de différentes couleurs. J'ai conçu une table 2D universelle de tels objets en créant la classe CChartObjectEditTable. Cette classe peut contenir un nombre arbitraire de lignes et de colonnes, car j'utilise un tableau dynamique d'objets 2D.

Lors de la conception, j'ai décidé de définir une couleur pour chaque cellule séparément et d'ajouter également la possibilité de mettre différentes chaînes de texte sur n'importe quelle cellule. Les cellules sont de taille identique, mais je voulais définir leur hauteur et leur largeur et l'espace entre les cellules.


4.1. Implémentation CChartObjectEditTable

La classe CChartObjectEditTable comporte le pointeur CArrayObj vers un tableau d'objets bi-dimensionnel, et les variables membres m_rows et m_columns comportent le nombre de lignes et de colonnes de la table.

Il existe une variable membre m_baseName qui comporte le préfixe de tous les objetsCChartObjectEdit de cellule dans la table. Les méthodes GetColor(), SetColor(), GetText(), SetText() permettent de définir et d'obtenir des valeurs de couleur et de texte dans n'importe quelle cellule souhaitée. La méthode Delete() supprime tous les objets créés par la méthode Create().

class CChartObjectEditTable
  {

private:
   CArrayObj        *array2D;
   int               m_rows;
   int               m_cols;
   string            m_baseName;

public:

   bool              Create(long chart_id,string name,int window,int rows,int cols,int startX,int startY,
                                int sizeX,int sizeY,color Bg,int deltaX,int deltaY);
   bool              Delete();
   bool              SetColor(int row,int col,color newColor);
   color             GetColor(int row,int col);
   bool              SetText(int row,int col,string newText);
   string            GetText(int row,int col);


  };

La méthode Create() crée une table dynamique bidimensionnelle d'objetsCChartObjectEdit

Veuillez observer comment créer un tableau d'objets 2D dans MQL5 : nous déclarons d'abord un pointeur vers un tableau 2D, puis nous remplissons le tableau avec un certain nombre d'objetsCArrayObj(), c'est-à-dire que nous créons des tableaux dans le tableau. Tous les tableaux peuvent être considérés comme des supports pour les colonnes du tableau.

Chaque colonne contient des lignes comportant des objets CChartObjectEdit, chaque objet étant une seule cellule à afficher. 

bool CChartObjectEditTable::Create(long chart_id,string name,int window,int rows=1,int cols=1,
                                       int startX=0,int startY=0,int sizeX=15,int sizeY=15,
                                  color Bg=White,int deltaX=5,int deltaY=5)
  {
   m_rows=rows;
   m_cols=cols;
   m_baseName=name;
   int i=0,j=0;

   array2D=new CArrayObj();
   if (array2D==NULL) return false;
   
   for(j=0; j<m_cols; j++)
     {
      CArrayObj *new_array=new CArrayObj();
      if (array2D==NULL) return false;
   
      array2D.Add(new_array);
      for(i=0; i<m_rows; i++)
        {
         CChartObjectEdit *new_edit=new CChartObjectEdit();

         new_edit.Create(chart_id, name+IntegerToString(i)+":"+IntegerToString(j), window, 
                         startX+j*(sizeX+deltaX), startY+i*(sizeY+deltaY), sizeX, sizeY);
         new_edit.BackColor(Bg);
         new_edit.Color(White);
         new_edit.Selectable(false);
         new_edit.ReadOnly(true);
         new_edit.Description("");
         new_array.Add(new_edit);
        }
     }

   return true;
  }

La méthode SetColor() définit la couleur de n'importe quelle cellule unique. Au début, elle trouve le tableau de colonnes, puis le nième élément dans le tableau de colonnes.

Ensuite, la valeur de couleur de l'élément est modifiée en faisant appel à la méthode BackColor().

bool CChartObjectEditTable::SetColor(int row,int col,color newColor)
  {
   CArrayObj *sub_array;
   CChartObjectEdit *element;

   if((row>=0 && row<m_rows) && (col>=0 && col<m_cols))
     {
      if(array2D!=NULL)
        {
         sub_array=array2D.At(col);
         element=(CChartObjectEdit*)sub_array.At(row);
         element.BackColor(newColor);

         return true;
        }
     }

   return false;
  }

La méthode GetColor () dispose du même algorithme pour trouver la cellule que la méthode SetColor () mais elle renvoie la valeur de couleur de n'importe quelle cellule donnée. 

color CChartObjectEditTable::GetColor(int row,int col)
  {
   CArrayObj *sub_array;
   CChartObjectEdit *element;

   if((row>=0 && row<m_rows) && (col>=0 && col<m_cols))
     {
      if(array2D!=NULL)
        {
         sub_array=array2D.At(col);
         element=(CChartObjectEdit*)sub_array.At(row);
         return element.BackColor();
        }
     }

   return NULL;
  }

La méthode SetText() trouve l'élément et définit sa valeur de texte en faisant appel à la méthode Description().

bool CChartObjectEditTable::SetText(int row,int col,string newText)
  {
   CArrayObj *sub_array;
   CChartObjectEdit *element;

   if((row>=0 && row<m_rows) && (col>=0 && col<m_cols))
     {
      if(array2D!=NULL)
        {
         sub_array=array2D.At(col);
         element=(CChartObjectEdit*)sub_array.At(row);
         element.Description(newText);

         return true;
        }
     }

   return false;
  }

La méthode Delete() supprime tous les objets créés par la méthode Create().

Au début, elle efface tous les tableaux de colonnes, puis supprime l'objet array2D de la mémoire.

bool CChartObjectEditTable::Delete(void)
  {
   for(int j=0; j<m_cols; j++)
     {
      CArrayObj *column_array=array2D.At(j);
      column_array.Clear();
      delete column_array;
     }
   delete array2D;
   return true;
  }


4.2. Démo CChartObjectEditTable 

Pour utiliser le widget CChartObjectEditTable, il est nécessaire de déclarer l'objet CChartEditTable et d'utiliser la méthode Create() avec des paramètres indiquant le nombre de lignes et de colonnes que la table doit comporter

Ensuite, en utilisant des modificateurs de propriétés, on peut simplement changer la couleur et le texte sur n'importe quelle cellule.

table.Create(0,"t",0,1,10,10,10,15,15,Yellow);
table.SetColor(2,2,Red);
table.SetText(2,2,"2");

Veuillez consulter le script que j'ai préparé qui indique les possibilités d'utilisation de l'objet CChartObjectEditTable.

Le code source du script est en pièce jointe.

 


Conclusion

J'ai décrit et introduit dans l'article un processus de création de nouveaux widgets graphiques dérivés de la classeCChartObject 

Le processus d'utilisation des widgets implémentés est très simple et n’occupe que quelques lignes de code.

Pour utiliser les widgets, veuillez inclure le fichier ChartObjectsExtControls.mqh dans Expert Advisor ou le code indicateur.


Traduit de l’anglais par MetaQuotes Ltd.
Article original : https://www.mql5.com/en/articles/196

Calculs Parallèles dans MetaTrader 5 Calculs Parallèles dans MetaTrader 5
Le temps a été une grande valeur tout au long de l'histoire de l'humanité, et nous nous efforçons de ne pas le gaspiller inutilement. Cet article vous indiquera comment accélérer le travail de votre Expert Advisor si votre ordinateur dispose d'un processeur multi-noyau. De plus, l’implémentation de la méthode proposée ne nécessite la connaissance d'aucun autre langage que MQL5.
Création d'un Expert Advisor semi-automatique interactif par glisser-déposer axé sur un risque et un ratio R/R prédéfinis Création d'un Expert Advisor semi-automatique interactif par glisser-déposer axé sur un risque et un ratio R/R prédéfinis
Certains traders exécutent tous leurs trades automatiquement, et certains mélangent des trades automatiques et manuels en fonction de la sortie de plusieurs indicateurs. En tant que membre de ce dernier groupe, j'avais besoin d'un outil interactif pour évaluer dynamiquement les niveaux de prix des risques et des rendements directement à partir du graphique. Cet article présentera un moyen d’implémenter un Expert Advisor semi-automatique interactif avec un risque sur actions et un ratio R/R prédéfinis. Les paramètres de risque, de R/R et de taille de lot de l'Expert Advisor peuvent être modifiés pendant l'exécution sur le panneau EA.
Canaux de traçage - Schéma intérieure et extérieure Canaux de traçage - Schéma intérieure et extérieure
Je suppose que ce ne sera pas une exagération, si je dis que les canaux sont l'outil le plus populaire pour l'analyse du marché et la prise de décisions en trade après les moyennes mobiles. Sans plonger profondément dans la masse des stratégies de trade qui utilisent des canaux et leurs composants, nous allons discuter de la base mathématique et de l’implémentation pratique d'un indicateur, qui trace un canal déterminé par trois extremums sur l'écran du terminal client.
Un démarrage rapide ou un petit guide pour les débutants Un démarrage rapide ou un petit guide pour les débutants
Bonjour cher lecteur ! Dans cet article, je vais essayer de vous expliquer et de vous montrer comment maîtriser facilement et rapidement les principes de création d'Expert Advisors, de travail avec des indicateurs, etc. Il est destiné aux débutants et ne comportera pas d'exemples difficiles ou abstraits.