![]() |
"Unendlicher Progressbar"
Hallo,
wie bekommt man diesen "unendlichen Progressbar", so wie Windows, der immer wieder durchläuft? Eigentlich hasse isch das Ding, habe aber eine Anwendung wo die verbleibende Restzeit nicht abschätzbar ist und wollte dem Anwender damit signalisieren dass da noch was im Hintergrund werkelt... Ciao Stefan |
AW: "Unendlicher Progressbar"
Hallo,
schau dir mal die "TProgressBar.Style Property" an. |
AW: "Unendlicher Progressbar"
Danke!
Ich schäme mich in Grund und Boden :-) Ciao Stefan |
AW: "Unendlicher Progressbar"
Ich muss da aber noch mal nachfragen:
Ich mache im Programmablauf die Progressbar visible damit der Anwender sieht dass da im Hintergrund etwas geladen wird, nach dem Ladevorgang wird sie wieder versteckt... Nur leider bewegt sich die Progressbar nicht... Ich bin eigentlich kein Freund von Application.ProcessMessages, geht das auch anders? Ciao Stefan |
AW: "Unendlicher Progressbar"
Korrekter Weise kannst Du die langwierige Aufgabe nur in einen Thread auslagern und die Darstellung durch eine Schleife im Hauptthread aktualisieren.
Mit Application.ProcessMessages kannst Du den Effekt zwar auch erreichen, aber das ist eher unsauber und kann u.U. zu Problemen führen. |
AW: "Unendlicher Progressbar"
Die langwierige Aufgabe ist die Erzeugung von Komponenten und das Laden von denen mit Inhalt.
Irgendwie sehe ich da Probleme das in einen Thread auszulagern? Da müsste der Thread ja im MainForm Konmponenten erzeugen und füllen, macht man so etwas? Ciao Stefan |
AW: "Unendlicher Progressbar"
Wenn man im Hautthread arbeiten muß,
dann muß man eben selber regelmäßig für eine Aktualisierung der GUI sorgen, da sie währeddessen ja keine Zeit bekommt. * Form.Repaint bzw. Komponente.Repaint (Achtung, Windows sieht hier dennoch die Form nach einer Weile als "hängend" an) * Application.ProgressMessages; (aufpassen, da hier alle Messages verarbeitet werden ... nicht dass da zwischendrin "unpassende" Aktionen starten) * * z.B. Nutzer klickt nochmal auf den Button (dann läuft das mehrmals ineinanderverschachtelt) * wir haben und mal ein eigenes "ProgressMessages" gebaut, was nur gewisse Messages ausführt (Windows-HangUp-Erkennung, Zeichenaktionen usw., aber keine Timer-, Maus oder Tastaturereignisse) * viele machen das in einer Schleife alle X Durchläufe (unschön, da PCs nicht immer gleich schnell sind) * ich mach es zeitabhängig, also in der schleife prüfen wann das letzte Mal und dann alle X Millisekunden (z.B. 100/200/250/500/1000ms ... je nach Anwendungsfall) OK, das Erstellen/Freigeben von Komponenten muß in dem Thread erfolgen, wo die Form erstellt wurde und behandelt wird. Füllen kann man manche Komponenten aber auch im Thread. * oftmals gibt es eine BeginUpdate-Methode, welche die Komponente von der automatischen Aktualisierung sperrt * und wenn beim Update des Inhaltes nicht auf globale Objekte zugegriffen wird, die noch von anderswo in Verwendung sind, * * dann kann man BeginUpdate und EndUpdate im Hauptthread ausführen und den Rest dazwischen auslagern |
AW: "Unendlicher Progressbar"
Ok, das wird in einem Thread schlecht gehen (außer Daten zu sammeln und zu puffern, um die Controls dann in einem Rutsch erzeugen zu können).
Um welche Zeitspannen handelt es sich denn? Ggf. wäre einfach ein statischer Splashscreen sinnvoller, der auf die mögliche Zeitspanne hinweist. Dann müssen keine Klimmzüge gemacht werden, damit ein grüner Balken hin und her zuckelt, was ohnehin nicht wirklich hilfreich ist. Welche Workarounds man gehen könnte, hängt sicherlich von den genauen Umständen ab. |
AW: "Unendlicher Progressbar"
Selbst mit Application.ProcessMessages wird der Progressbar merklich ruckeln, wenn man das nicht weniger als alle 50 ms macht. Etwas genügsamer ist ein TActivityIndicator, aber auch der kommt nicht ohne Application.ProcessMessages aus.
Zitat:
|
AW: "Unendlicher Progressbar"
Zitat:
![]() |
AW: "Unendlicher Progressbar"
Sowohl die TProgressBar im Marquee-Mode als auch der TActivityInidicator arbeiten timer-basiert. Simples Invalidate/Update/Repaint funktioniert da leider nicht.
Ich plädiere für ein simples Bitte warten... Fenster oder ein Splash-Form ohne viel Drumherum. |
AW: "Unendlicher Progressbar"
|
AW: "Unendlicher Progressbar"
Zitat:
|
AW: "Unendlicher Progressbar"
Man kann auf dem Splash-Screen ja auch den aktuellen Bearbeitungsfortschritt als Text in einem Label darstellen. Das reagiert dann in der Regel auch auf ein Refresh.
|
AW: "Unendlicher Progressbar"
Ich glaube das letzte ist der richtige Ansatz, da das Label ja sofort wenn gewünscht neuzeichnet könnte man da mit
Bitte warten . Bitte warten .. Bitte warten ... Bitte warten . Bitte warten .. Bitte warten ... dessen Caption per Thread aktualisieren? Ciao Stefan |
AW: "Unendlicher Progressbar"
Ja. Und wenn du informativere Infos ausgibst, wird es noch schöner:
. . . Labels erzeugt Daten der Labels geladen Edits erzeugt Daten der Edits geladen Grids erzeugt Daten der Grids geladen . . . etc. |
AW: "Unendlicher Progressbar"
Als Beispiel könnte der Splash-Screen der IDE dienen, die ja auch im unteren Bereich ihre Aktivitäten mitteilt.
|
AW: "Unendlicher Progressbar"
Ich habe das nun versucht, umzusetzen, aber irgendwie wird der Thread (und dessen Labelaktualisierung) vom HauptThread und der dort stattfindenden Control-Erzeugung blockiert...
Erst wenn im Mainthread alles abgelaufen ist, wird das Label im Thread aktualisiert! Irgendwas läuft da schief! Ciao Stefan |
AW: "Unendlicher Progressbar"
Zeig Mal...:P
|
AW: "Unendlicher Progressbar"
Hier der Thread:
Delphi-Quellcode:
Den starte ich jetzt testweise bereits im MainForm.Create.
unit ThreadProgress;
interface uses Classes, Sysutils, VCL.Dialogs, VCL.Forms, VCL.ComCtrls, VCL.ExtCtrls, VCL.Controls; var ShowProgress: boolean; type TProgressThread = class(TThread) constructor Create; destructor Destroy; override; private SplashForm: TForm; ProgressRand: TPanel; sb, sh: integer; step: integer; protected procedure Execute(); override; public end; var Thread_Progress: TProgressThread; implementation constructor TProgressThread.Create; begin FreeOnTerminate := True; // Suspended starten inherited Create(True); ShowProgress := True; SplashForm := TForm.Create(nil); sb := 200; sh := 50; step := 0; SplashForm.Width := sb; SplashForm.Height := sh; SplashForm.BorderStyle := bsNone; ProgressRand := TPanel.Create(SplashForm); ProgressRand.Margins.Left := 5; ProgressRand.Margins.Top := 5; ProgressRand.Margins.Right := 5; ProgressRand.Margins.Bottom := 5; ProgressRand.AlignWithMargins := True; ProgressRand.Parent := SplashForm; ProgressRand.Caption := 'Bitte warten'; ProgressRand.ShowCaption := True; ProgressRand.Align := alClient; ProgressRand.BorderStyle := bsSingle; ProgressRand.BevelOuter := bvNone; ProgressRand.StyleElements := []; SplashForm.Position := poMainFormCenter; SplashForm.Visible := True; SplashForm.BringToFront; end; destructor TProgressThread.Destroy; begin // globale Variable rücksetzen Thread_Progress := nil; inherited; end; procedure TProgressThread.Execute; begin while ShowProgress do begin SplashForm.BringToFront; inc(step); if step = 5 then step := 0; ProgressRand.Caption := 'Bitte warten' + StringOfChar('.', step); Application.ProcessMessages; sleep(500); end; end; end.
Delphi-Quellcode:
Das Fensterchen erscheint und die Caption läuft wie gewünscht.Thread_Progress := TProgressThread.Create; Thread_Progress.Start; Wird nun im Hauptprogramm der zeitintensive Erzeugungsteil gestartet, bleibt der Splashscreen stehen! Was habe ich übersehen? Ciao Stefan |
AW: "Unendlicher Progressbar"
KEINE VCL im Thread benutzen.
Wenn, dann als NonVCL direkt mit ![]() |
AW: "Unendlicher Progressbar"
Du darfst aus dem Thread nicht unsynchronsiert auf das Formular zugreifen.
Du könntest allerdings einen Dialog hiermit verwenden: ![]() Der funktioniert im Thread unabhängig von anderen Threads. An solch einer Komponentensammlung scheint nicht wirklich Interesse zu bestehen, so dass ich diese auch nicht mehr weiterentwickelt habe, zumindest öffentlich. Aber das Prinzip funktioniert. |
AW: "Unendlicher Progressbar"
Zitat:
Besser wäre es, den SplashScreen im HauptThread als erstes zu erzeugen und anzuzeigen und regelmäßig bei jeder abgeschlossenen Aufgabe den Texteintrag zu aktualisieren. Threads würde ich da komplett außen vor lassen. |
AW: "Unendlicher Progressbar"
Wenn ich das richtig verstanden habe, dann ist doch der MainThread ein VCL-Thread, der pausenlos läuft und auf Nachrichten wartet. Wenn ich jetzt alle Arbeiten in separate Threads verlege, dann müsste doch der MainThread frei sein und alle Anzeigen verzögerungsfrei und nicht blockierend verarbeiten. Es müsste also theoretisch alles ganz einfach sein. Tatsächlich klappt das auch, aber schwierig wird es, wenn man Application.ProcessMessages vermeiden will. Ich bringe ohne z.B. einen Timer unter AsyncCalls nicht zum Laufen. Und die Einarbeitung in die OTL ist schwieriger als gedacht, und bisher ist AsyncCalls auch schneller.
|
AW: "Unendlicher Progressbar"
Zitat:
|
AW: "Unendlicher Progressbar"
So, nun habe ich einen Versuch ohne Thread gestartet:
Soll der Splashscreen angezeigt werden, mache ich ein (bisher unsichtbares) Panel sichtbar und setze seine Caption. Danach läuft die Lange Erzeugungs-Routine. Lasse ich das Programm laufen, ist das Panel sichtbar, aber die Caption fehlt! Die kommt erst wenn die lange Routine fertig ist.... Ein Panel.Repaint, Refresh, etc. hat da nichts verbessert, das kann doch nicht so schwer sein, ich beiße langsam in die Tischkante! Ciao Stefan |
AW: "Unendlicher Progressbar"
Repaint, ProgessMessages oder Dergleichen ... wurde alles schonmal genannt, auch das Warum.
|
AW: "Unendlicher Progressbar"
Wenn das Holen der Daten 90% der Zeit braucht und das Erzeugen und füllen der Controls 10%, dann könnte es Sinn machen, das Holen der Daten in einen Thread auszulagern.
Der einzige Vorteil wäre allerdings, dass die Anwendung reaktiv bleibt und irgendein Pinökel da irgendwie rumrödeln kann. Im Ergebnis hätte man eine Rödel-Anzeige und irgendwann - schwupps - werden die Controls erzeugt. Das macht u.U. deutlich Mehraufwand und kann auch Probleme erzeugen, wenn man die Synchronisation nicht korrekt regelt. Diese 90:10-Verteilung würde ich aber im genannten Beispiel nicht erwarten. Insofern würde ich es wie Uwe machen und z.B. in einer Statusbar oder Splash-Formular kurze Zwischenstände anzeigen. 1/10 ... Daten werden gesammelt (ca. 2 min, also heute bis ca. 18:02 Uhr) 2/10 ... alles wird vorbereitet (ca. 5 min, also heute bis ca. 18:07 Uhr) ... 10/10 ... ferdisch (hat 2 h gedauert) |
AW: "Unendlicher Progressbar"
Diese Pinökel würde aber auch verhinder dass Windows daherkommt, von wegen "Programm reagiert nicht" und das Programm womöglich abgeschossen wird. (egal ob jetzt mit Thread oder regelmäßigem Refresh)
|
AW: "Unendlicher Progressbar"
Ein Panel.Update nach dem Setzen von Panel.Caption sollte aber funktionieren.
|
AW: "Unendlicher Progressbar"
Eventuell auch ab und man mal sowas wie ProgressMessages, denn Update zeichnet nur neu, aber die Messsages hägnen dennoch und sobald das "ragiert nicht" auf taucht, helfen auch keine Updates mehr.
Bei ProgressMessages aber unbedingt Doppel-/Fehlbedienung unterbinden, z.B. indem alles/bestimmtes Disabled wird, bzw. in der Schleife auch auf Application.Terminated reagieren. |
AW: "Unendlicher Progressbar"
Also, ich weiß nicht, warum diese Ablehnung von Threads. Wenn man langwierige Aufgaben parallelisieren kann, dann führt doch sowieso kein Weg am Multithreading vorbei, und auch da muss man den Anwender informieren. Wenn hier nicht parallelisiert werden kann - wie dies hier angenommener Weise der Fall ist - dann würde ich doch nicht zu so komplizierten Lösungen greifen. Ich würde den MainThread nur für die VCL (Fortschrittsanzeige) benutzen, die Arbeit in einem Parallelthread verrichten und in geeigneter Weise (abhängig von Zeit oder Fortschritt) den Anwender im MainThread informieren. Damit der Anwender nicht dazwischenfuhrwerkt, kann man ja notfalls die ganze Form sperren oder ihn sonstwie einhegen.
Ich führe hier mal das Beispiel von Andreas Hausladen an, das scheint mir doch wie geschaffen für den vorliegenden Fall:
Delphi-Quellcode:
procedure TForm1.MainProc;
procedure DoSomething; procedure UpdateProgressBar(Percentage: Integer); begin ProgressBar.Position := Percentage; Sleep(20); // This delay does not affect the time for the 0..100 loop // because UpdateProgressBar is non-blocking. end; procedure Finished; begin ShowMessage('Finished'); end; var I: Integer; begin for I := 0 to 100 do begin // Do some time consuming stuff Sleep(30); LocalAsyncVclCall(@UpdateProgressBar, I); // non-blocking end; LocalVclCall(@Finished); // blocking end; var a: IAsyncCall; begin a := LocalAsyncCall(@DoSomething); a.ForceDifferentThread; // Do not execute in the main thread because this will // change LocalAyncVclCall into a blocking LocalVclCall // do something //a.Sync; The Compiler will call this for us in the Interface._Release method end; |
AW: "Unendlicher Progressbar"
Zitat:
|
AW: "Unendlicher Progressbar"
@Benmik
Zitat:
In dem Thread würden wohl vorrangig Tätigkeiten ausgeführt, die die VCL betreffen. Da müsste dann jeder Schritt synchronisiert werden. Das macht keinen Sinn. Mich würde ja nochmal eine genauere Aussage interessieren, was da aufgebaut wird, woher die Daten geholt werden und wie lange das dauert. Ich denke immer noch, gelegentliche Zwischenstände während des Prozesses (innerhalb des Mainthreads) auszugeben, ist der einfachste und sinnvollste Weg. |
AW: "Unendlicher Progressbar"
Zitat:
Aber es hilft ja nichts. Auch du musst mit Threads arbeiten. |
AW: "Unendlicher Progressbar"
Zitat:
Dann würde ich einfach immer wieder in dem VCL-Thread Nachrichten an den Anwender einbauen. |
AW: "Unendlicher Progressbar"
Vielleicht eine andere Lösung: In meiner App läuft eh im Hauptthread ein Timer mit, der eine Uhr laufen lässt.
Wenn ich im Thread etwas mache, wo ich den Fortschritt anzeigen möchte, dann setze ich dazu Werte in einem Objekt, auf das auch der Hauptthread Zugriff hat (hat auch ein toUpdate-boolean als Trigger). Der Hauptthread schaut nun im Timer alle 250ms, ob toUpdate=true ist und aktualisiert dann die darin stehenden Werte [z.B. eine Liste von TLabel-Objekten mit dem anzuzeigenden Wert). Das funktioniert soweit gut und ist (in meinem Fall) kein Overhead, weil ich den Timer ja eh schon habe. Damit brauche ich auch keinerlei Synchronisationen. |
AW: "Unendlicher Progressbar"
Zitat:
Ich verwende die LMDTools, speziell das LMD-Panel, welches ganz schick per Maus aus dem Hauptformular ausgedockt und frei auf dem Bildschirm positioniert werden kann. Davon gibt es mehrere und die laden in enthaltene WPTools-Richtexts Dokumente. Und das braucht leider etwas Zeit und während der Erzeugung eines solchen Panels und Laden desselben mit Daten wird mein Fortschritts-Thread blockiert bzw. ein auf dem Hauptformular befindliches Label nicht aktualisiert... Ciao Stefan |
AW: "Unendlicher Progressbar"
Joar, Erstellen geht nur im richtigen Thread (MainThread, wo auch die VCL oder FMX läuft),
das holen der Daten ginge im Thread (bei Datenbanken und Dergleichen aber aufpassen, dass man die Connecion und Query dort erzeugt hat oder ob sie z.B. ein ThreadPooling unterstüzen) das befullen kommt drauf an > Edits, CheckBoxen und Dergleichen da ist das Befüllen via SendMessage threadsave > bei ListBoxen und Grids kann man teilweise mit BeginUpdate den Haupthtread vom Update abhalten und so lange im Thread arbeiten "kann" im Thread arbeiten = aber muß man nicht Wenn man im Haupthtread arbeiten muß/will, dann muß man eben selbst für die regelmäßige Aktualisierung sorgen. |
AW: "Unendlicher Progressbar"
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:58 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz