Page 1 sur 1

Delay et rafraîchissement interface utilisateur

Publié : lun. 06/avr./2020 12:34
par JagV12
Bonjour,

Je me demande à quel moment l'UI est rafraîchie : avec le code suivant, lorsque "WindowEvent" précède "SetGadgetText", le rafraîchissement se fait avec un compte de retard (visible uniquement en mode Débogueur, le mode "Run" n'ayant pas de référence temporelle). Ce serait donc "WindowEvent" qui permet ce rafraîchissement et il suffit d'intervertir les deux instructions pour résoudre le problème.

Code : Tout sélectionner

; FileName "Delay&Display.pb"
EnableExplicit

Enumeration
  #Gadget_Display
EndEnumeration

Define Event, aCounter.a

If OpenWindow(0, #PB_Ignore, #PB_Ignore, 400, 260, "PureBasic Window", #PB_Window_SystemMenu)

  StringGadget(#Gadget_Display, 10, 10, 310, 20, "")

  Repeat
    Event = WindowEvent()
    SetGadgetText(#Gadget_Display, "Text " + aCounter)
    Debug "New Value : " + aCounter                ; <------ Référence temporelle en mode Débogueur
    Delay(500)
    Debug "After Delay"
    aCounter = aCounter + 1
    
  Until Event = #PB_Event_CloseWindow
  
EndIf

End   ; All the opened windows are closed automatically by PureBasic
On remarque d'ailleurs que le StringGadget et son contenu ne sont visibles qu'au bout de 10 secondes (avec Débogueur, et environ 3 secondes sans) lorsque le compteur est déjà à 21 (ou 6 sans Débogueur). Et ce n'est même pas une question de temps car passer le Delay de 500 à 1000 ne change pas la première valeur affichée (21 ou 6) !

Existe-t-il une instruction qui rafraîchit l'UI sans consommer d'événement ? Et que faire pour que mon compteur ne démarre que lorsque mon StringGadget est opérationnel ?

Le but final est de recevoir des Strings RS232 et de les retourner avec un délai pour simuler le fonctionnement d'un appareil réel. Comme il semble que le port série ne sait pas déclencher d’événements (sinon, votre solution m'intéresse), j'utilise donc "If AvailableSerialPortInput(Com)" dans la boucle principale avec "Event = WaitWindowEvent(100)". Ça fonctionne mais l'affichage est retardé pour les raisons exposées dans l’exemple ci-dessus et là, je ne peux pas intervertir ni ajouter de "WindowEvent" sans consommer d'événement.

Un plan B serait d'utiliser un Timer (et des drapeaux anti-recouvrement "aEchoPending") mais j'aimerais aussi et avant tout résoudre ce problème d'UI.

Merci d'avance pour vos suggestions...

Re: Delay et rafraîchissement interface utilisateur

Publié : lun. 06/avr./2020 13:08
par Marc56
1. Oui, l'interface n'est rafraichie que quand un évènement est testé. Une astuce consiste à faire une boucle unique à l'endroit où on veut rafraichir.

Code : Tout sélectionner

While WindowEvent() : Wend
(Mais ceci n'est pas valable dans une procédure)

2. Se méfier de la fonction Delay() elle stoppe tout le programme

3. Oui, un timer c'est mieux

4. Eviter d'utiliser des ID numérique, on arrive vite à confondre les gadgets, même avec des petits programmes

5. Tu auras peut être par la suite d'autres problèmes avec les interfaces RS232 à cause des buffers, mais ça se résoudra après.

6. Voila un truc (à peu près) propre qui fait peut-être ce que tu souhaites

Code : Tout sélectionner

; FileName "Delay&Display.pb"
EnableExplicit

Enumeration
    #Win
    #Timer
    #Gadget_Display
EndEnumeration

Define aCounter.a

OpenWindow(#Win, #PB_Ignore, #PB_Ignore, 400, 260, "PureBasic Window", #PB_Window_SystemMenu)
AddWindowTimer(#Win, #Timer, 500)
StringGadget(#Gadget_Display, 10, 10, 310, 20, "")

Repeat
    Select WindowEvent()
        Case #PB_Event_CloseWindow
            End
            
        Case #PB_Event_Timer
            If EventTimer() = #Timer
                SetGadgetText(#Gadget_Display, "Text " + aCounter)
                Debug "New Value : " + aCounter               
                Debug "After Delay"
                aCounter = aCounter + 1
            EndIf
    EndSelect
ForEver

End   
7. Dans cette méthode tu peux remplacer WindowEvent() par WaitWindowEvent() qui pompera (peut-être) moins de CPU (pas testé)

:wink:

Re: Delay et rafraîchissement interface utilisateur

Publié : lun. 06/avr./2020 15:36
par JagV12
OK, reformulé pour être synchronisé sur un événement extérieur (réception RS232 au final mais clic gauche dans ce cas simple), j'obtiens ça :

Code : Tout sélectionner

; FileName "Delay&Display V2.pb"
EnableExplicit

Enumeration
  #Win
  #Timer
  #Gadget_Display
  #Gadget_Rx_LED
EndEnumeration

#LightGray            = $F1F1F1

Define aCounter.a, aEchoPending.a

OpenWindow(#Win, #PB_Ignore, #PB_Ignore, 400, 260, "PureBasic Window", #PB_Window_SystemMenu)
StringGadget(#Gadget_Display, 10, 10, 310, 20, "")
TextGadget(#Gadget_Rx_LED, 10, 40, 12, 12, "", #PB_Text_Border)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      End
    Case #PB_Event_LeftClick
      If aEchoPending
        Debug "EchoPending !!!"
      Else
        aEchoPending = #True
        SetGadgetColor(#Gadget_Rx_LED, #PB_Gadget_BackColor, #Red)
        AddWindowTimer(#Win, #Timer, 1000)
      EndIf
      
    Case #PB_Event_Timer
      If EventTimer() = #Timer
        If aEchoPending
          RemoveWindowTimer(#Win, #Timer)
          Debug "After Delay"
          aEchoPending = #False
          SetGadgetText(#Gadget_Display, "Text " + aCounter)
          Debug "New Value : " + aCounter
          aCounter = aCounter + 1
          SetGadgetColor(#Gadget_Rx_LED, #PB_Gadget_BackColor, #LightGray)
        Else
          Debug "MAJOR PROGRAMMING BUG !!!"
        EndIf
      EndIf
  EndSelect
ForEver
End   
Bizarrement, "Repeat ... WindowEvent() ... Forever" n'utilise que 2 à 4% du CPU. J'aurais cru plus... Bien sûr, "WaitWindowEvent()" le fait chuter à 0.x% (selon Process Explorer)

Par ailleurs, je suis en train d'essayer de créer un Event sur réception RS232 avec ça : (je suis parti de "SerialPort.pb" dans les exemples PB et je l'ai mixé avec l'aide de "PostEvent()

"), la coloration syntaxique en plus...

Code : Tout sélectionner

; FileName SerialPortWithThread.pb

EnableExplicit

Macro HexFormat(Prefix, Value, Size)
  Prefix + RSet(Hex(Value), Size, "0")
EndMacro

Enumeration Windows
  #MainWindow
EndEnumeration

Enumeration Gadgets
  #Gadget_Display
;   #ComPortGadget
EndEnumeration

Enumeration #PB_Event_FirstCustomValue   ; to not clash with internal events
  #EventSerialPortRx
EndEnumeration

; Enumeration #PB_EventType_FirstCustomValue   ; to not clash with internal events  ; <--- une tentative avortée
;   #EventTypeSerialPortRx
; EndEnumeration

Procedure ComPortThread(comID)
  Protected RxDataLength
  Debug "ComPortThread : RxDataLength Size = " + SizeOf(RxDataLength) + " Bytes"
  Debug "ComPortThread : comID Size = " + SizeOf(comID) + " Bytes"
  Debug "ComPortThread : comID = " + comID
  
  Repeat
    Delay(1)  ; <--- pour tester la charge CPU
    RxDataLength = AvailableSerialPortInput(comID)
  Until RxDataLength
;   ici, tentative de réception de la String mais sans intérêt...
  PostEvent(#EventSerialPortRx) ; , #MainWindow, #ComPortGadget, #EventTypeSerialPortRx,  ; <--- faisons simple
EndProcedure
  

Define Event, sPort.s, ComID, sRxData.s, RxDataLength, aInputBuffer.a

sPort = "COM11"         ; com0com virtual port pair is COM10-COM11

ComID = OpenSerialPort(#PB_Any, sPort, 9600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_RtsCtsHandshake, 1024, 1024)
Debug "ComID = " + SizeOf(ComID) + " Bytes"
Debug "Main : comID = " + comID

If Not(ComID)
  MessageRequester("Error", "Can't open the serial port: "+sPort)
Else
  If OpenWindow(#MainWindow, #PB_Ignore, #PB_Ignore, 400, 260, "PureBasic Window", #PB_Window_SystemMenu)
  
    StringGadget(#Gadget_Display, 10, 10, 310, 20, "")
    CreateThread(@ComPortThread(), comID)
    
    Repeat
      Event = WaitWindowEvent()
      
      Select Event
        Case #EventSerialPortRx
          Debug "Le Thread commence une action... "
          sRxData = ""
          Debug "RxDataLength = " + AvailableSerialPortInput(comID)
          Repeat
            ReadSerialPortData(comID, @aInputBuffer, 1)   ; <--- cette façon de faire n'est pas forcément définitive...
            sRxData = sRxData + HexFormat("", aInputBuffer, 2) + " "
          Until AvailableSerialPortInput(comID) = 0
          SetGadgetText(#Gadget_Display, sRxData)
          CreateThread(@ComPortThread(), comID)
      EndSelect
      
    Until Event = #PB_Event_CloseWindow
    
  EndIf
EndIf
End
mais j'ai pour l'instant un bug sur l'identité du port dans le Thread.
Nouvel essai, ça semble fonctionner. J'ai remplacé "OpenSerialPort(0, sPort..." par "OpenSerialPort(#PB_Any, sPort..." comme listé plus haut. (Un gars, un jour, m'avait dit de me méfier des ID numériques... et j'ajouterais "des copier-coller")

On progresse...

Re: Delay et rafraîchissement interface utilisateur

Publié : lun. 06/avr./2020 15:48
par JagV12
La charge CPU est de .14% avec Delay(1), monte à 12.5% sans ce Delay et tombe à .03 avec Delay(100). Je pense que je vais garder Delay(1)...

Pour revenir sur le tout début de ce sujet, quelqu'un sait-il pourquoi mon StringGadget ne se mettait pas à jour (il ne s'affichait même pas) pendant les 21 (ou 6) premiers comptage de mon minuteur ?

Re: Delay et rafraîchissement interface utilisateur

Publié : mar. 07/avr./2020 9:08
par microdevweb
IL serait à mon sens préférable d'exécuter des boucles de traitement dans un voir plusieurs thread. Tout cela dépend évidement de ce que tu veux faire. Et la ton interface ne se figera pas.

Re: Delay et rafraîchissement interface utilisateur

Publié : mar. 07/avr./2020 9:37
par MLD
Bonjour jagV12
Mettre un delaiy dans la boucle générale est un non sens, car tu ralentis tous les événements.
Rafraichir un string dans la boucle générale est aussi un non sens.
l'utilisation de débugs dans le programme prend un temps assez long
l'utilisation d'un timer a 1ms est une bonne solution. Marc56 a raison.
Enfin ce n'est que mon avis :roll:

Re: Delay et rafraîchissement interface utilisateur

Publié : mar. 07/avr./2020 11:00
par Zorro
a tout hasard, tu as essayé de virer ton Delay(1)

et de remplacer ton " Select WaitWindowEvent()"

par un " Select WaitWindowEvent(2)" ??
(ce qui souvent permet de rendre la main au system et permet un rafraichissement de l'interface )

ps: c'est une vraie question , pas une suggestion hein ? ! ;)

Re: Delay et rafraîchissement interface utilisateur

Publié : mar. 07/avr./2020 15:33
par Marc56
Bonjour JagV12,

Dans un premier temps, je te conseille de mettre au point ton application sans thread, juste pour vérifier la synchronisation du port RS232 avec le programme. Même si ça bloque tout.

Ensuite, si ça fonctionne, tu mets la partie communication en thread en faisant bien attention à ce que le thread de communication soit unique (d'où utilisation d'un numéro sur ID = CreateThread(...) ce qui te permet ensuite de suivre son état (If Not IsThread(ID) ...) et déclencher ou non un autre. Dans le cas contraire, ton programme risque de remplir plusieurs buffers (peut-être)
Le programme tel que tu l'as réalisé dans ton premier exemple créé deux theads

Au besoin créer un deuxième programme qui va simuler le périphérique RS232

:wink: