Page 1 sur 1

Charment images dans un Thread et affichage en même temps

Publié : sam. 14/mai/2016 10:06
par Thyphoon
Bonjour,

je profite de ce long week-end pour me remettre un peu au purebasic.
Je cherche a afficher des miniatures de photos... mais pour ne pas figer l'application durant le chargement des miniatures, j'essaye de charger ça dans un Thread
Mais le problème c'est que ça bug du fait que j'utilise la même liste dans le thread de chargement que dans la boucle principal pour les afficher.
Certaine miniatures disparaissent ou bien même se décalent ... Je sais qu'il faut utiliser les mutex ou les semaphores... mais ou bien ça n'affiche plus rien le temps que ça charge ou bien ça continue de déconner.
quelqu'un pourrait il m'aider ? ou me donner une piste ?
voici un version de mon code simplifier. il faut sélectionner un répertoire avec des images en ".jpg" (si possible plus d'une dizaine)

Code : Tout sélectionner

EnableExplicit

UseJPEGImageDecoder()

;-Applications Datas
Structure media
  name.s
  mutex.i
  image.i
EndStructure

Structure appDatas
  folder.s                ;current folder to examine
  exiftoolExe.s           ;path to exifTool executable
  thumbnailSize.l         ;thumbnail Size
  List media.media()     
EndStructure

Global appDatas.appDatas

;- Preferences
#Gdt_ThumbnailSize_Max=320
#Gdt_ThumbnailSize_Min=64
appDatas\folder=PathRequester("Choisissez un répertoire avec des photos", "")

appDatas\thumbnailSize=128

;-Windows And Gadget Init

; Windows
#win_Main=0

; Gadget
Enumeration
  #Gdt_Thumbnails
  #Gdt_ThumbnailSize
EndEnumeration

; Timer
Enumeration
   #Timer_RefreashThumbnails
EndEnumeration
OpenWindow(#win_Main,0,0,1900,980,"PhotoRanking", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(#Gdt_Thumbnails, WindowWidth(#win_Main)/6, 10, WindowWidth(#win_Main)/6*4, WindowHeight(#win_Main)-100)
TrackBarGadget(#Gdt_ThumbnailSize, WindowWidth(#win_Main)/6, GadgetY(#Gdt_Thumbnails)+GadgetHeight(#Gdt_Thumbnails), 200, 25, #Gdt_ThumbnailSize_Min, #Gdt_ThumbnailSize_Max)

Procedure loadFolder(n)
  AddWindowTimer(#win_Main, #Timer_RefreashThumbnails, 250) ; add a Timer to refreash Thumbnails
  If ExamineDirectory(0, appDatas\folder, "*.*")  
    While NextDirectoryEntry(0)
      If DirectoryEntryType(0) = #PB_DirectoryEntry_File
        ;Taille$ = " (Taille : " + DirectoryEntrySize(0) + ")"
        Select LCase(GetExtensionPart(DirectoryEntryName(0)))
          Case "jpg"
            AddElement(appDatas\media())
            appDatas\media()\mutex=CreateMutex()

            appDatas\media()\name=appDatas\folder+"\"+DirectoryEntryName(0)
            appDatas\media()\image=ListSize(appDatas\media())
            If  LoadImage(ListSize(appDatas\media()),appDatas\media()\name)
              ;ResizeImage(appDatas\media()\image,320,200,#PB_Image_Raw)
              Debug "Ok : "+Str(ListSize(appDatas\media()))+" >"+DirectoryEntryName(0)
            Else
              Debug "Error load :"+DirectoryEntryName(0)
            EndIf
        EndSelect
      EndIf
      
      ;Debug DirectoryEntryName(0)
    Wend
    FinishDirectory(0)
  EndIf
  
  RemoveWindowTimer(#win_Main, #Timer_RefreashThumbnails) ;Not needed anymore to refreash Thumbnails
  
EndProcedure

CreateThread(@loadFolder(),0)

Procedure DrawPreview()
  Protected width.l
  Protected n.l
  Protected nb.l      ;Thumb number on 1 line
  Protected delta.l   ;
  Protected x.l,y.l   ;Coord about a Thumbnail
  Protected nx.l
  StartDrawing(CanvasOutput(#Gdt_Thumbnails))
  Box(0,0,GadgetWidth(#Gdt_Thumbnails),GadgetHeight(#Gdt_Thumbnails),RGB(128,128,128))
  width=appDatas\thumbnailSize
  nb=Int(GadgetWidth(0)/width)
  delta=(GadgetWidth(0)-(nb*width))/nb
  ForEach appDatas\media()
    n=ListIndex(appDatas\media())
    y=Int(n/nb)*width
    nx=(n-Int(n/nb)*nb)
    x=delta/2+nx*width+nx*delta
    Box(x,y,width-2,width-2,#Red)
    
    If IsImage(appDatas\media()\image)
      DrawImage(ImageID(appDatas\media()\image),x,y,width,width)
    EndIf
    DrawText(x,y,Str(n))
  Next
  StopDrawing()
EndProcedure

DrawPreview()

Define event.i
 Repeat
     event = WaitWindowEvent()
     
     Select event
       Case #PB_Event_Timer
         Select EventTimer() 
           Case #Timer_RefreashThumbnails
             DrawPreview()
         EndSelect
       Case #PB_Event_Gadget
         Select EventGadget()
           Case #Gdt_ThumbnailSize 
             appDatas\thumbnailSize=GetGadgetState(#Gdt_ThumbnailSize)
             DrawPreview()
              ;CloseWindow(0)
              ;End  
         EndSelect
            
     EndSelect
   Until event = #PB_Event_CloseWindow



Re: Charment images dans un Thread et affichage en même temp

Publié : sam. 14/mai/2016 11:16
par microdevweb
Bonjour

As-tu cocher l'option Activer la gestion des Threds dans les option du compilateur?

Re: Charment images dans un Thread et affichage en même temp

Publié : sam. 14/mai/2016 11:44
par Thyphoon
microdevweb a écrit :Bonjour

As-tu cocher l'option Activer la gestion des Threds dans les option du compilateur?
oui c'est bien coché.

Re: Charment images dans un Thread et affichage en même temp

Publié : sam. 14/mai/2016 12:31
par GallyHC
Bonjour,

Perso je mettrais toutes les infos dans une liste, après j'envois le thread qui utilise cette même liste.

Cordialement,
GallyHC

Re: Charment images dans un Thread et affichage en même temp

Publié : sam. 14/mai/2016 12:39
par Thyphoon
GallyHC a écrit :Bonjour,

Perso je mettrais toutes les infos dans une liste, après j'envois le thread qui utilise cette même liste.

Cordialement,
GallyHC
Merci. C'est ce que je viens de faire c'est déjà mieux mais il y a encore des trucs currieux.... je posterai le code quand ça sera satisfaisant :P

Re: Charment images dans un Thread et affichage en même temp

Publié : sam. 14/mai/2016 17:49
par falsam
Sans Thread.

Code : Tout sélectionner

EnableExplicit

UseJPEGImageDecoder()

;-Applications Datas
Structure media
  name.s
  mutex.i
  image.i
EndStructure

Structure appDatas
  folder.s                ;current folder to examine
  exiftoolExe.s           ;path to exifTool executable
  thumbnailSize.l         ;thumbnail Size
  List media.media()     
EndStructure

Global appDatas.appDatas

;- Preferences
#Gdt_ThumbnailSize_Max=320
#Gdt_ThumbnailSize_Min=64
appDatas\folder=PathRequester("Choisissez un répertoire avec des photos", "")

appDatas\thumbnailSize=128

If Not ExamineDirectory(0, appDatas\folder, "*.*")
  Debug "oops souci"
  End
EndIf

;-Windows And Gadget Init

; Windows
#win_Main=0

; Gadget
Enumeration
  #Gdt_Thumbnails
  #Gdt_ThumbnailSize
EndEnumeration

; Timer
Enumeration
  #Timer_loadFolder  
EndEnumeration

Declare DrawPreview()

OpenWindow(#win_Main,0,0,1900,980,"PhotoRanking", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(#Gdt_Thumbnails, WindowWidth(#win_Main)/6, 10, WindowWidth(#win_Main)/6*4, WindowHeight(#win_Main)-100)
If StartDrawing(CanvasOutput(#Gdt_Thumbnails))
  Box(0,0,GadgetWidth(#Gdt_Thumbnails),GadgetHeight(#Gdt_Thumbnails),RGB(128,128,128))
  StopDrawing()
EndIf

TrackBarGadget(#Gdt_ThumbnailSize, WindowWidth(#win_Main)/6, GadgetY(#Gdt_Thumbnails)+GadgetHeight(#Gdt_Thumbnails), 200, 25, #Gdt_ThumbnailSize_Min, #Gdt_ThumbnailSize_Max)
AddWindowTimer(#win_Main, #Timer_loadFolder, 100)

Procedure loadFolder()
  If Not NextDirectoryEntry(0)
    RemoveWindowTimer(#win_main, #Timer_loadFolder)
    FinishDirectory(0)  
  Else
    
    If DirectoryEntryType(0) = #PB_DirectoryEntry_File
      ;Taille$ = " (Taille : " + DirectoryEntrySize(0) + ")"
      Select LCase(GetExtensionPart(DirectoryEntryName(0)))
        Case "jpg"
          AddElement(appDatas\media())
          appDatas\media()\mutex=CreateMutex()
          
          appDatas\media()\name=appDatas\folder+"\"+DirectoryEntryName(0)
          appDatas\media()\image=ListSize(appDatas\media())
          If  LoadImage(ListSize(appDatas\media()),appDatas\media()\name)
            ;ResizeImage(appDatas\media()\image,320,200,#PB_Image_Raw)
            Debug "Ok : "+Str(ListSize(appDatas\media()))+" >"+DirectoryEntryName(0)
            DrawPreview()
          Else
            Debug "Error load :"+DirectoryEntryName(0)
          EndIf
      EndSelect
    EndIf
  EndIf 
  ;Debug DirectoryEntryName(0)  
EndProcedure

Procedure DrawPreview()
  Protected width.l
  Protected n.l
  Protected nb.l      ;Thumb number on 1 line
  Protected delta.l   ;
  Protected x.l,y.l   ;Coord about a Thumbnail
  Protected nx.l
  
  StartDrawing(CanvasOutput(#Gdt_Thumbnails))
  width=appDatas\thumbnailSize
  nb=Int(GadgetWidth(0)/width)
  delta=(GadgetWidth(0)-(nb*width))/nb
  n=ListIndex(appDatas\media())
  y=Int(n/nb)*width
  nx=(n-Int(n/nb)*nb)
  x=delta/2+nx*width+nx*delta
  Box(x,y,width-2,width-2,#Red)
  
  If IsImage(appDatas\media()\image)
    DrawImage(ImageID(appDatas\media()\image),x,y,width,width)
  EndIf
  DrawText(x,y,Str(n))
  StopDrawing()
EndProcedure

;DrawPreview()

Define event.i
Repeat
  event = WaitWindowEvent(10)
  
  Select event
    Case #PB_Event_Timer
      Select EventTimer() 
        Case #Timer_loadFolder  
          loadFolder()
      EndSelect
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #Gdt_ThumbnailSize 
          appDatas\thumbnailSize=GetGadgetState(#Gdt_ThumbnailSize)
      EndSelect      
  EndSelect
Until event = #PB_Event_CloseWindow

Re: Charment images dans un Thread et affichage en même temp

Publié : sam. 14/mai/2016 18:55
par Thyphoon
merci falsam ! intéressant comme méthode ! :)

Re: Charment images dans un Thread et affichage en même temp

Publié : lun. 23/mai/2016 12:20
par Thyphoon
Voilà où j'en suis arrivé ! ça fonctionne ^_^ ! Pendant que ça charge vous pouvez changer la taille des previews avec le curseur en bas.
On pourra donc faire autre chose dans l'application pendant que ça charge.
Mais il y a encore un truc que je n'arrive pas a faire. c'est le chargement dans plusieurs Threads en même temps.
l'idéal serait d'avoir toujours 4 threads qui chargent et redimensionnent les images.
pour l'instant j'en ai un seul et je fais un WaitThread sinon tout plante... si quelqu'un a une idée ou une piste ..
Merci d'avance

Code : Tout sélectionner

EnableExplicit

UseJPEGImageDecoder()

;-Applications Datas
Structure media
  name.s
  image.i
EndStructure

Structure appDatas
  folder.s                ;current folder to examine
  exiftoolExe.s           ;path to exifTool executable
  thumbnailSize.l         ;thumbnail Size
  mutexListMedia.i    
  List media.media()     
EndStructure

Global appDatas.appDatas
appDatas\mutexListMedia=CreateMutex()

;- Preferences
#Gdt_ThumbnailSize_Max=320
#Gdt_ThumbnailSize_Min=64
appDatas\folder=PathRequester("Choisissez un répertoire avec des photos", "")

appDatas\thumbnailSize=128

;-Windows And Gadget Init

; Windows
#win_Main=0

; Gadget
Enumeration
  #Gdt_Thumbnails
  #Gdt_ThumbnailSize
EndEnumeration

; Tous nos évènements personnalisés
Enumeration #PB_Event_FirstCustomValue
  #Event_RefreashView
EndEnumeration
OpenWindow(#win_Main,0,0,1900,980,"PhotoRanking", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(#Gdt_Thumbnails, WindowWidth(#win_Main)/6, 10, WindowWidth(#win_Main)/6*4, WindowHeight(#win_Main)-100)
TrackBarGadget(#Gdt_ThumbnailSize, WindowWidth(#win_Main)/6, GadgetY(#Gdt_Thumbnails)+GadgetHeight(#Gdt_Thumbnails), 200, 25, #Gdt_ThumbnailSize_Min, #Gdt_ThumbnailSize_Max)


Structure imageFit
  width.l
  height.l
  centerWidth.l
  centerHeight.l
EndStructure

Procedure getImageFitSize(*result.imagefit,imgWidth.l,imgHeight.l,contWidth.l,contHeight.l)
  Protected imgRatio.l
  Protected contRatio.l
  imgRatio.l = imgWidth / imgHeight
  contRatio.l = contWidth /contHeight
  If imgRatio<contRatio
    *result\width=imgWidth*contHeight/ImgHeight
    *result\height=contHeight
    *result\centerWidth=(contWidth-*result\width)/2
    *result\centerHeight=0
  Else
    *result\width=contWidth
    *result\height=imgHeight*contWidth/ImgWidth
    *result\centerWidth=0
    *result\centerHeight=(contHeight-*result\height)/2
  EndIf
EndProcedure

Procedure ThreadLoadImage(*data.media)
  Protected imagefit.imagefit
  Debug "Thread:"+GetFilePart(*data\name)
  If IsImage(*data\image)
    FreeImage(*data\image)
  EndIf
  If FileSize(*data\name) 
    
    LoadImage(*data\image,*data\name)
    If IsImage(*Data\image)
      Debug GetFilePart(*data\name)+" OK"
      getImageFitSize(@imagefit,ImageWidth(*data\image),ImageHeight(*data\image),#Gdt_ThumbnailSize_Max,#Gdt_ThumbnailSize_Max)
      ResizeImage(*data\image,imagefit\width,imagefit\height,#PB_Image_Smooth) 
    EndIf 
  Else
    Debug GetFilePart(*data\name)+" ERROR"
  EndIf
EndProcedure

Procedure loadThumbnails(n)
  
  If ExamineDirectory(0, appDatas\folder, "*.*")  
    While NextDirectoryEntry(0)
      If DirectoryEntryType(0) = #PB_DirectoryEntry_File
        ;Taille$ = " (Taille : " + DirectoryEntrySize(0) + ")"
        Select LCase(GetExtensionPart(DirectoryEntryName(0)))
          Case "jpg"
            LockMutex(appDatas\mutexListMedia)
            AddElement(appDatas\media())
            appDatas\media()\name=appDatas\folder+"\"+DirectoryEntryName(0)
            appDatas\media()\image=ListIndex(appDatas\media())
            UnlockMutex(appDatas\mutexListMedia)
        EndSelect
      EndIf
    Wend
    FinishDirectory(0)
  EndIf
  
  Protected NewList tmpMedia.media()
  LockMutex(appDatas\mutexListMedia)
  CopyList(appDatas\media(),tmpMedia())
  UnlockMutex(appDatas\mutexListMedia)
  
  ForEach tmpMedia()
    Debug tmpMedia()\name
    Protected thread
    thread=CreateThread(@ThreadLoadImage(),@tmpMedia())
    WaitThread(thread) ;<-------------------------------------------------Un seul thread je sais pas comment faire pour en avoir 4
    PostEvent(#Event_RefreashView)
  Next
  
EndProcedure

CreateThread(@loadThumbnails(),0)

Procedure drawView()
  Protected width.l
  Protected n.l
  Protected nb.l      ;Thumb number on 1 line
  Protected delta.l   ;
  Protected x.l,y.l   ;Coord about a Thumbnail
  Protected nx.l
  Protected imagefit.imagefit
  
  StartDrawing(CanvasOutput(#Gdt_Thumbnails))
  
  Box(0,0,GadgetWidth(#Gdt_Thumbnails),GadgetHeight(#Gdt_Thumbnails),RGB(128,128,128))
  DrawText(15,15,Str(ElapsedMilliseconds()))
  width=appDatas\thumbnailSize
  nb=Int(GadgetWidth(0)/width)
  delta=(GadgetWidth(0)-(nb*width))/nb
  LockMutex(appDatas\mutexListMedia)
  ForEach appDatas\media()
    n=ListIndex(appDatas\media())
    y=Int(n/nb)*(width+delta)
    nx=(n-Int(n/nb)*nb)
    x=delta/2+nx*width+nx*delta
    Box(x,y,width-2,width-2,RGB(150,150,150))
    
    If IsImage(appDatas\media()\image)
      getImageFitSize(@imagefit,ImageWidth(appDatas\media()\image),ImageHeight(appDatas\media()\image),width-4,width-4)
      DrawImage(ImageID(appDatas\media()\image),x+imagefit\centerWidth,y+imagefit\centerHeight,imagefit\width,imagefit\height)
    EndIf
    DrawText(x,y,Str(n))
  Next
  UnlockMutex(appDatas\mutexListMedia)
  StopDrawing()
EndProcedure

drawview()

Define event.i
Repeat
  event = WaitWindowEvent()
  
  Select event
    Case #Event_RefreashView
      drawView()
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #Gdt_ThumbnailSize 
          appDatas\thumbnailSize=GetGadgetState(#Gdt_ThumbnailSize)
          drawView()
      EndSelect
      
  EndSelect
Until event = #PB_Event_CloseWindow

Re: Charment images dans un Thread et affichage en même temp

Publié : lun. 23/mai/2016 14:44
par Thyphoon
Et voilà je touche au but ... mais je sais pas si ma façon de faire est très "system friendly" quelqu'un a un avis sur la question ?
en tout cas les performances sont bien meilleur en chargant avec des Threads ça c'est certain ...
toute amélioration est la bienvenu ..

Code : Tout sélectionner

EnableExplicit

UseJPEGImageDecoder()

;-Applications Datas
Structure media
  name.s
  image.i
EndStructure

Structure appDatas
  folder.s                ;current folder to examine
  exiftoolExe.s           ;path to exifTool executable
  thumbnailSize.l         ;thumbnail Size
  mutexListMedia.i    
  List media.media()     
EndStructure

Global appDatas.appDatas
appDatas\mutexListMedia=CreateMutex()

;- Preferences
#Gdt_ThumbnailSize_Max=320
#Gdt_ThumbnailSize_Min=64
appDatas\folder=PathRequester("Choisissez un répertoire avec des photos .jpg", "")

appDatas\thumbnailSize=128

;-Windows And Gadget Init

; Windows
#win_Main=0

; Gadget
Enumeration
  #Gdt_Thumbnails
  #Gdt_ThumbnailSize
EndEnumeration

; Tous nos évènements personnalisés
Enumeration #PB_Event_FirstCustomValue
  #Event_RefreashView
EndEnumeration
OpenWindow(#win_Main,0,0,1900,980,"PhotoRanking", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(#Gdt_Thumbnails, WindowWidth(#win_Main)/6, 10, WindowWidth(#win_Main)/6*4, WindowHeight(#win_Main)-100)
TrackBarGadget(#Gdt_ThumbnailSize, WindowWidth(#win_Main)/6, GadgetY(#Gdt_Thumbnails)+GadgetHeight(#Gdt_Thumbnails), 200, 25, #Gdt_ThumbnailSize_Min, #Gdt_ThumbnailSize_Max)

Structure imageFit
  width.l
  height.l
  centerWidth.l
  centerHeight.l
EndStructure

Procedure getImageFitSize(*result.imagefit,imgWidth.l,imgHeight.l,contWidth.l,contHeight.l)
  Protected imgRatio.l
  Protected contRatio.l
  imgRatio.l = imgWidth / imgHeight
  contRatio.l = contWidth /contHeight
  If imgRatio<contRatio
    *result\width=imgWidth*contHeight/ImgHeight
    *result\height=contHeight
    *result\centerWidth=(contWidth-*result\width)/2
    *result\centerHeight=0
  Else
    *result\width=contWidth
    *result\height=imgHeight*contWidth/ImgWidth
    *result\centerWidth=0
    *result\centerHeight=(contHeight-*result\height)/2
  EndIf
EndProcedure
  Structure proc
    thread.i
    image.i
    name.s
  EndStructure 

Procedure ThreadLoadImage(*data.proc)
  Protected imagefit.imagefit
  Debug "Thread:"+GetFilePart(*data\name)
  If IsImage(*data\image)
    FreeImage(*data\image)
  EndIf
  If FileSize(*data\name) 
    
    LoadImage(*data\image,*data\name)
    If IsImage(*Data\image)
      Debug GetFilePart(*data\name)+" OK"
      getImageFitSize(@imagefit,ImageWidth(*data\image),ImageHeight(*data\image),#Gdt_ThumbnailSize_Max,#Gdt_ThumbnailSize_Max)
      ResizeImage(*data\image,imagefit\width,imagefit\height,#PB_Image_Smooth) 
    EndIf 
  Else
    Debug GetFilePart(*data\name)+" ERROR"
  EndIf
EndProcedure

Procedure loadThumbnails(z.l)
  
  If ExamineDirectory(0, appDatas\folder, "*.*")  
    While NextDirectoryEntry(0)
      If DirectoryEntryType(0) = #PB_DirectoryEntry_File
        ;Taille$ = " (Taille : " + DirectoryEntrySize(0) + ")"
        Select LCase(GetExtensionPart(DirectoryEntryName(0)))
          Case "jpg"
            LockMutex(appDatas\mutexListMedia)
            AddElement(appDatas\media())
            appDatas\media()\name=appDatas\folder+"\"+DirectoryEntryName(0)
            appDatas\media()\image=ListIndex(appDatas\media())
            UnlockMutex(appDatas\mutexListMedia)
        EndSelect
      EndIf
    Wend
    FinishDirectory(0)
  EndIf
  
  Protected NewList tmpMedia.media()
  LockMutex(appDatas\mutexListMedia)
  CopyList(appDatas\media(),tmpMedia())
  UnlockMutex(appDatas\mutexListMedia)
  

  
  Protected Dim proc.proc(10)
  Protected n.l=0
  Protected maxThread.l=8
  
  ForEach tmpMedia()
    
      ;this loop limit thread number to maxThread
      Repeat
        n=n+1
        Delay(1)
        If n>maxThread:n=1:EndIf 
      Until IsThread(proc(n)\thread)=0 ; Always a Thread if finished you can add another

      proc(n)\image=tmpMedia()\image
      proc(n)\name=tmpMedia()\name
      Debug ">>>>>>>>>>>>>>>>>>>"+Str(n)+" "+Str(proc(n)\image)+proc(n)\name
      proc(n)\thread=CreateThread(@ThreadLoadImage(),@proc(n))

    If n=maxThread:PostEvent(#Event_RefreashView):EndIf
  Next
  ;Wait all Thread are finished
  For n=1 To maxThread
    WaitThread(proc(n)\thread)
  Next
  
EndProcedure

CreateThread(@loadThumbnails(),0)

Procedure drawView()
  Protected width.l
  Protected n.l
  Protected nb.l      ;Thumb number on 1 line
  Protected delta.l   ;
  Protected x.l,y.l   ;Coord about a Thumbnail
  Protected nx.l
  Protected imagefit.imagefit
  
  StartDrawing(CanvasOutput(#Gdt_Thumbnails))
  
  Box(0,0,GadgetWidth(#Gdt_Thumbnails),GadgetHeight(#Gdt_Thumbnails),RGB(128,128,128))
  DrawText(15,15,Str(ElapsedMilliseconds()))
  width=appDatas\thumbnailSize
  nb=Int(GadgetWidth(0)/width)
  delta=(GadgetWidth(0)-(nb*width))/nb
  LockMutex(appDatas\mutexListMedia)
  ForEach appDatas\media()
    n=ListIndex(appDatas\media())
    y=Int(n/nb)*(width+delta)
    nx=(n-Int(n/nb)*nb)
    x=delta/2+nx*width+nx*delta
    If y+width>0 And y<GadgetHeight(#Gdt_Thumbnails)
    Box(x,y,width-2,width-2,RGB(150,150,150))
    
    If IsImage(appDatas\media()\image)
      getImageFitSize(@imagefit,ImageWidth(appDatas\media()\image),ImageHeight(appDatas\media()\image),width-4,width-4)
      DrawImage(ImageID(appDatas\media()\image),x+imagefit\centerWidth,y+imagefit\centerHeight,imagefit\width,imagefit\height)
    EndIf
    DrawText(x,y,Str(n))
    EndIf 
  Next
  UnlockMutex(appDatas\mutexListMedia)
  StopDrawing()
EndProcedure

drawview()

Define event.i
Repeat
  event = WaitWindowEvent()
  
  Select event
    Case #Event_RefreashView
      drawView()
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #Gdt_ThumbnailSize 
          appDatas\thumbnailSize=GetGadgetState(#Gdt_ThumbnailSize)
          drawView()
      EndSelect
      
  EndSelect
Until event = #PB_Event_CloseWindow