Bibliotheken: Die Bibliothek EasyAndFastGUI zum Erstellen von grafischen Interfaces - Seite 11

 
Anatoli Kazharski:

Ursprünglich gab es einfach keine solche Aufgabe, dass es notwendig war, Elemente zu verschieben, nachdem die GUI erstellt war. Alles basierte auf der Idee, dass jedes Element bereits das notwendige Verhalten implementiert hatte.

Gegenfragen: Warum müssen Sie Elemente verschieben? Was wollen Sie tun? Welches Verhalten bei der Interaktion mit der grafischen Benutzeroberfläche möchten Sie erreichen?

Um zu verstehen, ob etwas kaputt gehen wird, müssen Sie alle Elemente nach jeder Änderung der Basisklassen testen. Es ist schon schwer, das auf Anhieb zu sagen. Es ist schon eine Weile her, dass ich mich damit beschäftigt habe.

Erstellen Sie eine Test-GUI mit allen Elementen der Bibliothek und testen Sie diese nach Änderungen.

Zum Beispiel aufklappbare Listen. Wenn man klickt, werden einige der Elemente ausgeblendet, alles darunter wird nach oben gezogen. Und andersherum.

Test GUI ist eine gute Idee, ich werde versuchen, es zu machen :)

Übrigens, in der gleichen CElement::Moving.

//--- Wenn die Bindung auf der rechten Seite ist
   if(m_anchor_right_window_side)
     {
      //--- Speichern von Koordinaten in Elementfeldern
      CElementBase::X(m_main.X2()-XGap());
      //--- Speichern von Koordinaten in Objektfeldern
      m_canvas.X(m_main.X2()-m_canvas.XGap());
     }
   else
     {
      CElementBase::X(m_main.X()+XGap());
      m_canvas.X(m_main.X()+m_canvas.XGap());
     }
//--- Wenn die Bindung von unten erfolgt
   if(m_anchor_bottom_window_side)
     {
      CElementBase::Y(m_main.Y2()-YGap());
      m_canvas.Y(m_main.Y2()-m_canvas.YGap());
     }
   else
     {
      CElementBase::Y(m_main.Y()+YGap());
      m_canvas.Y(m_main.Y()+m_canvas.YGap());
     }

Wenn es eine Bindung gibt, bewegt sich das Element zusammen mit dem Canvas. Also sollte theoretisch nichts kaputt gehen :)

 

Hat jemand versucht, eine Combobox mit dieser Bibliothek zu erstellen - um Elemente darin zu ändern ????? Share Wie macht man das?

Ich erstelle eine Combobox, alles funktioniert. Dann fülle ich sie mit Elementen, indem ich die folgende Funktion verwende:

void CPresenter::setCombobox(CComboBox *cb_ptr,CArrayString &arr)
  {
   CListView *lv_ptr=cb_ptr.GetListViewPointer();
   lv_ptr.Clear(true);
   
   cb_ptr.ItemsTotal(arr.Total());
   for(int i=0;i<arr.Total();i++)
      cb_ptr.SetValue(i,arr[i]);
   lv_ptr.SelectItem(0,true);
   cb_ptr.SelectItem(0);
   cb_ptr.Update(true);
   cb_ptr.GetButtonPointer().Update(true);
  }

Alles funktioniert. Nachdem ich in der gefüllten Combobox einen Wert ausgewählt habe - zum Beispiel den dritten Wert - und auf die Schaltfläche geklickt habe, kommt es zu einer Reihe von Aktionen, die zum Überlauf der Combobox führen. Und ich fülle sie wieder mit einem Wert auf (z. B. waren ursprünglich 20 Elemente darin, nach dem Klicken auf die Schaltfläche ist nur noch ein Element übrig!)

Und hier kommt ein interessanter Fehler - nachdem alles überschrieben ist (mit der obigen Funktion) - versuche ich, die Combobox zu öffnen, aber ich kann es nicht tun, weil der Fehler Array out of range!
Der Fehler tritt in der Methode:
void CListView::RedrawItemsByMode(const bool is_selected_item=false).

In Zeile 1364. Wie ich durch Nachforschen herausgefunden habe, tritt es auf, weil:
1) bei der Auswahl des dritten Elements der Liste (vor dem Drücken der Schaltfläche) - die Variable
m_prev_item_index_focus mit einem Index gleich 3 gefüllt wird.

Dann wird dieser Index über die Variable prev_item_index an das Array
indexes

