AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi [FMX] eigene Komponente (Aufbau & Performance)
Thema durchsuchen
Ansicht
Themen-Optionen

[FMX] eigene Komponente (Aufbau & Performance)

Ein Thema von sintronic86 · begonnen am 9. Nov 2016 · letzter Beitrag vom 14. Dez 2016
Antwort Antwort
sintronic86

Registriert seit: 7. Dez 2009
Ort: Barsinghausen
90 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#1

[FMX] eigene Komponente (Aufbau & Performance)

  Alt 9. Nov 2016, 11:39
Hallo,

ich stehe derzeit vor einem Problem, welches mir wirklich Kopfzerbrechen bereitet.

Kurz zur Situation:

Ich will eine Komponente erstellen, welche in der GUI dargestellt wird.
Diese enthält viele Daten, welche angezeigt werden müssen. Bis zum Zeitpunkt der Erstellung steht nicht fest, wie viele das sein werden. Einige der Werte ändern sich im 500ms-Takt.
Es werden bis zu 100 dieser Komponenten gleichzeitig dargestellt werden müssen und werden in einer ScrollBox liegen.


Ich stehe nun vor der Entscheidung, wie ich die Komponente aufbaue:
Nachfahre von TStyledControl -> optischer Aufbau und füttern der Werte in die GUI über die FMX-Styles
Habe ich bereits mal mit angefangen, aber beim Scrollen der ScrollBox war die CPU-Auslastung wirklich hoch
Nachfahre von ??? -> alle Elemente (TText, TLayout, TRectangle, etc.) selbst erstellen und mit Werten füttern
Habe ich hier nicht auch ziemliche Performance-Schwierigkeiten, da super viele Elemente verwaltet werden müssen?
Nachfahre von ??? -> selbszeichnen der Komponente
Da bin ich gerade dran, aber ich habe Schwierigkeiten, durch den Paint-Ablauf durchzublicken. Ich möchte ja schliesslich nicht bei Änderung eines Wertes den gesamten Komponentenbereich neuzeichnen, sondern nur den entsprechenden Bereich. Leider bekomme ich das irgendwie nicht hin
Habt ihr da nützliche Tutorials oder ähnliches, wo ich mich durchwühlen kann?
Welche dieser Varianten würdet ihr wählen, um die Performance der Anwendung möglichst schlank zu halten? Habt ihr evtl. andere Vorschläge?


Ein anderes Problem ist folgendes:

Die Komponenten sollen in einem TFlowLayout (zu XE2-Zeiten war das ein TFlowPanel) liegen und dieses wiederrum in einer TScrollBox.
Leider hat das FlowLayout keine Eigenschaft wie Autosize! Damit die ScrollBox ihre ScrollBars entsprechend dem Inhalt anpasst, müssen sich die Abmessungen des FlowLayouts ja seinen Childs anpassen. Leider gibt es scheinbar keine passende Eigenschaft dafür.




Ich danke euch schonmal im Vorraus!
Björn
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.158 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: [FMX] eigene Komponente (Aufbau & Performance)

  Alt 9. Nov 2016, 11:55
Nur kurz zu:

Leider hat das FlowLayout keine Eigenschaft wie Autosize! Damit die ScrollBox ihre ScrollBars entsprechend dem Inhalt anpasst, müssen sich die Abmessungen des FlowLayouts ja seinen Childs anpassen. Leider gibt es scheinbar keine passende Eigenschaft dafür.
Warum es für so etwas grundlegendes nichts gibt ist mir auch rätselhaft. Schau mal hier, etwas besseres ist mir bislang auch nicht eingefallen:
http://www.delphipraxis.net/190153-p...ml#post1346910
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#3

AW: [FMX] eigene Komponente (Aufbau & Performance)

  Alt 9. Nov 2016, 12:03
Diese enthält viele Daten, welche angezeigt werden müssen. Bis zum Zeitpunkt der Erstellung steht nicht fest, wie viele das sein werden. Einige der Werte ändern sich im 500ms-Takt.
Es werden bis zu 100 dieser Komponenten gleichzeitig dargestellt werden müssen und werden in einer ScrollBox liegen.
Von den FMX Klassen habe ich keine Ahnung, aber das was du beschreibst schreit doch gradezu nach dem Virtual-Data Prinzip. Ist dir die VirtualTreeView Komponente ein Begriff?

