How to get an Editor gadget to refresh

Everything else that doesn't fall into one of the other PB categories.
dchisholm
New User
New User
Posts: 7
Joined: Tue Mar 31, 2015 9:44 pm
Location: Ottawa, ON Canada
Contact:

How to get an Editor gadget to refresh

Post by dchisholm »

I have defined a simple form as follows:

Code: Select all

Global Window_0

Global Image_0, Text_0, Button_0, Editor_0, Text_1, Text_2

Global Img_Window_0_0

UsePNGImageDecoder()

Img_Window_0_0 = LoadImage(#PB_Any,"C:\Data\PB\PFP\ba_logo.png")

Enumeration FormFont
  #Font_Window_0_0
  #Font_Window_0_1
EndEnumeration

LoadFont(#Font_Window_0_0,"Arial", 18, #PB_Font_Bold)
LoadFont(#Font_Window_0_1,"Arial", 12, #PB_Font_Bold)


Procedure OpenWindow_0(x = 0, y = 0, width = 590, height = 610)
  Window_0 = OpenWindow(#PB_Any, x, y, width, height, "Prepare for Print", #PB_Window_SystemMenu)
  SetWindowColor(Window_0, RGB(255,255,255))
  Image_0 = ImageGadget(#PB_Any, 60, 10, 470, 120, ImageID(Img_Window_0_0))
  Text_0 = TextGadget(#PB_Any, 70, 140, 450, 40, "Prepare for Print", #PB_Text_Center)
  SetGadgetColor(Text_0, #PB_Gadget_FrontColor,RGB(17,98,165))
  SetGadgetColor(Text_0, #PB_Gadget_BackColor,RGB(255,255,255))
  SetGadgetFont(Text_0, FontID(#Font_Window_0_0))
  Button_0 = ButtonGadget(#PB_Any, 230, 190, 100, 40, "Start")
  Editor_0 = EditorGadget(#PB_Any, 70, 270, 450, 290)
  SetGadgetColor(Editor_0, #PB_Gadget_BackColor,RGB(255,255,255))
  Text_1 = TextGadget(#PB_Any, 70, 250, 450, 20, "Processing Status", #PB_Text_Center)
  SetGadgetColor(Text_1, #PB_Gadget_FrontColor,RGB(17,98,165))
  SetGadgetColor(Text_1, #PB_Gadget_BackColor,RGB(255,255,255))
  SetGadgetFont(Text_1, FontID(#Font_Window_0_1))
  Text_2 = TextGadget(#PB_Any, 70, 580, 450, 25, "", #PB_Text_Center)
  SetGadgetColor(Text_2, #PB_Gadget_FrontColor,RGB(0,0,0))
  SetGadgetColor(Text_2, #PB_Gadget_BackColor,RGB(255,255,255))
EndProcedure
I then call the form (PFP.pbf) from my main code as follows:

Code: Select all

IncludeFile "PFP.pbf"

;--------------------------------------------------------------------------------
; MAIN PROGRAM STARTS HERE
;--------------------------------------------------------------------------------

Define.l event 

OpenWindow_0()
SetGadgetText(Text_2, #VERSION)

Repeat
  event = WaitWindowEvent()
  Select event
    Case #PB_Event_Gadget
      Select EventGadget()
        Case Button_0
          ClearGadgetItems(Editor_0)
          Process()
      EndSelect
  EndSelect  
Until event = #PB_Event_CloseWindow
End 
The procedure "Process" processes a large XML file and posts messages of its progress to the Editor via a call like:

Code: Select all

AddGadgetItem(Editor_0, -1, "***ERROR*** - failed to pass XML validation.  Program aborting...")
The problem is that the Editor_0 window does not refresh until all the processing is done. Is there a way to force a refresh of this window (or the form)?

Thanks.

/Dave
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: How to get an Editor gadget to refresh

Post by IdeasVacuum »

You might like to try a ListView or ListIcon instead.

However, you can give the EditorGadget the moment it needs to update by following AddGadgetItem(msg) with:

Code: Select all

While WindowEvent() : Wend
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to get an Editor gadget to refresh

Post by Danilo »

dchisholm wrote:The procedure "Process" processes a large XML file and posts messages of its progress to the Editor via a call like:

Code: Select all

AddGadgetItem(Editor_0, -1, "***ERROR*** - failed to pass XML validation.  Program aborting...")
The problem is that the Editor_0 window does not refresh until all the processing is done. Is there a way to force a refresh of this window (or the form)?
For Windows OS:

Code: Select all

Procedure UpdateGadget(gadget, repaintBackground = #True)
    InvalidateRect_( GadgetID(gadget), 0, repaintBackground )
    UpdateWindow_( GadgetID(gadget) )
EndProcedure

If OpenWindow(0, 0, 0, 322, 250, "EditorGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    EditorGadget(0, 8, 8, 306, 233)
    While WindowEvent() : Wend
    
    For a = 0 To 10
        AddGadgetItem(0, a, "Line "+Str(a))
        ;UpdateGadget(0)
        Delay(500)
    Next
    
    Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
UpdateWindow_() bypasses the event queue and calls the internal #WM_PAINT handler immediately.

The problem is that your event loop isn't running while you process the XML file. Long tasks are better
done within threads. For updating the GUI you post messages to the main event loop.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8433
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to get an Editor gadget to refresh

Post by netmaestro »

I modified your posted code with a potential approach utilizing Danilo's thread recommendation:

Code: Select all

CompilerIf #PB_Compiler_Thread = 0
  CompilerError "Please compile with threadsafe switch set "+#DQUOTE$+"ON"+#DQUOTE$
CompilerEndIf

Global Window_0

Global Image_0, Text_0, Button_0, Editor_0, Text_1, Text_2

Global Img_Window_0_0

Global processing, paused, tid, errorstring.s

UsePNGImageDecoder()

Img_Window_0_0 = LoadImage(#PB_Any, #PB_Compiler_Home+"examples\sources\data\purebasiclogo.bmp")

#DisplayError = #PB_EventType_FirstCustomValue

Enumeration FormFont
  #Font_Window_0_0
  #Font_Window_0_1
EndEnumeration

LoadFont(#Font_Window_0_0,"Arial", 18, #PB_Font_Bold)
LoadFont(#Font_Window_0_1,"Arial", 12, #PB_Font_Bold)


Procedure OpenWindow_0(x = 0, y = 0, width = 590, height = 610)
  Window_0 = OpenWindow(#PB_Any, x, y, width, height, "Prepare for Print", #PB_Window_SystemMenu)
  SetWindowColor(Window_0, RGB(255,255,255))
  Image_0 = ImageGadget(#PB_Any, 60, 10, 470, 120, ImageID(Img_Window_0_0))
  Text_0 = TextGadget(#PB_Any, 70, 140, 450, 40, "Prepare for Print", #PB_Text_Center)
  SetGadgetColor(Text_0, #PB_Gadget_FrontColor,RGB(17,98,165))
  SetGadgetColor(Text_0, #PB_Gadget_BackColor,RGB(255,255,255))
  SetGadgetFont(Text_0, FontID(#Font_Window_0_0))
  Button_0 = ButtonGadget(#PB_Any, 230, 190, 100, 40, "Start")
  Editor_0 = EditorGadget(#PB_Any, 70, 270, 450, 290)
  SetGadgetColor(Editor_0, #PB_Gadget_BackColor,RGB(255,255,255))
  Text_1 = TextGadget(#PB_Any, 70, 250, 450, 20, "Processing Status", #PB_Text_Center)
  SetGadgetColor(Text_1, #PB_Gadget_FrontColor,RGB(17,98,165))
  SetGadgetColor(Text_1, #PB_Gadget_BackColor,RGB(255,255,255))
  SetGadgetFont(Text_1, FontID(#Font_Window_0_1))
  Text_2 = TextGadget(#PB_Any, 70, 580, 450, 25, "", #PB_Text_Center)
  SetGadgetColor(Text_2, #PB_Gadget_FrontColor,RGB(0,0,0))
  SetGadgetColor(Text_2, #PB_Gadget_BackColor,RGB(255,255,255))
EndProcedure

Procedure SendLine(msg$)
  *msg = AllocateMemory(StringByteLength(msg$)+SizeOf(Character))
  PokeS(*msg, msg$)
  PostEvent(#PB_Event_Gadget, Window_0, Editor_0, #DisplayError, *msg)
EndProcedure

Procedure Process(iterations)
  Protected cc
  processing=#True
  While processing
    If Not paused
      SendLine("***ERROR*** "+Str(Random(100)))
      cc+1: If cc>=iterations : processing=#False : Else : Delay(1000) : EndIf
    Else
      Delay(1)
    EndIf
  Wend
  If cc=iterations
    SendLine("processing Complete")
  EndIf
EndProcedure

;--------------------------------------------------------------------------------
; MAIN PROGRAM STARTS HERE
;--------------------------------------------------------------------------------

Define.l event, quit=#False

OpenWindow_0()

SetGadgetText(Text_2, "Version 1")

Repeat
  event = WaitWindowEvent()
  Select event
    Case #PB_Event_CloseWindow
      If processing
        paused=#True
        result = MessageRequester("Error", "Still processing - Really quit?", #PB_MessageRequester_YesNoCancel)
        If result = #PB_MessageRequester_Yes
          processing=#False
          WaitThread(tid)
          quit=#True
        Else
          paused=#False
        EndIf
      Else
        quit=#True
      EndIf
    Case #PB_Event_Gadget
      Select EventGadget()
        Case Editor_0
          If EventType() = #DisplayError
            AddGadgetItem(Editor_0, -1, PeekS(EventData()))
            FreeMemory(EventData())
          EndIf
        Case Button_0
          If Not processing
            ClearGadgetItems(Editor_0)
            tid = CreateThread(@Process(), 10)
          Else
            paused=#True
            MessageRequester("Error", "Please wait until current processing has finished")
            paused=#False
          EndIf
      EndSelect
  EndSelect  
Until quit
End
Last edited by netmaestro on Thu Apr 02, 2015 2:06 am, edited 4 times in total.
BERESHEIT
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: How to get an Editor gadget to refresh

Post by luis »

@netmaestro, the code is a nice starting example minus two bad things IMHO

I don't think

Code: Select all

PostEvent(#PB_Event_Gadget, Window_0, Editor_0, #DisplayError, @errorstring)
is a reliable way to do it.

I think the string could change before the event has been processed, or worst referenced just while updating, depending on the message activity in the main window.

And I don't think KillThread() is a good example for how to end the thread.

A global var like the one you used for pausing the thread processing set to something like RUNNING, PAUSED, and SHOULDCLOSE would be better.

@ideasvacumm

I think this is bad:

Code: Select all

While WindowEvent() : Wend
OK for testing if events not processed frequently enough are the cause of a low update frequency (since it make events flow in the queue) but PB events should never blindly discarded that way.
Events should be processed or willingly ignored based on what they are telling you.
That loop just throw them away.
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8433
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to get an Editor gadget to refresh

Post by netmaestro »

My code above is updated to comply with the luis quality control standards:
- Events are only posted when confirmation has been received that processing of the last posted event is complete.
- Thread is ended under the control of a global variable rather than KillThread()
BERESHEIT
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8433
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to get an Editor gadget to refresh

Post by netmaestro »

Made a further improvement, creating each message individually and freeing it when it's applied. Eliminates the need for a global variable.
BERESHEIT
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: How to get an Editor gadget to refresh

Post by luis »

netmaestro wrote:My code above is updated to comply with the luis quality control standards
You must admit I wasn't being pedantic, the postevent() in particular was flawed in a not obvious way.

Now it's a nice example which can certainly help everyone approaching multithreading for the first time. :)
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8433
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to get an Editor gadget to refresh

Post by netmaestro »

No not at all, your observations are right on the money. I often write lazy code and I'm grateful to have someone around to call me on it from time to time.
BERESHEIT
dchisholm
New User
New User
Posts: 7
Joined: Tue Mar 31, 2015 9:44 pm
Location: Ottawa, ON Canada
Contact:

Re: How to get an Editor gadget to refresh

Post by dchisholm »

Folks,

Thanks for all the feedback. In the end I created a simple procedure that did the trick.

Code: Select all

;--------------------------------------------------------------------------------
  Procedure Write_Status(status_string.s)
;--------------------------------------------------------------------------------    
;
;  This procedure writes a string to status window on the main form
;
    AddGadgetItem(Status_Window, -1, status_string)
    While WindowEvent() : Wend
  EndProcedure
/Dave
Post Reply