TChart aus TAGraph: LineSeries mit mehreren Farben

Rund um die LCL und andere Komponenten
Antworten
Rob
Beiträge: 34
Registriert: Fr 8. Jul 2011, 10:45
OS, Lazarus, FPC: Win7, Ubuntu 64 und 32bit Lazarus (immer aktuellstes Release) FPC 2.6.4
CPU-Target: amd_64 und i386
Kontaktdaten:

TChart aus TAGraph: LineSeries mit mehreren Farben

Beitrag von Rob »

Hallo,

ich habe ein Problem mit TChart und LineSeries
Ich will eine Kurve darstellen hierzu füge ich mait AddXY die Messwerte zur LineSeries.
Nun möchte ich aber das z.B. die Linie von Messert 0 - 17 Schwarz, von 17 - 30 rot, 30 -54 irgendeineanderefarbe usw usw bekommt.
Kann ich das mit TChart und einer LineSeries realisieren?

Ich nutze Lazarus 1.2.6 und FPC 2.6.4 unter Win7 und Ubuntu.
Die Lösung soll zwischen den Betriebssystemem portabel sein. ;-)

Die Alternative ist für jede Farbe eine eigene Lineseries mit der entsprechenden Farbe
zu erstellen und dann darzustellen, aber das erschient mir weniger elegant.

Grüße
Rob
Zuletzt geändert von Rob am Fr 14. Nov 2014, 09:25, insgesamt 2-mal geändert.

wp_xyz
Beiträge: 5130
Registriert: Fr 8. Apr 2011, 09:01

Re: TChart aus TAGraph: LineSeries mit mehreren Farben

Beitrag von wp_xyz »

Du kannst bei jedem AddXY eine Farbe für jeden Datenpunkt angeben:

Code: Alles auswählen

 
  LineSeries.AddXY(x, y, '', clRed);
 

Rob
Beiträge: 34
Registriert: Fr 8. Jul 2011, 10:45
OS, Lazarus, FPC: Win7, Ubuntu 64 und 32bit Lazarus (immer aktuellstes Release) FPC 2.6.4
CPU-Target: amd_64 und i386
Kontaktdaten:

Re: TChart aus TAGraph: LineSeries mit mehreren Farben

Beitrag von Rob »

wp_xyz hat geschrieben:Du kannst bei jedem AddXY eine Farbe für jeden Datenpunkt angeben:

Code: Alles auswählen

 
  LineSeries.AddXY(x, y, '', clRed);
 
Genau das funktioniert aber nicht.
Du schreibst Datenpunkt ich habe nur eine Linie ohne
Punkte oder Marker darauf und ich will die Linienfarbe selbst ändern.

Ich habe im PropertyEditor clBlack zur entwicklungszeit gesetzt und zur Laufzeit bei
AddXY(x,y, '' clRed ) aber die Linie ist nur schwarz.

Muss ich dazu noch irgendein Property setzen?

Grüße
Rob

wp_xyz
Beiträge: 5130
Registriert: Fr 8. Apr 2011, 09:01

Re: TChart aus TAGraph: LineSeries mit mehreren Farben

Beitrag von wp_xyz »

Achso, mein Vorschlag färbt die Datenpunkt-SYMBOLE ein, die per Default nicht sichtbar sind; du müsstest sie mit "ShowPoints = true" noch anzeigen. Die VERBINDUNGSLINIEN abschnittweise einzufärben (wie du korrekt geschrieben hast, aber ich nicht richtig gelesen habe...), kann man m.E. nur so erreichen, indem man für jeden Abschnitt eine eigene Series anlegt.

Rob
Beiträge: 34
Registriert: Fr 8. Jul 2011, 10:45
OS, Lazarus, FPC: Win7, Ubuntu 64 und 32bit Lazarus (immer aktuellstes Release) FPC 2.6.4
CPU-Target: amd_64 und i386
Kontaktdaten:

