Updating listviews...
- airsoftsoftwair
- Posts: 5446
- Joined: Fri Feb 12, 2010 2:33 pm
- Location: Germany
- Contact:
Re: Updating listviews...
I'm afraid that this is a "feature". GUI applications are expected not to block the main (UI) thread so your application must always make sure to return control to the main loop to give your app a chance to handle refresh and get user input. Instead of timers you could use functions like RunCallback() to push functions on a call stack to make sure your script doesn't block the main loop.
The fact that it works on Windows and on AmigaOS is that the GUI implementation on those OS is more "old-school" than with GTK3 or something. With modern GUI implementations (e.g. GTK3, macOS, Android) is absolutely mandatory to not block the UI thread or your app won't refresh.
The fact that it works on Windows and on AmigaOS is that the GUI implementation on those OS is more "old-school" than with GTK3 or something. With modern GUI implementations (e.g. GTK3, macOS, Android) is absolutely mandatory to not block the UI thread or your app won't refresh.
Re: Updating listviews...
Thank you for the clarification, now the problem is how to implement long callback operations and giving feedback to the user... I was thinking about "application states" to be processed in the main loop.airsoftsoftwair wrote: ↑Sun Jan 28, 2024 12:42 pm I'm afraid that this is a "feature". GUI applications are expected not to block the main (UI) thread so your application must always make sure to return control to the main loop to give your app a chance to handle refresh and get user input. Instead of timers you could use functions like RunCallback() to push functions on a call stack to make sure your script doesn't block the main loop.
The fact that it works on Windows and on AmigaOS is that the GUI implementation on those OS is more "old-school" than with GTK3 or something. With modern GUI implementations (e.g. GTK3, macOS, Android) is absolutely mandatory to not block the UI thread or your app won't refresh.
The idea is that the callback function initialize the long operation and change the app state returning almost immediately, in the main loop, just after the WaitEvent() command I could check the app state and do the processing step by step without blocking the app... I've to think about it...
Code: Select all
--- just an idea ---
Do
If state = "idle"
WaitEvent()
Else
Switch state
Case "process-file"
; When the last file is processed the function sets the state to "idle"
process_one_file()
...
EndSwitch
CheckEvent()
EndIf
Forever
Could this work?
----------------------------
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
Re: Updating listviews...
I was experimenting with my idea and I've noticed that the WaitEvent() never returns, I don't know if this is normal or not with RapaGUI, please have a look at my modified example:airsoftsoftwair wrote: ↑Sun Jan 28, 2024 12:42 pm I'm afraid that this is a "feature". GUI applications are expected not to block the main (UI) thread so your application must always make sure to return control to the main loop to give your app a chance to handle refresh and get user input. Instead of timers you could use functions like RunCallback() to push functions on a call stack to make sure your script doesn't block the main loop.
The fact that it works on Windows and on AmigaOS is that the GUI implementation on those OS is more "old-school" than with GTK3 or something. With modern GUI implementations (e.g. GTK3, macOS, Android) is absolutely mandatory to not block the UI thread or your app won't refresh.
Code: Select all
@REQUIRE "RapaGUI"
Function testFunc()
For i=1 To 100
moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom")
;moai.DoMethod("lv", "jump", "bottom")
moai.DoMethod("lv", "redraw")
Wait(20, #MILLISECONDS)
DebugPrint("Added item " .. i)
Next
EndFunction
Function msgHandler(msg)
Switch msg.action
; Parse RapaGUI events
Case "RapaGUI"
; Filter actions by attributes
Switch msg.attribute
; Check button presses
Case "Pressed"
; Check buttons
Switch msg.id
Case "go"
testFunc()
EndSwitch
EndSwitch
EndSwitch
EndFunction
moai.CreateApp([[<?xml version="1.0" encoding="iso-8859-1"?>
<application id="app">
<menubar id="menu">
<menu title="_File">
<item id="mn_about">_About...</item>
<item/>
<item id="mn_quit">_Quit</item>
</menu>
</menubar>
<window title="TEST LV" id="window" menubar="menu">
<vgroup frame="true" frametitle="Update Test">
<texteditor id="lv" readonly="true">
</texteditor>
<button id="go">GO</button>
</vgroup>
</window>
</application>
]])
; listen to these events!
InstallEventHandler({
RapaGUI = msgHandler
})
; Welcome message
moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom")
; Let's go!
Repeat
DebugPrint("Before Wait Event")
WaitEvent
DebugPrint("After Wait Event")
Forever
I'm still on Linux
----------------------------
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
Re: Updating listviews...
I think that is normal. From the Help About RapaGUI / Compatibility Notes:
– WaitEvent() will never return. On most platforms, a call to WaitEvent() will be a one-way ticket that starts your application's main event loop and never returns.
Re: Updating listviews...
Wow, goods to know, thank you for the info. This complicates things at least until I fully understand how to update widget during a callbackFlinx wrote: ↑Thu Feb 01, 2024 10:42 am I think that is normal. From the Help About RapaGUI / Compatibility Notes:– WaitEvent() will never return. On most platforms, a call to WaitEvent() will be a one-way ticket that starts your application's main event loop and never returns.
----------------------------
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
Re: Updating listviews...
Hmm. From the same help page:
All RapaGUI projects must use WaitEvent() now. It's no longer acceptable to implement custom event handling using CheckEvent() or CheckEvents().
Re: Updating listviews...
I'm a bit lost here... below there is the sample test changed to use RunCallback() but it seems that the listview is updated only at the end of the loop... callbacks are set all at once but when the queued callbacks are processed they do not update the listview... OMG this is tricky
Code: Select all
@REQUIRE "RapaGUI"
Function testFunc()
For i=1 To 100
moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom")
;moai.DoMethod("lv", "jump", "bottom")
;moai.DoMethod("lv", "redraw")
Wait(50, #MILLISECONDS)
DebugPrint("Added item " .. i)
Next
EndFunction
Function msgHandler(msg)
Switch msg.action
; Parse RapaGUI events
Case "RapaGUI"
; Filter actions by attributes
Switch msg.attribute
; Check button presses
Case "Pressed"
; Check buttons
Switch msg.id
Case "go"
;--testFunc()
f=Function(msg) DebugPrint("--> ", msg.userdata) Wait(50,#MILLISECONDS) moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom") EndFunction
fm=Function(msg) moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom") EndFunction
For n = 0 To 10
DebugPrint(f,fm)
RunCallback(fm,n)
RunCallback(f,n)
Next
EndSwitch
EndSwitch
EndSwitch
EndFunction
moai.CreateApp([[<?xml version="1.0" encoding="iso-8859-1"?>
<application id="app">
<menubar id="menu">
<menu title="_File">
<item id="mn_about">_About...</item>
<item/>
<item id="mn_quit">_Quit</item>
</menu>
</menubar>
<window title="TEST LV" id="window" menubar="menu">
<vgroup frame="true" frametitle="Update Test">
<texteditor id="lv" readonly="true">
</texteditor>
<button id="go">GO</button>
</vgroup>
</window>
</application>
]])
; listen to these events!
InstallEventHandler({
RapaGUI = msgHandler
})
; Welcome message
moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom")
; Let's go!
Repeat
DebugPrint("Before Wait Event")
WaitEvent
DebugPrint("After Wait Event")
Forever
----------------------------
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
Re: Updating listviews...
The problem with the last example is that there is also a For loop that does not pass control to the main loop, so all RunCallback calls are executed at once.
I have converted your previous example into a recursive function that only executes one action and then calls itself for the next one via RunCallback.
The start value is passed as simulated userdata on the first call.
I have converted your previous example into a recursive function that only executes one action and then calls itself for the next one via RunCallback.
The start value is passed as simulated userdata on the first call.
Code: Select all
@REQUIRE "RapaGUI"
Function testFunc(msg)
moai.DoMethod("lv", "insert", ToString(msg.userdata).." " .. GetTime(True) .. ": " .. "Hello!\n", "bottom")
;moai.DoMethod("lv", "jump", "bottom")
moai.DoMethod("lv", "redraw")
Wait(20, #MILLISECONDS)
DebugPrint("Added item " .. msg.userdata)
If msg.userdata<100 Then RunCallback(testFunc,msg.userdata+1)
EndFunction
Function msgHandler(msg)
Switch msg.action
; Parse RapaGUI events
Case "RapaGUI"
; Filter actions by attributes
Switch msg.attribute
; Check button presses
Case "Pressed"
; Check buttons
Switch msg.id
Case "go"
testFunc({["userdata"] =1})
EndSwitch
EndSwitch
EndSwitch
EndFunction
moai.CreateApp([[<?xml version="1.0" encoding="iso-8859-1"?>
<application id="app">
<menubar id="menu">
<menu title="_File">
<item id="mn_about">_About...</item>
<item/>
<item id="mn_quit">_Quit</item>
</menu>
</menubar>
<window title="TEST LV" id="window" menubar="menu">
<vgroup frame="true" frametitle="Update Test">
<texteditor id="lv" readonly="true">
</texteditor>
<button id="go">GO</button>
</vgroup>
</window>
</application>
]])
; listen to these events!
InstallEventHandler({
RapaGUI = msgHandler
})
; Welcome message
moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom")
; Let's go!
Repeat
DebugPrint("Before Wait Event")
WaitEvent
DebugPrint("After Wait Event")
Forever
Re: Updating listviews...
Thank you @Flinx to try to resolve this puzzle and taking time to experiment, your example is working but I have a couple of observations about my previous example's loop
The For/Next that executes the RunCallback() is not blocking, it just put the callback functions into the event's queue, infact look at this piece of code executed when the button is pressed:
DebugPrint(f,fm) prints all the stuff in one go because the For/Next is executed in no time and the RunCallback() commands put the function's calls in the event queue, then the control is returned to the WaitEvent().
Now the system should call the functions placed on the events stack, alternatining the f() and the fm() function calls, infact in the debug window the DebugPrint() of the f() function prints some text but the fm() function does not trigger the listview update which is updated only at the end of the event's queue. That's the question: are widgets updated only when all events has been served? Looking at your example seems that the answer is no because you are calling a calback function that never returns calling itself recursively.
I'm not sure why your example works, using recursion like you did is ok but what happens if the recursion goes deep like 30000 times? Stack overflow?
I want to expose my real case for which I want to show some user feedback.
I'm building an utility to calculate checksums & hashes of some files: the user drops the files to process into a list view then, when ready, he push a button and the program starts the calculations. Well, it was an epic fail
I'm not able to update the listview while the files are being processed or better: I'm not able to force the listview to show what I've added to it.
About implementing this stuff using recursion.. well, maybe it could work, but I think it's unneeded overcomplication that could lead to stack overflows, I don't know how many files the user wants to calculate, he could ask for the sha1 of his entire disk.
There should be a legal/elegant way to implement such stuff without involving sorceries like transforming a simple loop into a recursive loop
Any ideas?
The For/Next that executes the RunCallback() is not blocking, it just put the callback functions into the event's queue, infact look at this piece of code executed when the button is pressed:
Code: Select all
Case "go"
;--testFunc()
f=Function(msg) DebugPrint("--> ", msg.userdata) Wait(50,#MILLISECONDS) moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom") EndFunction
fm=Function(msg) moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom") EndFunction
For n = 0 To 10
DebugPrint(f,fm)
RunCallback(fm,n)
RunCallback(f,n)
Next
Now the system should call the functions placed on the events stack, alternatining the f() and the fm() function calls, infact in the debug window the DebugPrint() of the f() function prints some text but the fm() function does not trigger the listview update which is updated only at the end of the event's queue. That's the question: are widgets updated only when all events has been served? Looking at your example seems that the answer is no because you are calling a calback function that never returns calling itself recursively.
I'm not sure why your example works, using recursion like you did is ok but what happens if the recursion goes deep like 30000 times? Stack overflow?
I want to expose my real case for which I want to show some user feedback.
I'm building an utility to calculate checksums & hashes of some files: the user drops the files to process into a list view then, when ready, he push a button and the program starts the calculations. Well, it was an epic fail
I'm not able to update the listview while the files are being processed or better: I'm not able to force the listview to show what I've added to it.
About implementing this stuff using recursion.. well, maybe it could work, but I think it's unneeded overcomplication that could lead to stack overflows, I don't know how many files the user wants to calculate, he could ask for the sha1 of his entire disk.
There should be a legal/elegant way to implement such stuff without involving sorceries like transforming a simple loop into a recursive loop
Any ideas?
Flinx wrote: ↑Fri Feb 02, 2024 4:37 pm The problem with the last example is that there is also a For loop that does not pass control to the main loop, so all RunCallback calls are executed at once.
I have converted your previous example into a recursive function that only executes one action and then calls itself for the next one via RunCallback.
The start value is passed as simulated userdata on the first call.Code: Select all
@REQUIRE "RapaGUI" Function testFunc(msg) moai.DoMethod("lv", "insert", ToString(msg.userdata).." " .. GetTime(True) .. ": " .. "Hello!\n", "bottom") ;moai.DoMethod("lv", "jump", "bottom") moai.DoMethod("lv", "redraw") Wait(20, #MILLISECONDS) DebugPrint("Added item " .. msg.userdata) If msg.userdata<100 Then RunCallback(testFunc,msg.userdata+1) EndFunction Function msgHandler(msg) Switch msg.action ; Parse RapaGUI events Case "RapaGUI" ; Filter actions by attributes Switch msg.attribute ; Check button presses Case "Pressed" ; Check buttons Switch msg.id Case "go" testFunc({["userdata"] =1}) EndSwitch EndSwitch EndSwitch EndFunction moai.CreateApp([[<?xml version="1.0" encoding="iso-8859-1"?> <application id="app"> <menubar id="menu"> <menu title="_File"> <item id="mn_about">_About...</item> <item/> <item id="mn_quit">_Quit</item> </menu> </menubar> <window title="TEST LV" id="window" menubar="menu"> <vgroup frame="true" frametitle="Update Test"> <texteditor id="lv" readonly="true"> </texteditor> <button id="go">GO</button> </vgroup> </window> </application> ]]) ; listen to these events! InstallEventHandler({ RapaGUI = msgHandler }) ; Welcome message moai.DoMethod("lv", "insert", GetTime(True) .. ": " .. "Hello!\n", "bottom") ; Let's go! Repeat DebugPrint("Before Wait Event") WaitEvent DebugPrint("After Wait Event") Forever
----------------------------
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆
[Allanon] Fabio Falcucci | GitHub for recent works | Support me on Patreon for Hollywood libraries | ☆★ All my links ★☆