TLineSeries zur Laufzeit mit UserDefinesChartSource erstelle

Für Fragen von Einsteigern und Programmieranfängern...
ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

TLineSeries zur Laufzeit mit UserDefinesChartSource erstelle

Beitrag von ErnstVolker »

Guten Abend,

ich habe ein Problem mit einer zur Laufzeit erzeugten TLineSeries.

Code: Alles auswählen

function TFrm_GUI.ErzeugeSerie(AChart: TAGraph.TChart): TASeries.TLineSeries;
begin
  Result := TLineSeries.Create(AChart.Owner);
  with TLineSeries(Result) do
  begin
    Title := tb_Titel.Text;
    ShowPoints := True;
    ShowLines := True;
    LinePen.Style := psSolid;
    SeriesColor := btn_Farbe.ButtonColor;
    Pointer.Brush.Color := SeriesColor;
    Pointer.Pen.Color := SeriesColor;
    OnCustomDrawPointer := @MeineKurvePointerHandler;
    Source := @Frm_WZ.MeineChartQuelleGetChartDataItem;
  end;
  AChart.AddSeries(Result);
end;


Ich wollte an "Source" das Ergebnis der Procedur von ......GetChartDataItem übergeben. Es kommt aber folgende Fehlermeldung:

wegzeitgui.pas(128,55) Error: Incompatible type for arg no. 1: Got "<procedure variable type of procedure(TUserDefinedChartSource;LongInt;var TChartDataItem) of object;Register>", expected "TCustomChartSource"

Wie bekommt man ein Typecasting der procedure zu TCustomChartSource? Funktioniert das überhaupt was ich da vorhabe?

Ach noch eine Kleinigkeit am Rande:

Wenn ich auf meinem Mac unter Windows (in Parallels) in der Lazarus IDE ein @ erzeugen will, dann erscheint ein Fenster mit Bezeichnung "Lokale Variablen".

Vielen Dank für Eure Hilfe.

Volker

sstvmaster
Beiträge: 575
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.6
CPU-Target: 32+64bit
Wohnort: Dresden

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von sstvmaster »

ErnstVolker hat geschrieben:Wenn ich auf meinem Mac unter Windows (in Parallels) in der Lazarus IDE ein @ erzeugen will, dann erscheint ein Fenster mit Bezeichnung "Lokale Variablen".


Das wäre eigentlich die Tastenkombination "Strg + Alt + L" unter Windows, "crtl + alt + L Mac. Beim Mac für @ nur alt + L
LG Maik

Windows 10,
- Lazarus 2.2.6 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.7 (fixes) + fpc 3.3.1 (main/trunk)

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

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von wp_xyz »

Nein, an LineSeries.Source musst du die UserDefineChartSource übergeben, wahrscheinlich heißt sie bei dir "MeineChartQuelle". Die UserDefinedChartSource benötigt auch einen Eventhandler für OnGetChartData, in dem die Daten an die Source übergeben werden. Außerdem muss der UserDefinedChartSource mitgeteilt werden, wieviele Datenpunkte übertrangen werden sollen (PointsNumber). Bevor ich das alles ausformuliere, ein kleines Beispiel im Anhang; dabei werden sowohl UserDefinedChartSource als auch LineSeries zur Laufzeit erzeugt.
Dateianhänge
runtime_userdefchartsource_lineseries.zip
(2.37 KiB) 118-mal heruntergeladen

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von ErnstVolker »

Hallo und guten Morgen,

vielen Dank für Eure Hilfe.

Bei der Tastenkombination sollte alt + L den @ erzeugen. Tut es auch hier beim Antwort schreiben. In der IDE muß ich control + command + alt + L drücken. Hab' ich gerade herausgefunden. Dann kommt das @-Zeichen. Ansonsten nur das Variablen-Fenster.

Das Beispiel aus dem Anhang habe ich mir gerade angesehen. Ausprobieren werde ich es heute Abend. Muß jetzt (leider) zur Arbeit.