Re: TChart aus TAGraph: LineSeries mit mehreren Farben

Beitrag von Rob »

wp_xyz hat geschrieben:, indem man für jeden Abschnitt eine eigene Series anlegt.
Das habe ich fast befürchtet.
Danke!

Gruß
Rob

wp_xyz
Beiträge: 5130
Registriert: Fr 8. Apr 2011, 09:01

Re: TChart aus TAGraph: LineSeries mit mehreren Farben

Beitrag von wp_xyz »

rob hat geschrieben: Das habe ich fast befürchtet.
Was ist denn daran so schlimm?

Ich finde, das Schöne an TAChart ist, dass man sich baukastenmäßig seinen gewünschten Plot mit allen Effekten und Eigenheiten zusammenbauen kann. Die Alternative wäre, alle erdenklichen Varianten in dem Chart unterzubringen. Also bei der LineSeries auch die abschnittsweise Färbung der Verbindungslinie. Das hieße aber, dass jede LineSeries dieses selten benötigte Feature mitschleppt, das im Bedarfsfalls dann vielleicht doch nicht richtig realisiert ist, weil du vielleicht den Farbwechsel nach dem letzten Punkt eines Abschnitts haben willst, der nächste User aber vielleicht vor dem ersten Punkt des nächsten Abschnitts.

Rob
Beiträge: 34
Registriert: Fr 8. Jul 2011, 10:45
OS, Lazarus, FPC: Win7, Ubuntu 64 und 32bit Lazarus (immer aktuellstes Release) FPC 2.6.4
CPU-Target: amd_64 und i386
Kontaktdaten:

Re: TChart aus TAGraph: LineSeries mit mehreren Farben

Beitrag von Rob »

wp_xyz hat geschrieben:
rob hat geschrieben: Das habe ich fast befürchtet.
Was ist denn daran so schlimm?
Eigentlich nix, nur dass es für mich jetzt mehr Arbeit ist als einfach die Farbe umzuwechseln.
Aber egal, ist jetzt gelöst.
Allerdings muss ich sagen das TChart sich ganz schön sträubt wenn ich von ihm was will.

Mal sehen wie die Performance unter Linux ist wenn ich damit fertig bin.
Vorher hatte ich plotpanel benutzt und da ist die Performance unter Linux einfach nur schlecht.
(Dafür geht das Farben umschalten auf einer Linie sehr einfach ... man kann nicht alles haben.)

Ich finde es allerdings Super wie hier geholfen wird!!!!

Grüße
Rob

wp_xyz
Beiträge: 5130
Registriert: Fr 8. Apr 2011, 09:01

Re: [gelöst] TChart aus TAGraph: LineSeries mit mehreren Far

Beitrag von wp_xyz »

rob hat geschrieben: nur dass es für mich jetzt mehr Arbeit ist als einfach die Farbe umzuwechseln.
Kommt drauf an, wie du's genau machst...
rob hat geschrieben:Allerdings muss ich sagen das TChart sich ganz schön sträubt wenn ich von ihm was will.
Wo denn?
rob hat geschrieben:Mal sehen wie die Performance unter Linux ist wenn ich damit fertig bin.
Wenn du sehr viele Punkte übergibst, solltest du die Schleife mit den AddXY Aufrufen mit BeginUpdate/EndUpdate einklammern, so dass nach jedem neuen Punkt der Chart nicht neu gezeichnet wird, sondern erst wenn alles da ist. Das folgende Code-Schnippsel mit 1 Millionen Datenpunkten läuft bei mir in 140 ms durch, ohne das Begin/EndUpdate mit knapp 1 Sekunde:

Code: Alles auswählen

 
const
  MIN = 0;
  MAX = 100;
  N = 1000000; 
var
  x,y: Double;
  i: Integer;
  t: Integer;
