TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Rund um die LCL und andere Komponenten
Antworten
Johasch
Beiträge: 10
Registriert: Sa 8. Feb 2020, 10:50

TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von Johasch »

Hallo,
ich verwende TAChart zur Darstellung von Vektorberechnungen.
Die Darstellung der Pfeile usw. erfolgt mit Hilfe von AfterCustomDrawBackWall. Alles wird am Bildschirm einwandfrei dargestellt, jedoch beim Versuch die Grafik auszudrucken oder als SVG zu exportieren erhalte ich jedoch nur einen "leeren Chart"

Hier ist mein Beispielsprojekt.

Code: Alles auswählen

procedure TForm1.Chart1AfterCustomDrawBackWall(ASender: TChart;
  ADrawer: IChartDrawer; const ARect: TRect);
var
  Xmin,Xmax,Ymin,Ymax : integer;
  Lx, Ly : integer;

begin
  // Nur als Beispielscode:

  ADrawer.Pen.Width := 2;  {-> Ergibt eine Fehlermeldung}

  // Zuweisen von bereit von Chartwerten auf Pixel umgerechneten Werte:
  Xmin := 50;
  Ymin := 50;
  Xmax := 250;
  Ymax := 150;
  Lx   := (Xmax - Xmin) div 2;
  Ly   := (Ymax - Ymin) div 2;

  ADrawer.Rectangle(Xmin,Ymin,Xmax,Ymax);
  ADrawer.Ellipse(Xmin,Ymin,Xmax,Ymax);
  ADrawer.Line(Xmin + Lx,Ymin,Xmin + Lx,Ymax);
  ADrawer.Line(Xmin,Ymin + Ly,Xmax ,Ymin + Ly);
end;

procedure TForm1.ButtonSVGClick(Sender: TObject);
begin
  Chart1.SaveToSVGFile(GetFilename('svg'));
end;

procedure TForm1.ButtonPrintClick(Sender: TObject);
const
  MARGIN = 10;
var
  r: TRect;
  d: Integer;
begin
  if not PrintDialog1.Execute then exit;
  Printer.BeginDoc;
  try
    r := Rect(0, 0, Printer.PageWidth, Printer.PageHeight div 2);
    d := r.Right - r.Left;
    r.Left += d div MARGIN;
    r.Right -= d div MARGIN;
    d := r.Bottom - r.Top;
    r.Top += d div MARGIN;
    r.Bottom -= d div MARGIN;
    Chart1.Draw(TPrinterDrawer.Create(Printer,False), r);
  finally
    Printer.EndDoc;
  end;

end;

function TForm1.GetFileName(const AExt: String): String;
begin
  with SaveDialog1 do begin
    FileName := '';
    DefaultExt := AExt;
    if not Execute then Abort;
    Result := FileName;
  end;
end;                           

PrintSVG.zip
(457.2 KiB) 108-mal heruntergeladen

Gibt es eine Möglichkeit diese Grafiken zu exportieren oder zu drucken? Wie müsste der Code dafür aussehen? Ich vermute, es ist spezieller IChartDrawer notwendig, habe aber keine Ahnung, wie dieser zu implementieren ist.

Zudem möchte ich gerne auch die Strichweite des Pens verändern, erhalte jedoch bei der Zuweisung nur eine Fehlermeldung "No member is provided to access property".

Code: Alles auswählen

ADrawer.Pen.Width := 2;  {-> Ergibt eine Fehlermeldung}   
Vielen Dank schon im Voraus.
Ausgabe.jpg
Ausgabe.jpg (20.98 KiB) 2911 mal betrachtet

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

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von wp_xyz »

Oh Mann, ich muss gestehen, dass ich diese Kombination anscheinend noch nie getestet habe. Und so ist ein fehlendes "A" durch die Maschen gefallen.

