TAChart - Umschalten zwischen Linie und Spline?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Timm Thaler
Beiträge: 1187
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

TAChart - Umschalten zwischen Linie und Spline?

Beitrag von Timm Thaler »

Ich habe ein TAChart, in dem bis zu XY-Datenreihen dargestellt werden. Ich würde gern zwischen Liniendarstellung (Gerade zwischen benachbarten Punkten) und kubischem Spline umschalten.

Aktuelle mache ich es so, dass ich je 12 TLineSeries und 12 TCubicSplineSeries erstelle und die Daten entweder in die einen oder die anderen schreibe.

Gibt es auch eine Möglichkeit, nur mit einer Art TxxxSeries zu arbeiten und die Darstellung umzuschalten?

Und gibt es eine Übersicht, welche Setter was am Diagramm verändert (Linienart, Dicke, Farbe, Punkte...). Ich hab zwar einige Seiten zu TACHarts durch aber es ist eine ganz schöne Sucherei.

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

Re: TAChart - Umschalten zwischen Linie und Spline?

Beitrag von wp_xyz »

Nein, eine so vielgesichtige "LineSpline" Series gibt es nicht. Du musst tatsächlich 12 Series von jeder Sorte in den Chart klicken (oder zur Laufzeit erzeugen). Du kannst die Daten allerdings zentral z.B. in ein einer TListChartSource ablegen, die du dann der jeweils anzuzeigenden Series zuweist (Property Source := ListChartSource1) bzw. von der anderen Sorte wegnimmst (Property Source := nil).

Etwa so (nicht getestet):

Code: Alles auswählen

procedure TForm1.FomrCreate(Sender: TObject);
var
  i: Integer;
  x,y: Double;
begin
  for i := 0 to 100 do begin
    x := i/10;
    y := sin(x);
    ListChartsource1.Add(x, y);
  end;
end;

procedure TForm1.LineOrSpline(line: Boolean);
begin
  if line then
  begin
    Chart1LineSeries1.Source := ListChartSource1
    Chart1SplineSeries1.Source := nil;
  end else
  begin
    Chart1LineSeries1.Source := nil;
    Chart1SplineSeries1.Source := ListChartSource1;
  end;
end;
Wenn du die Series zur Laufzeit erzeugst, könnte dir auch mir einer Routine wie "ChangeSeriesType" geholfen sein, die ich in einem größeren Projekt einmal verwendet habe (ich hoffe, ich habe alles mit rauskopiert):

Code: Alles auswählen

type
  TSeriesType = (stAreaSeries, stBarSeries, stLineSeries,
    stCubicSplineSeries, stBSplineSeries, stOther);

procedure ApplyColorsToSeries(ASeries: TChartSeries;
  ASeriesColor, ABorderColor, ASymbolColor: TColor);
begin
  SetSeriesColor(ASeries, ASeriesColor);
  SetSeriesBorderColor(ASeries, ABorderColor);
  SetSeriesSymbolColor(ASeries, ASymbolColor);
end;

procedure ExtractColorsFromSeries(ASeries: TChartSeries;
  var ASeriesColor, ABorderColor, ASymbolColor: TColor);
begin
  ASeriesColor := GetSeriesColor(ASeries);
  ABorderColor := GetSeriesBorderColor(ASeries);
  if ASeries is TLineSeries then
    ASymbolColor := GetSeriesSymbolColor(ASeries)
  else
    ASymbolColor := ASeriesColor;
end;

function GetSeriesBorderColor(ASeries: TChartSeries): TColor;
begin
  if ASeries is TLineSeries then
    Result := TLineSeries(ASeries).Pointer.Pen.Color
  else
  if ASeries is TCubicSplineSeries then
    Result := TCubicSplineSeries(ASeries).Pointer.Pen.Color
  else
  if ASeries is TBSplineSeries then
    Result := TBSplineSeries(ASeries).Pointer.Pen.Color
  else
  if ASeries is TBarSeries then
    Result := TBarSeries(ASeries).BarPen.Color
  else
  if ASeries is TAreaSeries then
    Result := TAreaSeries(ASeries).AreaContourPen.Color
  else
    Result := clNone;
end;

procedure SetSeriesBorderColor(ASeries: TChartSeries; AColor: TColor);
begin
  if ASeries is TLineSeries then
    TLineSeries(ASeries).Pointer.Pen.Color := AColor
  else
  if ASeries is TCubicSplineSeries then
    TCubicSplineSeries(ASeries).Pointer.Pen.Color := AColor
  else
  if ASeries is TBSplineSeries then
    TBSplineSeries(ASeries).Pointer.Pen.Color := AColor
  else
  if ASeries is TBarSeries then
    TBarSeries(ASeries).BarPen.Color := AColor
  else
  if ASeries is TAreaSeries then
    TAreaSeries(ASeries).AreaContourPen.Color := AColor;
end;

function GetSeriesSymbolColor(ASeries: TChartSeries): TColor;
begin
  if ASeries is TLineSeries then
    Result := TLineSeries(ASeries).Pointer.Brush.Color
  else
  if ASeries is TCubicSplineSeries then
    Result := TCubicSplineSeries(ASeries).Pointer.Brush.Color
  else
  if ASeries is TBSplineSeries then
    Result := TBSplineSeries(ASeries).Pointer.Brush.Color
  else
    Result := clNone;
end;

procedure SetSeriesSymbolColor(ASeries: TChartSeries; AColor: TColor);
begin
  if ASeries is TLineSeries then
    TLineSeries(ASeries).Pointer.Brush.Color := AColor
  else
  if ASeries is TCubicSplineSeries then
    TCubicSplineSeries(ASeries).Pointer.Brush.Color := AColor
  else
  if ASeries is TBSplineSeries then
    TBSplineSeries(ASeries).Pointer.Brush.Color := AColor;
end;

function GetSeriesColor(ASeries: TChartSeries): TColor;
begin
  if ASeries is TLineSeries then
    Result := TLineSeries(ASeries).SeriesColor
  else
  if ASeries is TCubicSplineSeries then
    Result := TCubicSplineSeries(ASeries).Pen.Color
  else
  if ASeries is TBSplineSeries then
    Result := TBSplineSeries(ASeries).Pen.Color
  else
  if ASeries is TBarSeries then
    Result := TBarSeries(ASeries).BarBrush.Color
  else
  if ASeries is TAreaSeries then
    Result := TAreaSeries(ASeries).AreaBrush.Color
  else
    Result := clNone;
end;

procedure SetSeriesColor(ASeries: TChartSeries; AColor: TColor);
begin
  if ASeries is TLineSeries then
    TLineSeries(ASeries).SeriesColor := AColor
  else
  if ASeries is TCubicSplineSeries then
    TCubicSplineSeries(ASeries).Pen.Color := AColor
  else
  if ASeries is TBSplineSeries then
    TBSplineSeries(ASeries).Pen.Color := AColor
  else
  if ASeries is TBarSeries then
    TBarSeries(ASeries).BarBrush.Color := AColor
  else
  if ASeries is TAreaSeries then begin
    TAreaSeries(ASeries).AreaBrush.Color := AColor;
    TAreaSeries(ASeries).AreaLinesPen.Color := AColor;
  end;
end;

function GetSeriesLineSymbolMode(ASeries: TChartSeries): TLineSymbolMode;
begin
  Result := lsmLinesOnly;
  if ASeries is TLineSeries then
    with TLineSeries(ASeries) do
      Result := TLineSymbolMode(ord(ShowPoints) + ord(ShowLines)*2 - 1)
  else
  if ASeries is TCubicSplineSeries then
    with TCubicSplineSeries(ASeries) do
      Result := TLineSymbolMode(ord(Pointer.Visible) + ord(Pen.Visible)*2 - 1)
  else
  if ASeries is TBSplineSeries then
    with TBSplineSeries(ASeries) do
      Result := TLineSymbolMode(ord(Pointer.Visible) + ord(Pen.Visible)*2 - 1);
end;

procedure SetSeriesLineSymbolMode(ASeries: TChartSeries; AMode: TLineSymbolMode);
begin
  if ASeries is TLineSeries then
    with TLineSeries(ASeries) do begin
      ShowPoints := AMode in [lsmSymbolsOnly, lsmBoth];
      ShowLines := AMode in [lsmLinesOnly, lsmBoth];
    end
  else
  if ASeries is TCubicSplineSeries then
    with TCubicSplineSeries(ASeries) do begin
      Pointer.Visible := AMode in [lsmSymbolsOnly, lsmBoth];
      Pen.Visible := AMode in [lsmLinesOnly, lsmBoth];
    end
  else
  if ASeries is TBSplineSeries then
    with TBSplineSeries(ASeries) do begin
      Pointer.Visible := AMode in [lsmSymbolsOnly, lsmBoth];
      Pen.Visible := AMode in [lsmLinesOnly, lsmBoth];
    end;
end;

function GetSeriesType(ASeries: TChartSeries): TSeriesType;
begin
  if ASeries is TLineSeries then
    Result := stLineSeries
  else
  if ASeries is TCubicSplineSeries then
    Result := stCubicSplineSeries
  else
  if ASeries is TBSplineSeries then
    Result := stBSplineSeries
  else
  if ASeries is TBarSeries then
    Result := stBarSeries
  else
  if ASeries is TAreaSeries then
    Result := stAreaSeries
  else
    Result := stOther;
end;

function ChangeSeriesType(ASeries: TChartSeries;
  ANewType: TSeriesType): TChartSeries;
var
  sertype: TSeriesType;
  src: TCustomChartSource;
  cs, cb, csym: TColor;
begin
  sertype := GetSeriesType(ASeries);
  if sertype = ANewType then begin
    Result := ASeries;
    exit;
  end;

  case ANewType of
    stLineSeries:
      begin
        Result := TLineSeries.Create(ASeries.Owner);
      end;

    stCubicSplineSeries:
      begin
        Result := TCubicSplineSeries.Create(ASeries.Owner);
        {$IFDEF LCL_FullVersion < 2000000}
        with TCubicSplineSeries(Result) do begin
          Options := Options + [csoDrawFewPoints];
        end;
        {$IFEND}
      end;

    stBSplineSeries:
      begin
        Result := TBSplineSeries.Create(ASeries.Owner);
      end;

    stBarSeries:
      begin
        Result := TBarSeries.Create(ASeries.Owner);
      end;

    stAreaSeries:
      begin
        Result := TAreaSeries.Create(ASeries.Owner);
        TAreaSeries(Result).UseZeroLevel := true;
      end;

    stOther:
      raise Exception.Create('Unknown series type in ChangeSeriesType.');
  end;

  // Copy colors from old to new series
  ExtractColorsFromSeries(ASeries, cs, cb, csym);
  ApplyColorsToSeries(Result, cs, cb, csym);
  if Result is TCubicSplineSeries then
    with TCubicSplineSeries(Result) do
      BadDataPen.Color := Pen.Color;

  // Copy properties to new series
  Result.Active := ASeries.Active;
  Result.AxisIndexX := ASeries.AxisIndexX;
  Result.AxisIndexY := ASeries.AxisIndexY;
  Result.Legend.Assign(ASeries.Legend);
  //Result.MarkPositions := ASeries.MarkPosition;:
  Result.Marks.Assign(ASeries.Marks);
  Result.Tag := ASeries.Tag;
  Result.Title := ASeries.Title;
  Result.Transparency := ASeries.Transparency;
  Result.ZPosition := ASeries.ZPosition;

  // Now copy the chart sources. If no external source is used by ASeries then
  // copy the built-in source to the result series. If an external source is
  // used just assign it to the result source as well.
  if IsBuiltInChartSource(ASeries.Source) then
    Result.ListSource.CopyFrom(ASeries.ListSource)
  else
    Result.Source := ASeries.Source;

  // Add new series to chart and move to the index of the old series
  ASeries.ParentChart.AddSeries(Result);
  Result.Index := ASeries.Index;

  // Remove and destroy old series
  ASeries.ParentChart.RemoveSeries(ASeries);
  ASeries.Free;
end;
Eine Übersicht, wie du sie vielleicht meinst, gibt es nicht. Aber in der TAChart-Documentation wird alles angesprochen, aber halt nur sehr knapp. Wenn du es ausführlicher willst, musst du dir das Lazarus-Handbook besorgen, dort habe ich mich über 50 Seiten ausgetobt.

Timm Thaler
Beiträge: 1187
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: TAChart - Umschalten zwischen Linie und Spline?

Beitrag von Timm Thaler »

Danke, hab ich so umgesetzt.

Antworten