Entwickler-Ecke
Grafische Benutzeroberflächen (VCL & FireMonkey) - ProgressBar laufen lassen, während ein Prozess läuft?
del1312 - Fr 20.08.10 12:25
Titel: ProgressBar laufen lassen, während ein Prozess läuft?
Hallo Leute,
bastel grad an einem kleinen Tool, welches mir auf der Festplatte bestimmte Daten sucht. Das ganze mach ich so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42:
| procedure GetFilesInDirectory(Directory: string; const Mask: string; List: TStrings; WithSubDirs, ClearList: Boolean);
procedure ScanDir(const Directory: string); var SR: TSearchRec; begin if FindFirst(Directory + Mask, faAnyFile and not faDirectory, SR) = 0 then try repeat List.Add(Directory + SR.Name) until FindNext(SR) <> 0; finally FindClose(SR); end;
if WithSubDirs then begin if FindFirst(Directory + '*.*', faAnyFile, SR) = 0 then try repeat if ((SR.attr and faDirectory) = faDirectory) and (SR.Name <> '.') and (SR.Name <> '..') then ScanDir(Directory + SR.Name + '\'); until FindNext(SR) <> 0; finally FindClose(SR); end; end; end;
begin List.BeginUpdate; try if ClearList then List.Clear; if Directory = '' then Exit; if Directory[Length(Directory)] <> '\' then Directory := Directory + '\'; ScanDir(Directory); finally List.EndUpdate; end; end; |
Aufrufen tue ich das dann hier:
Delphi-Quelltext
1:
| GetFilesInDirectory(Pfad, '*-2010_*', Listbox1.Items, True, True); |
Jetzt meine Frage, da es manchmal doch ne Weile dauert möchte ich gerne so eine ProgressBar einbauen.
Nur weiss ich nicht so recht wie ich die so einbaue das sie während des Prozesses läuft.
Hatte das mit einem Timer probiert, aber der schein auch nicht in Hintergrund weiter zulaufen während
die HDD durchsucht wird:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure TForm1.Button1Click(Sender: TObject); begin Pfad:=Edit1.Text; Timer1.Enabled:=true; ProgressBar1.Visible:=true; GetFilesInDirectory(Pfad, '*-2010_*', Listbox1.Items, True, True); listcount:=ListBox1.Count; Timer1.Enabled:=false; ProgressBar1.Visible:=false; ProgressBar1.Position :=0; ShowMessage('Es wurden: '+ IntToStr(listcount)+ ' passende Datensätze gefunden!'); end; |
und der im Timer stand das:
Delphi-Quelltext
1: 2: 3: 4: 5:
| procedure TForm1.Timer1Timer(Sender: TObject); begin ProgressBar1.Position :=ProgressBar1.Position +1; ProgressBar1.Update ; end; |
Ich vermute ich muss
Delphi-Quelltext
1:
| GetFilesInDirectory(Pfad, '*-2010_*', Listbox1.Items, True, True); |
irgendwie in eine Schleife bauen oder? Kann mir da bitte einer helfen oder nen Tipp geben?
Noch ne Frage zu der ProgressBar. Wie kann ich die eigentlich so einrichten das sie auch korrekt
den Status anzeigt? Also ich meine irgendwo muss das errechnet werden, so und soviel % sind bereits abgearbeitet damit
die Bar überhaupt richtig anzeigt oder?
DANKE schon mal für eure Hilfe!
bummi - Fr 20.08.10 12:37
Du könntest GetFilesInDirectory eine Paramter eines ProgressDialogs mitgegeben, wenn dieser ASSIGNED ist in der Schleife updaten.
ALF - Fr 20.08.10 13:24
Hi, dazu müsstest Du aber vorher wissen, wie viel Dateien es auf Deiner Festplatte gibt!
Sonst kannst du keine prozentuale Anzeige machen wie viel oder wie lange es noch dauern wird.
Gruss Alf
bummi - Fr 20.08.10 13:40
Geht doch .... so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85:
| procedure GetFilesInDirectory(Directory: string; const Mask: string; List: TStrings; WithSubDirs, ClearList: Boolean;PG:TProgressbar); var Cur:TCursor;
Function CountDir(const Directory: string):Integer; var SR: TSearchRec; begin Result := 0;
if FindFirst(Directory + Mask, faAnyFile and not faDirectory, SR) = 0 then try repeat inc(result); until FindNext(SR) <> 0; finally FindClose(SR); end; if WithSubDirs then begin if FindFirst(Directory + '*.*', faAnyFile, SR) = 0 then try repeat if ((SR.attr and faDirectory) = faDirectory) and (SR.Name <> '.') and (SR.Name <> '..') then Result := Result + CountDir(Directory + SR.Name + '\'); until FindNext(SR) <> 0; finally FindClose(SR); end; end; end;
procedure ScanDir(const Directory: string); var SR: TSearchRec; begin
if FindFirst(Directory + Mask, faAnyFile and not faDirectory, SR) = 0 then try repeat List.Add(Directory + SR.Name); if Assigned(PG) then begin pg.Position := List.Count; end;
until FindNext(SR) <> 0; finally FindClose(SR); end;
if WithSubDirs then begin if FindFirst(Directory + '*.*', faAnyFile, SR) = 0 then try repeat if ((SR.attr and faDirectory) = faDirectory) and (SR.Name <> '.') and (SR.Name <> '..') then ScanDir(Directory + SR.Name + '\'); until FindNext(SR) <> 0; finally FindClose(SR); end; end; end;
begin Cur := Screen.Cursor; try Screen.Cursor := crHourGlass; if Assigned(PG) then pg.State := pbsNormal; List.BeginUpdate;
if ClearList then List.Clear; if Directory = '' then Exit; if Directory[Length(Directory)] <> '\' then Directory := Directory + '\'; if Assigned(PG) then pg.Max := CountDir(Directory); ScanDir(Directory); finally List.EndUpdate; if Assigned(PG) then pg.State := pbsPaused;
end; Screen.Cursor := Cur; end; |
del1312 - Fr 20.08.10 13:54
DANKE! Also ich habs mal eingebaut und hier noch die Progressbar1 eingetragen, das war doch richtig oder?
GetFilesInDirectory(Pfad, '*-2010_*', Listbox1.Items, True, True, Progressbar1);
Also er macht erstmal was, aber warum wird die Bar gelb und so richtig flüssig ist es auch nicht, bzw erst kommt
nichts und dann ist halb und dann voll. Geht warhscheinlich nicht anders oder? Na erstmal vielen DANK bummi!!!!
ALF - Fr 20.08.10 13:56
Jo,
bummi ,damit hat er aber leider keine Progressanzeige wie lange es noch dauert :wink:
Problem mit Progessbar ist, das Du vorher erst einmal die gesammte Platte scannen must um festzustellen wie viel Dateien da drauf sind, um anzuzeigen wie lange es noch dauern wird!
Andere Möglichkeit, etwas schneller, den belegten Platz auf der Festplatte zu nehmen und dann beim Scannen den belegten Platz, (nicht Dateigrösse), den eine Datei einimmt, zu addieren um somit eine Progressanzeige zu verwirklichen! Ob dies aber sauber ist weis ich nicht!
Gruss Alf
del1312 - Fr 20.08.10 13:59
Hm ok dann muss ich das wohl weglassen, schade. Vielen Dank für eure Hilfe
Tankard - Fr 20.08.10 14:24
hallo,
ruf in der schleife ab und zu mal
Application.ProcessMessages
auf. sonst werden die messages nicht abgearbeitet und deine ganze application scheint für den benutzer zu stehen. das bedeutet auch das die progressbar sich nicht bewegt und erst nach beenden der suche neu gezeichnet wird.
gruss
tankard
ALF - Fr 20.08.10 14:39
Warum so schnell aufgeben? Wenn du nicht unbedingt im Windows Ordner selbst was suchen willst, sondern nur in den Bereichen, Programme und alles was für den User gültigkeit hat, pro Partition/Fetsplatte ist auch dieses sehr flink!
Zumal man ja unter umständen den Scann für die Anzahl der vorhandenen Dateien, ja auch in einen Thread auslagern kann (der muss ja nur die Dateien Zählen nix vergleichen) und dann bei Deinem eigentlichen Suchvorgang die anzahl der Dateien für Deine Progressbar zu aktualliesieren! Ist nur so eine Idee 8)
Gruss Alf
Xion - Fr 20.08.10 15:52
Wenn ich nicht grad total bescheurt bin, dann braucht bummies Code DOPPELT so lange wie der normale Code, weil er zweimal alle Dateien sucht...also das wäre ja ein Verbrechen :D
:arrow: Das einfachste wäre wohl so eine tolle Windows-"Ich bin noch nicht abgestürzt"-Leiste hinzumachen, die einfach immer durchläuft
In der Machart:
http://www.linnekogel.de/images/layout/ladebalken.gif
Das ist immer gut wenn man garkeine Ahnung hat was los ist. Super ist, wenn das Programm abgestürzt ist und das Ding dreht sich trotzdem weiter :P
:arrow: Dann könntest du, wenn du immer die ganze Platte durchsucht (was ich nicht empfehlen würde) auch die Zeit messen, die du beim ersten mal brauchst. Beim zweiten mal Suchen verwendest du die als ProgressBar.Max. Und du misst wieder die Zeit, mittelst die beiden Zeiten usw. Vorteil: Braucht garkeine Zeit und nach paarmal Suchen sollte es einigermaßen passen.
:arrow: Wenn du die Dateien erst zählen willst, ist das meiner Meinung nach Quatsch...denn in dem Moment bist du ja dann schon fertig, da du ja nur die Dateien suchst. Also in dem Moment wo du sie mitzählst, warst du ja schon bei ihr. Anders ist es, wenn du die Dateien z.B. kopierst. Dann fällt die Zeit, die du zum zählen verwendest, nicht so ins Gewicht, da das Kopieren viel länger dauert.
del1312 - Fr 20.08.10 17:08
Stimmt das einfach mit ner Windows-"Ich bin noch nicht abgestürzt"-Leiste ist ne super Idee, das werd ich mir glatt mal anschauen, danke!
ALF - Fr 20.08.10 17:18
Dann könnte er auch den Cursor dafür setzten, wie @Bummi es vorgeschlagen hat! :wink:
Xion hat folgendes geschrieben : |
Das ist immer gut wenn man garkeine Ahnung hat was los ist. Super ist, wenn das Programm abgestürzt ist und das Ding dreht sich trotzdem weiter :P |
Halt der Nachteil! :shock:
Xion hat folgendes geschrieben : |
:arrow: Dann könntest du, wenn du immer die ganze Platte durchsucht (was ich nicht empfehlen würde) |
Würd ich auch nicht empfehlen, nur manchmal brauch man es halt :wink:
Xion hat folgendes geschrieben : |
auch die Zeit messen, die du beim ersten mal brauchst. Beim zweiten mal Suchen verwendest du die als ProgressBar.Max. Und du misst wieder die Zeit, mittelst die beiden Zeiten usw. Vorteil: Braucht garkeine Zeit und nach paarmal Suchen sollte es einigermaßen passen. |
Auf sowas würde noch nicht mal ich kommen 8)
Xion hat folgendes geschrieben : |
:arrow: Wenn du die Dateien erst zählen willst, ist das meiner Meinung nach Quatsch.. |
Nein, er soll ja erst vor den Suchen Zählen,
wenn er mit Progressbar arbeiten will, nicht zur gleichen Zeit wenn er sucht. Habe ich aber deutlich geschrieben :?
Dabei hab ich ihm 3 Möglichkeiten aufgezählt! Ne 4. währe, nur den Inhalt des jeweiligen Ordner zu zählen und dann im Ordner zu suchen. Wenn man mit Progressbar arbeiten
will!!!
Das bleibt aber jedem selbst überlassen :P
Gruss Alf
delfiphan - Fr 20.08.10 20:40
Längere Operationen auf dem UI Thread laufen zu lassen ist für mich zu mindest Tabu. Ich würde die Suche in einem Thread machen. Dort kannst den jeweils den aktuellen Stand über ein Property auslesbar machen (die muss threadsafe sein). Der Progressbar liest dann z.B. über einen Timer regelmässig den Wert ab. Falls der Progress unbekannt desto einfacher (dann lässt du die Geschichte einfach weg).
Beim Listbox1.Items müsstest du bei Threads aufpassen, da diese nicht threadsafe ist. Da du ja aber sowieso BeginUpdate/EndUpdate verwendest bist du offenbar nicht am Zwischenresultat interessiert. D.h. dann würde ich die Methode eine eigene TStringList erzeugen lassen und diese über einen Callback zurückgeben.
delfiphan - Fr 20.08.10 21:10
Die Lösung mit Application.ProcessMessages oder einer blockierenden UI würde ich absolut davon abraten. Das Design mit Threads ist State of the Art. Ich sag ja nicht überall, aber Festplatte durchsuchen ist dafür bestens geeignet.
In neuen Technologien wie Silverlight gibt es für längere Operationen überhaupt keine Blocking Calls mehr sondern ausschliesslich asynchrone Pendants.
Gutes Design ist, wenn der UI Thread zu keiner Zeit stillsteht. Wenn du die UI eine Sekunde lang blockierst hast du was falsch gemacht. Wenn sie 5 Sekunden still steht heisst es seitens Windows nur noch "Not Responding".
Tranx - Sa 21.08.10 17:39
Vielleicht ist mal das alte DIR ganz sinnvoll zur Ermittlung der Dateienanzahl:
DIR "Pfad" /S >"Textdatei"
dann steht in der Textdatei die Anzahl der Dateien in der vorletzten Zeile.
Du musst dan nur folgendes Aufrufen:
Habe das Starten in eine Prozedur Progstarten geschrieben:
Ich glaube Uses Windows muss dazu in der Unit sein, sionst funktioniert das Ganze nicht.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure ProgStarten(const DateiName: String; const Parameter: String=''; const immer : boolean = FALSE); var Erg : integer; begin if not(FileExists(Dateiname)) and not(immer) then begin Fehler('Das Programm ['+Dateiname+'] existiert nicht! In den Optionen bitte korrigieren.'); Exit; end; Erg := ShellExecute(Application.Handle,'open',PChar(DateiName),PChar(Parameter),PChar(ExtractFilePath(DateiName)),SW_SHOW); if Erg<32 then Mitteilung('Beim Start des Programms '+Dateiname+' ist ein Fehler aufgetreten!'); end; |
Du rufst dann das Dir mit
Delphi-Quelltext
1:
| Progstarten('DIR','/S > '+Dateiname,TRUE); |
auf
Dann musst Du nur die vorletzte Zeile der Textdatei einlesen und daraus de Anzahl der Dateien auslesen.
Für die Restzeit brauchst Du nur die Anzahl, die derzeitige Position, die Startzeit und die aktuelle Zeit
Delphi-Quelltext
1:
| Restzeit := (now - Startzeit)*(Anzahl - n)/Anzahl; |
Das gibst Du unter dem Progressbar aus.
Am Besten Du nimmst die angehängte Unit für den Progressbar. Dann erscheint dieser in einem Extrafenster und der Vorgang kann jederzeit abgebrochen werden.
Hierzu wird die globale Variable PROCAbbruch auf TRUE gesetzt.
Den Fortschritt akitivierst Du mit
Delphi-Quelltext
1:
| FortschrittBeginnen(Anzahl, TRUE/FALSE); |
innerhalb der Schleife:
Delphi-Quelltext
1:
| FortschrittAktualisieren(Text); |
und am Ende:
Über Aufzählen von n oder I oder sonst was - kein Gedanke daran. Steht alles in der Unit.
Nur am Anfang einmal die Gesamtzahl vorgeben!
Ich habe es mit Delphi 5 geschrieben. Hoffentlich funktioniert dies auch mit anderen Versionen.
Moderiert von
Narses: Delphi-Tags hinzugefügt
jaenicke - Sa 21.08.10 17:58
:autsch:
Was hat es für einen Sinn einen Kommandozeilenbefehl zu benutzen und die Ausgabe auszulesen? Der Effekt ist nur, dass dein Formular erst recht nicht mehr reagiert, weil du keine Möglichkeit hast während der Ausführung und dem Warten darauf etwas anderes zu machen oder Rechenzeit freizugeben.
Klar kannst du dann das wiederum in einen Thread legen, aber das kannst du dann auch gleich mit dem Delphicode zur Suche machen.
Ja, und dann willst du die Anzahl der gefundenen Dateien für den Fortschritt der erneuten (!!) Suche benutzen?!? :shock:
Du hast dann doch schon die Liste der Dateien...
Ja, und zuguterletzt:
"dir" ist keine Exe, dementsprechend musst du den Befehl auch als Parameter an cmd übergeben und kannst den Befehl nicht als Dateiname an ShellExecute übergeben...
ssb-blume - So 22.08.10 09:10
Hallo,
habe mal eine sehr alte Version für dieses Problem erstellt, siehe Anhang (.ZIP)
Es sind alle Quellen dabei, auch eine kurze Doc.
Ich hoffe, Du kannst damit etwas anfangen!
Hansi
Gerd Kayser - Di 24.08.10 22:35
del1312 hat folgendes geschrieben : |
Stimmt das einfach mit ner Windows-"Ich bin noch nicht abgestürzt"-Leiste ist ne super Idee, das werd ich mir glatt mal anschauen, danke! |
Ich würde einfach eine Art Statistik ausgeben. So in der Art:
Delphi-Quelltext
1: 2: 3:
| Aktuelles Verzeichnis: c:\programme\borland Anzahl Dateien: 2197 gefundene Dateien: 12 |
Dazu vielleicht noch eine kleine Animation. Falls sich an das Suchen der Dateien noch ein weiterer (zeitintensiver) Lauf zum Verarbeiten der gefundenen Dateien anschließt, kann man dann eine ProgressBar verwenden.
jaenicke - Mi 25.08.10 05:51
Wobei wie schon gesagt wurde zu bedenken ist, dass die Anzeige den Prozess deutlich verlangsamen kann. Insbesondere wenn jede Datei als Fortschritt angezeigt wird. Dann kann das ganze schon mal z.B. 50% länger dauern.
Selbst stark optimiert (minimierte Aufrufanzahl, zeitliche Abstände zwischen Anzeigeschritten, ...) sind es bei mir bei einem aktuellen Projekt noch ca. 4%, die das ganze durch die Fortschrittsanzeige langsamer wird.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!