in Zeile 1357 übergeben. Das Ergebnis davon - in Zeile 1364 - ist die Auswahl eines Wertes aus dem Array
m_items

bei Index #2 (der dem letzten ausgewählten Element entspricht), während das Array (m_items) nur einen Wert bei Index #0 hat.

Ich suche nun schon den zweiten Tag und konnte immer noch nicht die Stelle finden, an der der Wert der Variablen
m_prev_item_index_focus auf Null zurückgesetzt wird.

Logischerweise sollte er in der Clear-Methode der CListView-Klasse gelöscht werden, die ich verwendet habe, um alle Elemente dort zu löschen, aber leider konnte ich dort keine solche Methode finden....


Im Zusammenhang mit all den obigen Fragen:
1) Ist es ein Fehler in der Bibliothek, oder aktualisiere ich die Werte falsch?
2) Wie umgeht man dieses Problem am besten?

 
Das Minimum an reproduzierbarem Code für meine Frage:

//+------------------------------------------------------------------+
//|TestCombobox.mq5 |
//| Copyright 2018, MetaQuotes Software Corp. | |
//| https://www.mql5.com |
//+------------------------------------------------------------------+

#include <EasyAndFastGUI\WndEvents.mqh>
#include <Arrays/ArrayString.mqh>
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CWindowManager : public CWndEvents
  {
public:
   void              OnDeinitEvent(const int reason){CWndEvents::Destroy();};
   //--- Graph-Ereignishandler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);//

   //--- Erzeugt die grafische Oberfläche des Programms
   bool              CreateGUI(void);

private:
   CComboBox         m_cb;
   CButton           m_btn;

   void              Btn_Click(int id,long lparam);

   bool              CreateComboBox(const string comboBox_name,
                                    const string &items_text[],
                                    const int x_gap,
                                    const int y_gap,
                                    const int x_size,
                                    const int y_size,
                                    CComboBox &comboBox_link,
                                    int x_ButtonSize=0);
   bool              CreateButton(const string text,
                                  const int x_gap,
                                  const int y_gap,
                                  const int x_size,
                                  const int y_size,
                                  CButton &btn_link);
   bool              CreateWindow(const string text);

   void              setCombobox(CComboBox *cb_ptr,CArrayString &arr);
   //--- Hauptfenster
   CWindow           m_window;
   //--- ID und Nummer des Diagrammfensters
   long              m_chart_id;
   int               m_subwin;
  };

CWindowManager _window;
//+------------------------------------------------------------------+
//| Experteninitialisierungsfunktion|
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(!_window.CreateGUI())
     {
      Print(__FUNCTION__," > Fehler bei der Erstellung einer GUI!");
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Experten-Deinitialisierungsfunktion|
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   _window.OnDeinitEvent(reason);
  }