Da die Komponenten in einer ScrollBox liegen, impliziere ich, dass immer nur ein Paar davon gleichzeitig auf dem Bildschirm zu sehen sind. Wie wäre es denn, wenn deine Komponente die Möglichkeit hätte, mehrere Datensätze untereinander anzeigen zu lassen und selbstständig zu scrollen? Dann könntest du nämlich hingehen und immer nur die jeweils sichtbaren Datensätze rendern und bräuchtest auch nur noch eine Instanz mit 100 Datensätzen statt 100 Instanzen mit einem Datensatz.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: [FMX] eigene Komponente (Aufbau & Performance)

  Alt 9. Nov 2016, 12:25
Sollen diese Controls eine Interaktion ermöglichen oder nur Werte darstellen?
Im Sinne Gauge, Oszillator oder Panel mit Zahlenwerten?

Wenn keine Interaktion erforderlich und Performance wichtig ist würde ich - gerade unter FMX - alles selbst regeln.
Also auch die Scrollbar und den sichtbaren Ausschnitt selbst berechnen und nur in diesem "virtuellen" Bereich eine Grafik berechnen und zeichnen.
Je nach Ausgangslage immer den gesamten sichtbaren Bereich oder auch nur den jeweiligen grafischen Bereich des Controls, in dem Daten geändert wurden.

Wenn eine Interaktion erforderlich ist wäre die Verwendung von FMX-Controls evtl. einfacher. Aber mit einer hohen Performance wird das vermutlich schwer zu vereinbaren sein.
Es kann sogar sein, dass das heute vielleicht ganz gut klappt und mit den nächsten Updates wird wieder etwas geändert, so dass Dein Konzept dann nicht mehr performant läuft.

Also ich würde in einem solchen Fall so viel wie möglich selbst regeln und zeichnen.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
sintronic86

Registriert seit: 7. Dez 2009
Ort: Barsinghausen
90 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#5

AW: [FMX] eigene Komponente (Aufbau & Performance)

  Alt 9. Nov 2016, 16:13
Zunächst mal danke für eure schnellen und interessanten Antworten!


Zitat von Der schöne Günther:
Warum es für so etwas grundlegendes nichts gibt ist mir auch rätselhaft. Schau mal hier, etwas besseres ist mir bislang auch nicht eingefallen:
http://www.delphipraxis.net/190153-p...ml#post1346910
Ist mir auch schleierhaft. Ich nutze solche Funktionalitäten häufig. Ich werde mir dein Beispiel bei Zeiten mal ansehen!


Zitat von Zacherl:
...Wie wäre es denn, wenn deine Komponente die Möglichkeit hätte, mehrere Datensätze untereinander anzeigen zu lassen und selbstständig zu scrollen?...
Leider geht das so nicht. Es ist definitiv gewollt, das eine Komponente nur einen Datensatz anzeigen soll. Vor Allem, da keine "Zeilen-Ansicht" vorgesehen ist und sie nicht nur untereinander, sondern auch nebeneinander stehen sollen (TFlowLayout).


Zitat von stahli:
Sollen diese Controls eine Interaktion ermöglichen oder nur Werte darstellen?
Die bisherige Planung sieht auch Interaktionen vor. So sollen z.B. 1-2 TCheckBoxes drauf.
Zitat von stahli:
... Also auch die Scrollbar und den sichtbaren Ausschnitt selbst berechnen und nur in diesem "virtuellen" Bereich eine Grafik berechnen und zeichnen.
Das Scrollen sollte schon automatisiert vom Parent (TScrollBox) übernommen werden. Ob eine Komponente gezeichnet werden muss oder nicht, weiß sie ja im Prinzip selber.
Zitat von stahli:
Je nach Ausgangslage immer den gesamten sichtbaren Bereich oder auch nur den jeweiligen grafischen Bereich des Controls, in dem Daten geändert wurden.
Und genau da sehe ich aktuell nicht durch.
Überschreibe ich die "Paint"-Procedure, könnte ich auf dem eigenen Canvas zeichnen. Wie kann ich an dieser Stelle aber ermitteln, welcher Bereich überhaupt neugezeichnet werden muss? Um zu wissen, das der Bereich eines bestimmten Wertes erneuert werden muss, brauche ich ja lediglich auf Änderung der Werte zu reagieren. Aber wie bekomme ich die anderen Bereich raus? Bspw. wenn ein anderes Fenster meine Komponente ganz oder teilweise überdeckt hat?