Vielen Dank und viele Grüße

Volker

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von ErnstVolker »

Hallo,

habe das Beispiel ausgetestet und auch, ich denke ansatzweise verstanden. Ich habe es mit dem Code aus dem Kurve verschieben kombiniert. Das funktioniert auch für eine Kurve. Wie kann man mehrere Kurven darstellen, mit der Maus anwählen und dann über das Chart schieben?

Ich habe die Vorstellung, dass man die x und y Werte einer Kurve im Chart auslesen und als "Data" in GetChartDatahandler verwenden muß.

Chart.Series.Item[integer].wie kommt man an die x und y Werte einer bereits gezeichneten Kurve?

Oder braucht jede zur Laufzeit erzeugte TLineSeries eine eigene Chart Source?

Vielen Dank und viele Grüße

Volker

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

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von wp_xyz »

Ja, für jede Series braucht man in der Regel eine eigene ChartSource (es sei denn, man möchte dieselben Daten mit verschiedenen Series-Typen anzeigen). Entweder man fügt die Datenpunkte per AddXY (z.B.) der Series hinzu, dann sind sie in einer internen ListchartSource gespeichert, oder du klickst explizit eine eigene ChartSource aufs Formular und verbindest sie mit der Eigenschaft Source der Series, wobei verschiedene Arten von Chartsources zur Verfügung stehen. Du hast dich für die TUserDefinedChartSource entschieden, die ein Interface zwischen dem eigentlichen Speicherort der Daten (z.B. ein Array) und der Series darstellt; die Datenabfrage erfolgt in dem schon erwähnten Event OnGetChartDataItem.

Bei zwei Series ist es genauso; setze einfach eine weitere UserDefinedChartSource aufs Formular, verbinde sie mit der entsprechenden Series und verwende einen weiteren OnGetChartDataItem-Handler, um die zweiten Daten, die ja irgendwo gespeichert sein müssen, in die Series zu kriegen.

Das Verschieben einer Series mit der Maus ist in dem Demoprogramm ChartMover gezeigt, auf das du schon hingewiesen hast. es kommt darin alles vor, was du brauchst, um die geklickte Series zu erkennen, und auch wie man Koordinaten zwischen Pixeln und "Welt" umrechnet.

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von ErnstVolker »

Kann man nicht die Kurven mittels Addxy erzeugen, in der ListChartSource speichern, und durch Markieren einer von mehreren Kurven in einer TChartListbox die zugehörigen Daten aus der ListChartSource an den GetChartDataItem-Handler übergeben und den Offsett der x- und y- Verschiebeung addieren?

Die Kopplung (durch markieren einer Series-Legende in der TChartListbox) an den "CurveMover" (TDataPointHintTool) habe ich mit AffectedSeries des TDataPointHintTool bewerksteligt.
Die beiden Prozeduren in "OnAfterMouseDown" und "OnAfterMouseMove" liefern dann den XOffset und YOffset des Mausschiebens auf dem Chart.

Bei "OnAfterMouseDown" macht man die erste Zeile zu: if TDataPointTool(ATool).Series = (ChartLineSeries der Legende) then usw.

Jetzt müssen die "Offsets" auf die Ursprungsdaten in der ListChartSource.
Die UserDefinedChartSource löst das, dass sich die Grunddaten der Kurve von vornherein innerhalb des GetChartDataItem-Handlers befinden.

Ich kann natürlich 10 (sollten ausreichend sein) TLineSeries mit zugehörigen 10 TUserDefinedChartSource auf dem Diagramm platzieren. So viele Kurven wird man gleichzeitig sowieso nicht Darstellen wollen, da das Ganze vermutlich unübersichtlich wird.

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

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von wp_xyz »

Tut mir leid, ich verstehe das nicht. Du redest von ListChartsource, dann wieder von OnChartDataItem-Event und UserdefinedChartSource. Was jetzt? Eine Series braucht nur eine.