//+------------------------------------------------------------------+
//| Experten-Tick-Funktion|
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   _window.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void CWindowManager::setCombobox(CComboBox *cb_ptr,CArrayString &arr)
  {
   CListView *lv_ptr=cb_ptr.GetListViewPointer();

   lv_ptr.Clear(true);
   cb_ptr.ItemsTotal(arr.Total());
   for(int i=0;i<arr.Total();i++)
      cb_ptr.SetValue(i,arr[i]);
   lv_ptr.SelectItem(0,true);
   cb_ptr.SelectItem(0);
   cb_ptr.Update(true);
   cb_ptr.GetButtonPointer().Update(true);
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
bool CWindowManager::CreateWindow(const string text)
  {
//--- Hinzufügen des Fensterzeigers zum Fenster-Array
   CWndContainer::AddWindow(m_window);
//--- Koordinaten
   int x=(m_window.X()>0) ? m_window.X() : 1;
   int y=(m_window.Y()>0) ? m_window.Y() : 1;
//--- Eigenschaften
   m_window.XSize(300);
   m_window.YSize(300);
   m_window.Alpha(200);
   m_window.IconXGap(3);
   m_window.IconYGap(2);
   m_window.IsMovable(true);
   m_window.ResizeMode(false);
   m_window.CloseButtonIsUsed(true);
   m_window.FullscreenButtonIsUsed(false);
   m_window.CollapseButtonIsUsed(true);
   m_window.TooltipsButtonIsUsed(false);
   m_window.RollUpSubwindowMode(true,true);
   m_window.TransparentOnlyCaption(true);

//--- Tooltips einstellen
   m_window.GetCloseButtonPointer().Tooltip("Close");
   m_window.GetCollapseButtonPointer().Tooltip("Collapse/Expand");
//--- Formularerstellung
   if(!m_window.CreateWindow(m_chart_id,m_subwin,text,x,y))
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
bool CWindowManager::CreateButton(const string text,
                                  const int x_gap,
                                  const int y_gap,
                                  const int x_size,
                                  const int y_size,
                                  CButton &btn_link)
  {
//--- Speichern des Zeigers auf das Hauptelement
   btn_link.MainPointer(m_window);
//--- Eigenschaften
   btn_link.XSize(x_size);
   btn_link.YSize(y_size);
   btn_link.IconXGap(3);
   btn_link.IconYGap(3);
   btn_link.IsCenterText(true);
//--- Ein Steuerelement erstellen
   if(!btn_link.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Hinzufügen eines Zeigers auf das Element in der Basis
   CWndContainer::AddToElementsArray(0,btn_link);
   return(true);
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
bool CWindowManager::CreateComboBox(const string comboBox_name,
                                    const string &items_text[],
                                    const int x_gap,
                                    const int y_gap,
                                    const int x_size,
                                    const int y_size,
                                    CComboBox &comboBox_link,
                                    int x_lableXSize)
  {
//--- Speichern des Zeigers auf das Hauptelement
   comboBox_link.MainPointer(m_window);

   if(x_lableXSize==0)
      x_lableXSize=x_size;

   int items_total=ArraySize(items_text);

//--- Eigenschaften vor der Erstellung festlegen
   comboBox_link.XSize(x_size);
   comboBox_link.YSize(y_size);
   comboBox_link.ItemsTotal(items_total);
   comboBox_link.AnchorRightWindowSide(false);
   comboBox_link.GetButtonPointer().YSize(y_size);

   if(StringCompare(comboBox_name,"")==0)
     {
      comboBox_link.GetButtonPointer().XSize(x_size);
      comboBox_link.GetButtonPointer().AnchorRightWindowSide(true);
     }
   else
     {
      comboBox_link.GetButtonPointer().XSize(x_lableXSize);
      comboBox_link.GetButtonPointer().AnchorRightWindowSide(false);
     }

//--- Speichern der Elementwerte in der Liste des Kombinationsfeldes
   for(int i=0;i<items_total;i++)
      comboBox_link.SetValue(i,items_text[i]);

//--- Abrufen des Listenzeigers
   CListView *lv=comboBox_link.GetListViewPointer();

//--- Listeneigenschaften festlegen
   lv.YSize((int)MathMin(items_total*y_size,150));
   lv.LightsHover(true);
   lv.SelectItem(lv.SelectedItemIndex()==WRONG_VALUE ? 0 : lv.SelectedItemIndex());

//--- Ein Steuerelement erstellen
   if(!comboBox_link.CreateComboBox(comboBox_name,x_gap,y_gap))
      return false;

   CWndContainer::AddToElementsArray(0,comboBox_link);
   return true;
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
bool CWindowManager::CreateGUI(void)
  {
   if(!CreateWindow("Combobox Bug ???? "))
      return(false);
   string cb_arr[20];
   for(int i=0;i<20;i++)
      cb_arr[i]=IntegerToString(i+1);
   if(!CreateComboBox("",cb_arr,100,100,100,20,m_cb))
      return false;
   if(!CreateButton("Action",100,130,50,20,m_btn))
      return false;

// Fenster anzeigen
   CWndEvents::CompletedGUI();
   return true;
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void CWindowManager::Btn_Click(int id,long lparam)
  {
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_btn.Id())
     {
      CArrayString s;
      s.Add("new value");
      setCombobox(&m_cb,s);
     }
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void CWindowManager::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   Btn_Click(id,lparam);
  }
//+------------------------------------------------------------------+



Bis jetzt laufen alle Lösungen, die ich gefunden habe, darauf hinaus, den Bibliothekscode zu bearbeiten (3 neue Codezeilen hinzuzufügen) - aber ich mag die Idee nicht, den Code von jemand anderem zu verändern...

 
Andrey Azatskiy:
Minimaler reproduzierbarer Code für meine Frage:

...

Bisher laufen alle Lösungen, die ich gefunden habe, darauf hinaus, den Bibliothekscode zu bearbeiten (3 neue Codezeilen hinzuzufügen) - aber ich mag die Idee nicht, den Code von jemand anderem zu verändern...

Danke für die Nachricht.

Versuchen Sie, eine kleine Ergänzung zur Methode der Klasse CListView vorzunehmen. Sie müssen die Hilfsfelder wie unten angegeben löschen:

//+------------------------------------------------------------------+
//| Löscht die Liste (löscht alle Einträge).
//+------------------------------------------------------------------+
void CListView::Clear(const bool redraw=false)
  {
//--- Hilfsfelder auf Null zurücksetzen
   m_item_index_focus      =WRONG_VALUE;
   m_prev_selected_item    =WRONG_VALUE;
   m_prev_item_index_focus =WRONG_VALUE;
//--- Die Größe auf Null setzen
   ListSize(0);
//--- Berechnen und Festlegen neuer Listengrößen
   RecalculateAndResizeList(redraw);
  }
 
Anatoli Kazharski:

Vielen Dank für diese Nachricht.

Versuchen Sie, eine kleine Ergänzung zur Methode der Klasse CListView vorzunehmen. Sie müssen die Hilfsfelder wie unten beschrieben löschen:

Vielen Dank für Ihre Antwort.

 
Anatoli Kazharski:

Wie wäre es mit einem Repository auf Bitbucket, das Commits von aktiven Bibliotheksbenutzern annimmt?

Wir werden es gemeinsam schneller fertigstellen ;)

 

Können Sie mir sagen, wie ich die Schaltfläche zum Schließen des Fensters richtig handhaben kann?

Das Problem ist folgendes: Ich platziere einen Expert Advisor (z.B. ExampleEAF.ex5) und meinen Indikator in einem separaten Fenster unter dem Chart (der Minimalcode zeigt nur ein leeres Fenster an). Wenn ich dann in einer dieser Anwendungen auf die Schaltfläche "Fenster schließen" klicke, werden beide geschlossen (aus dem Chart entfernt).

Dieses Verhalten ist nicht ganz korrekt. Gibt es eine Möglichkeit, Ereignisse für verschiedene Anwendungen, die gleichzeitig auf demselben Diagramm arbeiten, zu unterscheiden? Es besteht der Verdacht, dass sich auch andere Ereignisse überschneiden könnten.

 
Andrey Khatimlianskii:

Wie wäre es mit einem Repository auf Bitbucket, das Commits von aktiven Bibliotheksbenutzern annimmt?

Wir werden es gemeinsam schneller fertigstellen ;)

Ich habe bereits Zugang erhalten. Viele der Bearbeitungen sind für mich fragwürdig.

Es gibt keine Rechtfertigung für diese oder jene Änderungen. Es ist einfacher, sie hier im Forum zu diskutieren.

Wenn irgendwelche Änderungen vorgenommen werden, ist es notwendig, Beispiele und Testergebnisse zu nennen und zu erklären, warum es besser ist.

 
dmyger:

Können Sie mir sagen, wie ich die Schaltfläche "Fenster schließen" richtig handhaben kann?

Das Problem ist folgendes: Ich platziere einen Expert Advisor (z.B. ExampleEAF.ex5) und meinen Indikator in einem separaten Fenster unter dem Chart (der Minimalcode zeigt nur ein leeres Fenster an). Wenn ich dann auf die Schaltfläche "Fenster schließen" in einer dieser Anwendungen klicke, werden beide geschlossen (aus dem Chart entfernt).

Dieses Verhalten ist nicht ganz korrekt. Gibt es eine Möglichkeit, Ereignisse für verschiedene Anwendungen, die gleichzeitig auf demselben Diagramm arbeiten, zu unterscheiden? Ich vermute, dass sich auch andere Ereignisse überschneiden können.

Ich werde es testen, wenn ich Zeit habe, und das Ergebnis hier mitteilen.

 

Andrey Khatimlianskii:

Как на счет репозитория на битбакете и принятия коммитов от активных пользователей библиотеки?

Вместе быстрее допилим ;)

Anatoli Kazharski:

...

Wenn Änderungen vorgenommen werden, ist es notwendig, Beispiele und Testergebnisse mit einer Erklärung, warum es besser wäre, zu geben.

Zumindest kurz. Zum Beispiel:

Korrekturen in der CListView-Klasse. In der Clear()-Methode müssen wir einige Hilfsfelder auf Null setzen, um zu verhindern, dass andere Methoden der Klasse außerhalb des Arrays gehen.