Habe ich denn eine Möglichkeit, das Paint nicht direkt anzufassen, sondern gesondert auf die Zeichenfläche zu zeichnen und der Komponente selbst das Aktualisieren zu überlassen?
Björn
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: [FMX] eigene Komponente (Aufbau & Performance)

  Alt 9. Nov 2016, 16:50
Wenn Du viele eigene Controls erstellst (ähnlich Panel), die sich selbst zeichnen, dann musst Du FMX vertrauen, dass es die Neuzeichenaktionen sinnvoll veranlasst.

Bei meinen frühen Versuchen mit XE3 habe ich da allerdings regelrechte Neuzeichnen-Orgien festgestellt. Jede Änderung wurde MINDESTENS 3 mal neu gezeichnet und es wurden bei jeder Aktionen auch jede Menge abhängige Controls auch wieder neu gezeichnet.
Also da es Dir auf Performance ankommt würde ich an Deiner Stelle

-> ein großes Control erstellen, das eine Zeichenfläche und eine (auch selbst gezeichnete) Scrollbar enthält.

Dann platzierst Du virtuell mehrere Sichten für auf Deine Daten-Objekte und lässt diese sich in ihren Bereich zeichnen.
Klickt der User in den Bereich der Komponente, wo die Scrollbar gemalt ist, berechnest Du einen neuen sichtbaren Bereich, malst den Slider an die neue Position und lässt jetzt die Controls 100-120 aus den insgesamt 500 Datenobjekten in die Zeichenfläche malen.

Das macht mehr Aufwand, aber so kannst Du es m.E. am besten optimieren.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
mensch72

Registriert seit: 6. Feb 2008
838 Beiträge
 
#7

AW: [FMX] eigene Komponente (Aufbau & Performance)

  Alt 10. Nov 2016, 03:51
solltest du das "TMS FMX (UI) ComponentPack" haben, wäre dort wohl die "TTMSFMXTileList" das was du suchst...

Ist zwar eigentlich für "Kacheldesign", macht aber um die Ecke gedacht genau das was du willst:
- kann beliebig viele verschieden große Objekte automatisch angeordnet anzeigen
- hat Events "pro Element" zum Selbstzeichen oder zuweisen der Daten
- TMS zeichnet/aktualisiert nur das was gerade angezeigt wird

Für einen schnellen Versuch, würde ich mir selbst pro Objekt intern ein Bild rendern(Größe&Inhalt ergo 100% frei und selbst bestimmt).
Wenn sich was ändert, dann zunächst wieder selbst für das Objekt das Bild intern neu rendern, dann schaun ob es "sichtbar" ist und ein Invalidate auf dessen Position absetzen, alle "Kacheln" stellen per Eventfunktion nur ihr zugehöriges Objektbild dar... fertig.
(Neuzeichnen bei/nach Fremdüberdeckung klappen so auch automatisch, weil das die GUI das Invalidate der betreffenden Regionen selbst auslöst)

Wenn kein TTMSFMXTileList oder das (noch)nicht passt, dann würde ich von dort nur den internen AutoLayoutTeil nutzen mir darauf meine Komponente mit einem "EventsPerObject" Konzept und eigener/virtueller Datenhaltung möglichst über "Pufferbilder" realisieren. Solange der Speicher für die Bilder reicht, ist das (Zeit)Verhalten der GUI somit unabhängig von der Anzahl der Objekte und der Ursache der Aktualisierung immer deterministisch und 100% "non Blocked" wenn man das rendern der Pufferbilder sauber asyncron mit Threads ausserhalb des GUI/Main Treads realisiert.
  Mit Zitat antworten Zitat
sintronic86

Registriert seit: 7. Dez 2009
Ort: Barsinghausen
90 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#8