Was nochmal ist genau das Problem? Du willst mit der Maus eine Series verschieben? In alle Richtungen, oder nur horizontal oder vertikal? Die Verschiebung soll in den Daten veranket werden, nicht so wie in dem CurveMover-Beispiel temporär angewendet werden? Wie liegen die Daten vor, in einem Array oder per AddXY der Series hinzugefügt? Der Hintergrund dieser Frage ist, ob man eine UserdefinedChartSource braucht oder nicht? Wieso 10 Kurven? Die zu verschiebende Series soll in einer ChartListbox ausgewählt werden? Warum nicht gleich diejenige, die mit der Maus angeklickt worden ist?

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von ErnstVolker »

Ja, zugegeben etwas verwirrend.

Mit ListChartSource meinte ich die interne Chart-ListSource, in welcher die Werte mit Addxy() gespeichert werden. Das sind die, welche ich für das Verschieben der Kurve (in x und y oder nur x oder nur y) eigentlich manipulieren (XOffset und YOffset dazurechnen) möchte. In Deinem Beispiel des ChartMover machst Du es aber nicht über die interne Source, sondern über eine UserDefinedChartSource. Innerhalb des OnChartDataItem-Event werden die jeweiligen Offsets zu den Grunddaten addiert. OnChartDataItem gehört doch zur UserDefinedChartSource, dachte ich.

Ich dachte es reicht ein TDataPointHint-Tool (also der CurveMover) um die Kurven bewegen zu können. Man wähle über eine Legenden-Listbox die Kurve aus die man verschieben möchte. Das mit der Listbox ist ein Sicherheitsaspekt, damit man nur die Kurve schiebt, die auch verschoben werden soll. Wenn Kurven im Schnittpunkt schon fast richtig sind und man evtl. doch noch mal nachfasst, dann muß vermieden werden, dass man die Falsche erwischt. Man kann vermutlich auch vorm Schieben (beim Markieren) den Style der Kurve auf gestrichelt oder so ändern, damit das Auge des Betrachters erkennt ob er die Richtige erwischt hat.

Ich habe jetzt zu jeder TLineSeries eine UserDefinedCharSource und ein TDataPointHint-Tool erzeugt und miteinander verknüpft. Das Ganze für 10 Kurven. Es reichen vielleicht auch 6 oder 8.

Zusätzlich sollen die "Kurven" die sich als Geraden darstellen (Wegstrecke bei konstanter Geschwindigkeit) an den Endpunkten proportional der Steigung verlängert werden können.

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

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von wp_xyz »

Eine Userdefinedchartsource brauchst du gar nicht, es geht alles mit der eingebauten ListChartSource. Auch ist es nicht nötig, Kurven vorzudefinieren. Schau dir das beigefügte Beispiel an.
Dateianhänge
ChartMover_ListChartSource.zip
(3.73 KiB) 99-mal heruntergeladen

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von ErnstVolker »

Das Beispiel ist klasse. Ich habe es auf mein Chart übertragen.
Es funktioniert leider nur bedingt. Bei zwei vordefinierten Kurven bekomme ich einen RunError(219) an der Stelle: ser := TDataPointTool(ATool).Series as TChartSeries;

Mit zwei zur Laufzeit erzeugten Kurven ist das Bewegen leider nicht flüssig. Auch das Umschalten auf die dicke Linie funktioniert nicht immer.
Liegt wahrscheinlich an der Größe der Series. Über 3000 Einträge.

Vielen Dank.

Du kennst scheinbar die TChart-Komponente in- und auswendig :-)

Viele Grüße
Volker

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von ErnstVolker »

Ich habe mir Dein Beispiel nochmal vorgenommen und analog zu x die y-Verschiebung eingebaut.

Code: Alles auswählen

dy := Chart1.XImageTograph(APoint.Y) - Chart1.XImageToGraph(FMouseDownPt.Y);


