Test de CGraphic - questions et suggestions - page 6

 
Roman Konopelko:
Dans la classe CGraphic, j'ai remplacé le type de couleur par uint à tous les endroits, comme vous l'avez demandé.

J'ai également ajouté de nouvelles méthodes à la classe CCanvas, qui permettent de dessiner des primitives avec une épaisseur spécifiée :
Dans la lignée de l'innovation de CCanvas, j'ai étendu les propriétés de CCurve :
Lorsque vous dessinez une courbe avec des lignes, vous pouvez désormais spécifier l'épaisseur des lignes et le style de ses extrémités.


C'est juste génial.
 

La gestion des splines (interpolation avec des courbes de Bézier) a été revue. Son implémentation a été déplacée de la classe CGraphics directement dans CCanvas, ce qui permet de construire des splines en dehors de la bibliothèque Graphics.

En outre, un algorithme de rendu de spline fermé a été ajouté.

En conséquence, la classe CCanvas dispose désormais de deux nouvelles méthodes publiques.

void              PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size,
                                    ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                                    double tension=0.5,double step=10);
void              PolygoneSmooth(int &x[],int &y[],const uint clr,const int size,
                                    ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                                    double tension=0.5,double step=10);

Ces méthodes permettent de dessiner des splines dans un style donné et avec une épaisseur donnée.

Puisque les courbes de Bézier décrivent les cercles et les ellipses de manière assez précise, il n'est pas nécessaire d'ajouter à la classe CCanvas de nouvelles méthodes pour rendre ces primitives avec une épaisseur donnée.

Exemple d'approximation d'une ellipse par des courbes de Bézier basée sur la méthode PolygoneSmooth :

#include <Canvas\Canvas.mqh>
//+------------------------------------------------------------------+
//| Get arrays with ellipse coordinates                              |
//+------------------------------------------------------------------+
void Ellipse(int &x[],int &y[])
  {
   int xc = 750;
   int yc = 300;
   int rx = 300;
   int ry = 150;
   ArrayResize(x,16);
   ArrayResize(y,16);
   int i=0;
   for(double fi=0; fi<2*M_PI; fi+=M_PI_4/2,i++)
     {
      x[i]=(int)MathRound(xc+cos(fi)*rx);
      y[i]=(int)MathRound(yc+sin(fi)*ry);
     }
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   CCanvas canvas;
   canvas.CreateBitmapLabel(0,0,"Canvas",0,0,1500,600);
   canvas.Erase(ColorToARGB(clrWhite));
   int x[];
   int y[]; 
   Ellipse(x,y);
   canvas.PolygoneSmooth(x,y,ColorToARGB(clrBlack),20,STYLE_SOLID,LINE_END_BUTT,0.5,1);
   canvas.Arc(750,300,300,150,0,M_PI*2,ColorToARGB(clrRed));   
   canvas.Update();
  }

Résultat :

 

Une autre étape possible vers la polyvalence de la bibliothèque graphique : un mode de dessin de courbes personnalisées CURVE_CUSTOM.

Ce mode élimine le besoin d'hériter de la classe CGraphic et de surcharger les méthodes ...Plot afin de dessiner une courbe différemment de ce que les outils standard de la bibliothèque permettent.

Pour implémenter ce mode CURVE_CUSTOM, de nouvelles propriétés seront ajoutées à la classe CCurve.

   //--- gets or sets the custom properties
   PlotFucntion      CustomPlotFunction(void)              const { return(m_custom_plot_func);   }
   void             *CustomPlotCBData(void)                const { return(m_custom_plot_cbdata); }
   void              CustomPlotFunction(PlotFucntion func) { m_custom_plot_func=func;     }
   void              CustomPlotCBData(void *cbdata)        { m_custom_plot_cbdata=cbdata; }

Il est basé sur un nouveau pointeur vers la fonction PlotFucntion.

typedef void(*PlotFucntion)(double &x[],double &y[], int size, CGraphic *graphic,CCanvas *canvas,void *cbdata);

Cette approche ouvre de nouvelles possibilités pour le dessin de parcelles.

Implémentons le dessin des chandeliers dans la bibliothèque CGraphics à titre d'exemple :

1. Créons une classe conteneur dans laquelle toutes les données d'une seule bougie seront stockées.

