Can a ListView be actualized while being filled ?

Just starting out? Need help? Post your questions and find answers here.
User avatar
Blue
Addict
Addict
Posts: 864
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Can a ListView be actualized while being filled ?

Post by Blue »

The same question would apply equally well to ListIcon gadgets.

system : PB 6.02 under Windows 11 x64

When reading long lists of data (dictionary style, between 10 000 and 20 000 items) into a ListView gadget, the gadget's display remains blank until the lengthy operation completes. That leaves the user with the false impression that nothing is happening or that the app has frozen.

I tried using a progress bar to indicate that the operation is progressing, but even that does not work since control does not revert to the system until the filling operation has completed.

Anyone with a possible solution ? I remember a DoEvents() (or something similar) function in Visual Basic that made possible a screen refresh during lengthy operations. I can't figure an equivalent in PB.

The provided code attempts to demonstrate the problem. When the data quantity gets beyond 10000, the progress bar can clearly be seen not updating, and the ListView display boxes remain desperately blank for a long time...

Code: Select all

  ; ••• ••• •••  ••• ••• ••• ••• 
  ; •     Guy - mai 2023       •
  ; • remplissage de ListViews •
  ; ••• ••• •••  ••• ••• ••• •••


EnableExplicit

; - constantes des gadgets
  Enumeration 
  ;{
  #APP_titre
  #CMD_encore : #CMD_combien
  
  #LABEL_1 : #LV_1 : #TOTAL_1
  #LABEL_2 : #LV_2 : #TOTAL_2
  #LABEL_3 : #LV_3 : #TOTAL_3
 
  #TiMER_bar : #TiMER_chrono
;}
  EndEnumeration

