[Resolu] Mise à jour de gadgets depuis un Thread

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
erhk
Messages : 30
Inscription : lun. 24/nov./2008 12:47

[Resolu] Mise à jour de gadgets depuis un Thread

Message 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
cha0s
Messages : 681
Inscription : sam. 05/mars/2005 16:09

Message 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.
Random
Messages : 11
Inscription : mer. 12/nov./2008 16:35

Message 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]
Dernière modification par Random le lun. 24/nov./2008 14:14, modifié 1 fois.
Random
Messages : 11
Inscription : mer. 12/nov./2008 16:35

Message 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
brossden
Messages : 833
Inscription : lun. 26/janv./2004 14:37

Message 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
Denis

Bonne Jounée à tous
erhk
Messages : 30
Inscription : lun. 24/nov./2008 12:47

Message 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.
Random
Messages : 11
Inscription : mer. 12/nov./2008 16:35

Message 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.
brossden
Messages : 833
Inscription : lun. 26/janv./2004 14:37

Message 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 !!
Denis

Bonne Jounée à tous
erhk
Messages : 30
Inscription : lun. 24/nov./2008 12:47

Message 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
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message 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 ... :)
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

ta posé la question sur le forum anglais ? 8O
erhk
Messages : 30
Inscription : lun. 24/nov./2008 12:47

Message 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.
Random
Messages : 11
Inscription : mer. 12/nov./2008 16:35

Message 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.
erhk
Messages : 30
Inscription : lun. 24/nov./2008 12:47

Message 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. :)
Random
Messages : 11
Inscription : mer. 12/nov./2008 16:35

Message 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.
Répondre