Zur Behebung: Entweder neue Version von SVN laden, oder:
  • Öffne die Unit TAGraph.pas (in Ordner (lazarus)/components/tachart)
  • Suche die Methode TChart.DrawBackwall
  • Etwas nach der Mitte gibt es die Zeilen

    Code: Alles auswählen

      if Assigned(OnAfterCustomDrawBackWall) then
        OnAfterCustomDrawBackwall(Self, Drawer, FClipRect);
    
  • Im Aufruf des Events fehlt das berühmte "A": Drawer --> ADrawer. Das ist hier besonders fatal, denn "Drawer" ist der für den Bildschirm, "ADrawer" der für die externe Datei...
  • Also: Richtig ist

    Code: Alles auswählen

      if Assigned(OnAfterCustomDrawBackWall) then
        OnAfterCustomDrawBackwall(Self, ADrawer, FClipRect);
Wegen der Strichbreite muss ich noch etwas nachforschen.

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

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von wp_xyz »

Zur Strichstärke. Ich weiß nicht, warum Alexander S. Klenin, der das alles geschrieben hat, hierfür nichts vorgesehen hat. Habe es jetzt in Laz-trunk nachgerüstet. Das heißt, es gibt eine Methode Drawer.SetPenWidth(w), und die Methode Drawer.SetPenParams hat nun als optionalen dritten Parameter die Linienbreite w.

Da das an vielen Stellen vorkommt, sind mehrere Dateien davon betroffen, und ich möchte hier keine Patch-Anleitung für die Release geben. Wenn du nicht SVN verwendest, fällt mir noch ein Workaround ein, wie du mit der vorhandenen Version die Strichstärke verändern kannst: Lazarus hat diverse fertige Pens auf Vorrat, z.B. den AxisPen der Achsen - der ist normalerweise ausgeschaltet, weil die Achsen vom Frame übermalt werden. Und diesen könntest du nun "zweckentfremden"; Setze im Objektinspektor für Chart.LeftAxis.AxisPen (oder einer anderen Achse) den gewünschten Wert ein und weise im OnCustomAfterDrawBackWall-Ereignis dies dem Drawer.Pen zu:

Code: Alles auswählen

procedure TForm1.Chart1AfterCustomDrawBackWall(ASender: TChart;
  ADrawer: IChartDrawer; const ARect: TRect);
begin
  ...
  ADrawer.SetBrushParams(bsSolid, clWhite);
  ADrawer.Pen := ASender.LeftAxis.AxisPen;    // <------------ HIER dicken Pen zuweisen

  ADrawer.Rectangle(Xmin,Ymin,Xmax,Ymax);
  ADrawer.Ellipse(Xmin,Ymin,Xmax,Ymax);
  ADrawer.Line(Xmin + Lx,Ymin,Xmin + Lx,Ymax);
  ADrawer.Line(Xmin,Ymin + Ly,Xmax ,Ymin + Ly); 
end;
So, jetzt fehlt noch das mit dem Drucken. Hätte eigentlich nach dem gefundenen "A" funktionieren sollen...

Johasch
Beiträge: 10
Registriert: Sa 8. Feb 2020, 10:50

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von Johasch »

Hallo,
danke für die rasche Antwort. Echt toll!
Ich habe den Patch bei der DrawBackwall Methode durchgeführt und jetzt lässt sich der Export gut durchführen.
Auch wenn die Linienführung im PDF noch etwas seltsam ist - aber da muss ich erst noch sehen, ob das vielleicht an meinem Code liegt .... (Siehe SVG-Ausgabe.jpg)

Die Idee mit dem Verwenden eines fremden Pens ist auch super. Anstelle des Axis-Pens habe ich ein bischen Expermentiert und einen eigenen erstellt. Damit existiert damit auch eine sauber Lösung.

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
var
  a : integer;
begin
 ...
  cPen := TFPCustomPen.Create;    
  ...
end;

procedure TForm1.SetChartPenStyle(ADrawer: IChartDrawer; vStyle: integer);
var
  pStyle  : PTStyle;
  cColor  : TFPColor;
