For me the relevant question is:
How can I work through the long processes without restricting or slowing down the responsiveness of my program interface?
-. Use Threads (and deal with it, not part of this, because there are already many beautiful examples here)
1. The way PB_IDE itself does it (at least in some places)
2. Using BindEvent(), BindGadgetEvent()
Now I hope that the examples will help interested beginners with their basic architectural considerations. And if the decision is, I won't do it under any circumstances -- then that's fine too.
to 1. The way PB_IDE itself does it (at least in some places) with some modifications
Code: Select all
;| Advanced Event Handling (concept like PB_IDE)
EnableExplicit
;DebugLevel 9 ; 9 .. show all debug messages
Enumeration Window
#WINDOW_Main
EndEnumeration
Enumeration Gadget
#GADGET_btnStart
#GADGET_btnStop
#GADGET_chkTest
#GADGET_txtLoopOutput
#GADGET_lbTrace
EndEnumeration
#WINDOW_Main_Flags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget
#LOOP_MaxLoops = 1000
#LOOP_SlowDownDelay = 100
; ---------------------------------------------------------------------------------------------------------------------
Global ApplicationQuit = #False ; Application flag
Global LoopRunningState = #False ; init --> not running
; ---------------------------------------------------------------------------------------------------------------------
Declare MainWindowEvents(Event)
Declare MainWindowSizeEvent() ; bind (for a nicer display when adjusting the size)
Declare FlushEvents()
Declare DispatchEvent(Event)
; ---------------------------------------------------------------------------------------------------------------------
Macro Trace(MessageText)
AddGadgetItem(#GADGET_lbTrace, -1, MessageText)
SetGadgetState(#GADGET_lbTrace, CountGadgetItems(#GADGET_lbTrace) - 1)
EndMacro
;/---------------------------------------------------------------------------------------------------------------------
;| Test Functions
;|
;| the stupid long lasting code in one function (i.e. scanning dirs, copying files, parsing files, etc.
;|
;| use FlushEvents()
;| --> process all events
;|
;\
Procedure Test_LongLoop()
Protected index, msg$
For index = 0 To #LOOP_MaxLoops
Delay(#LOOP_SlowDownDelay) ; spend some time with nothing
msg$ = " Item " + Str(index) + "/" + Str(#LOOP_MaxLoops) + " | Checkbox.State == " + GetGadgetState(#GADGET_chkTest)
SetGadgetText(#GADGET_txtLoopOutput, msg$)
If index % 10 ; ............................................. only every 10th iteration we call the event loop !!!
FlushEvents() ; ........................................... check the message queue
If ApplicationQuit
Trace("ApplicationQuit == " + ApplicationQuit)
Break ; .................................................. leeve the loop, now
EndIf
EndIf
If Not LoopRunningState
Debug #PB_Compiler_Procedure + "() ::" + "LoopRunningState == FALSE " ;,9
Trace("LoopRunningState == FALSE ")
Break
EndIf
Next index
If index >= #LOOP_MaxLoops
SetGadgetText(#GADGET_txtLoopOutput, "Loop finished.")
Trace("Loop finished (reached MaxLoops == " + index + ")")
EndIf
LoopRunningState = #False
EndProcedure
;-=== GUI Main Window =================================================================================================
Procedure OpenMainWindow(Width = 400, Height = 320)
Protected ww
If OpenWindow(#WINDOW_Main, 0, 0, Width, Height, "Event handling example...", #WINDOW_Main_Flags)
StickyWindow(#WINDOW_Main, 1) ; show test app always above the PB_IDE
ww = (Width - 16) / 2
ButtonGadget(#GADGET_btnStart, 8, 4, ww, 32, "Start Looping")
ButtonGadget(#GADGET_btnStop, ww + 8, 4, ww, 32, "Stop Looping")
CheckBoxGadget(#GADGET_chkTest, 8, 40, Width - 16, 24, "Check me -- (see the event handling while the loop is running)")
TextGadget(#GADGET_txtLoopOutput, 8, 72, Width - 16, 20, "Loop is stopped.")
;SetGadgetColor(#GADGET_txtLoopOutput, #PB_Gadget_BackColor, GetSysColor_(#COLOR_INFOBK)) ; windows only
ListViewGadget(#GADGET_lbTrace, 8, 104, Width - 16, Height - 112) ;, $4000) ; #LBS_NOSEL == 0x4000 ; .. windows only
; If CreateMenu(0, WindowID(#WINDOW_Main))
; MenuTitle("Menu")
; MenuItem(1, "Item 1")
; MenuItem(2, "Item 2")
; MenuItem(3, "Item 3")
; EndIf
BindEvent(#PB_Event_SizeWindow, @MainWindowSizeEvent(), #WINDOW_Main)
EndIf
ProcedureReturn WindowID(#WINDOW_Main)
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure MainWindowSizeEvent()
Protected ww, wndW, wndH : Debug #PB_Compiler_Procedure, 9
wndW = WindowWidth(#WINDOW_Main)
wndH = WindowHeight(#WINDOW_Main)
Trace("Window Size == " + wndW + " x " + wndH)
ww = (wndW - 16) / 2
ResizeGadget(#GADGET_btnStart, #PB_Ignore, #PB_Ignore, ww, #PB_Ignore)
ResizeGadget(#GADGET_btnStop, ww + 10, #PB_Ignore, ww, #PB_Ignore)
ResizeGadget(#GADGET_txtLoopOutput, #PB_Ignore, #PB_Ignore, wndW - 16, #PB_Ignore)
ResizeGadget(#GADGET_lbTrace, #PB_Ignore, #PB_Ignore, wndW - 16, wndH - 112)
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure MainWindowEvents(Event) ; returns #False (continue) or #True (quit appl.)
;/
;| Use ApplicationQuit (global)
;\
Select Event
Case #PB_Event_ActivateWindow ;{- ....
If GetActiveWindow() = #WINDOW_Main ; check if it still got the focus and it is not just a delayed focus event!
; ........... Always give back the focus to the editor when the window gets activated
EndIf
;}
Case #PB_Event_CloseWindow ;{- ....
ApplicationQuit = #True ; set once, cannot released by further calls
;}
Case #PB_Event_GadgetDrop ;{- ....
; Select EventGadget()
; Case #GADGET_Xxx
; Case #GADGET_Yyy
; Default
; EndSelect
;} End of #PB_Event_GadgetDrop
Case #PB_Event_Timer ;{- ....
; Select EventTimer()
; Case #TIMER_Xxxx
; Case #TIMER_Yyyy
; EndSelect
;} End of #PB_Event_Timer
Case #PB_Event_Gadget ;{- ....
Select EventGadget()
Case #GADGET_btnStart : Debug "#GADGET_btnStart clicked!", 9
LoopRunningState = #True
ClearGadgetItems(#GADGET_lbTrace)
SetGadgetText(#GADGET_txtLoopOutput, "Loop is running")
Test_LongLoop()
Case #GADGET_btnStop : Debug "#GADGET_btnStop clicked!", 9
SetGadgetText(#GADGET_txtLoopOutput, "Loop stopped.")
LoopRunningState = #False
Case #GADGET_chkTest : Debug "#GADGET_chkTest clicked!", 9
Debug " State == " + GetGadgetState(#GADGET_chkTest), 9
Trace("Gadget = #GADGET_chkTest == " + GetGadgetState(#GADGET_chkTest))
; Default ; <--> unknown gadget ..
EndSelect
;} End of #PB_Event_Gadget
Case #PB_Event_Menu ;{- ....
; ApplicationQuit = MainMenuEvent(EventMenu())
;}
Case #PB_Event_SizeWindow ;{- .... ; after sizing is done....
; ResizeMainWindow()
Trace(" New Window Size == " + WindowWidth(#WINDOW_Main) + " x " + WindowHeight(#WINDOW_Main))
;}
Case #PB_Event_MoveWindow ;{- .... ; after moving is done....
If GetWindowState(#WINDOW_Main) = #PB_Window_Normal
Trace(" New Window Pos == " + WindowX(#WINDOW_Main) + ", " +WindowY(#WINDOW_Main))
EndIf
;}
EndSelect ; Event
ProcedureReturn ApplicationQuit ; == #False or #True (probably not needed)
EndProcedure
;-=== Basic Event Handler =============================================================================================
Procedure FlushEvents() ; returns nothing
While DispatchEvent(WindowEvent()) : Wend ; as long as the queue is not empty
EndProcedure
;/---------------------------------------------------------------------------------------------------------------------
;| Processes all events.
;| moved into a procedure, so it can be called from the FlushEvents() too, so no events are lost.
;\
Procedure DispatchEvent(Event) ; returns Event | Event == #PB_Gadget_Event, ...
; shortcut for empty queue
;
If Event = #PB_Event_None ; (0)
ProcedureReturn #PB_Event_None ; (0)
EndIf
Select EventWindow() ; <-- process events of all the windows
Case #WINDOW_Main
MainWindowEvents(Event) ; ApplicationQuit could be true
; Case #WINDOW_Edit ; more windows
EndSelect
ProcedureReturn Event ; return the event still, to be able to check for 0 events (empty queue)
EndProcedure
;-=== main ============================================================================================================
Procedure main()
If OpenMainWindow()
Repeat
DispatchEvent(WaitWindowEvent()) ; no check for empty queue here!
Until ApplicationQuit
EndIf
ProcedureReturn 0
EndProcedure
End main()
;-=== BoF =============================================================================================================
to 2. Using BindEvent(), BindGadgetEvent()
To compare it with the use of bindevent I did the task again.
Code: Select all
;| Advanced Event Handling -- Version 2 (BindEvent)
EnableExplicit
DebugLevel 9
Enumeration Window
#WINDOW_Main
EndEnumeration
Enumeration Gadget
#GADGET_btnStart
#GADGET_btnStop
#GADGET_chkTest
#GADGET_txtLoopOutput
#GADGET_lbTrace
EndEnumeration
#WINDOW_Main_Flags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget
#LOOP_MaxLoops = 1000
#LOOP_SlowDownDelay = 100
; ---------------------------------------------------------------------------------------------------------------------
Global ApplicationQuit = #False ; Application flag
Global LoopRunningState = #False ; init --> not running
; ---------------------------------------------------------------------------------------------------------------------
Declare MainWindowSizeEvent()
Declare OnButtonEvents()
Declare OnCloseWindowEvent()
;
Declare ClearWindowMessages()
; ---------------------------------------------------------------------------------------------------------------------
Macro Trace(MessageText)
AddGadgetItem(#GADGET_lbTrace, -1, #PB_Compiler_Procedure + "() :: " + MessageText)
SetGadgetState(#GADGET_lbTrace, CountGadgetItems(#GADGET_lbTrace) - 1)
EndMacro
; ---------------------------------------------------------------------------------------------------------------------
Procedure ClearWindowMessages()
While WindowEvent(): Wend
EndProcedure
;/---------------------------------------------------------------------------------------------------------------------
;| Test Function
;|
;| the stupid long lasting code in one function (i.e. scanning dirs, copying files, parsing files, etc.
;|
;\
Procedure Test_LongLoop()
Protected index, msg$
For index = 0 To #LOOP_MaxLoops
Delay(#LOOP_SlowDownDelay) ; spend some time with nothing
msg$ = " Item " + Str(index) + "/" + Str(#LOOP_MaxLoops) + " | Checkbox.State == " + GetGadgetState(#GADGET_chkTest)
SetGadgetText(#GADGET_txtLoopOutput, msg$)
If index % 10
ClearWindowMessages()
EndIf
If ApplicationQuit ; ...................................... main window close button clicked, escape the loop
Debug #PB_Compiler_Procedure + "() :: ApplicationQuit == TRUE " ;,9
Trace("ApplicationQuit == " + ApplicationQuit)
Break ; .................................................. leeve the loop, now
EndIf
If Not LoopRunningState
Debug #PB_Compiler_Procedure + "() :: LoopRunningState == FALSE " ;,9
Trace("LoopRunningState == FALSE ")
Break
EndIf
Next index
If index >= #LOOP_MaxLoops
SetGadgetText(#GADGET_txtLoopOutput, "Loop finished.")
Trace("Loop finished (reached MaxLoops == " + index + ")")
EndIf
LoopRunningState = #False
EndProcedure
;-=== GUI Main Window =================================================================================================
Procedure OpenMainWindow(Width = 400, Height = 320)
Protected ww
If OpenWindow(#WINDOW_Main, 0, 0, Width, Height, "Event handling example...", #WINDOW_Main_Flags)
StickyWindow(#WINDOW_Main, 1) ; show test app always above the PB_IDE
ww = (Width - 16) / 2
ButtonGadget(#GADGET_btnStart, 8, 4, ww, 32, "Start Looping")
ButtonGadget(#GADGET_btnStop, ww + 8, 4, ww, 32, "Stop Looping")
CheckBoxGadget(#GADGET_chkTest, 8, 40, Width - 16, 24, "Check me -- (see the event handling while the loop is running)")
TextGadget(#GADGET_txtLoopOutput, 8, 72, Width - 16, 20, "Loop is stopped.")
;SetGadgetColor(#GADGET_txtLoopOutput, #PB_Gadget_BackColor, GetSysColor_(#COLOR_INFOBK)) ; windows only
ListViewGadget(#GADGET_lbTrace, 8, 104, Width - 16, Height - 112) ;, $4000) ; #LBS_NOSEL == 0x4000 .. windows only
; If CreateMenu(0, WindowID(#WINDOW_Main))
; MenuTitle("Menu")
; MenuItem(1, "Item 1")
; MenuItem(2, "Item 2")
; MenuItem(3, "Item 3")
; EndIf
BindEvent(#PB_Event_SizeWindow, @MainWindowSizeEvent(), #WINDOW_Main)
BindEvent(#PB_Event_CloseWindow, @OnCloseWindowEvent(), #WINDOW_Main)
BindGadgetEvent(#GADGET_btnStart, @OnButtonEvents())
BindGadgetEvent(#GADGET_btnStop, @OnButtonEvents())
BindGadgetEvent(#GADGET_chkTest, @OnButtonEvents())
EndIf
ProcedureReturn WindowID(#WINDOW_Main)
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure MainWindowSizeEvent()
Protected ww, wndW, wndH : Debug #PB_Compiler_Procedure, 9
wndW = WindowWidth(#WINDOW_Main)
wndH = WindowHeight(#WINDOW_Main)
Trace("Window Size == " + wndW + " x " + wndH)
ww = (wndW - 16) / 2
ResizeGadget(#GADGET_btnStart, #PB_Ignore, #PB_Ignore, ww, #PB_Ignore)
ResizeGadget(#GADGET_btnStop, ww + 10, #PB_Ignore, ww, #PB_Ignore)
ResizeGadget(#GADGET_txtLoopOutput, #PB_Ignore, #PB_Ignore, wndW - 16, #PB_Ignore)
ResizeGadget(#GADGET_lbTrace, #PB_Ignore, #PB_Ignore, wndW - 16, wndH - 112)
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure OnButtonEvents()
Select EventGadget()
Case #GADGET_btnStart : Debug "#GADGET_btnStart clicked!", 9
LoopRunningState = #True
ClearGadgetItems(#GADGET_lbTrace)
SetGadgetText(#GADGET_txtLoopOutput, "Loop is running")
Case #GADGET_btnStop : Debug "#GADGET_btnStop clicked!", 9
SetGadgetText(#GADGET_txtLoopOutput, "Loop stopped.")
LoopRunningState = #False
Case #GADGET_chkTest : Debug "#GADGET_chkTest clicked!", 9
; Debug " State == " + GetGadgetState(#GADGET_chkTest), 9
Trace("Gadget = #GADGET_chkTest == " + GetGadgetState(#GADGET_chkTest))
Default
Trace("Gadget = " + EventGadget())
EndSelect
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure OnCloseWindowEvent()
Trace("CloseWindow .. set flag to TRUE ") : Debug "CloseWindow .. set flag to TRUE ", 9
ApplicationQuit = #True ; set once, cannot released by further calls
;
; or call a procedure like ExitApplication()
;
EndProcedure
;-=== main ============================================================================================================
Procedure main()
Protected event, first
If OpenMainWindow()
Repeat
event = WaitWindowEvent(20)
If event = #PB_Event_None
If LoopRunningState = #True
If first = #False
Test_LongLoop() ; <-- cannot called by BindEvent() procedures
first = #True
EndIf
Else
first = #False
EndIf
ElseIf event = #PB_Event_CloseWindow
If EventWindow() = #WINDOW_Main
Debug "MainLoop Exit "
Break
EndIf
EndIf
Until ApplicationQuit
EndIf
ProcedureReturn 0
EndProcedure
End main()
;-=== BoF =============================================================================================================