begin
  t := GetTickCount;
  Chart1LineSeries1.ListSource.BeginUpdate;
  try
    for i:=0 to N-1 do
    begin
      x := MIN + (MAX-MIN)/(N-1)*i;
      y := x;
      Chart1Lineseries1.AddXY(x,y);
    end;
  finally
    Chart1LineSeries1.ListSource.EndUpdate;
  end;
  ShowMessage(Format('%d ms', [GetTickCount-t]));
end;
 

Rob
Beiträge: 34
Registriert: Fr 8. Jul 2011, 10:45
OS, Lazarus, FPC: Win7, Ubuntu 64 und 32bit Lazarus (immer aktuellstes Release) FPC 2.6.4
CPU-Target: amd_64 und i386
Kontaktdaten:

Re: [gelöst] TChart aus TAGraph: LineSeries mit mehreren Far

Beitrag von Rob »

wp_xyz hat geschrieben:
rob hat geschrieben: nur dass es für mich jetzt mehr Arbeit ist als einfach die Farbe umzuwechseln.
Kommt drauf an, wie du's genau machst...
Da hab ich mich zu früh gefreut. TChart sträubt sich schon wieder ...
Die Grafik wird nun korrekt angezeigt, allerdings wenn ich das Programm beende kommt ein Heapdump by heaptrc unit
True heap size: 3276800
true free heap: 3158784
Should be: 3172600

EDIT: Aus den Thread Memleaks hab ich gesehen das das an den Debug Infol liegen kann...
Im Nicht-Debug-Modus funktioniert alles, trotzdem die Frage ist das prinzipiell so richtig, habe ich Property Einstellugnen vergessen,
Kann ich auch den Versuch 1 ohne das Array nehmen, denn die Lineseries wird ja auch un der Liste von TChart gehalten.

Ich räume die dynamisch erzeugten TLineSeries nicht auf.
Muss ich das beim Beenden des Programmes selbst tun oder erzeuge ich sie falsch?

Hier mein Code:

Code: Alles auswählen

 
var
  ...
  ActualSeries: TLineSeries;
  SeriesArray: Array of TLineSeries;
  ActualChange: integer;
  ActualColor: Integer;
 
begin
  ActualColor := 1;
 
  ActualChange:=0;
  SetLength(SeriesArray, ActualChange+1);
  SeriesArray[ActualChange] := TLineSeries.Create(Chart1);
  SeriesArray[ActualChange].ShowPoints := false;
 
 [b]// Ich frage mich ob das hier sein muss, es funktioniert irgendwie auch ohne ...[/b]
  SeriesArray[ActualChange].AxisIndexX:=MPSeries.AxisIndexX;
  SeriesArray[ActualChange].AxisIndexY:=MPSeries.AxisIndexY;
 
  SeriesArray[ActualChange].SeriesColor:=GetColor(ActualColor);
  Chart1.AddSeries(SeriesArray[ActualChange]);
 
  (* Das war Versuch 1
    ActualSeries := TLineSeries.Create(Chart1);
    ActualSeries.ShowPoints := false;
    ActualSeries.AxisIndexX:=MPSeries.AxisIndexX;
    ActualSeries.AxisIndexY:=MPSeries.AxisIndexY;
    ActualSeries.SeriesColor:=GetColor(ActualColor);
    Chart1.AddSeries(ActualSeries);
    *)
  // -------------------------------------------------------------------------
 
  SeriesArray[ActualChange].AddXY(0, 0, '0:00', GetColor(ActualColor));
 
  for i := 1 to MesswertCount do begin
      GetMesswerte(i, pe2);
 
      (* Das war Versuch 1
      ActualSeries.AddXY(i,pe2.Messwert, pe2.GetTimeString, GetColor(ActualColor));
      *) 
      SeriesArray[ActualChange].AddXY(i, pe2.Messwert, pe2.GetTimeString, GetColor(ActualColor));
 
      if pe2.doColorChange then begin
        ActualColor := pe2.GetColorChanged;
 
        Inc(ActualChange, 1);
        SetLength(SeriesArray, ActualChange+1);
        SeriesArray[ActualChange] := TLineSeries.Create(Chart1);
        SeriesArray[ActualChange].ShowPoints := false;
        SeriesArray[ActualChange].AxisIndexX:=MPSeries.AxisIndexX;
        SeriesArray[ActualChange].AxisIndexY:=MPSeries.AxisIndexY;
        SeriesArray[ActualChange].SeriesColor:=GetColor(ActualColor);
        Chart1.AddSeries(SeriesArray[ActualChange]);
 
        SeriesArray[ActualChange].AddXY(i,pe2.GetDepthInM,pe2.GetTimeString, GetColor(ActualColor));
 
      (* Das war Versuch 1
        ActualSeries := TLineSeries.Create(Chart1);
        ActualSeries.ShowPoints := false;
        ActualSeries.AxisIndexX:=MPSeries.AxisIndexX;
        ActualSeries.AxisIndexY:=MPSeries.AxisIndexY;
        ActualSeries.SeriesColor:=GetColor(ActualColor);
        Chart1.AddSeries(ActualSeries);
 
        ActualSeries.AddXY(i,pe2.GetDepthInM,pe2.GetTimeString, GetColor(ActualColor));
        *)
      end;
 
      ....
 
   end;
 