begin
  pStyle        := aStyle.Items[vStyle];
  cPen.Width    := pStyle^.LineWidth;
  cPen.Style    := pStyle^.LineStyle;
  cColor.green  := pStyle^.LineColorG;
  cColor.red    := pStyle^.LineColorR;
  cColor.blue   := pStyle^.LineColorB;
  cColor.alpha  := pStyle^.LineAlpha;
  cPen.FPColor  := cColor;
  ADrawer.Pen    := cPen;
end;             
Dateianhänge
SVG-Ausgabe.jpg
(637.36 KiB) Noch nie heruntergeladen

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

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von wp_xyz »

Ja, sieht seltsam aus. Vor allem sollte eigentlich die Bildschirmausgabe identisch mit dem Export sein, egal wie dein Code ist. Könnte sein, dass da noch ein paar Fehler TAChart-seitig drin sind. Passiert das auch beim Export in ein Bitmap? Kannst du mir mal eine Minimalversion dieses Programms zusammenklicken und hier hochladen?

Allerdings frage ich mich, was genau dein Ziel ist. Wäre es nicht sinnvoller, mit den Standardmitteln von TAChart zu arbeiten (vor allem den Series), anstatt auf eigene Faust auf dem Hintergrund herumzumalen? Dafür brauchst du eigentlich kein TChart, sondern eine einfache Paintbox würde auch reichen.

Es gibt z.B. eine TFieldSeries, die ein Vektor-Feld anhand von Pfeilen darstellt, also genau das, was du in dem Screenshot zeigst. Es gibt ein Demo im Ordner components/tachart/demo/multi der Lazarus-Installation). Allerdings ist die Dateninterpretation nicht ganz so wie du sie brauchst (1. Wertepaar = Mittelpunkt des Pfeils, 2.Wertepaar = Vektor-Richtung). Es gibt auch eine "UserdefinedSeries", in der du nach belieben selbst zeichnen kannst. Mit Series zu arbeiten hat den Vorteil, dass du für den Export den Drawer nicht mehr selbst ansprechen musst. Auch hast du die ganzen Hilfsmittel wie ChartTools (Zoomen, verschieben, Datenpunkt-Klicks usw) zur Verfügung.
Dateianhänge
arrows.zip
(2.12 KiB) 96-mal heruntergeladen

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

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von wp_xyz »

wp_xyz hat geschrieben:
Di 12. Jan 2021, 19:49
Allerdings ist die Dateninterpretation nicht ganz so wie du sie brauchst (1. Wertepaar = Mittelpunkt des Pfeils, 2.Wertepaar = Vektor-Richtung).
Ich habe der TFieldSeries nun ein Property "VectorCoordKind" (= vckCenterDir, vckStartEnd) spendiert, womit man festlegen kann, ob die beiden Wertepaare als Pfeilmittelpunkt/-Richtung bzw. als Pfeilstart-/-endpunkt interpretiert werden.

Johasch
Beiträge: 10
Registriert: Sa 8. Feb 2020, 10:50

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von Johasch »

Allerdings frage ich mich, was genau dein Ziel ist. Wäre es nicht sinnvoller, mit den Standardmitteln von TAChart zu arbeiten (vor allem den Series), anstatt auf eigene Faust auf dem Hintergrund herumzumalen? Dafür brauchst du eigentlich kein TChart, sondern eine einfache Paintbox würde auch reichen.
Momentan bin ich selbst noch dabei, die Möglichkeiten auszuloten. Was mir jedoch vorschwebt ist jedoch eine grafische Ausgabe von z. B folgender Rechenausgabe:
Ein dreieckiges Tuch ist an jeder Ecke mit einem bis drei Seile befestigt. Zeige für jedes Seil die wirkende Kraft als Vektor (Winkel und Größe) an.

Ich hatte auch schon versucht, die notwendigen Routinen für die Canvas-Ausgaben selbst zu schreiben, bin aber nicht wirklich weitergekommen. Daher experimentiere ich jetzt mit TAChart, da es die notwendigen Skalierungen, Achsenverwaltung, Exporte usw. schon mitbringt.
Den Fehler mit der unterschiedlichen Ausgabe im SVG-Export habe ich in der Zwischenzeit gefunden. Die kopierte Zeichenfunktion zeichnete die Linien ohne Verwendung von "MoveTo"

