ProgressBar() & PostEvent() - A Threaded Example

Share your advanced PureBasic knowledge/code with the community.
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

ProgressBar() & PostEvent() - A Threaded Example

Post by TI-994A »

This small example demonstrates the implementation of a ProgressBar() gadget updating a threaded process using the PostEvent() function. The inline comments are quite self-explanatory.

Code: Select all

; this example simulates a lengthy blocking process executed in a thread to
; demonstrate the implementations of PostEvent and the ProgressBar gadget 

; assign the custom event value
#progressBarEvent = #PB_Event_FirstCustomValue

; read a large 13mb file to simulate a blocking process
CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
  file$ = #PB_Compiler_Home + "compilers/engine3d.dylib"
CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux   
  file$ = #PB_Compiler_Home + "compilers/engine3d.so"
CompilerElse
  file$ = #PB_Compiler_Home + "compilers/engine3d.dll"
CompilerEndIf

Procedure threadedProcess(null)     
  Shared file$
  If ReadFile(0, file$)        
    fileLen = Lof(0)    
    While Not Eof(0)
      
      ; read one byte at a time 
      ReadByte(0)      
      
      ; get current read location
      readBytes = Loc(0)      
      
      ; update the progress bar in 1k increments
      If Mod(readBytes, 1000) = 0   
        
        ; post the custom event with the current read location
        PostEvent(#progressBarEvent, -1, -1, -1, readBytes)   
        
        Delay(1)        
      EndIf
    Wend    
    
    ; one last post to send the total read size
    PostEvent(#progressBarEvent, -1, -1, -1, readBytes)
    
    CloseFile(0)    
  EndIf  
EndProcedure

; ensure threadsafe enabled
CompilerIf #PB_Compiler_Thread
  
  wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
  OpenWindow(0, 0, 0, 800, 300,"ProgressBar/PostEvent Example", wFlags)
  TextGadget(0, 10, 10, 780, 50, "00:00:00", #PB_Text_Right)
  TextGadget(1, 10, 200, 780,  40, "", #PB_Text_Center)
  ButtonGadget(2, 300, 60, 200, 30, "Start threaded process...")
  AddWindowTimer(0, 0, 1000)   
  
  ; instantiate a progress bar scaled to the file size
  If FileSize(file$) > 0 And ReadFile(0, file$)      
    
    ; ProgressBar() maximum allowable x-platform value = 65536
    ; calculate the maximum value according to this scale
    fileLen = Lof(0)
    If fileLen > 65536
      max = 65536
    Else
      max = fileLen
    EndIf    
    ProgressBarGadget(3, 10, 120, 780, 50, 0, max)  
    
    HideGadget(3, #True)
    CloseFile(0)
  Else 
    MessageRequester("ProgressBar/PostEvent Example", "File error!")
    appQuit = 1
  EndIf    
  
  Repeat
    event = WaitWindowEvent()
    Select event
        
      Case #PB_Event_CloseWindow
        appQuit = 1
        
      Case #PB_Event_Gadget
        If EventGadget() = 2
          HideGadget(3, #False)
          DisableGadget(2, #True)
          
          ; start reading the file in a thread
          CreateThread(@threadedProcess(), 0)
          
        EndIf
        
      Case #PB_Event_Timer
        If EventTimer() = 0
          
          ; a running clock shows that window events are not blocked
          SetGadgetText(0, FormatDate("%hh:%ii:%ss", Date()))     
          
        EndIf
        
      ; process the custom event   
      Case #progressBarEvent        
        
        ; get the current read location
        currentRead = EventData()       
              
        ; calculate the current location of the progress bar
        If fileLen > 65536
          inc = currentRead / (fileLen / 65536)
        Else
          inc = currentRead
        EndIf
                
        ; increment the progress bar & label with the read location value
        SetGadgetState (3, inc)               
        SetGadgetText(1, "File Progress (" + Str(currentRead) + "/" + Str(fileLen) + ")")
        
        If currentRead = fileLen
          DisableGadget(2, #False)
        EndIf
        
    EndSelect
  Until appQuit
  
CompilerElse 
    
  MessageRequester("ProgressBar/PostEvent Example", "Please enable Compiler > Compiler Options > Create threadsafe executable.")
  
CompilerEndIf
For those who asked. :wink:
Last edited by TI-994A on Sun Aug 18, 2024 7:02 pm, edited 3 times in total.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
mk-soft
Always Here
Always Here
Posts: 6246
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: ProgressBar() & PostEvent() - A Threaded Example

Post by mk-soft »

If #PB_Compiler_Thread = 1 :?:

Code: Select all

CompilerIf Not #PB_Compiler_Thread
  CompilerError "Use Compiler Option ThreadSafe!"
CompilerEndIf
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: ProgressBar() & PostEvent() - A Threaded Example

Post by TI-994A »

mk-soft wrote: Sun Aug 18, 2024 12:43 pm

Code: Select all

CompilerIf Not #PB_Compiler_Thread
  CompilerError "Use Compiler Option ThreadSafe!"
CompilerEndIf
You're right; best practices would dictate the use of the compiler conditions with the compiler thread directive; although it works with standard if/else conditions just as well. Moreover, for such small tutorial examples, I'd rather implement the gentler message requester instead of the hasher compiler error. In this instance, it doesn't make a difference.

Nevertheless, I've edited the code accordingly, but purely to comply with syntactical best practices. :D
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
tester
User
User
Posts: 34
Joined: Sun Dec 28, 2014 1:12 pm

Re: ProgressBar() & PostEvent() - A Threaded Example

Post by tester »

Code: Select all

[18:26:39] Waiting for executable to start...
[18:26:39] Executable type: Windows - x64  (64bit, Unicode, Thread, Purifier)
[18:26:39] Executable started.
[18:26:47] [ERROR] Line: 80
[18:26:47] [ERROR] The specified #Gadget is not initialised.
[18:26:54] The debugged executable quit unexpectedly.
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: ProgressBar() & PostEvent() - A Threaded Example

Post by TI-994A »

tester wrote: Sun Aug 18, 2024 4:27 pm

Code: Select all

[18:26:47] [ERROR] Line: 80
[18:26:47] [ERROR] The specified #Gadget is not initialised.
The gadget on line 80 is the progress bar. It's not initialised probably because the sample file (.../compilers/engine3d.xxx) was not found.

I've amended the code to handle this. Please try it again now, and ensure that the path to the sample file is correct and that the file exists. Otherwise, just replace it with any other large file.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
tester
User
User
Posts: 34
Joined: Sun Dec 28, 2014 1:12 pm

Re: ProgressBar() & PostEvent() - A Threaded Example

Post by tester »

For Windows, we need to replace

Code: Select all

OpenFile(0, file$)
with

Code: Select all

ReadFile(0, file$)
otherwise for

Code: Select all

C:\Program Files\PureBasic\Compilers\Engine3d.dll
will be "access denied".
User avatar
TI-994A
Addict
Addict
Posts: 2740
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: ProgressBar() & PostEvent() - A Threaded Example

Post by TI-994A »

tester wrote: Sun Aug 18, 2024 5:56 pmFor Windows, we need to replace

Code: Select all

OpenFile(0, file$)
with

Code: Select all

ReadFile(0, file$)
After testing, I can confirm that the platform or version does not cause the error, but rather, the location where PureBasic is installed. If installed in a write-restricted region, the OpenFile() function will fail. Thus the ReadFile() function would be a better option.

Thank you for pointing that out - code amended accordingly. :D
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
Post Reply