Seite 1 von 2
Gelöst: Einfache "Busy"-Anzeige will nicht
Verfasst: 28.10.2024 16:43
von EmmJott
Hallo Ihr Lieben,
habe ein Progrämmchen geschrieben, dass Verzeichnisse liest und in eine Liste einträgt. Je nach Verzeichnis kann das schon mal eine Weile dauern. Dachte mir, ich könnte mittels
Code: Alles auswählen
AddWindowTimer(#window,123,1000)
Verzeichnis einlesen, in Liste eintragen, sortieren, ...
RemoveWindowTimer (#window,123)
und
Code: Alles auswählen
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Timer And EventTimer() = 123
string$ + "X"
StatusBarText(#statusbar,1,string$)
If LEN(string$) = 10
string$ =""
EndIf
EndIf
Until Event = #PB_Event_CloseWindow
zwischenzeitlich jede Sekunde ein weiteres "X" in die Statuszeile eintragen, so daß der Anwender sieht, dass die Kiste rödelt und nicht tatenlos ist oder sich gar aufgehängt hätte. Tut's aber nicht. Hab ich's nur nicht richtig gemacht, oder geht das gar nicht auf diese Art?
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 28.10.2024 17:46
von mk-soft
Du wirst wohl das Einlesen der Verzeichnisse in einem Thread auslagern müssen damit der EventLoop weiter funktioniert.
Für Anfänger muss man sich aber mit Threads intensive beschäftigen da hier einiges zu beachten ist.
Zum Beispiel sollte man nicht die Gadgets aus Threads ändern da diese unter Windows Teilweise geht, aber unter macOS oder Linux zum Absturz führt.
Und dann immer die Compiler Option ThreadSafe aktivieren.
Hier einige Beispiele um mit Threads zu arbeiten. Aber bitte sehr genau studieren und das Control nicht umbauen (Aber die Examples).
Link:
Mini Thread Control
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 28.10.2024 17:48
von NicTheQuick
Um genauer zu sagen, was du falsch machst, müsste ich mehr Kontext sehen. Aber ich habe eine Vermutung:
Das Einlesen des Verzeichnisses läuft vermutlich nicht in einem Thread, oder? Denn nur dann könnte es es unabhängig von der Eventschleife laufen. Oder umgekehrt beschrieben: Solange dein Programm `WaitWindowEvent()` nicht aufrufen kann, werden auch die Timer-Events nicht abgearbeitet.
Das Einlesen des Verzeichnisses müsstet du also in einen Thread auslagern, damit parallel deine Eventschleife laufen kann. Das ist dann wieder nicht mehr ganz so Anfänger-geeignet, aber trotzdem noch relativ leicht machbar. Schau dir dazu `CreateThread()` und vielleicht ein paar Beispiele im Forum an. Und stelle sicher, dass du in den Compiler-Optionen "Thread-sichere Executable erstellen" aktiviert hast.
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 28.10.2024 18:12
von EmmJott
Hallo,
vielen Dank für die Antworten. Klingt nach starkem Tobak für mich - das Thema Thread erscheint mir doch sehr nebulös. Wenn ich euch richtig verstanden habe, müsste ich entweder das Datensammeln oder die Fortschrittsanzeige in einen Thread auslagern. Wenn ich mir das tatsächlich aussuchen kann, was wäre denn eurer Erfahrung nach einfacher? Könnte mir auch eine animierte GIF in einem separaten Fenster vorstellen, da müsste ich nicht allzuviel meines jetzigen Progs über'n Haufen werfen ...
edit: ... glaube ich ...
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 28.10.2024 18:29
von mk-soft
Du must das Datensammeln in Thread auslagern. Die GUI EventLoop darf nicht durch lange Prozesse unterbrochen werden.
Schau dir die Beispiele in Mini Thread Control an.
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 28.10.2024 18:37
von H.Brill
Naja, wenn das Programm sonst nichts machen muß, kannste die Suche ja mit einem Button starten und halt
einfach warten, bis es mit dem Directory fertig ist.
Die Progressbar kann man ja so anpassen, daß der Maximalwert immer die Anzahl der Dateien eines Ordners ist.
(siehe SetGadgetAttribute() ). Den Timer habe ich durch For i = 1 To 500 ersetzt, damit man auch was sieht.
Den kannst du auch später wieder rausnehmen und mit dem Code ersetzen, was du mit den Dateien machen willst.
Vielleicht so etwa :
Code: Alles auswählen
Procedure.i CountFiles (sPath.s)
Protected numFiles = 0
Protected handle
If Right(sPath, 1) <> "\"
sPath + "\"
EndIf
handle = ExamineDirectory(#PB_Any, sPath, "*.*")
If handle = 0
ProcedureReturn 0
EndIf
While NextDirectoryEntry(handle)
If DirectoryEntryType(handle) = #PB_DirectoryEntry_File
numFiles + 1
Else
If DirectoryEntryName(handle) <> "." And DirectoryEntryName(handle) <> ".."
numFiles + CountFiles(sPath + DirectoryEntryName(handle))
EndIf
EndIf
Wend
FinishDirectory(handle)
ProcedureReturn numFiles
EndProcedure
path$ = "C:\Temp\"
cfiles.l = CountFiles(path$)
If OpenWindow(0, 0, 0, 400, 100, "Timer Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ProgressBarGadget(0, 10, 10, 380, 20, 0, cfiles)
Value = 0
Repeat
Event = WaitWindowEvent()
If ExamineDirectory(0, path$, "*.*")
While NextDirectoryEntry(0)
If DirectoryEntryType(0) = #PB_DirectoryEntry_File
Type$ = "[File] "
Value + 1
SetGadgetState(0, Value)
For i = 1 To 500
Next
EndIf
Wend
EndIf
Until Event = #PB_Event_CloseWindow
EndIf
Ist jetzt nur für den einen Ordner.
Vielleicht kann man das auch noch mit einer Sanduhr als Cursor verfeinern.
Evtl kannst du damit ja was anfangen.
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 28.10.2024 20:17
von #NULL
Das geht notfalls auch ohne Threads, wird dann halt etwas komplizierter. Du musst die Aufgabe nur so aufteilen, dass sie stückchenweise arbeitet und nach jedem Schritt rausspringen und später weitermachen kann.
Ein Beispiel mit ExamineDirectory().
Erweitern kann man das mit einer Structure, die die Daten für eine examine session enthält und übergeben wird, so dass auch mehrere sessions parallel laufen können und auch rekursiv.
Du kannst auch in der Prozedur ein custom event mit Daten des aktuellen Eintrags oder dem Status posten, dann hast du jeden Eintrag direkt als Event in der Event Schleife.
Code: Alles auswählen
EnableExplicit
Define win, event, quit
win = OpenWindow(#PB_Any, 50, 100, 800, 600, "title")
AddKeyboardShortcut(win, #PB_Shortcut_Escape, 10)
Procedure examineDir(dir.s = "")
Static dirPath.s
Static exDir
Protected Type$
Protected Size$
If Not Len(dirPath)
If Len(dir)
dirPath = dir
EndIf
EndIf
If Len(dirPath)
If Not exDir
Debug "examine.. (" + dirPath + ")"
exDir = ExamineDirectory(#PB_Any, dirPath, "*.*")
EndIf
If exDir
If NextDirectoryEntry(exDir)
If DirectoryEntryType(exDir) = #PB_DirectoryEntry_File
Debug "[File] " + DirectoryEntryName(exDir) + " (Size: " + DirectoryEntrySize(exDir) + ")"
Else
Debug "[Directory] " + DirectoryEntryName(exDir)
EndIf
Else
Debug "..finished"
FinishDirectory(exDir)
exDir = 0
dirPath = ""
ProcedureReturn #False
EndIf
EndIf
EndIf
ProcedureReturn #True
EndProcedure
Define more
more = examineDir(#PB_Compiler_Home)
Repeat
If more
more = examineDir()
PostEvent(#PB_Event_FirstCustomValue) ; damit WaitWindowEvent nicht stehen bleibt
If Not more
Debug "is alle"
EndIf
EndIf
event = WaitWindowEvent()
Select event
Case #PB_Event_CloseWindow
quit = #True
Case #PB_Event_Menu
Select EventMenu()
Case 10
quit = #True
EndSelect
EndSelect
Until quit
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 28.10.2024 23:03
von HeX0R
Es geht auch viel einfacher, nicht ganz so wie man es eigentlich macht, aber zumindest anfängerfreundlich:
Den Timer per BindEvent() einbinden, eine Timerprozedur erstellen, die den Fortschritt zeigt und dann in der zeitaufwändigen Schleife While WindowEvent() : Wend einbauen.
Man kann auch den CloseWindow Event per BindEvent() dazunehmen, damit man die Schleife auch abbrechen könnte.
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 31.10.2024 01:17
von TheCube
Klasse Idee die Tmer einfach per Bindevent() einzubinden, und nicht klassisch in der Eventschleife abzuarbeien ... selbst nie dran gedacht.
So muss man nicht gleich Threads einrichten, wenn man zuverlässig vllt. nur alle Sekunde in z.B. der Statusbar einen Text aktualisiert,
ein Icon blinken lässt oder was sonst so zeitunkritisch aber sicher regelmässig ausgeführt werden soll.
Bei der klassischen Timer-Eventabfrage pausiert ja alles bei Bedienung von Menüs, anpacken/schieben des Fensters, oder gar Messagerequestern.
Re: Einfache "Busy"-Anzeige will nicht
Verfasst: 01.11.2024 15:57
von EmmJott
Hallo Allerseits,
sitze mit offenem Mund staunend auf dem Beifahrersitz und lausche den Fachleuten. Könnte mir jemand ein Beispiel mit dem Timer per Bindevent() formulieren?