//+------------------------------------------------------------------+
//| Class CCandle                                                    |
//| Usage: class to represent the candle                             |
//+------------------------------------------------------------------+
class CCandle: public CObject
  {
private:
   double            m_open;
   double            m_close;
   double            m_high;
   double            m_low;
   uint              m_clr_inc;
   uint              m_clr_dec;
   int               m_width;

public:
                     CCandle(const double open,const double close,const double high,const double low,
                                                       const int width,const uint clr_inc=0x000000,const uint clr_dec=0xF5F5F5);
                    ~CCandle(void);
   double            OpenValue(void)            const { return(m_open);     }
   double            CloseValue(void)           const { return(m_close);    }
   double            HigthValue(void)           const { return(m_high);     }
   double            LowValue(void)             const { return(m_low);      }
   uint              CandleColorIncrement(void) const { return(m_clr_inc);  }
   uint              CandleColorDecrement(void) const { return(m_clr_dec);  }
   int               CandleWidth(void)          const { return(m_width);    }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCandle::CCandle(const double open,const double close,const double high,const double low,
                                 const int width,const uint clr_inc=0x000000,const uint clr_dec=0xF5F5F5):
                                 m_open(open),m_close(close),m_high(high),m_low(low),
                                 m_clr_inc(clr_inc),m_clr_dec(clr_dec),m_width(width)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CCandle::~CCandle(void)
  {
  }

Comme la classe CCandle est un descendant de la classe CObject, tous les chandeliers que nous voulons dessiner, nous pouvons les écrire séquentiellement dans l'objet de la classe CArrayObj. Ce tableau sera intégré à notre méthode de dessin personnalisée en tant que paramètre cbdata. Par conséquent, la méthode de dessin des chandeliers se présentera comme suit.

//+------------------------------------------------------------------+
//| Custom method for plot candles                                   |
//+------------------------------------------------------------------+
void PlotCandles(double &x[],double &y[],int size,CGraphic *graphic,CCanvas *canvas,void *cbdata)
  {
//--- check obj
   CArrayObj *candles=dynamic_cast<CArrayObj*>(cbdata);
   if(candles==NULL || candles.Total()!=size)
      return;
//--- plot candles  
   for(int i=0; i<size; i++)
     {
      CCandle *candle=dynamic_cast<CCandle*>(candles.At(i));
      if(candle==NULL)
         return;
      //--- primary calculate
      int xc=graphic.ScaleX(x[i]);
      int width_2=candle.CandleWidth()/2;
      int open=graphic.ScaleY(candle.OpenValue());
      int close=graphic.ScaleY(candle.CloseValue());
      int high=graphic.ScaleY(candle.HigthValue());
      int low=graphic.ScaleY(candle.LowValue());
      uint clr=(open<=close) ? candle.CandleColorIncrement() :  candle.CandleColorDecrement();
      //--- plot candle
      canvas.LineVertical(xc,high,low,0x000000);
      //--- plot candle real body
      canvas.FillRectangle(xc+width_2,open,xc-width_2,close,clr);
      canvas.Rectangle(xc+width_2,open,xc-width_2,close,0x000000);
     }
  }

3. Pour des raisons de simplicité, tous les chandeliers seront générés de manière aléatoire. Nous générons donc séquentiellement 10 chandeliers et remplissons l'objet de classe CArrayObj avec ceux-ci. Ensuite, nous créons un objet CGraphics et y ajoutons une courbe, tout en spécifiant qu'elle sera dessinée à l'aide de notre fonction PlotCandles. Nous devons également modifier les valeurs maximale et minimale de l'axe des ordonnées, afin que nos bougies soient entièrement visibles.

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int count=10;
   int width=10;
   double x[];
   double y[];
   ArrayResize(x,count);
   ArrayResize(y,count);
   CArrayObj candles();
   double max=0;
   double min=0;
//--- create values 
   for(int i=0; i<count; i++)
     {
      x[i] = i;
      y[i] = i;
      //--- calculate values
      double open=MathRound(50.0+(MathRand()/32767.0)*50.0);
      double close=MathRound(50.0+(MathRand()/32767.0)*50.0);
      double high=MathRound(MathMax(open,close)+(MathRand()/32767.0)*10.0);
      double low=MathRound(MathMin(open,close) -(MathRand()/32767.0)*10.0);
      //--- find max and min
      if(i==0 || max<high)
         max=high;
      if(i==0 || min>low)
         min=low;
      //--- create candle
      CCandle *candle=new CCandle(open,close,high,low,width);
      candles.Add(candle);
     }
//--- create graphic
   CGraphic graphic;
   if(!graphic.Create(0,"CandleGraphic",0,30,30,780,380))
     {
      graphic.Attach(0,"CandleGraphic");
     }
//--- create curve
   CCurve *curve=graphic.CurveAdd(x,y,CURVE_CUSTOM,"Candles");
//--- sets the curve properties
   curve.CustomPlotFunction(PlotCandles);
   curve.CustomPlotCBData(GetPointer(candles));
//--- sets the graphic properties
   graphic.YAxis().Max((int)max);
   graphic.YAxis().Min((int)min);
//--- plot 
   graphic.CurvePlotAll();
   graphic.Update();
  }

En conséquence, nous obtenons le graphique suivant :


Dossiers :
Canvas.mqh  304 kb
Axis.mqh  12 kb
Curve.mqh  23 kb
Graphic.mqh  73 kb
Candle.mq5  6 kb
 

@Roman Konopelko

il y a une petite erreur dans la fonction CGraphic::SetDefaultParameters

Les couleurs doivent être initialisées en tenant compte de l'opacité.