Code: Alles auswählen

  atLine: begin
      ADrawer.LineTo(ArrowPoint[0].x,ArrowPoint[0].y);
      ADrawer.Line(p2.x,p2.y,ArrowPoint[1].x,ArrowPoint[1].y);
    end;

Offensichtlich kam die Bildschirmausgabe damit ohne Fehler zurecht, währen SVG nicht wusste, von wo es starten sollte. Nach einfügen der "MoveTo" Anweisungen stimmen sowohl die Bildschirmausgabe als auch der SVG-Export.
Es gibt z.B. eine TFieldSeries, die ein Vektor-Feld anhand von Pfeilen darstellt, also genau das, was du in dem Screenshot zeigst. ... Es gibt auch eine "UserdefinedSeries", in der du nach belieben selbst zeichnen kannst.
Ich hatte mir alle Demos durchgesehen und genau nach dieser Funktion gesucht, aber nichts gefunden. Und werde jetzt auch die UserdefinedSeries mal gründlicher unter die Lupe nehmen.
Auf jeden Fall bin ich jetzt einen Schritt weiter.

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

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von wp_xyz »

wp_xyz hat geschrieben:
Di 12. Jan 2021, 18:05
So, jetzt fehlt noch das mit dem Drucken. Hätte eigentlich nach dem gefundenen "A" funktionieren sollen...
Funktioniert auch. Das Problem ist aber, dass dein Code in dem Event-Handler absolute Pixel-Koordinaten angibt. Das geht beim Drucker schief, da der eine viel höhere Auflösung hat als der Bildschirm. Richtig wäre, die Original-Gleitkomma-Koordinaten (in TAChart-Sprache: Graph coordinates) zu nehmen und mit Chart.GraphToImage in Pixel ("Image coorindates") umzurechnen - dabei wird die Auflösung des Graphik-Devices berücksichtigt:

Code: Alles auswählen

  XMin := ASender.XGraphToImage(-0.95);
  YMin := ASender.YGraphToImage(0.2);
  XMax := ASender.XGraphToImage(-0.3);
  YMax := ASender.YGraphToImage(0.79);    
Das nächste Problem sind die Linienstärken. Der PrinterDrawer skaliert diese zwar automatisch, natürlich aber nicht die, die du selbst gesetzt hast. Deshalb muss die Linienbreite, die du in SetChartPenStyle einträgst, noch mit der Methode Scale des Drawers umgerechnet werden (nicht getestet):

Code: Alles auswählen

cPen.Width    := ADrawer.Scale(pStyle^.LineWidth);

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

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von wp_xyz »

Also mit der aktuellen Version von TAChart kannst du dieses Vorhaben mit den vorhandenen Bausteinen ganz einfach lösen:
  • Setze Chart.Proportional auf true, damit beide Achsen denselben Skalierungsfaktor erhalten, das Diagramm also winkeltreu wird.
  • Es gibt seit kurzem eine TPolygonSeries, deren Punkte ein geschlossenes Polygon aufspannen. Damit kannst du das Tuch zeichnen und nach Belieben einfärben. Die Polygonpunkte werden durch AddXY definiert. Zur Not tut's auch eine TLineSeries, die natürlich nicht gefüllt wird.
  • Mit der Änderung der TFieldSeries von gestern kannst du, nach Setzen von VectorCoordKind auf vckStartEnd, die Pfeile durch Angabe von Start- und Endpunkt angeben. (In der alten Version ist der erste Punkt der Mittelpunkt, der zweite die Richtung des Pfeils, man müsste sich durch "Mittelpunkt -/+ 1/2 Richtung" den Start und Endpunkt selbst ausrechnen).
Der folgende Code führt zu dem angehängten Screenshot:

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
const
  P: array[0..2] of TDoublePoint = (
    (X:5.0; Y:-3.0),
    (X:-2.0; Y:4.5),
    (X:-3.3; Y:-4.1)
  );
  V: array[0..2] of TDoublePoint = (
    (X:2.5; Y:-1.2),
    (X:-0.4; Y:2.0),
    (X:-1.9; Y:-2.3)
  );