und dann try-finally:

Code: Alles auswählen

  try
    for i:=0 to ser.Count-1 do begin
      ser.XValue[i] := ser.XValue[i] + dx;
      ser.YValue[i] := ser.YValue[i] + dy;
    end;
  finally
    ser.ListSource.EndUpdate;
  end;


Hierbei ist festzustellen, dass bei Mausbewegung nach oben sich die Kurve nach unten bewegt und umgekehrt. Subtrahiere ich dy bewegt sich auch die Kurve entsprechend der Mausbewegung.
Weiterhin stelle ich fest, dass die Kurven (alle) beim schieben entlang der x-Achse besser am Mauszeiger hängen und diesem folgen. Beim schieben in Richtung y geht der Mauszeiger von der Kurve weg und diese folgt langsamer.

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

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von wp_xyz »

ErnstVolker hat geschrieben:... bekomme ich einen RunError(219) an der Stelle: ser := TDataPointTool(ATool).Series as TChartSeries;

219 heißt, dass die Series, auf der mit dem Tool geklickt wurde, keine TChartSeries ist. Schau dir auf http://wiki.lazarus.freepascal.org/TACh ... ion#Series, die Abstammungshierarchie der verschiedenen Seriesklassen an. Welcher Typ ist die Series, bei der es kracht? Evtl. eine TFuncSeries? Wenn ja, dann musst du die Verschiebung im Funktionsausdruck unterbringen, denn nur Nachfahren von TChartSeries haben eine interne ListChartSource für die Daten.

ErnstVolker hat geschrieben:Mit zwei zur Laufzeit erzeugten Kurven ist das Bewegen leider nicht flüssig. Auch das Umschalten auf die dicke Linie funktioniert nicht immer.
Liegt wahrscheinlich an der Größe der Series. Über 3000 Einträge.

Dazu fällt mir noch ein, dass ich das Aufaddieren des Verschiebungsvektors zwar lesbar, aber nicht laufzeit-optimiert gemacht habe, denn der Setter von TChartSeries.XValue[..] wirft die ganze Nachrichtenkette an, schneller geht es, die Änderung direkt in die ChartSource zu schreiben. Ersetze in der i-Schleife von CurveMoverToolAfterMouseMove die Anweisung

Code: Alles auswählen

      ser.XValue[i] := ser.XValue[i] + dx; 
durch

Code: Alles auswählen

      ser.Source.Item[i]^.X := ser.Source.Item[i]^.X + dx;

Damit kann auch ich in dem Demprogramm jede Kurve praktisch beliebig schnell hin- und herschieben, ohne dass etw ruckelig wird - vorher blieb die Kurve gelegentlich hängen und hat sich von der Maus gelöst.

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von ErnstVolker »

Hab' ich geändert. Erzeugt bei mir immer noch irgendwie unkontrollierte Sprünge und bei Deiner Demo ändert es nichts am gegenläufigen Verhalten der y-Richtung bzw. am weglaufen des Mauszeigers von der Kurve.

Gehe jetzt aber in die Falle. Morgen Abend (vielleicht auch kurz in der Früh') geht's weiter.

Vielen Dank und gute Nacht.

Volker

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

Re: TLineSeries zur Laufzeit mit UserDefinesChartSource erst

Beitrag von wp_xyz »

ErnstVolker hat geschrieben:Ich habe mir Dein Beispiel nochmal vorgenommen und analog zu x die y-Verschiebung eingebaut.

Code: Alles auswählen

dy := Chart1.XImageTograph(APoint.Y) - Chart1.XImageToGraph(FMouseDownPt.Y);

Das ist so nicht richtig, denn du verwendest die Skalierungsfaktoren der x-Achse (XImageToGraph) um einen y-Wert von Pixel- auf Graph-Koordinaten umzurechnen. Nimm YImageToGraph, dann sollte die Kurve genau der Maus folgen.

Antworten