void CGraphic::SetDefaultParameters(void)
  {
...
...
//--- sets the default values for grid
   m_grid.clr_line=ColorToARGB(clrWhiteSmoke,255);
   m_grid.clr_axis_line=ColorToARGB(clrSilver,255);
 
o_o:

@Roman Konopelko

il y a une petite erreur dans la fonction CGraphic::SetDefaultParameters

Les couleurs doivent être initialisées en tenant compte de l'opacité.

Bonjour, j'ai déjà corrigé ce point, mais malheureusement ces modifications n'ont pas encore été intégrées dans le build.
 

Cet exemple a provoqué le blocage de l'ordinateur. Ce que j'ai fait : après avoir placé l'indicateur sur le graphique dans l'éditeur, j'ai joué avec différentes combinaisons de commentaires/décommentations des lignes 87 et 88 (une à la fois, ensemble).

//+------------------------------------------------------------------+
//|                                        tst.mq5 |
//|                              Copyright © 2017, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2017, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.000"
#property description "Panel indicator: \"Evening Star\" pattern " 
#property description "search results for different periods" 
#property indicator_chart_window 
#property indicator_buffers 0
#property indicator_plots   0
#include <Graphics\Graphic.mqh>
//--- object for creating graphs
CGraphic my_graphic;
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
bool           m_first_start=false;
//+------------------------------------------------------------------+ 
//| Custom indicator initialization function                         | 
//+------------------------------------------------------------------+ 
int OnInit()
  {
   if(!EventSetTimer(3))
      if(!EventSetTimer(3))
         if(!EventSetTimer(3))
           {
            Print("Error create timer! THREE attempts!");
            return(INIT_FAILED);
           }
//--- canvas creation
   my_graphic.Create(0,"Evening Star Statistics",0,10,10,800,550);

   my_graphic.CurvePlotAll();
   my_graphic.Update();
//---
   m_first_start=false;
//--- 
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   my_graphic.Destroy();
  }
//+------------------------------------------------------------------+ 
//| Custom indicator iteration function                              | 
//+------------------------------------------------------------------+ 
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   if(!m_first_start)
     {
      //--- revert access to arrays - do it like in timeseries 
      ArraySetAsSeries(time,true);
      ArraySetAsSeries(open,true);
      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);
      ArraySetAsSeries(close,true);

      int size=ArraySize(time);
      double arrY[],arrX[];
      ArrayResize(arrX,size);
      ArrayResize(arrY,size);
      for(int i=0; i<size;++i)
        {
         arrX[i]=(double)time[i];
         arrY[i]=close[i];
        }
      CCurve *curve_b=my_graphic.CurveAdd(arrX,arrY,CURVE_LINES,"Close");
      CAxis *xAxis=my_graphic.XAxis();           // получаем ось X
      //---попеременно комбинирую (комментировать, раскомментировать) строки 87
      //--- и 88 можно словить момент поглощения памяти и зависания компьютера
      //xAxis.AutoScale(false); 
      //xAxis.Type(AXIS_TYPE_DATETIME); 
      my_graphic.CurvePlotAll();
      my_graphic.Update();
      m_first_start=true;
     }
//--- return value of prev_calculated for next call 
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {

  }
//+------------------------------------------------------------------+

Répétez l'exploit deux fois. Je n'ai pas enregistré la séquence des actions. La troisième fois est insignifiante, j'ai peur de vérifier.


Ajouté : build 1607 x64

Dossiers :
tst.mq5  8 kb
 
Vladimir Karputov:

Cet exemple a provoqué le blocage de l'ordinateur. Ce que j'ai fait : après avoir placé l'indicateur sur le graphique dans l'éditeur, j'ai joué avec différentes combinaisons de commentaires/décommentations des lignes 87 et 88 (une à la fois, ensemble).

Répétez l'exploit deux fois. Je n'ai pas enregistré la séquence des actions. La troisième fois est insignifiante, j'ai peur de vérifier.


Ajouté : build 1607 x64


J'ai répété l'enregistrement aujourd'hui - l'ordinateur s'est arrêté de fonctionner, et j'ai réussi à voir la consommation de RAM passer de 2 à 5,5 Go. Il semble avoir réussi à fermer le programme, mais l'ordinateur se bloque depuis cinq minutes.

Cette fois, j'ai fixé une limite à la taille du tableau - pas plus de 300 éléments. Comme vous pouvez le voir, ça n'a pas aidé 🤔

 

Que dit le débogage ?

 
o_o:

Que dit le débogage ?


Jbug ne l'a pas compris, j'ai fait ceci : j'ai survolé un indicateur et décommenté/commenté une ou deux lignes et j'ai compilé. J'ai fini par écrire depuis une tablette, l'ordinateur portable est tombé en panne...
 
Vladimir Karputov:

Le gbug ne l'a pas fait, j'ai fait ceci : j'ai survolé l'indicateur et commenté/commenté une ou deux lignes et j'ai compilé. J'ai fini par écrire depuis une tablette, l'ordinateur portable est tombé en panne...


Ainsi, après un redémarrage brutal, l'ordinateur portable est revenu à la vie, mais je ne veux pas mener d'autres expériences destructives - il y a plusieurs conseillers experts en cours d'exécution sur l'ordinateur portable, donc il n'y a aucune envie d'attraper un gel pendant une heure entière.