Ja das ist wirklich etwas tricky in diesem Fall.
Klären wir erstmal das Warum:
Wenn man einen Button drückt, dann löst dieser ein bestimmtes Event aus und dieses kommt in eine Art Warteschlange.
Wenn man dann GuiGetMsg() aufruft, liest es diese Warteschlange aus und gibt das Event zurück und mit diesem kann man dann entsprechend darauf reagieren.
Bei dir passiert nun folgendes: Du drückst den Start-Menü-Button, das Event $gm_Menu_run wird abgefeuert und in deiner Message-Loop wird nun GUIGetMsg() aufgerufen.
Dieses findet nun das Event $gm_Menu_run und gibt es zurück. Darauf reagierst du in deinem Code damit, dass die Funktion _startRoutine() aufgerufen wird.
Nun befinden wir uns in der Funktion _startRoutine().
Und da wir uns in einer Endlosschleife befinden, kommen wir da erstmal nicht heraus.
Wenn man nun den Stopp-Button drückt, dann wird das Event $gB_Button1 gefeuert.
Das Problem: Niemand schaut nun nach ob Events in der Warteschlange stehen, denn GuiGetMsg() wird nicht mehr aufgerufen.
Das würde erst passieren wenn die Funktion _startRoutine() beendet wird, denn dann befinden wir uns wieder in der Message-Loop.
HotkeySet macht das hingegen anders. Es setzt nicht das Event in die Schlange sondern reagiert direkt darauf.
Daher funktioniert das und die GUI-Events nicht.
Es gibt (das sind zumindest die Variante, welche mir einfallen) da nun 2 Möglichkeiten:
1. GuiGetMsg() muss auch in der _startRoutine()-Funktion kontinuierlich abgefragt werden.
2. Auf den GuiOnEvent()-Modus wechseln. Das klingt erstmal einleuchtender, hat aber auch einen Haken - dazu später mehr.
Variante 1 - GuiGetMsg() in der _startRoutine():
Das GuiGetMsg() muss in die Schleife von startRoutine() mit hinein. Da die Events aber nur eine bestimmte Lebenszeit in der Schlange haben, kann es passieren, dass wenn sie erst nach dem 1s-Sleep aufgerufen wird, dass das Event dann schon wieder weg ist.
Daher muss GuiGetMsg() auch während dieser Sekunde permanent abgefragt werden.
Ich habe das mal folgendermaßen umgesetzt:
#include <GUIConstants.au3>
HotKeySet('{F10}','_startRoutine')
HotKeySet('{F11}','_stopRoutine')
Global Const $GUI = GUICreate('Test',300,150, -1, -1, $WS_SYSMENU,$WS_EX_TOPMOST)
Global $gm_Menu = GUICtrlCreateMenu("Steuerung")
Global $gm_Menu_run = GUICtrlCreateMenuItem("Programm starten",$gm_Menu)
Global $gm_Menu_stop = GUICtrlCreateMenuItem("Programm stoppen",$gm_Menu)
GUICtrlCreateMenuItem("", $gm_Menu, 3)
Global $gm_Menu_exit = GUICtrlCreateMenuItem("Beenden",$gm_Menu)
Global $gB_Button1 = GUICtrlCreateButton ("Stopp", 10, 50,60,30)
Global $gL_Label1 = GUICtrlCreateLabel("Zeit",10,10,100,20,1,5)
Global $stop = false
GUISetState(@SW_SHOW, $GUI)
While 1
Switch GUIGetMsg()
Case -3,$GUI_EVENT_CLOSE,$gm_Menu_exit
GUIDelete()
Exit
Case $gm_Menu_run
_startRoutine()
Case $gm_Menu_stop,$gB_Button1
_stopRoutine()
EndSwitch
WEnd
Func _startRoutine()
$stop = False
Local $iMsg, $iT
Do
For $i = 0 to 60
GUICtrlSetData($gL_Label1,$i)
; Sleep durch Msg-Loop-Schleife ersetzt
$iT = TimerInit()
While TimerDiff($iT) < 1000
$iMsg = GUIGetMsg()
If $iMsg = $gB_Button1 Or $iMsg = $gm_Menu_stop Or $stop then ExitLoop 3
WEnd
Next
Until False
EndFunc
Func _stopRoutine()
$stop = true
EndFunc
Alles anzeigen
Variante 2 - OnEvent-Modus:
Die Idee dahinter ist, dass der OnEvent-Modus Funktionen ähnlich wie Hotkeyset aufruft und daher ohne explizite Event-Abfragen funktioniert.
Das heißt man verknüpft die Events einfach mit den Funktionen und fertig.
Problem: Auch der OnEvent-Modus arbeitet mit einer Art Warteschlange. Und er arbeitet erst das nächste Event ab, wenn die Funktion, welche durch das vorherige Event aufgerufen wurde beendet wurde.
Das heißt wir stehen eigentlich wieder vor dem selben Problem.
Eine Idee hierzu wäre es, den Aufruf von _startRoutine() möglichst kurz zu halten.
Ich habe das umgesetzt indem diese einfach nur ein AdlibRegister initiiert und dann sich wieder beendet.
Auf diese Art können dann die weiteren Events bearbeitet werden.
Der Code ist dann evtl. etwas übersichtlicher:
#include <GUIConstants.au3>
; schalte auf OnEvent-Modus um
Opt("GUIOnEventMode", 1)
; -------- GUI-Aufbau -----------------------
Global Const $GUI = GUICreate('Test',300,150, -1, -1, $WS_SYSMENU,$WS_EX_TOPMOST)
Global $gm_Menu = GUICtrlCreateMenu("Steuerung")
Global $gm_Menu_run = GUICtrlCreateMenuItem("Programm starten",$gm_Menu)
Global $gm_Menu_stop = GUICtrlCreateMenuItem("Programm stoppen",$gm_Menu)
GUICtrlCreateMenuItem("", $gm_Menu, 3)
Global $gm_Menu_exit = GUICtrlCreateMenuItem("Beenden",$gm_Menu)
Global $gB_Button1 = GUICtrlCreateButton ("Stopp", 10, 50,60,30)
Global $gL_Label1 = GUICtrlCreateLabel("Zeit",10,10,100,20,1,5)
Global $iCount
; -------- Event-Behandlung -----------------------
; Verknüpfe das Ereignis $GUI_EVENT_CLOSE mit der Funktion _beendeProgramm
GUISetOnEvent($GUI_EVENT_CLOSE, "_beendeProgramm")
; Verknüpfe die Klick-Events der Controls mit entsprechenden Funktionen
GUICtrlSetOnEvent($gm_Menu_run, "_startRoutine")
GUICtrlSetOnEvent($gm_Menu_stop, "_stopRoutine")
GUICtrlSetOnEvent($gB_Button1, "_stopRoutine")
; -------- Hotkeys --------------------------------
; (evtl. Alternative: GUISetAccelerators)
HotKeySet('{F10}','_startRoutine')
HotKeySet('{F11}','_stopRoutine')
; GUI sichtbar machen
GUISetState(@SW_SHOW, $GUI)
; Endlossschleife (damit sich Programm nicht beendet)
; Der Programmablauf wird nun über Hotkeys und Events beeinflusst
While Sleep(100)
WEnd
; startet den Aufruf von _countUp aller 1s
Func _startRoutine()
$iCount = 0
AdlibRegister("_countUp", 1000)
EndFunc
; stoppt den periodischen Aufruf von _countUp
Func _stopRoutine()
AdlibUnRegister("_countUp")
EndFunc
; Funktion, welche hochzählt und die Zahl anzeigt
Func _countUp()
$iCount = $iCount > 60 ? 0 : $iCount + 1
GUICtrlSetData($gL_Label1, $iCount)
EndFunc
; Funktion welche das Programm beendet
Func _beendeProgramm()
Exit
EndFunc
Alles anzeigen