; - polices
  Define font$ = "Segoe UI"
  Define font0 = LoadFont(0, font$, 14, #PB_Font_Bold)
  Define font1 = LoadFont(1, font$, 12, #PB_Font_Bold)
  Define font2 = LoadFont(2, font$, 11)
  Define font3 = LoadFont(3, font$, 9)

; - fenêtre
  OpenWindow(0, 0, 0, 400, 300, "LV tests w PROC", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

; - gadgets
  Define gi, gX,gY,gW,gH
  #dX = 10  :  #dY = 10

  gi = #CMD_encore
    #multiplic = 3
    gH = 30  :  gW = 60 
    gY = #dY :  gX = WindowWidth(0) - gW - #dx 
    ButtonGadget(gi, gX,gY,gW,gH,"< Again")
    SetGadgetFont(gi, font3)

  gi = #CMD_combien
    gX = #dx 
    ButtonGadget(gi, gX,gY,gW,gH,"" + #multiplic + " * >")
    SetGadgetFont(gi, font3)

  gi = #APP_titre
    gX + gW
    gW = GadgetX(#CMD_encore) - gX
    TextGadget(gi, gX,gY,gW,gH,"infos", #PB_Text_Center| #PB_Text_Border)
    SetGadgetFont(gi, font0)
    SetGadgetColor(gi,#PB_Gadget_FrontColor,#Red)
    SetGadgetColor(gi,#PB_Gadget_BackColor,#Yellow)

  gi = #LABEL_1
    gX = #dX  :  gY + gH + 4
    gW = 120  :  gH = 20
    TextGadget(gi, gX,gY,gW,gH," items A")
  gi + 3  
    gX + gW + #dX
    TextGadget(gi, gX,gY,gW,gH," items B")
  gi + 3  
    gX + gW + #dX
    TextGadget(gi, gX,gY,gW,gH," items C")

  gi = #TOTAL_1
    gX = #dX  : gY = WindowHeight(0) - #dY - #dY - #dY - gH
    TextGadget(gi, gX,gY,gW,gH,"Please wait...")
  gi + 3
    gX + gW + #dX
    TextGadget(gi, gX,gY,gW,gH,"Please wait...")
  gi + 3
    gX + gW + #dX
    TextGadget(gi, gX,gY,gW,gH,"Please wait...")

  gi = #LV_1
    gY = GadgetY(#LABEL_1) + gH  
    gH = GadgetY(#TOTAL_1) - gY - 4
    gX = #dX
    ListViewGadget(gi, gX,gY,gW,gH)
  gi + 3  
    gX + gW + #dX
    ListViewGadget(gi, gX,gY,gW,gH)
    SetGadgetColor(gi,#PB_Gadget_BackColor,$E1FFFF)
  gi + 3  
    gX + gW + #dX
    ListViewGadget(gi, gX,gY,gW,gH)

  For gi = #LABEL_1 To #LABEL_3 Step 3
    SetGadgetColor(gi,#PB_Gadget_FrontColor,#Blue)
    SetGadgetColor(gi+2,#PB_Gadget_FrontColor,#Red)
    SetGadgetFont(gi  ,font1)   ; identificateur
    SetGadgetFont(gi+1,font3)   ; ListView
    SetGadgetFont(gi+2,font2)   ; information
  Next
  ProgressBarGadget(#TiMER_bar, 0, WindowHeight(0)-16, WindowWidth(0), 16, 0, 100)

  Declare Remplir_LV(nLines)
  Define n = 150
  Remplir_LV(n)

; - -------------------------------------
; - boucle Windows
  Define event, item$, barre
  Repeat
    event = WaitWindowEvent()
       gi = EventGadget()

    Select event
      Case #PB_Event_Timer       : Gosub mettre_a_jour
      Case #PB_Event_CloseWindow : Break

      Case #PB_Event_Gadget :
        Select gi
          Case #LV_1, #LV_2, #LV_3 : Gosub montrer_choix
          Case #CMD_combien        : Gosub augmenter
          Case #CMD_encore         : Remplir_LV(n)
        EndSelect
    EndSelect
  ForEver
  End
; - -------------------------------------
#inc = 3
mettre_a_jour:
  barre + #inc
  SetGadgetState(#TiMER_bar, barre)
  If barre >= 100 : barre = -#inc : EndIf
  Return

montrer_choix:
  item$ = GetGadgetText(gi)
  SetGadgetText(gi+1,item$)
  Debug GetGadgetText(EventGadget())
  Return

augmenter:
  n * #multiplic
  If n > 125000
    n = 150
  EndIf
  Remplir_LV(n)
  Return

Procedure Remplir_LV(nLines)
  ; - affichage des données
  Protected i, maxi, n, t1, t2
  SetGadgetText(#APP_titre, Str(nLines) + " data entries")
  RemoveWindowTimer(0, #TiMER_chrono)
  AddWindowTimer(0, #TiMER_chrono, 200)

  For i = #LV_1 To  #LV_3 Step 3
    ClearGadgetItems(i) 
  Next

  t2 = ElapsedMilliseconds()
  t1 = t2
  n = nLines/3                      ; pcq 3 LV
  maxi = n
  For i = 1 To maxi
    AddGadgetItem(#LV_1,-1,"item # "+ i)
  Next
  t2 = ElapsedMilliseconds() - t2
  SetGadgetText(#TOTAL_1, "" + t2 + " ms")
  Debug "LV 1  " + t2 + " ms"

  maxi + n
  t2 = ElapsedMilliseconds()
  For i = i To maxi
    AddGadgetItem(#LV_2,-1,"item # "+ i)
  Next
  t2 = ElapsedMilliseconds() - t2
  SetGadgetText(#TOTAL_2, "" + t2 + " ms")
  Debug "LV 2  " + t2 + " ms"

  maxi + n
  t2 = ElapsedMilliseconds()
  For i = i To maxi
    AddGadgetItem(#LV_3,-1,"item # "+ i)
  Next
  t2 = ElapsedMilliseconds() - t2
  SetGadgetText(#TOTAL_3, "" + t2 + " ms")
  Debug "LV 3  " + t2 + " ms"

  t1 = ElapsedMilliseconds() - t1
  SetWindowTitle(0, "LV remplis en " + t1 + " ms")
  Debug "durée totale " + t1 + " ms"

EndProcedure
"That's not a bug..." said the programmer. "it's a feature! "
"Oh! I see..." replied the blind man.
User avatar
Caronte3D
Addict
Addict
Posts: 1027
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Can a ListView be actualized while being filled ?

Post by Caronte3D »

I think the best solution is to use threads and actualice the lists sending messages.
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Can a ListView be actualized while being filled ?

Post by mk-soft »

Example with thread.
Only windows. On macOS or Linux crashed. See signature ThreadToGUI

Update

Code: Select all

;-TOP

#ProgramTitle = "Main Window"
#ProgramVersion = "v1.01.2"

Enumeration Windows
  #Main
EndEnumeration

Enumeration MenuBar
  #MainMenu
EndEnumeration

Enumeration MenuItems
  #MainMenuAbout
  #MainMenuExit
  #MainMenuFill
EndEnumeration

Enumeration Gadgets
  #MainList
EndEnumeration

Enumeration StatusBar
  #MainStatusBar
EndEnumeration

Enumeration CustomEvent #PB_Event_FirstCustomValue
  #MyEvent_FillUpdateStatus
  #MyEvent_FillDone
EndEnumeration

Procedure thFillList(Gadget)
  Protected i, text.s, state
  
  max = Random(30000, 10000)
  For i = 1 To max
    If i % 1000 = 0
      state + 10
      If state > 100
        state = 0
      EndIf
      PostEvent(#MyEvent_FillUpdateStatus, 0, 0, 0, state)
    EndIf
    text = "Item " + i
    AddGadgetItem(Gadget, -1, text)
  Next
  PostEvent(#MyEvent_FillUpdateStatus, 0, 0, 0, 0)
  PostEvent(#MyEvent_FillDone, 0, 0, 0, 0)
  
EndProcedure

    
Procedure UpdateWindow()
  Protected dx, dy
  dx = WindowWidth(#Main)
  dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
  ; Resize gadgets
  ResizeGadget(#MainList, 0, 0, dx, dy)
EndProcedure

Procedure Main()
  Protected dx, dy
  
  #MainStyle = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget
  
  If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, 800, 600, #ProgramTitle , #MainStyle)
    ; Menu
    CreateMenu(#MainMenu, WindowID(#Main))
    MenuTitle("&File")
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      MenuItem(#PB_Menu_About, "")
    CompilerElse
      MenuItem(#MainMenuAbout, "About")
    CompilerEndIf
    ; Menu File Items
    MenuItem(#MainMenuFill, "Fill List")
    
    CompilerIf Not #PB_Compiler_OS = #PB_OS_MacOS
      MenuBar()
      MenuItem(#MainMenuExit, "E&xit")
    CompilerEndIf
    
    ; StatusBar
    CreateStatusBar(#MainStatusBar, WindowID(#Main))
    AddStatusBarField(200)
    AddStatusBarField(#PB_Ignore)
    StatusBarProgress(#MainStatusBar, 0, 0) 
    
    ; Gadgets
    dx = WindowWidth(#Main)
    dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
    ListViewGadget(#MainList, 0, 0, dx, dy)
    
    ; Bind Events
    BindEvent(#PB_Event_SizeWindow, @UpdateWindow(), #Main)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Select EventWindow()
            Case #Main
              Break
              
          EndSelect
          
        Case #PB_Event_Menu
          Select EventMenu()
            CompilerIf #PB_Compiler_OS = #PB_OS_MacOS   
              Case #PB_Menu_About
                PostEvent(#PB_Event_Menu, #Main, #MainMenuAbout)
                
              Case #PB_Menu_Preferences
                
              Case #PB_Menu_Quit
                PostEvent(#PB_Event_CloseWindow, #Main, #Null)
                
            CompilerEndIf
              
            Case #MainMenuExit
              PostEvent(#PB_Event_CloseWindow, #Main, #Null)
              
            Case #MainMenuAbout
              MessageRequester("About", #ProgramTitle + #LF$ + #ProgramVersion, #PB_MessageRequester_Info)
              
            Case #MainMenuFill
              If Not IsThread(thFill)
                StatusBarText(#MainStatusBar, 1, "Fill Start")
                thFill = CreateThread(@thFillList(), #MainList)
              EndIf
              
          EndSelect
          
          
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #MainList
              Select EventType()
                Case #PB_EventType_Change
                  ;
              EndSelect
              
          EndSelect
          
        Case #MyEvent_FillUpdateStatus
          StatusBarProgress(#MainStatusBar, 0, EventData()) 
          
        Case #MyEvent_FillDone
          ;TODO
          StatusBarText(#MainStatusBar, 1, "Fill Done")
          
      EndSelect
    ForEver
    
  EndIf
  
EndProcedure : Main()
Last edited by mk-soft on Wed May 24, 2023 7:05 pm, edited 1 time in total.
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
Fred
Administrator
Administrator
Posts: 16617
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Can a ListView be actualized while being filled ?

Post by Fred »

You can run the event loop every X items, it should fo the trick
User avatar
Blue
Addict
Addict
Posts: 864
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Can a ListView be actualized while being filled ?

Post by Blue »

Fred wrote: Wed May 24, 2023 6:48 pm You can run the event loop every X items, it should fo the trick
That’s what I’m busily stitching together, but I thought it was a clunky workaround. Hence this topic and my question.
I was hoping that there was a better, simpler solution., that had escaped my attention.
Is there no PB equivalent to the DoEvents() VB function ?
(Although, I don’t know if that would do the trick either…)

Thanks for the answer, just the same.
"That's not a bug..." said the programmer. "it's a feature! "
"Oh! I see..." replied the blind man.
User avatar
HeX0R
Addict
Addict
Posts: 980
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: Can a ListView be actualized while being filled ?

Post by HeX0R »

You probably should think the other way round.
Speed-up the filling with a virtual listview and there is no need for any event handling.
Here an example from netmaestro:
viewtopic.php?p=553384#p553384
(needs some rework for PB6.02!)
User avatar
Blue
Addict
Addict
Posts: 864
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Can a ListView be actualized while being filled ?

Post by Blue »

mk-soft wrote: Wed May 24, 2023 6:41 pm Example with thread.
Only windows.
Thanks, mk-soft. That was quick !
And with a coded how-to, as well...
Much appreciated.
"That's not a bug..." said the programmer. "it's a feature! "
"Oh! I see..." replied the blind man.
User avatar
Blue
Addict
Addict
Posts: 864
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Can a ListView be actualized while being filled ?

Post by Blue »

HeX0R wrote: Wed May 24, 2023 7:11 pm You probably should think the other way round.
Always a good suggestion, HeX0R.
I'll keep that in mind.

Nice find, that code from NetMaestro. I should have searched for ListIcon solutions as well as LV solutions...
In fact, nice finding that topic. It's right on the button for the problem I'm dealing with. Plus i'm learning how i was approaching my problem totally the wrong way.
"That's not a bug..." said the programmer. "it's a feature! "
"Oh! I see..." replied the blind man.
User avatar
Caronte3D
Addict
Addict
Posts: 1027
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Can a ListView be actualized while being filled ?

Post by Caronte3D »

Blue wrote: Wed May 24, 2023 7:09 pm Is there no PB equivalent to the DoEvents() VB function ?
Maybe the near equivalent could be:

While WindowEvent() : Wend
User avatar
Blue
Addict
Addict
Posts: 864
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Can a ListView be actualized while being filled ?

Post by Blue »

Updated procedure to populate ListView :
I applied a very simple solution proposed by morosh (see viewtopic.php?p=553380#p553380) and, for the number of items i have to display, it does the job nicely. It's not the speediest solution, but, for the type of need expressed here, it's optimal.

So, thank you morosh, and here's the augmented procedure

Code: Select all

Procedure Remplir_LV(nLines)
  ; - affichage des données
  Protected i, maxi, n, t1, t2
  SetGadgetText(#APP_titre, Str(nLines) + " data entries")
  RemoveWindowTimer(0, #TiMER_chrono)
  AddWindowTimer(0, #TiMER_chrono, 200)

  SendMessage_(GadgetID(#LV_1),#WM_SETREDRAW,0,0)
  SendMessage_(GadgetID(#LV_2),#WM_SETREDRAW,0,0)
  SendMessage_(GadgetID(#LV_3),#WM_SETREDRAW,0,0)

  For i = #LV_1 To  #LV_3 Step 3
    ClearGadgetItems(i) 
    SetGadgetText(i+1,"Please wait...")
  Next


  t2 = ElapsedMilliseconds()
  t1 = t2
  n = nLines/3                      ; pcq 3 LV
  maxi = n
  For i = 1 To maxi
    AddGadgetItem(#LV_1,-1,"item # "+ i)
  Next
  SendMessage_(GadgetID(#LV_1),#WM_SETREDRAW,1,0)
  t2 = ElapsedMilliseconds() - t2
  SetGadgetText(#DATA_i1, "" + t2 + " ms")
  Debug "LV 1  " + t2 + " ms"

  maxi + n
  t2 = ElapsedMilliseconds()
  For i = i To maxi
    AddGadgetItem(#LV_2,-1,"item # "+ i)
  Next
  SendMessage_(GadgetID(#LV_2),#WM_SETREDRAW,1,0)
  t2 = ElapsedMilliseconds() - t2
  SetGadgetText(#DATA_i2, "" + t2 + " ms")
  Debug "LV 2  " + t2 + " ms"

  maxi + n
  t2 = ElapsedMilliseconds()
  For i = i To maxi
    AddGadgetItem(#LV_3,-1,"item # "+ i)
  Next
  SendMessage_(GadgetID(#LV_3),#WM_SETREDRAW,1,0)
  t2 = ElapsedMilliseconds() - t2
  SetGadgetText(#DATA_i3, "" + t2 + " ms")
  Debug "LV 3  " + t2 + " ms"
  
  t1 = ElapsedMilliseconds() - t1
  SetWindowTitle(0, "LV remplis en " + t1 + " ms")
  Debug "durée totale " + t1 + " ms"

EndProcedure
"That's not a bug..." said the programmer. "it's a feature! "
"Oh! I see..." replied the blind man.
User avatar
jacdelad
Addict
Addict
Posts: 1431
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: Can a ListView be actualized while being filled ?

Post by jacdelad »

Under Windows you should also use

Code: Select all

SendMessage_(GadgetID(Gadget),#WM_SETREDRAW,0,0)
before populating the gadget and

Code: Select all

SendMessage_(GadgetID(Gadget),#WM_SETREDRAW,1,0)
after populating it to speed up the process at all.
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
User avatar
Blue
Addict
Addict
Posts: 864
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Can a ListView be actualized while being filled ?

Post by Blue »

jacdelad wrote: Wed May 24, 2023 8:49 pm Under Windows you should also use […]
hi jacdelad
Look at the post just above yours, specifically at the beginning of the updated procedure.
You’re 20 minutes late to the party… :D :wink:
"That's not a bug..." said the programmer. "it's a feature! "
"Oh! I see..." replied the blind man.
morosh
Enthusiast
Enthusiast
Posts: 293
Joined: Wed Aug 03, 2011 4:52 am
Location: Beirut, Lebanon

Re: Can a ListView be actualized while being filled ?

Post by morosh »

So, thank you morosh, and here's the augmented procedure
Welcome Blue, I'm glad that my tip was helpfull, enjoy!!
PureBasic: Surprisingly simple, diabolically powerful
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: Can a ListView be actualized while being filled ?

Post by BarryG »

Another way:

Code: Select all

OpenWindow(0,400,300,640,320,"ListIcon fill",#PB_Window_SystemMenu)
ListIconGadget(0,10,10,620,250,"ListIcon",620)
ButtonGadget(1,10,270,620,35,"Fill list")
Repeat
  ev=WaitWindowEvent()
  If ev=#PB_Event_Gadget And EventGadget()=1
    ClearGadgetItems(0)
    For i=1 To 10000
      AddGadgetItem(0,-1,Str(i))
      If Mod(i,100)=0 ; Update list after every 100th item added.
        WindowEvent()
      EndIf
    Next
  EndIf
Until ev=#PB_Event_CloseWindow
User avatar
Blue
Addict
Addict
Posts: 864
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: Can a ListView be actualized while being filled ?

Post by Blue »

BarryG wrote: Fri May 26, 2023 2:09 am Another way:

Code: Select all

[...]
      If Mod(i,100)=0 ; Update list after every 100th item added.
        WindowEvent()
      EndIf
[...]
Thank you BarryG. Your suggestion is bang on !
WindowEvent() is exactly what I went hunting for when I posted this topic (hence, my question to Fred : "Is there no PB equivalent to the DoEvents() VB function ?")

The call to WindowEvent() after a number of steps is what I recall doing years ago. But it was long forgotten...
That memory blank turned out to be a boon anyway. Posting this topic brought me to discover numerous ways to improve the handling of ListView gadgets and to discover, to my surprise, that there are quite major performance differences between ListView and ListIcon gadgets.
"That's not a bug..." said the programmer. "it's a feature! "
"Oh! I see..." replied the blind man.
Post Reply