Delay et rafraîchissement interface utilisateur

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Delay et rafraîchissement interface utilisateur

Message 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...
Marc56
Messages : 2198
Inscription : sam. 08/févr./2014 15:19

Re: Delay et rafraîchissement interface utilisateur

Message 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:
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Re: Delay et rafraîchissement interface utilisateur

Message 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...
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Re: Delay et rafraîchissement interface utilisateur

Message 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 ?
Avatar de l’utilisateur
microdevweb
Messages : 1802
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: Delay et rafraîchissement interface utilisateur

Message 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.
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Avatar de l’utilisateur
MLD
Messages : 1124
Inscription : jeu. 05/févr./2009 17:58
Localisation : Bretagne

Re: Delay et rafraîchissement interface utilisateur

Message 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:
Avatar de l’utilisateur
Zorro
Messages : 2186
Inscription : mar. 31/mai/2016 9:06

Re: Delay et rafraîchissement interface utilisateur

Message 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 ? ! ;)
Marc56
Messages : 2198
Inscription : sam. 08/févr./2014 15:19

Re: Delay et rafraîchissement interface utilisateur

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