Page 1 sur 1

Timer / Minuterie

Publié : ven. 03/août/2007 18:04
par ThoT
Re bonjour!

J'ai un probleme de comprehension avec les timers.
J'ai trouvé ce code en cherchant sur le forum :

Code : Tout sélectionner

Procedure TimerProc(hwnd.l, uMsg.l, idEvent.l, dwTime.l) 
     Select uMsg 
         Case #WM_TIMER 
             Select idEvent 
                 Case 1 
                     Debug "--------------------1 seconde" 
                     ; Ici, le code à executer toutes les secondes 
                 Case 2 
                     Debug "0.2 secondes" 
                     ; Ici, le code à executer toutes les 200 millisecondes 
                 Case 3 
                     Debug "-------------------------------------------3 secondes" 
                     ; Ici, le code à executer toutes les 3 secondes 
             EndSelect 
     EndSelect 
EndProcedure 


If OpenWindow (0, 100, 100, 100, 100, #PB_Window_SystemMenu , "" ) 
    Handle = WindowID (0) 
     
     SetTimer_ (Handle, 1, 1000, @TimerProc()) ; envoie un evenement toutes les 1000 millisecondes 
     SetTimer_ (Handle, 2, 200, @TimerProc()) ; envoie un evenement toutes les 200 millisecondes 
     SetTimer_ (Handle, 3, 3000, @TimerProc()) ; envoie un evenement toutes les 3000 millisecondes 
     
     Repeat 
     Until WaitWindowEvent () = #PB_Event_CloseWindow 
EndIf 
Il fonctionne nickel, mais je souhaiterai des precisions sur son fonctionnement (je n'en trouve pas dans l'aide de PB).
Notament sur les argument de la procedure "TimerProc(hwnd.l, uMsg.l, idEvent.l, dwTime.l)" et sur l'utilité du @ de "SetTimer_ (Handle, 3, 3000, @TimerProc())".

Un grand merci a celui ou celle qui arrivera à m'expliquer tout ca :lol:

Publié : ven. 03/août/2007 19:21
par Flype
salut,

c'est normal que tu ne trouves pas de doc dans l'aide purebasic
puisqu'il s'agit de fonctions 'système', provenant directement de l'API Windows (en gros, les dll qui sont dans c:\windows\).

ces fonctions win32 sont différenciées dans pure par un underscore '_' à la fin.

donc la doc pour çà il faut la chercher sur MSDN.
http://msdn2.microsoft.com/en-us/library/ms644906.aspx

:wink:

Publié : ven. 03/août/2007 19:30
par Flype
et concernant le fonctionnement,

dans cet exemple tu es confronté à 3 choses différentes.

1/ du langage purebasic
2/ une fonction de l'API Win32 (SetTimer)
3/ une fonction 'callback' qui sont si particulières au début.

une fonction callback est une fonction que l'on écrit soi meme (ici TimerProc) mais dont on doit absolument respecter le nombre et le type des arguments (ici hwnd.l, uMsg.l, idEvent.l, dwTime.l) spécifiés par microsoft.

http://msdn2.microsoft.com/en-us/library/ms644907.aspx

une fois écrite, cette fonction pourra être utilisée en la 'passant' à la fonction SetTimer() grace au caractère @ qui indique l'adresse mémoire de la fonction callback (TimerProc).

et donc le but final, est que SetTimer() appelle tout les x temps la fonction callback, indépendamment de ton code principal qui gère le reste de l'application.

en espèrant être assez clair.

Publié : ven. 03/août/2007 20:08
par Droopy
Tu peux facilement générer un timer avec un thread

Code : Tout sélectionner

Procedure Timer()
  Repeat
    Delay(1000)
    beep_(700,100)
  ForEver
EndProcedure

CreateThread(@Timer(),0)
Delay(10000)

Publié : ven. 03/août/2007 20:38
par Flype
mais attention, les threads engendrent une programmation plus délicate.

Publié : ven. 03/août/2007 22:36
par ThoT
Ok, comme toujours en programmations plusieurs façons de faire pour un même resultat...
Du coup, j'ai deux interrogations :

1 - Dans le cas present, quels sont les avantages / inconvenients de chacune des solutions (api - thread) ??

2 - Mon abjectif du moment est de réaliser un chronometre.
De ces deux methodes, laquelle serait la plus appropriée?

Publié : ven. 03/août/2007 22:47
par Backup
ThoT a écrit : 2 - Mon abjectif du moment est de réaliser un chronometre.
De ces deux methodes, laquelle serait la plus appropriée?
les timers sont fait pour ça !! plus precis !! indexé sur l'horloge du processeur

alors que les threads ne sont pas aussi precis (temporellement parlant)
a cause des priorités !!..
ils ne sont pas fais pour ça !!
il sont fait pour décharger une partie des calculs pour accelerer un traitement

bref c'est du temps partagé dans un system multitache !! :D

Publié : ven. 03/août/2007 23:23
par Flype
je dirais que pour un simple chrono de base il n'y a ni besoin de thread, ni de timer. WaitWindowEvent(MilliSecondes) peut suffir dans beaucoup de cas.

Code : Tout sélectionner

Enumeration 0 ; Window Id.
  #WindowMain
EndEnumeration

Enumeration 0 ; MenuItem Id.
  #MenuItemExit
EndEnumeration

Enumeration 0 ; Gadget Id.
  #GadgetStart
  #GadgetStop
  #GadgetText
  #GadgetExit
EndEnumeration

Define timer.l, chrono.l, t.l, s.l, ms.l

LoadFont(0, "Tahoma", 12, #PB_Font_Bold)

If OpenWindow(#WindowMain, 0, 0, 150, 55, "PureChrono", #PB_Window_TitleBar | #PB_Window_ScreenCentered)
  
  SetWindowColor(#WindowMain, RGB(0, 0, 0))
  
  AddKeyboardShortcut(#WindowMain, #PB_Shortcut_Escape, #MenuItemExit)
  
  If CreateGadgetList(WindowID(#WindowMain))
    
    ButtonGadget(#GadgetStart, 5, 5, 50, 20, "Start")
    ButtonGadget(#GadgetStop, 60, 5, 50, 20, "Stop")
    ButtonGadget(#GadgetExit, 115, 5, 30, 20, "Exit")
    TextGadget(#GadgetText, 5, 30, 140, 20, "", #PB_Text_Center)
    
    SetGadgetColor(#GadgetText, #PB_Gadget_FrontColor, RGB(0, 255, 0))
    SetGadgetColor(#GadgetText, #PB_Gadget_BackColor, RGB(0, 0, 0))
    
    SetGadgetFont(#GadgetText, FontID(0))
    
  EndIf
  
  Repeat
    
    Select WaitWindowEvent(1) ; <------ attend 1 milliseconde maxi.
      
      Case #PB_Event_CloseWindow
        Break
        
      Case #PB_Event_Menu
        
        Select EventMenu()
          Case #MenuItemExit
            Break
        EndSelect
        
      Case #PB_Event_Gadget
        
        Select EventGadget()
          
          Case #GadgetExit
            Break
            
          Case #GadgetStart
            
            timer = ElapsedMilliseconds()
            chrono = #True
            
          Case #GadgetStop
            
            timer = 0
            chrono = #False
            
        EndSelect
        
      Case 0
        
        If chrono
          
          t = ElapsedMilliseconds() - timer
          
          ms = t % 1000
          
          s = t / 1000
          
          SetGadgetText(#GadgetText, RSet(Str(s), 2, "0") + " s " + RSet(Str(ms), 3, "0") + " ms")
          
        EndIf
        
    EndSelect
    
  ForEver
  
  CloseWindow(#WindowMain)
  
EndIf

End
mais les timers sont bien utiles (et sont fait pour çà aussi).

Publié : sam. 04/août/2007 0:15
par Backup
Flype a écrit : WaitWindowEvent(MilliSecondes) peut suffir dans beaucoup de cas..
8O 8O j'avais jamais fais gaffe a cette particularité de cette fonction ! 8O

ce parametre "MilliSecondes" il n'existe que depuis la V4.10 non ? 8O

Publié : sam. 04/août/2007 0:48
par Flype
depuis la 4.0 mon cher 8) çà fait un petit moment donc.

Publié : sam. 04/août/2007 9:34
par Droopy
J'avais déjà posté un chrono que je remert ici :

Code : Tout sélectionner

;/ Droopy 05/06/06 / PureBasic 4.0 

Global Chrono 

Enumeration 
  #Frame  
  #Start_Pause 
  #Reset_Intermediaire 
  #Chrono 
  #Status 
EndEnumeration 

Procedure AffichageChrono() 
  
  Repeat 
    If T<>Chrono ;/ N'affiche le Chrono que si le contenu a changé 
      ms=(Chrono%1000)/10 
      Temp.s=Str(ms) 
      If ms<10 
        Temp.s="0"+Temp 
      EndIf 
      
      SetGadgetText(#Chrono,FormatDate("%ii:%ss",Chrono/1000)+":"+Temp) 
      T=Chrono 
      
    EndIf 
    
    Delay(10) 
    
  ForEver 
EndProcedure 

Procedure Chronometre() 
  Repeat 
    Delay(10) 
    Chrono+10 
  ForEver 
EndProcedure 

;{/ Génération du Visuel 
OpenWindow(0, 0, 0, 180, 150,"Droopy",#PB_Window_SystemMenu|#PB_Window_ScreenCentered) 
LoadFont(0,"Arial",26) 
CreateGadgetList(WindowID(0)) 
Frame3DGadget(#Frame,10,10,160,60,"Chronomètre") 
TextGadget(#Chrono, 20, 25 , 140, 40, "00:00:00",#PB_Text_Center) 
SetGadgetFont(#Chrono,FontID(0)) 
ButtonGadget(#Start_Pause, 10, 75, 160, 20, "Start / Pause") 
ButtonGadget(#Reset_Intermediaire, 10, 100, 160, 20, "Reset / Tps intermédiaire") 
CreateStatusBar(#Status,WindowID(0)) 
StatusBarText(#Status,0,"  Chronomètre Arrêté") 
;} 

IdChrono=CreateThread(@Chronometre(),0) 
PauseThread(IdChrono) 
IdAffichage=CreateThread(@AffichageChrono(),0) 

;{/ Gestion des évènements 
Repeat 
  Event=WaitWindowEvent() 
  If Event=#PB_Event_Gadget 
    If EventType()=#PB_EventType_LeftClick 
      
      Select EventGadget() 
        
        
        Case #Start_Pause 
          
          ;/ Si le chrono n'est pas en marche 
          If Start=0 
            Start=1 
            ResumeThread(IdChrono) ;/ On le démarre 
            StatusBarText(#Status,0,"  Chronomètre Démarré") 
          Else 
            ;/ Si on presse Start pendant l'affichage d'un temps intermédiaire on relance l'affichage 
            If TpsIntermediaire=1 
              ResumeThread(IdAffichage) 
              TpsIntermediaire=0 
            Else 
              ;/ Sinon on met le chrono en pause 
              Start=0 
              PauseThread(IdChrono) 
              StatusBarText(#Status,0,"  Chronomètre en Pause") 
            EndIf 
          EndIf 
          
        Case #Reset_Intermediaire 
          ;/ Si le chrono est arrêté on met le chrono à zéro 
          If Start=0 
            Chrono=0 
            StatusBarText(#Status,0,"  Remise à zéro du Chronomètre") 
          Else 
            ;/ Le Chrono est démarré --> On bloque l'affichage 
            If TpsIntermediaire=0 
              PauseThread(IdAffichage) 
              TpsIntermediaire=1 
            EndIf 
            
            ;/ Puis on affiche un résultat intermédiaire 
            ms=(Chrono%1000)/10 
            Temp.s=Str(ms) 
            If ms<10 
              Temp.s="0"+Temp 
            EndIf 
            SetGadgetText(#Chrono,FormatDate("%ii:%ss",Chrono/1000)+":"+Temp) 
            StatusBarText(#Status,0,"  Affichage d'un temps intermédiaire") 
            
          EndIf 
      EndSelect 
    EndIf 
  EndIf 
Until Event=#PB_Event_CloseWindow 
;}

Publié : sam. 04/août/2007 14:11
par ThoT
Sympa les codes que vous me donnez là! :)

Juste un truc qui me chiffone avec WaitWindowEvent(milliseconds) :

Admettons que je veuille un compteur seconde par seconde, je ferai donc un code du genre

Code : Tout sélectionner

repeat
Event = WaitWindowEvent(1000)
chrono = chrono + 1
afficher_chrono()
until Event=#PB_Event_CloseWindow
Ca devrait declencher toutes les 1000 ms (soit toutes les secondes) l'incrementation de 1 de la variable "chrono" et son affichage par la procedure "afficher_chrono()".
Mais l'incrementation et l'affichage prenent un certain temps alors est-ce que j'aurai bien des secondes de 1000ms ou est-ce qu'elles feront 1000ms + le temps de realiser les actions?

Publié : mer. 08/août/2007 21:39
par ThoT
Pas de reponse ? Personne ne sait ?! 8O

Publié : mer. 08/août/2007 22:08
par Octavius
Je suis pas un expert mais je pense que cette dernière méthode est imprécise.

Si je devais faire un chrono précis sans me casser la tête j'utiliserais la fonction ElapsedMilliseconds()

Une action (par exemple cliquer sur un bouton) déclencherait le chrono et tu aurais :

Code : Tout sélectionner

TimeStart=ElapsedMilliseconds()
Et à chaque passage dans la boucle principale de ton programme tu affiches le temps écoulé :

Code : Tout sélectionner

Time=ElapsedMilliseconds()-TimeStart
SetGadgetText(#affichage_chrono,Str(Time))
D'après l'aide de PB cette fonction est assez précise, elle dépend de l'horloge de l'ordinateur directement je crois. Ton gadget #affichage_chrono ça peut être un StringGadget.

Avec un peu de travail sur le nombre Time, tu pourras différencier les millisecondes, les secondes et les minutes dans des variables différentes. (à condition de savoir manipuler la division et la fonction valeur entière)