begin
  Chart1.Proportional := true;

  Chart1PolygonSeries1.Brush.Color := clYellow;
  Chart1PolygonSeries1.Pen.Color := clOlive;
  Chart1PolygonSeries1.AddXY(P[0].X, P[0].Y);
  Chart1PolygonSeries1.AddXY(P[1].X, P[1].Y);
  Chart1PolygonSeries1.AddXY(P[2].x, P[2].Y);
  Chart1PolygonSeries1.AddXY(P[0].X, P[0].Y);  // das Polygon muss manuell geschlossen werden.

  Chart1FieldSeries1.VectorCoordKind := vckStartEnd;
  Chart1FieldSeries1.Arrow.Width := 6;
  Chart1FieldSeries1.Arrow.Length := 15;
  Chart1FieldSeries1.Arrow.BaseLength := Chart1FieldSeries1.Arrow.Length;
  Chart1FieldSeries1.Pen.Color := clRed;
  Chart1fieldSeries1.Pen.Width := 3;
  Chart1FieldSeries1.AddVector(P[0].X, P[0].Y, P[0].X + V[0].X, P[0].Y + V[0].Y);
  Chart1FieldSeries1.AddVector(P[1].X, P[1].Y, P[1].X + V[1].X, P[1].Y + V[1].Y);
  Chart1FieldSeries1.AddVector(P[2].X, P[2].Y, P[2].X + V[2].X, P[2].Y + V[2].Y);
end;
Dateianhänge
PolygonFieldSeries.png
PolygonFieldSeries.png (9.22 KiB) 2788 mal betrachtet

Johasch
Beiträge: 10
Registriert: Sa 8. Feb 2020, 10:50

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von Johasch »

Toll - ist genau, was ich für mein Projekt brauche. Werde ich auf jedenfall so umsetzen.
Allerdings scheine ich ein bischen zu naiv zu sein - habe inzwischen fast zwei Stunden im Internet an allen möglichen Stellen gesucht und immer noch nicht den Download der letzten Version gefunden. :oops: Der Verweis auf das Lazarus SVN repository hat mir auch nicht weitergeholfen.
Wie lautet der Link?

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

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von wp_xyz »

Zum Runterladen gibt es das nicht. Du brauchst SVN (oder FPCUpDeluxe) und etwas Zeit; dazu gab es vor kurzem eine längere Diskussion hier im Forum.

Aber es geht auch ohne Trunk, nur ein klein wenig umständiglicher. Ich habe das Demo Projekt auf v2.0.10 umgeschrieben und hier angehängt. Im Quellcode findest du einige Kommentare zur Erklärung.

Ich setze voraus, dass du weißt, wie man in TAChart mit Series umgeht. Ansonsten verweise ich auf die Tutorial-Reihe im wiki: wiki.lazarus.freepascal.org/TAChart_Tutorial:_Getting_started, von dort findest du auch die weiteren Tutorials. Vielleicht sollte ich auch auf die offizielle Dokumentation verweisen: https://wiki.lazarus.freepascal.org/TAC ... umentation

Johasch
Beiträge: 10
Registriert: Sa 8. Feb 2020, 10:50

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von Johasch »

Ich habe das Demo Projekt auf v2.0.10 umgeschrieben und hier angehängt.
Hast du den Anhang vergessen oder habe ich ihn übersehen?
Auf jeden Fall werde ich jetzt SVN installieren und damit sollte alles gelöst sein.

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

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von wp_xyz »

Mist...
Dateianhänge
polygon_field_2.0.10.zip
(2.88 KiB) 102-mal heruntergeladen

Johasch
Beiträge: 10
Registriert: Sa 8. Feb 2020, 10:50

Re: TAChart: Drucken bzw. exportieren von AfterCustomDrawBackWall Ausgaben

Beitrag von Johasch »

Alles bestens - Danke
Jetzt ist wirklich keine Frage mehr offen :D

Antworten