wp_xyz
Beiträge: 5130
Registriert: Fr 8. Apr 2011, 09:01

Re: TChart aus TAGraph: LineSeries mit mehreren Farben

Beitrag von wp_xyz »

Ich habe mir aus deinem Code folgendes Test-Programm zusammengebaut (mit einigen Vereinfachungen, weil ich einige von dir verwendete Routinen nicht habe):

Code: Alles auswählen

 
unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, TAGraph, Forms, Controls, Graphics, Dialogs,
  StdCtrls, ExtCtrls, TASeries;
 
type
 
  { TForm1 }
 
  TMessWert = record
    MessWert: Double;
    GetTimeString: String;
  end;
 
  TForm1 = class(TForm)
    Button1: TButton;
    Chart1: TChart;
    Chart1LineSeries1: TLineSeries;
    Panel1: TPanel;
    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
    function GetColor(AColorIndex: Integer): TColor;
    procedure GetMessWerte(AIndex: Integer; out AMesswert: TMesswert);
  public
    { public declarations }
  end;
 
var
  Form1: TForm1;
 
const
  MessWertCount = 150;
 
implementation
 
{$R *.lfm}
 
const
  PALETTESIZE = 5;
 
  PALETTE: Array[0..PALETTESIZE-1] of TColor = (
    clRed, clBlue, clBlack, clGreen, clFuchsia
  );
 
{ TForm1 }
 
procedure TForm1.GetMesswerte(AIndex: Integer; out AMessWert: TMessWert);
// Dummy-Prozedur, weil ich deine Routine nicht habe
var
  t: TTime;
begin
  AMessWert.MessWert := sin(AIndex/2);
  t := AIndex / (24*60*60);
  // Nehme an, dass jede Sekunde 1 Messung anfällt und die Messung um Mitternacht beginnt
  AMessWert.GetTimeString := FormatDateTime('hh:nn:ss', t);
end;
 
function TForm1.GetColor(AColorIndex: Integer): TColor;
// Dummy-Prozedur:
// Farbe in Palette nachschlagen. Falls die Palette nicht ausreicht, von vorne
// anfangen.
begin
  Result := PALETTE[AColorIndex mod PALETTESIZE];
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  pe2: TMesswert;
  ActualColor, ActualChange: Integer;
  SeriesArray: Array of TLineSeries;
