Page 1 sur 2

[Resolu] Mise à jour de gadgets depuis un Thread

Publié : lun. 24/nov./2008 13:08
par erhk
Bonjour, bonjour !

Premier programme : premier souci ! :oops:

Peut-on mettre à jour un gadget (ProgressBarGadget) dans une fenêtre, à partir d'un Thread. Ce dernier n'ayant pas ouvert lui-même la fenêtre concernée ?

Merci

Dans le code suivant, la barre n'est actualisée qu'a la sortie du Thread.

Code : Tout sélectionner

Enumeration
  #Win0       : #Win1
  #Btn_Run    : #Btn_Retour    : #Btn_Exit0    : #Btn_Exit1
  #List       : #Bar
EndEnumeration

Global Stop_Bar

Procedure Chg_Bar(Prm)

  Repeat
    avance = avance + 10
    If avance > 100 : avance = 10 : EndIf
    SetGadgetState(#Bar, avance)
    Delay(Prm)
  Until Stop_Bar = 1
  
EndProcedure

If OpenWindow(#Win0, 400, 250, 180, 160, "Fenetre 0", #PB_Window_TitleBar)

  If CreateGadgetList(WindowID(#Win0))
    ButtonGadget(#Btn_Run,    10, 130, 70, 20, "Lancer")
    ButtonGadget(#Btn_Exit0, 100, 130, 70, 20, "Quitter")
  EndIf

  Repeat
    Event = WaitWindowEvent(1)
     
      If Event =  #PB_Event_Gadget
        Select EventGadget()
        
          Case #Btn_Run ; Bouton "Lancer --> Affiche 2ème fenetre
                        ;                     Démarrage du Thread gérant la barre d'avancement
                        ;                     Remplissage de la liste par boucle For
            If OpenWindow(#Win1, 400, 250, 500, 160, "Fenetre 1", #PB_Window_TitleBar)
              HideWindow(#Win0, 1)
              If CreateGadgetList(WindowID(#Win1))
                ButtonGadget     (#Btn_Retour, 10, 130,  70,  20, "Retour")
                ButtonGadget     (#Btn_Exit1, 100, 130,  70,  20, "Quitter")
                ListViewGadget   (#List,      190,  30, 290, 110)
                ProgressBarGadget(#Bar,        30,  75, 120,  20,  0, 100)
                UpdateWindow_(WindowID(#Win1))
                Stop_Bar = 0
                ThreadID = CreateThread(@Chg_Bar(), 500)     
                For i = 0 To 9
                  AddGadgetItem (#List, i, Str(i+1))
                  SetGadgetState(#List, i)
                  Delay(500)
                Next
                Stop_Bar = 1
                SetGadgetState(#Bar, 100)
              EndIf
            EndIf

          Case #Btn_Retour ; Ferme fenetre 1, réaffiche fenetre 0
            HideWindow(#Win0, 0) : CloseWindow(#Win1)
          Case #Btn_Exit1
            Event = #PB_Event_CloseWindow  
          Case #Btn_Exit0
            Event = #PB_Event_CloseWindow  
        EndSelect
      EndIf

  Until Event = #PB_Event_CloseWindow
EndIf

Publié : lun. 24/nov./2008 13:31
par cha0s
Non cela ne marche pas ! le mieux est d'utiliser une variables globale ou une zone de mémoire partagé, de traiter l'ago dans le thread et mettre a jours le ProgressBarGadget dans la boucle principale.

Publié : lun. 24/nov./2008 14:04
par Random
Je débute depuis peu, voilà toutefois mon idée :

Le problème vient du 'delay' dans ta boucle principale

Code : Tout sélectionner

For i = 0 To 9 
                  AddGadgetItem (#List, i, Str(i+1)) 
                  SetGadgetState(#List, i) 
                  Delay(500) 
                Next 
ton thread fonctionne bien en arrière plan mais il ne peut pas interagir avec le gadget List car ce dernier est bloqué par le 'delay'.

La preuve : si on ote le 'delay' du thread et qu'on affiche après le 'for' le résultat de la variable 'avance' (qui est incrémentée dans le thread) alors on constate que 'avance' a bel et bien été incrémenté. (j'ai oté également la réinit de 'avance' pour bien constater cet état)

Code : Tout sélectionner

Enumeration 
  #Win0       : #Win1 
  #Btn_Run    : #Btn_Retour    : #Btn_Exit0    : #Btn_Exit1 
  #List       : #Bar 
EndEnumeration 

Global Stop_Bar =0
Global ThreadID
Global avance

Procedure ChgBar(val.l) 

  Repeat 
    avance = avance + 10 
   ; If avance > 100 : avance = 10 : EndIf 
   ; SetGadgetState(#Bar, avance) 
    Delay(500)
  Until Stop_Bar = 1 
  
EndProcedure 

If OpenWindow(#Win0, 400, 250, 180, 160, "Fenetre 0", #PB_Window_TitleBar) 

  If CreateGadgetList(WindowID(#Win0)) 
    ButtonGadget(#Btn_Run,    10, 130, 70, 20, "Lancer") 
    ButtonGadget(#Btn_Exit0, 100, 130, 70, 20, "Quitter") 
  EndIf 

  Repeat 
    Event = WaitWindowEvent() 
      
      If Event =  #PB_Event_Gadget 
        Select EventGadget() 
        
          Case #Btn_Run ; Bouton "Lancer --> Affiche 2ème fenetre 
                        ;                     Démarrage du Thread gérant la barre d'avancement 
                        ;                     Remplissage de la liste par boucle For 
            If OpenWindow(#Win1, 400, 250, 500, 160, "Fenetre 1", #PB_Window_TitleBar) 
              HideWindow(#Win0, 1) 
              If CreateGadgetList(WindowID(#Win1)) 
                ButtonGadget     (#Btn_Retour, 10, 130,  70,  20, "Retour") 
                ButtonGadget     (#Btn_Exit1, 100, 130,  70,  20, "Quitter") 
                ListViewGadget   (#List,      190,  30, 290, 110) 
                ProgressBarGadget(#Bar,        30,  75, 120,  20,  0, 100) 
                UpdateWindow_(WindowID(#Win1)) 
                Stop_Bar = 0 
                ThreadID = CreateThread(@ChgBar(), 1)
              EndIf
                For i = 0 To 9
                  AddGadgetItem (#List, i, Str(i)) 
                  SetGadgetState(#List, i) 
                  Delay(500) 
                Next 
                Stop_Bar = 1 
              EndIf 
              AddGadgetItem (#List, 10, Str(avance)) 
              SetGadgetState(#List, 10) 
          Case #Btn_Retour ; Ferme fenetre 1, réaffiche fenetre 0 
            HideWindow(#Win0, 0) : CloseWindow(#Win1) 
          Case #Btn_Exit1 
            Event = #PB_Event_CloseWindow  
          Case #Btn_Exit0 
            Event = #PB_Event_CloseWindow  
        EndSelect 
      EndIf 

  Until Event = #PB_Event_CloseWindow 
EndIf
[/code]

Publié : lun. 24/nov./2008 14:12
par Random
Je sais pas si ca va t'avancer à quelque chose mais :
si tu actualise l'affichage de ta barre dans la boucle principale (et pas dans le thread qui lui continue à incrémenter ta variable 'avance'), ca marche :

Code : Tout sélectionner

Procedure Chg_Bar(Prm) 

  Repeat 
    avance = avance + 10 
    If avance > 100 : avance = 10 : EndIf 
    
    Delay(Prm) 
  Until Stop_Bar = 1 
  
EndProcedure 

Code : Tout sélectionner

For i = 0 To 9 
  AddGadgetItem (#List, i, Str(i+1)) 
  SetGadgetState(#List, i)
  SetGadgetState(#Bar, avance) 
  Delay(500) 
Next 
Les experts PB me corrigeront.
je crains hélas que le thread perde complétement son interet dans ce cas

Publié : lun. 24/nov./2008 14:17
par brossden
Je ne vois pas pourquoi ??

Code : Tout sélectionner

 Enumeration
  #Win0       : #Win1
  #Btn_Run    : #Btn_Retour    : #Btn_Exit0    : #Btn_Exit1
  #List       : #Bar
EndEnumeration
 
Procedure Chg_Bar(Prm)
  
  Repeat
    avance = avance + 10
    If i <10
      i+1
      AddGadgetItem (#List, i-1, Str(i))
      SetGadgetState(#List,i-1)
    EndIf
    If avance > 100 : avance = 10 : EndIf
    SetGadgetState(#Bar, avance)
    res=UpdateWindow_(WindowID(#Win1))
    SetGadgetState(#List,10)
    Delay(500)
  Until Stop_Bar = 1
  
EndProcedure
 
If OpenWindow(#Win0, 400, 250, 180, 160, "Fenetre 0", #PB_Window_TitleBar)
  
  If CreateGadgetList(WindowID(#Win0))
    ButtonGadget(#Btn_Run,    10, 130, 70, 20, "Lancer")
    ButtonGadget(#Btn_Exit0, 100, 130, 70, 20, "Quitter")
  EndIf
  
  Repeat
    Event = WaitWindowEvent(1)
    
    If Event =  #PB_Event_Gadget
      Select EventGadget()
          
        Case #Btn_Run ; Bouton "Lancer --> Affiche 2ème fenetre
          ;                     Démarrage du Thread gérant la barre d'avancement
          ;                     Remplissage de la liste par boucle For
          If OpenWindow(#Win1, 400, 250, 500, 160, "Fenetre 1", #PB_Window_TitleBar)
            HideWindow(#Win0, 1)
            If CreateGadgetList(WindowID(#Win1))
              ButtonGadget     (#Btn_Retour, 10, 130,  70,  20, "Retour")
              ButtonGadget     (#Btn_Exit1, 100, 130,  70,  20, "Quitter")
              ListViewGadget   (#List,      190,  30, 290, 110)
              ProgressBarGadget(#Bar,        30,  75, 120,  20,  0, 100)
              ThreadID = CreateThread(@Chg_Bar(), 500)   
              
            EndIf
          EndIf
          
        Case #Btn_Retour ; Ferme fenetre 1, réaffiche fenetre 0
          HideWindow(#Win0, 0) : CloseWindow(#Win1)
          KillThread(ThreadID)
        Case #Btn_Exit1
          Event = #PB_Event_CloseWindow
        Case #Btn_Exit0
          Event = #PB_Event_CloseWindow
      EndSelect
    EndIf
    
  Until Event = #PB_Event_CloseWindow
EndIf

Publié : lun. 24/nov./2008 14:35
par erhk
Waoooo !!!!

Je suis très impressionné par la réactivité de la communauté. :D
Un grand merci à tous.
Je teste vos solutions dès que possible.

Publié : lun. 24/nov./2008 14:40
par Random
brossden a écrit :Je ne vois pas pourquoi ??

Code : Tout sélectionner

 Enumeration
  #Win0       : #Win1
  #Btn_Run    : #Btn_Retour    : #Btn_Exit0    : #Btn_Exit1
  #List       : #Bar
EndEnumeration
 
Procedure Chg_Bar(Prm)
  
  Repeat
    avance = avance + 10
    If i <10
      i+1
      AddGadgetItem (#List, i-1, Str(i))
      SetGadgetState(#List,i-1)
    EndIf
    If avance > 100 : avance = 10 : EndIf
    SetGadgetState(#Bar, avance)
    res=UpdateWindow_(WindowID(#Win1))
    SetGadgetState(#List,10)
    Delay(500)
  Until Stop_Bar = 1
  
EndProcedure
 
If OpenWindow(#Win0, 400, 250, 180, 160, "Fenetre 0", #PB_Window_TitleBar)
  
  If CreateGadgetList(WindowID(#Win0))
    ButtonGadget(#Btn_Run,    10, 130, 70, 20, "Lancer")
    ButtonGadget(#Btn_Exit0, 100, 130, 70, 20, "Quitter")
  EndIf
  
  Repeat
    Event = WaitWindowEvent(1)
    
    If Event =  #PB_Event_Gadget
      Select EventGadget()
          
        Case #Btn_Run ; Bouton "Lancer --> Affiche 2ème fenetre
          ;                     Démarrage du Thread gérant la barre d'avancement
          ;                     Remplissage de la liste par boucle For
          If OpenWindow(#Win1, 400, 250, 500, 160, "Fenetre 1", #PB_Window_TitleBar)
            HideWindow(#Win0, 1)
            If CreateGadgetList(WindowID(#Win1))
              ButtonGadget     (#Btn_Retour, 10, 130,  70,  20, "Retour")
              ButtonGadget     (#Btn_Exit1, 100, 130,  70,  20, "Quitter")
              ListViewGadget   (#List,      190,  30, 290, 110)
              ProgressBarGadget(#Bar,        30,  75, 120,  20,  0, 100)
              ThreadID = CreateThread(@Chg_Bar(), 500)   
              
            EndIf
          EndIf
          
        Case #Btn_Retour ; Ferme fenetre 1, réaffiche fenetre 0
          HideWindow(#Win0, 0) : CloseWindow(#Win1)
          KillThread(ThreadID)
        Case #Btn_Exit1
          Event = #PB_Event_CloseWindow
        Case #Btn_Exit0
          Event = #PB_Event_CloseWindow
      EndSelect
    EndIf
    
  Until Event = #PB_Event_CloseWindow
EndIf
*

Tu ne fais que déplacer le problème, au lieu que tout soit dans la boucle, tout est dans le thread, apparement Erhk souhaite d'une part que la bar s'actualise via le thread et d'autre part que la boucle principale traite la variable avec un delay.

Publié : lun. 24/nov./2008 16:50
par brossden
Désolé mais je ne vois pas où est cette condition dans la question d'origine ! Deplus ce n'est pas la boucle qui arrête le programme mais le Delay() qui bloque l'ensemble du programme ce qui est tout à fait normal hors d'un thread !!

Publié : lun. 24/nov./2008 18:08
par erhk
Houlà !

En lisant vos réponses, je comprends a quel point il est important d'expliquer ce que l'on souhaite obtenir dans sa globalité.
Donc voilà :
Je veux à partir d'une fenêtre lancer un ou plusieurs traitements, dont certains peuvent être long (arrêt de process en cours, sauvegardes, compression des fichiers en résultant ... ).
Pendant l'exécution de cet ensemble de traitement, je souhaite être informé dans une seconde fenêtre de son déroulement par l'envoi de messages (ListViewGadget) et avoir "un quelque chose" qui bouge (ProgressBarGadget) parceque c'est quand plus sympa.
Je pense que c'est plutôt classique comme déroulement.

Mais visiblement, ce n'est pas si simple !
Il est vrai que je me suis bien emmelé avec la commande Delay. Je viens de comprendre en faisant des essais, qu'elle arrete le programme "ET" les threads que celui-ci a lancé. Alors qu'utilisée dans un thread, elle n'arrete que le thread. Merci de me corriger si je me plante.

Mais un autre phénomène me parait moins evident : il semblerait, que deux partie de code distintes (programme principale et thread) travaillant sur une même fenêtre, celà provoque un conflit. Le thread semble attendre que la boucle principale soit en attente d'évenement (et peut-être libére la fenêtre) pour à son tour modifier le gadget.
Je ne sais pas si cette explication est valable, mais elle expliquerait bien le comportement du code. Et va dans le sens de la réponse de chaOs.

Je suis bien sur preneur de toute solution me permettant de m'approcher du resultat souhaité. Pour le plaisir !

Mes essais de code simplifiés qui m'ont conduit à cet conclusion:

Code : Tout sélectionner

Enumeration 
  #Win0
  #Btn_Run
  #Btn_Exit
  #Compteur_Thread
  #Compteur_Win0
EndEnumeration 

Global Compteur_Thread

Procedure Add(val.l) 

Debug "Début Thread"

  For Compteur_Thread = 0 To 9999
    SetGadgetText(#Compteur_Thread, Str(Compteur_Thread))
  Next
  
EndProcedure 

If OpenWindow(#Win0, 400, 250, 180, 160, "Fenetre 0", #PB_Window_TitleBar) 

  If CreateGadgetList(WindowID(#Win0))
    TextGadget(#PB_Any, 20, 20, 100, 20, "Compteur Thread :")
    TextGadget(#Compteur_Thread, 120, 20, 50, 20, Str(Compteur_Thread))
    TextGadget(#PB_Any, 20, 50, 100, 20, "Compteur Win0    :")
    TextGadget(#Compteur_Win0, 120, 50, 50, 20, Str(Compteur_Win0))
    ButtonGadget(#Btn_Run,    10, 130, 70, 20, "Lancer") 
    ButtonGadget(#Btn_Exit, 100, 130, 70, 20, "Quitter") 
  EndIf 

  Repeat 
    Event = WaitWindowEvent() 
      
      If Event =  #PB_Event_Gadget 
        Select EventGadget()
        
          Case #Btn_Run
            
            SetGadgetText(#Compteur_Thread, "0")
            SetGadgetText(#Compteur_Win0, "0")
            
            ThreadID = CreateThread(@Add(), 1)
            If ThreadID
              ;  WaitThread(ThreadID)    ; Si la ligne est activée : Le programme se fige et ne répond pas !
            EndIf
            
            For Compteur_Win0 = 0 To 29999
              SetGadgetText(#Compteur_Win0, Str(Compteur_Win0))
            Next
            
          Case #Btn_Exit
            Event = #PB_Event_CloseWindow  
        EndSelect 
      EndIf 

  Until Event = #PB_Event_CloseWindow 
EndIf

Code : Tout sélectionner

Enumeration 
  #Win0
  #Win1
  #Btn_Run
  #Btn_Exit
  #Compteur_Thread
  #Compteur_Win0
EndEnumeration 

Global Compteur_Thread

Procedure Add(val.l) 

  OpenWindow(#Win1, 400, 250, 180, 160, "Fenetre 0", #PB_Window_TitleBar) 
  CreateGadgetList(WindowID(#Win1))
  TextGadget(#PB_Any, 20, 20, 100, 20, "Compteur Thread :")
  TextGadget(#Compteur_Thread, 120, 20, 50, 20, Str(Compteur_Thread))

  For Compteur_Thread = 0 To 29999
    SetGadgetText(#Compteur_Thread, Str(Compteur_Thread))
  Next
  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow  
  
EndProcedure 

If OpenWindow(#Win0, 200, 100, 180, 160, "Fenetre 0", #PB_Window_TitleBar) 

  If CreateGadgetList(WindowID(#Win0))
    TextGadget(#PB_Any, 20, 50, 100, 20, "Compteur Win0    :")
    TextGadget(#Compteur_Win0, 120, 50, 50, 20, Str(Compteur_Win0))
    ButtonGadget(#Btn_Run,    10, 130, 70, 20, "Lancer") 
    ButtonGadget(#Btn_Exit, 100, 130, 70, 20, "Quitter") 
  EndIf 

  Repeat 
    Event = WaitWindowEvent() 
      
      If Event =  #PB_Event_Gadget 
        Select EventGadget()
        
          Case #Btn_Run
            
            SetGadgetText(#Compteur_Win0, "0")
            
            ThreadID = CreateThread(@Add(), 1)
            If ThreadID
              ;  WaitThread(ThreadID)    ; Si la ligne est activée : Le programme se fige et ne répond pas !
            EndIf
            
            For Compteur_Win0 = 0 To 29999
              SetGadgetText(#Compteur_Win0, Str(Compteur_Win0))
            Next
            
          Case #Btn_Exit
            Event = #PB_Event_CloseWindow  
        EndSelect 
      EndIf 

  Until Event = #PB_Event_CloseWindow 
EndIf

Publié : lun. 24/nov./2008 18:34
par Backup
pour l'exemple numero 1 , il semble en effet y avoir un blocage avec la fonction
SetGadgetText(#Compteur_Thread, Str(Compteur_Thread))
8O :?

peut etre un bug a signaler alors ... :)

Publié : mar. 25/nov./2008 1:06
par Backup
ta posé la question sur le forum anglais ? 8O

Publié : mar. 25/nov./2008 7:56
par erhk
Dobro a écrit :ta posé la question sur le forum anglais ? 8O
J'ai connu des vaches espagnoles qui parlaient beaucoup mieux l'anglais que moi ! :lol:
Donc, non.
Je ne sais pas trop comment faire.

Publié : mar. 25/nov./2008 9:35
par Random
Je ne suis pas expert mais effectivement, il semblerait que si la boucle principale et un thread accède au même gadget il y ait conflit.
Le thread attend que la boucle principale ait libéré le gadget pour s'exécuter.

Sinon pour ton cas, peut être vaut-il mieux que le thread traite tes opérations (sans aucune intervention sur les gadgets), stocke les résultats dans des variables globales que la boucle principale lit pour actualiser l'affichage des gadgets. Mais c'est en gros ce qui a été proposé plus haut.

Publié : mar. 25/nov./2008 10:05
par erhk
Random a écrit :Le thread attend que la boucle principale ait libéré le gadget pour s'exécuter.
Effectivement, mais dans le premier des deux petits bouts de code, je n'interviens pas sur le même gadget. Il semble que se soit l'ensemble de la fenêtre qui est verrouillé. Peut-être que, comme le suggère Dobro, c'est un bug.
Malheureusement, je suis incapable de poser la question sur le forum officiel. :cry: j'ai loupé quelques cours d'anglais et le dernier remonte à 1975 !
Random a écrit :Sinon pour ton cas, peut être vaut-il mieux que le thread traite tes opérations (sans aucune intervention sur les gadgets), stocke les résultats dans des variables globales que la boucle principale lit pour actualiser l'affichage des gadgets.
Pour avancer, je vais m'orienter vers cette solution. Mais dans tous les cas, il est bon de savoir ce que l'on peut faire, ou pas.
Merci pour ta participation. :)

Publié : mar. 25/nov./2008 10:59
par Random
erhk a écrit : Effectivement, mais dans le premier des deux petits bouts de code, je n'interviens pas sur le même gadget.
Oui mais le gadget est déclaré dans la boucle principale donc cela revient au même je pense.