AW: [FMX] eigene Komponente (Aufbau & Performance)

  Alt 14. Dez 2016, 09:29
Hallo Leute,

bitte entschuldigt die verspätete Antwort.

Ich habe jetzt für alle meine genannten Probleme eine Lösung gefunden und möchte euch und natürlich auch allen, die evtl. nach mir an diesem Thema interessiert sind, meine Ergebnisse nicht vorenthalten.

1. Zu der Autosize-Property des TFlowLayouts:
Ich habe einen Nachfahren von TFlowLayout erstellt und diesem die gewünschte Property einfach selbst mitgegeben. Natürlich muss ich dann alle proceduren überschreiben, in denen eine Neuberechnung der Größe erforderlich ist:
- DoAddObject() - sobald dem FlowLayout ein Object hinzugefügt wird
- DoRemoveObject() - sobald vom FlowLayout ein Object entfernt wird
- Resize() - sobald sich die Größe des FlowLayouts ändert (bspw. durch vergrößern der Form, etc.)
Code:
  TFlowLayout = class(FMX.Layouts.TFlowLayout)
    private
      FAutoSize          : Boolean;
      FIsInAddRemoveObject: Boolean;

      procedure SetAutoSize(AAutoSize: Boolean);
    protected
      procedure DoAddObject(const AObject: TFmxObject); override;
      procedure DoAutoSize; virtual;
      procedure DoRemoveObject(const AObject: TFmxObject); override;
      procedure Resize; override;
    public
      constructor Create(AOwner: TComponent); override;

      property AutoSize: Boolean read FAutoSize write SetAutoSize;
  end;
Bisher funktioniert das wirklich einwandfrei!


2. Zu der Problematik meiner Komponente:
Ich habe jetzt eine wirklich performante Lösung gefunden, mit der ich mehr als zufrieden bin.
Meine Komponente ist ein Nachfahre von TControl. Und das ist auch das einzige Object, welches pro Komponente verwaltet werden muss, da ich die Oberfläche vollständig selbst zeichne.
Ich hatte anfänglich ja einige Schwierigkeiten mit der Paint-Procedure und den Entscheidungen, was genau neugezeichnet werden muss. Als ich mir dann aber das TImage genauer angesehen habe, habe ich festgestellt, dass dieses bei jedem Aufruf der Paint-Methode IMMER seinen GESAMTEN Bereich erneuert.
Es läuft jetzt folgendermaßen ab:
- Die Komponente hält ein "temporäres" TBitmap vor, auf das alle Sachen gezeichnet werden. Da dieses ja nicht sichtbar ist, gehen die Zeichen-Routinen sehr schnell von der Hand.
- Es gibt auslösende Events (mal von außen, mal von innen), die der Komponente mitteilen, das zumindest ein gewisser Bereich neugezeichnet werden muss
- Diese "Änderung" speichere ich innerhalb der Komponente und sage dann
Code:
Self.InvalidateRect(<< ein RectF >>);
Dadurch weiß die Komponente, dass sie neugezeichnet werden muss, wenn es soweit ist.
- Das daraus resultierende Aufrufen der "Paint"-Methode erfolgt erst dann, wenn die Komponente tatsächlich im sichtbaren Bereich ist. Das wird vom Framework gesteuert, muss ich mich also nicht drum kümmern.
- In der Paint-Methode gehe ich dann meine Änderungen durch, zeichne die entsprechenden Bereiche auf mein temporäres TBitmap und kopiere dieses am Ende IMMER auf das Canvas meiner Komponente.

So habe ich jetzt mal einen kleinen Test gemacht:
1000 * meine Komponente (wobei aus Platzgründen nur ca. 70 zeitgleich zu sehen sind) => neuzeichnen eines Bereichs alle 200ms => und es läuft, und es läuft, und es läuft....



Ich hoffe, ich konnte verständlich erklären, wie ich das jetzt gelöst habe. Wenn nicht, fragt einfach!
Und nochmal vielen Dank an alle, die sich die Mühe gemacht haben, mir Ideen zu liefern.
Björn
  Mit Zitat antworten Zitat
Antwort Antwort

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:35 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz