MQL5 v1.50: Knopf-Flackern - der Chart trug den Indikator DREIMAL
User: "beim Wechseln der Zeit blinken die Knoepfe kurz gross auf und sind dann wieder klein". Nicht die Groesse war das Problem. BELEG aus dem Terminal-Journal: je Zeitebenen-Wechsel DREI identische Zeilen "custom indicator SR_Levels loaded succesfully" im selben Zehntelsekunden- Fenster. Auf dem Chart lagen drei Instanzen. Jede hat ihren EIGENEN gespeicherten Eingabesatz - die vor v1.49 angehaengten trugen noch 66/17/8, die neue 132/34/16. Alle drei schrieben im Sekundentakt auf dieselben Objektnamen, also wechselte die Groesse permanent hin und her. Doppelte Instanzen sind von aussen unsichtbar, solange nichts schreibend kollidiert - sie kosteten hier zusaetzlich das Dreifache an CSV-Lesevorgaengen und Objekt-Auf-/Abbau je Sekunde. Merksatz: bei unerklaerlichem Chart-Verhalten ZUERST die Ladezeilen im Journal zaehlen. FIX 1: nur eine Instanz zeichnet die Knoepfe. Anspruch je Chart ueber eine temporaere GlobalVariable, gesetzt mit GlobalVariableSetOnCondition - also ATOMAR (ein Check-dann-Set haetten bei gleichzeitigem Start alle drei gewonnen). Nicht-Inhaber steigen in BtnDraw() sofort aus und melden einmalig im Experten-Log, dass eine zweite Instanz laeuft. OnDeinit raeumt die Knoepfe nur noch als Inhaber weg und gibt den Anspruch frei - sonst haette eine beendete Zweit-Instanz die Knoepfe der noch laufenden ersten geloescht. FIX 2: die Groesse heilt sich selbst. BtnDraw() legt einen Knopf neu an, wenn seine gespeicherte XSIZE/YSIZE/FONTSIZE nicht mehr zum Soll passt; reines Nachsetzen der Eigenschaften (v1.49) reichte in der Praxis nicht. Der eigentliche Fix liegt beim Nutzer: doppelte Instanzen entfernen (Rechtsklick > Indikatorenliste). Der Guard verhindert nur den sichtbaren Schaden, nicht die dreifache Last. 0 errors 0 warnings, .ex5 frisch, Spiegel bitgleich.
This commit is contained in:
@@ -9661,3 +9661,46 @@ erreichbar schlägt schön, und der Normalfall ist eine Spalte.
|
||||
✅ `0 errors, 0 warnings`, `.ex5` frisch, Spiegel bitgleich.
|
||||
⚠ Wer noch größer will, dreht `InpBtnBreite`/`InpBtnHoehe`/`InpBtnFont` weiter —
|
||||
der Spalten-Rückfall fängt zu hohe Stapel ab.
|
||||
|
||||
### ⚠⚠ „Knöpfe blinken kurz groß auf und sind dann wieder klein" — der Chart trug den Indikator DREIMAL (2026-08-22, v1.50)
|
||||
|
||||
User-Meldung beim Zeitebenen-Wechsel. **Nicht die Größe war das Problem.**
|
||||
Beleg aus dem Terminal-Journal (`<terminal>/logs/20260822.log`) — je Wechsel
|
||||
**drei** identische Zeilen im selben Zehntelsekunden-Fenster:
|
||||
```
|
||||
13:19:16.071 custom indicator SR_Levels (SpotCrude,M5) loaded succesfully
|
||||
13:19:16.072 custom indicator SR_Levels (SpotCrude,M5) loaded succesfully
|
||||
13:19:16.072 custom indicator SR_Levels (SpotCrude,M5) loaded succesfully
|
||||
```
|
||||
**Auf dem Chart lagen DREI Instanzen.** Jede hat ihren **eigenen gespeicherten
|
||||
Eingabesatz**: die vor v1.49 angehängten trugen noch `66/17/8`, die neue
|
||||
`132/34/16`. Alle drei schrieben im Sekundentakt auf **dieselben** Objektnamen —
|
||||
also wechselte die Größe permanent hin und her.
|
||||
|
||||
⚠⚠ **Das ist die Falle hinter dem Symptom: doppelte Instanzen sind von außen
|
||||
unsichtbar**, solange nichts schreibend kollidiert. Sie kosteten hier ausserdem
|
||||
das Dreifache an CSV-Lesevorgängen und Objekt-Auf-/Abbau je Sekunde.
|
||||
**➜ Beim nächsten unerklärlichen Chart-Verhalten ZUERST die Ladezeilen im
|
||||
Journal zählen.** Das kostet eine Minute und hätte hier drei Hypothesen erspart.
|
||||
|
||||
✅ **Fix im Code: nur EINE Instanz zeichnet die Knöpfe.** Anspruch je Chart über
|
||||
eine temporäre `GlobalVariable` (`SRB_BTNOWN_<ChartID>`), gesetzt mit
|
||||
**`GlobalVariableSetOnCondition`** — also **atomar**. Ein Check-dann-Set hätten
|
||||
bei praktisch gleichzeitigem Start alle drei gewonnen. Nicht-Inhaber steigen in
|
||||
`BtnDraw()` sofort aus und melden **einmalig** im Experten-Log, dass eine zweite
|
||||
Instanz läuft (sichtbar machen statt still schlucken).
|
||||
⚠ **`OnDeinit` räumt die Knöpfe nur noch als Inhaber weg** und gibt den Anspruch
|
||||
frei — sonst hätte eine beendete Zweit-Instanz die Knöpfe der noch laufenden
|
||||
ersten gelöscht. Fällt der Inhaber weg, holt sich der nächste Timer-Durchlauf
|
||||
einer anderen Instanz den Anspruch.
|
||||
|
||||
✅ **Zweite Absicherung: die Größe heilt sich selbst.** `BtnDraw()` legt einen
|
||||
Knopf **neu an**, wenn seine gespeicherte `XSIZE`/`YSIZE`/`FONTSIZE` nicht mehr
|
||||
zum Soll passt. Reines Nachsetzen der Eigenschaften (v1.49) reichte in der
|
||||
Praxis nicht.
|
||||
|
||||
⚠ **Der eigentliche Fix liegt beim Nutzer:** die doppelten Instanzen entfernen
|
||||
(Rechtsklick auf den Chart → *Indikatorenliste* → alle `SR_Levels` bis auf eines
|
||||
löschen). Der Code-Guard verhindert nur den sichtbaren Schaden, nicht die
|
||||
dreifache Last.
|
||||
✅ `0 errors, 0 warnings`, `.ex5` frisch, Spiegel bitgleich.
|
||||
|
||||
+56
-1
@@ -12,7 +12,7 @@
|
||||
//| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Oil Trading Bot"
|
||||
#property version "1.49"
|
||||
#property version "1.50"
|
||||
// v1.42 (2026-08-19): Y-Stapel oben rechts GEMESSEN statt geschaetzt. Der
|
||||
// Faktor 1,8 aus v1.41 war nur die naechste Schaetzung; jetzt liefert
|
||||
// TextGetSize die TATSAECHLICHE Pixelhoehe (LabelH). Ausserdem wird Y bei
|
||||
@@ -334,6 +334,43 @@ bool g_bvis[BTN_N];
|
||||
//| Sichtbar? Einzige Quelle fuer alle Zeichen-Zweige.
|
||||
bool Vis(int i) { return (i >= 0 && i < BTN_N) ? g_bvis[i] : true; }
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| ⚠⚠ NUR EINE INSTANZ DARF DIE KNOEPFE ZEICHNEN (v1.50). |
|
||||
//| Real am 2026-08-22: der Chart trug SR_Levels DREIMAL (im Journal |
|
||||
//| belegt: drei "loaded succesfully" je Zeitebenen-Wechsel). Jede |
|
||||
//| Instanz hat ihren EIGENEN gespeicherten Eingabesatz — die vor |
|
||||
//| v1.49 angehaengten trugen noch 66/17/8, die neue 132/34/16. Alle |
|
||||
//| drei schrieben im Sekundentakt auf DIESELBEN Objektnamen, also |
|
||||
//| wechselte die Groesse staendig hin und her: "blinken kurz gross |
|
||||
//| auf und sind dann wieder klein". |
|
||||
//| ⚠ Der Anspruch wird ATOMAR gesetzt (GlobalVariableSetOnCondition) |
|
||||
//| — drei Instanzen starten praktisch gleichzeitig, ein |
|
||||
//| Check-dann-Set haetten alle drei gewonnen. |
|
||||
//| ⚠ Faellt der Inhaber weg (OnDeinit gibt frei), holt sich der |
|
||||
//| naechste Timer-Durchlauf einer anderen Instanz den Anspruch. |
|
||||
//+------------------------------------------------------------------+
|
||||
double g_stamp = 0.0; // eigene Kennung (nie 0)
|
||||
bool g_isowner = false;
|
||||
bool g_warned = false;
|
||||
|
||||
string BtnOwnGV() { return "SRB_BTNOWN_" + IntegerToString(ChartID()); }
|
||||
|
||||
bool BtnClaim()
|
||||
{
|
||||
if(g_isowner) return true;
|
||||
string gv = BtnOwnGV();
|
||||
if(!GlobalVariableCheck(gv)) GlobalVariableTemp(gv); // legt mit 0 an
|
||||
g_isowner = GlobalVariableSetOnCondition(gv, g_stamp, 0.0);
|
||||
if(!g_isowner && !g_warned)
|
||||
{
|
||||
g_warned = true;
|
||||
Print("SR_Levels: weitere Instanz auf diesem Chart — die Knoepfe "
|
||||
"zeichnet nur die erste. Doppelte Instanzen bitte entfernen "
|
||||
"(Rechtsklick > Indikatorenliste).");
|
||||
}
|
||||
return g_isowner;
|
||||
}
|
||||
|
||||
string BtnGV(int i) { return "SRB_" + _Symbol + "_"
|
||||
+ IntegerToString((int)_Period) + "_" + g_bkey[i]; }
|
||||
|
||||
@@ -376,6 +413,7 @@ void BtnPaint(int i)
|
||||
|
||||
void BtnDraw()
|
||||
{
|
||||
if(!BtnClaim()) return; // eine andere Instanz zeichnet
|
||||
if(!InpShowButtons) { ObjectsDeleteAll(0, BPFX); return; }
|
||||
int h = MathMax(10, InpBtnHoehe);
|
||||
// ⚠⚠ SPALTEN-RUECKFALL. Mit der 200-%-Groesse ist der Stapel 490 px hoch;
|
||||
@@ -390,6 +428,14 @@ void BtnDraw()
|
||||
for(int i = 0; i < BTN_N; i++)
|
||||
{
|
||||
string nm = BPFX + g_bkey[i];
|
||||
// ⚠ Auch NEU ANLEGEN, wenn die Groesse nicht mehr passt: ein von einer
|
||||
// frueheren Fassung erzeugter Knopf behaelt sonst seine alten Masse.
|
||||
// Reines Nachsetzen der Eigenschaften reichte in der Praxis nicht.
|
||||
bool falsch = (ObjectFind(0, nm) >= 0)
|
||||
&& ((int)ObjectGetInteger(0, nm, OBJPROP_XSIZE) != InpBtnBreite
|
||||
|| (int)ObjectGetInteger(0, nm, OBJPROP_YSIZE) != h
|
||||
|| (int)ObjectGetInteger(0, nm, OBJPROP_FONTSIZE) != InpBtnFont);
|
||||
if(falsch) ObjectDelete(0, nm);
|
||||
if(ObjectFind(0, nm) < 0)
|
||||
{
|
||||
if(!ObjectCreate(0, nm, OBJ_BUTTON, 0, 0, 0)) continue;
|
||||
@@ -469,6 +515,9 @@ int LabelH(const string font, const int size)
|
||||
int OnInit()
|
||||
{
|
||||
if(InpApplyTheme) ApplyTheme();
|
||||
// Kennung: nie 0 (0 heisst "frei"), pro Instanz verschieden.
|
||||
g_stamp = (double)(GetTickCount() % 1000000) + 1.0 + (double)(MathRand() % 997) / 1000.0;
|
||||
BtnClaim();
|
||||
BtnInit();
|
||||
EventSetTimer(MathMax(1, InpRefreshS));
|
||||
Redraw();
|
||||
@@ -590,7 +639,13 @@ void OnDeinit(const int reason)
|
||||
EventKillTimer();
|
||||
ObjectsDeleteAll(0, PFX);
|
||||
ObjectsDeleteAll(0, RPFX);
|
||||
// ⚠ NUR der Inhaber raeumt die Knoepfe weg — sonst loescht eine
|
||||
// beendete Zweit-Instanz die Knoepfe der noch laufenden ersten.
|
||||
if(g_isowner)
|
||||
{
|
||||
ObjectsDeleteAll(0, BPFX);
|
||||
GlobalVariableSet(BtnOwnGV(), 0.0); // Anspruch freigeben
|
||||
}
|
||||
ChartRedraw();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
Reference in New Issue
Block a user