Hallo zusammen,
ich komme mir ziemlich blöd vor, weil ich mir sicher bin, dass es ein ganz einfacher Fehler ist, aber ich finde ihn nicht
Es geht darum, dass ich Messwerte in einem TChart anzeigen will. Es werden nacheinander mehrere Messungen (Messwerte kommen über einen am USB-Port hängenen Mikrocontroller) aufgenommen und die sollen alle im Chart angezeigt werden.
Ich versuche, die Rechnerei so gering wie möglich zu halten und speichere daher die LineSeries der älteren messungen zwischen anstatt sie bei jedem Update des Charts neu zu berechnen.
Das Problem jedoch ist, dass das Chart sich weigert, die LineSeries' anzuzeigen.
Ich habe mein Projekt mal auf das Folgende runterbrechen können:
FListData := TObjectList<TMessung>
enthält die Messungen, die angezeigt werden sollen,
FListSeries := TObjectList<TLineSeries>
enthält die LineSeries, die bereits angelegt worden sind, damit sie nicht jedes Mal neu berechnet werden müssen.
Delphi-Quellcode:
procedure TfrmMain.btStartClick(Sender: TObject);
//-> Neue Messung starten
// -> Neue Mess-Liste anlegen (am Anfang der Liste), Messbeginn der neuen Liste auf "jetzt" setzen
// -> Timer starten
begin
FListData.Insert(0, TMessung.Create);
FListData[0].recordStart := Now;
FAngle := 0;
Timer1.Enabled := True;
end;
procedure TfrmMain.Timer1Timer(Sender: TObject);
//-> wird später durch einen zyklisch sendenden Mikrocontroller am USB-Port ersetzt
// -> Messwerte werden momentan "zufällig" ermittelt
// -> Messwerte werden der neuen Messung (die am Anfang
var torque : Double;
begin
Inc(FAngle);
torque := 50 * Random - 25; //Zufallswerte -25 .. +25
FListData[0].AddValue(FAngle, torque);
DisplayValues;
end;
procedure TfrmMain.DisplayValues;
//-> zeigt die Daten im Chart an
// -> versucht, so wenig wie möglich neu zu berechnen, daher werden die "alten" Lineseries' zwischengespeichert, damit sie eben nicht neu berechnet werden müssen
// -> schaut bei jedem Aufruf, ob der Messbeginn der aktuellen Messung mit dem gemerkten Messbeginn übereinstimmt
// -> wenn ja, ist seit dem letzten Aufruf keine neue Messung begonnen worden, die neuen Werte können einfach der aktuellen messung angehängt werden
// -> wenn nein: In der Zwischenzet ist eine neue Messung begonnen worden oder alle messungen gelöscht oder oder oder...
// -> lösche in dem Fall alle LineSeries und baue sie neu auf
var ls : TLineSeries;
i, j : Integer;
startAngle : Integer;
const
COLOR_LIST : array [0 .. 3] of TColor = (clRed, clBlue, clGreen, clYellow);
begin
//Titel setzen
chDisplay.Title.Text.Clear;
chDisplay.Title.Font.Color := clBlue;
chDisplay.Title.Text.Add('Test Display');
//alle bisherigen Line-Series' löschen
//-----------------------------------------------edit--------------------------------------------------------------------------
chDisplay.SeriesList.Clear; //<-[edit] hier steckt das problem
//Lösung: die obere Zeile mit dem Clear löschen, stattdessen die beiden folgenden Zeilen eintragen
while chDisplay.SeriesList.Count > 0 do //<- [edit] so funktioniert es
chDisplay.RemoveSeries(0);
//-----------------------------------------------edit--------------------------------------------------------------------------
//Legende nur anzeigen, wenn mehr als eine Messung existiert.
chDisplay.Legend.Visible := FListData.Count > 1;
//Test ob die aktuelle Messung (Index 0) in FListData dieselbe ist, die sie beim letzten Aufruf war (wenn ja, stimmen die Aufnahmedaten überein)
if FListData[0].RecordStart <> FRecStartCurrent then //der aktuelle Graph ist nicht mehr der "alte aktuelle" Graph, eine neue Messung wurde begonnen, alles neu aufbauen
begin
//eine neue Messung ist seit dem letzten Aufruf gestartet worden, lösche die Line-Series' und abue sie neue auf
FRecStartCurrent := FListData[0].RecordStart;
FListSeries.Clear;
for i := 0 to FListData.Count - 1 do
begin
ls := TLineSeries.Create(nil);
try
ls.BeginUpdate;
ls.Title := FormatDateTime('hh:mm:ss', FListData[i].RecordStart);
ls.LinePen.Color := COLOR_LIST[i mod (High(COLOR_LIST) + 1)];
ls.LinePen.Width := 2;
startAngle := FListData[i].values[0].Angle;
for j := 0 to FListData[i].values.Count - 1 do
ls.AddXY(FListData[i].values[j].Angle - startAngle, FListData[i].values[j].Torque);
ls.EndUpdate;
FListSeries.Add(ls);
except
//
end;
end;
end
else
begin //die erste messung ist noch die aktuelle, einfach alle Punkte seit dem letzten Update dazutun, die anderen Lineseries' nicht anfassen
startAngle := FListData[0].values[0].Angle;
FListSeries[0].BeginUpdate;
for i := FListSeries[0].Count to FListData[0].values.count - 1 do
FListSeries[0].AddXY(FListData[0].values[i].Angle - startAngle, FListData[0].values[i].Torque);
FListSeries[0].EndUpdate;
end;
for i := FListSeries.Count - 1 downto 0 do
chDisplay.AddSeries(FListSeries[i]);
end;
Wie beschrieben, wird verglichen, ob die erste Messung nach wie vor die aktuelle ist. Wenn ja, werden die Daten einfach an die erste Messung angehängt, wenn nein, wird alles verworfen und neu aufgebaut.
Das Problem ist nun, dass die Graphen im Chart einmal kurz angezeigt werden, nämlich immer dann, wenn eine neue LineSeries angelegt worden ist. Sobald der zweite Timer-Aufruf kommt, die Daten also eigentlich an die neuste Messung angehängt werden sollen, verschwinden die Messungen vom Chart.
Ein
chDisplay.Invalidate;
nützt genauso wenig wie ein
ls.parentChart := chDisplay;
Wie gesagt, ich bin mir sicher, dass es ein dämlicher und einfacher Fehler ist, aber ich seh' momentan den Wald vor lauter Bäumen nicht mehr... Mit anderen Worten: HILFE!!!!!11einself