begin
  ActualChange:=0;
  ActualColor := 0;
 
  SetLength(SeriesArray, ActualChange+1);
  SeriesArray[ActualChange] := TLineSeries.Create(Chart1);     // besser: Form1 statt Chart1
  SeriesArray[ActualChange].AxisIndexX := 1; // MPSeries.AxisIndexX;  -- weiß nicht, was MPSeries ist!
  SeriesArray[ActualChange].AxisIndexY := 0; // MPSeries.AxisIndexY;
  SeriesArray[ActualChange].ShowPoints := false;
  SeriesArray[ActualChange].SeriesColor := GetColor(ActualColor);
  Chart1.AddSeries(SeriesArray[ActualChange]);
 
  SeriesArray[ActualChange].AddXY(0, 0, '0:00', GetColor(ActualColor));
 
  for i := 1 to MesswertCount do begin
     GetMesswerte(i, pe2);
 
     SeriesArray[ActualChange].AddXY(i, pe2.Messwert, pe2.GetTimeString, GetColor(ActualColor));
 
     if i mod 10 = 0 then begin  // alle 10 Werte die Farbe wechseln
       inc(ActualColor);
       inc(ActualChange);
       SetLength(SeriesArray, ActualChange+1);
       SeriesArray[ActualChange] := TLineSeries.Create(Chart1);  // besser: Form1 statt Chart1
       SeriesArray[ActualChange].ShowPoints := false;
       SeriesArray[ActualChange].AxisIndexX := 1; // MPSeries.AxisIndexX;  -- weiß nicht, was MPSeries ist!
       SeriesArray[ActualChange].AxisIndexY := 0; // MPSeries.AxisIndexY;
       SeriesArray[ActualChange].SeriesColor := GetColor(ActualColor);
       Chart1.AddSeries(SeriesArray[ActualChange]);
 
       SeriesArray[ActualChange].AddXY(i, pe2.MessWert, pe2.GetTimeString, GetColor(ActualColor));
       // weiß nicht, was "GetDepthInM" ist...
     end;
  end;
end;
 
end.
 
Zum Nachvollziehen: Einfach ein Formular mit einem Chart und einem Button erstellen.

Das Speicherleck sehe ich nicht, es wird wahrscheinlich irgendwo in dem restlichen Code erzeugt werden. Unüblich ist meiner Meinung nach, dass du den Chart als Owner der Series verwendest; ich nehme da immer das Formular, ich erinnere mich, dass es sonst mindestens 1x Probleme gab.
rob hat geschrieben: Ich frage mich ob das hier sein muss, es funktioniert irgendwie auch ohne ...[/b]
SeriesArray[ActualChange].AxisIndexX:=MPSeries.AxisIndexX;
SeriesArray[ActualChange].AxisIndexY:=MPSeries.AxisIndexY;
Ist normalerweise nicht nötig, wenn du nur die zwei Standardachsen und keine Achsen-Transformationen brauchst. Wenn du aber, wie in den anderen Thread angefragt, mehrere Achsen verwenden willst, ist es ein Muss.

Wenn du diese Klippe umschifft hast, wirst du als nächstes Probleme haben, die Labels, die du den Datenpunkten zugeordnet hast, an der x-Achse angezeigt zu kriegen. Für die Marks der Achse brauchst du eine Datenquelle (ChartSource), aber da die Series nun in viele Teilseries zerstückelt ist, gibt es keine ChartSource, die durchgängig alle Labels für die x-Achse enthält. Deine Original-Daten stehen wahrscheinlich in einem Array. Daher würde ich vorschlagen, für jede Series noch eine "UserDefinedChartSouce" zu erzeugen,die jeweils ihre Daten je nach Indexbereich aus dem Array holt. Wird aber schon ganz schön "advanced"...

[EDIT]
Oder du nimmst dir eine ListChartSource, in die du die Datum/Zeit-Werte für jeden Datenpunkt der Gesamtserie schreibst. Diese wird der Marks.Source der x-Achse zugewiesen. Die Zuweisung eines Labels an die einzelnen Datenpunkte kann dann entfallen (genauso wie jetzt schon die Zuweisung einer Datenpunkt-Farbe, die ja nur für die (nicht angezeigten) Symbole gilt.

Antworten