IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Tkinter Python Discussion :

PySerial - Tkinter et boucle infinie [Python 3.X]


Sujet :

Tkinter Python

  1. #1
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut PySerial - Tkinter et boucle infinie
    Bonjour tout le monde.

    Je suis encore trop novice pour comprendre ce qui se passe, mais je bloque un peu sur un problème de boucle infinie quand je veux récupérer des datas qui viennent de PySerial.

    Comme je ne suis pas très doué, je ne vous donne que le code minimum pour comprendre ce qui se passe.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
     
    #!/usr/bin/python
    # -*- coding: iso-8859-15 -*-
     
    # -- IMPORTATION --
     
    #import Tkinter as tk
    import tkinter as tk
    import serial
     
    # -- INTERFACE GRAPHIQUE
    class iface1(tk.Frame):
        def __init__(self, parent):
            #Variables
            self.parent = parent
            self.serialConnection = 0
            self.locations=['/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyUSB2', '/dev/ttyUSB3', '/dev/ttyS0', '/dev/ttyS1', '/dev/ttyS2', '/dev/ttyS3']
            self.ser = ''
            self.currentInterface = 0
     
            #Les frames
            tk.Frame.__init__(self, self.parent)
            self.interfaceDirect = tk.LabelFrame(self, text="Direction standard", padx = 20, pady = 20)
            self.interfaceInfos = tk.LabelFrame(self, text="Informations", padx = 20, pady = 20)
            self.interfaceConnexion = tk.LabelFrame(self, text="Connection", padx = 20, pady = 20)
     
            #Images
            self.myImgAV = tk.PhotoImage(file="ressources/icones/av.gif")
            self.myImgRG = tk.PhotoImage(file="ressources/icones/rg.gif")
            self.myImgRC = tk.PhotoImage(file="ressources/icones/rc.gif")
            self.myImgRD = tk.PhotoImage(file="ressources/icones/rd.gif")
            self.myImgRDG = tk.PhotoImage(file="ressources/icones/rdg.gif")
            self.myImgRDD = tk.PhotoImage(file="ressources/icones/rdd.gif")
            self.myImgAR = tk.PhotoImage(file="ressources/icones/ar.gif")
            self.myImgRT = tk.PhotoImage(file="ressources/icones/rt.gif")
            self.myImgRTG = tk.PhotoImage(file="ressources/icones/rtg.gif")
            self.myImgRTD = tk.PhotoImage(file="ressources/icones/rtd.gif")
            self.myImgTSPG = tk.PhotoImage(file="ressources/icones/tspg.gif")
            self.myImgRSP = tk.PhotoImage(file="ressources/icones/rsp.gif")
            self.myImgTSPD = tk.PhotoImage(file="ressources/icones/tspd.gif")
            self.myImgCO = tk.PhotoImage(file="ressources/icones/co.gif")
            self.myImgCMD = tk.PhotoImage(file="ressources/icones/cmd.gif")
            self.myImgSD = tk.PhotoImage(file="ressources/icones/send.gif")
     
            #Boutons
            self.btAV = tk.Button(self.interfaceDirect, image=self.myImgAV, bg = "grey", command=self.av)
            self.btCo = tk.Button(self.interfaceConnexion, image=self.myImgCO, bg = "green", command=self.serialConn)
     
            self.btSendDirect = tk.Button(self.interfaceSaisieDirect, image=self.myImgSD, bg = "green", command=self.send_direct)
     
            #Positionnement des boutons généraux
            self.interfaceDirect.grid(column=0, row=0, sticky='NS')
            self.btAV.grid(column=2, row=0)
     
            #Positionement du log
            self.interfaceInfos.grid(column=0, row=3, sticky='NS')
            self.textInfos.grid(column=0, row=0)
     
            #Positionnement de la partie connection
            self.interfaceConnexion.grid(column=1, row=3, sticky='NS')
            self.btCo.grid(column=0, row=0)
     
        #Calcul du temps donné par rapport à une distance demandé
        #delay(3000) = 1 tour de roue = 18cm parcourus
        #distance (cm) * 3000 / 18 = temps à effectué.
        def distanceRoute(self):
            distanceCommand = self.distance.get() #Mise à jour de la distance
            timeRoute = ((int(distanceCommand) * 3000)/18)
            return timeRoute
     
        #Avant
        def av(self):
            speed = self.scaleSpeed.get() #Mise à jour de la vitesse
            distance = self.distanceRoute() #Récupère le temps demandé par rapport à une distance demandé
            #Si on utilise l'interface 1 
            if self.currentInterface == 1:
                #Si on a la connection lancé
                if self.serialConnection == 1:
                    self.serialOrdre("!,1,1," + str(speed) + ",1," + str(speed) + ",1," + str(speed) + ",1," + str(speed) + "," + str(distance) + ",*")
     
        #Ecrire sur le port série l'ordre
        def serialOrdre(self, ordre):
            data = bytearray(ordre, 'ascii')
            self.ser.write(data)
     
            information = "Envoyé -> " + ordre + "...\n"
            self.textInfos.insert(0.0, information)
            #print("Ordre envoyé",ordre)
     
     
        #Active les boutons standard
        def btnStandard(self):
            #Si on a la connection lancé
            if self.serialConnection == 1:
                self.btAV.configure(bg = "green")
                self.btRG.configure(bg = "green")
                self.btRC.configure(bg = "grey")
                self.btRD.configure(bg = "green")
                self.btRDG.configure(bg = "green")
                self.btRDD.configure(bg = "green")
                self.btAR.configure(bg = "green")
                self.btRTG.configure(bg = "green")
                self.btRT.configure(bg = "green")
                self.btRTD.configure(bg = "green")
                self.currentManDir.configure(bg = "green")
                self.btTSPG.configure(bg = "red")
                self.btRSP.configure(bg = "blue")
                self.btTSPD.configure(bg = "red")
                #Pour se rappeler dans quelle interface on est
                self.currentInterface = 1
     
        #Active les boutons spéciaux
        def btnSpeciaux(self):
            #Si on a la connection lancé
            if self.serialConnection == 1:
                self.btAV.configure(bg = "red")
                self.btRG.configure(bg = "green")
                self.btRC.configure(bg = "blue")
                self.btRD.configure(bg = "green")
                self.btRDG.configure(bg = "green")
                self.btRDD.configure(bg = "green")
                self.btAR.configure(bg = "red")
                self.btRTG.configure(bg = "red")
                self.btRT.configure(bg = "blue")
                self.btRTD.configure(bg = "red")
                self.currentManDir.configure(bg = "red")
                self.btTSPG.configure(bg = "green")
                self.btRSP.configure(bg = "grey")
                self.btTSPD.configure(bg = "green")
                #Pour se rappeler dans quelle interface on est
                self.currentInterface = 2
     
        #Etablir une connexion
        def serialConn(self):
            #Tentative de connexion au XBee Usb
            for device in self.locations:
                try:
                    information = "Tentative de connecion sur le port" + device + "...\n"
                    self.textInfos.insert(0.0, information)
     
                    self.ser = serial.Serial(device, 9600)
                    self.serialConnection = 1
                    break
                except:
                    information = "Connection échoué sur " + device + "!\n"
                    self.textInfos.insert(0.0, information)
     
            #Si la connexion est assuré
            if self.serialConnection == 1:
                self.initRover()    #Lancer l'initialisation du rover
     
    posl_app = tk.Tk()              #Instancie la classe
     
    iface_graphique = iface1(posl_app)
    iface_graphique.grid(column = 0, row = 0, sticky='NS')
     
    posl_app.title('POSL Rover')    #Titre de l'application
    posl_app.mainloop()             #Boucler
    Normal que dans ce code on retrouve des choses qui ne vont pas ou qui manque ! C'est voulu pour le moment. De mon côté, je vous ai évité de lire 400 lignes inutiles.

    Je cherche donc à implémenter une fonction qui ira régulièrement voir si des datas sont disponibles et les affichera dans la fenêtre de log. Tout ça sans avoir un while(1) car pas propre et bloquant.

    Merci pour votre aide.

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Salut,

    Vous pouvez poster un callback dans la boucle d'évèmenent qui fera le boulot.

    En gros çà se passe comme çà:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> import tkinter as tk
    >>> root = tk.Tk()
    >>> label = tk.Label()
    >>> counter = 100
    >>> def update():
    ...     global counter
    ...     label['text'] = counter
    ...     counter -= 1
    ...     if counter: root.after(100, update)
    ...
    >>> update()
    Mais les échanges avec des ports série ayant une durée plus ou moins importante, il serait plus judicieux d'écrire ces fonctions hors GUI puis de réaliser un thread qui s'occupera de les lancer et de poster les mises à jour du GUI.
    En gros une mécanique comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import tkinter as tk
    import threading
    import time
     
    root = tk.Tk()
    label = tk.Label()
    label.pack()
     
    def update(label, counter):
        for x in range(counter):
            root.after_idle(lambda: label.configure(text=x))
            time.sleep(0.1)
     
    task = threading.Thread(target=update, args=(label, 100))
    task.start()
    tk.mainloop()
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    Hum... C'est un peu chaud pour moi.

    Aller, on s'accroche, on va y arrivé !

    Donc, oui si je comprends faut un tread en dehors. Ok, ça me conviendrais bien. Quand je tente d'implémenter le premier exemple, il ne ser passe rien

    Voila, tout à la fin j'ai fait ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    posl_app = tk.Tk()              #Instancie la classe
     
    iface_graphique = iface1(posl_app)
    iface_graphique.grid(column = 0, row = 0, sticky='NS')
     
    posl_app.title('POSL Rover')    #Titre de l'application
    posl_app.mainloop()             #Boucler
     
    #Tread de vérification
    counter_A = 100
    def update(counter):
        global counter_A
        print("test")
        counter_A -= 1
        if counter_A:
            posl_app.after(100, update)
     
    update()
    Pour infos, j'ai changer counter par counter_A afin de libéré un mot clef. et j'ai volontairement tracer avec un print pour checker le test.

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Citation Envoyé par Caxton Voir le message
    Quand je tente d'implémenter le premier exemple, il ne ser passe rien .
    Ben lorsque vous écrivez:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    posl_app.mainloop()             #Boucler
    l'ordinateur est docile: il boucle jusqu'a ce que .quit soit reçu.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    J'ai descendu posl_app.mainloop() #Boucler
    en dessous de mon code et ça plante !!!

    Bon, donc là, je suis pas sur la bonne piste je pense.

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Citation Envoyé par Caxton Voir le message
    J'ai descendu posl_app.mainloop() #Boucler
    en dessous de mon code et ça plante !!!
    Vous avez pris la liberté d'ajouter un paramètre à update mais outre qu'il ne sert à rien (pour l'exemple), tkinter ne va pas l'inventer lorsqu'il va appeler le callback. Ceci dit le message d'erreur devrait être assez clair pour vous mettre sur la piste, non?

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    Oh oui ! Exact !

    Bon, une fois rectifié, en effet, je scrute les infos.

    J'ai essayer de faire cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    posl_app = tk.Tk()              #Instancie la classe
     
    iface_graphique = iface1(posl_app)
    iface_graphique.grid(column = 0, row = 0, sticky='NS')
     
    posl_app.title('POSL Rover')    #Titre de l'application
     
    #Tread de vérification
    counter = 100
    def update():
        global counter
     
        information = "Rover : " + str(iface_graphique.ser.read(4), encoding="ascii")
        iface_graphique.textInfos.insert(0.0, information)
     
        counter -= 1
        if counter:
            posl_app.after(100, update)
     
    update()
     
    posl_app.mainloop()             #Boucler
    Sauf que je bloque sur str()...

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Citation Envoyé par Caxton Voir le message
    Sauf que je bloque sur str()...
    Ca retourne peut être un message d'erreur intéressant?

    De toutes façons tant que vous voudrez mettre au point la communication avec l'interface série et la mise à jour de l'interface graphique en même temps, vous aurez des difficultés pour savoir ce qui foire et...la mise au point sera au petit bonheur la chance et bien plus longue.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  9. #9
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    Oui, c'est bien ce que je voie.

    Bon, j'ai plus que deux options.
    - Soit je me passe de cette fonction pour le moment
    - Soit j'ai pas le choix et faut en profiter pour coder ça mieux

    Hélas, je ne peux accepté la première solution car elle fait parti intégrante d'un outils de tracking à distance.

    Il ne me reste que la solution recodage

    Quelle est donc la solution la moins complexe pour récupéré mon interface graphique actuelle et ajouter de quoi recevoir les datas ?
    Il ne faut pas que ça soit bloquant logiciellement parlant !

  10. #10
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Salut,

    Citation Envoyé par Caxton Voir le message
    Quelle est donc la solution la moins complexe pour récupéré mon interface graphique actuelle et ajouter de quoi recevoir les datas ?
    "complexe" est assez relatif.
    Si vous restez sur la logique rafraîchissement toutes les 100 ms (c'est ce que fait "update"...after(100, update)) c'est là qu'il faudra lancer les fonctions qui iront récupérer les données.
    Il est plus simple (et rapide) de les mettre au point avec une interface console ("print" le retour de la fonction).
    Reste à définir ces fonctions, les coder, les tester et les intégrer à l'interface graphique.
    note: Si les temps de réponses sont trop longs, vous pourrez alors mettre un thread entre l'interface graphique et ce tas de fonctions (que vous pourrez encapsuler dans une classe, un module,...)

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  11. #11
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    Et bien, j'en suis pas là...

    Je débute. Faut un début à tout !!!

    j'ai pensé à faire une fonction interne pour chaque choses. Il ne sera pas bien difficile de récupéré un bouton ou un autre. Faut effectivement s'attendre à ne pas avoir de réponses pendant longtemps et d'un coup à en obtenir.

    Faut qu'on fasse comme si c'était une interruption sur micro-contrôleur mais sans pour autant bloquer le programme. L'arrêt d'urgence doit pouvoir continuer à être opérationnel.

    Donc, si je résume.

    Faut un début de programme qui appel deux thread encapsulé.
    Faut que l'un des thread soit orienté graphique avec la classe.
    Faut un autre qui se charge de récupérer les datas.
    Et il faut que les fonctions de pilotages soit extérnalisé dans une classe qui permettre aussi de jouer protection comme c'est le cas actuellement.

    J'ai un peu honte de mes débuts en python mais bon ! Puisque c'est aussi intéressant de faire du code pour améliorer les choses voila l'ampleur des dégats
    https://github.com/Almisuifre/POSL-R.../Version_1.0.0

    [Mode troll : ON]
    Attention, on ne se moque qu'après avoir tout lue
    Attention bis : Si vous voyez un message du style "#si vous êtres arrivé là c'est que c'est une voie sans issue et que seul un dieu qui sait réellement coder avec les pieds saura s'en sortir".
    {Mode troll : OFF]

  12. #12
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Salut,

    Je n'ai pas envie de discutailler sur les déconvenues rencontrées par les débutants qui se lancent trop tôt, trop vite dans la construction de choses bien trop compliquées... C'est comme çà.

    Par contre, il serait peut être utile que vous racontiez un peu mieux ce qu'il se passe (ou pas) lorsque s'exécutent les instructions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        information = "Rover : " + str(iface_graphique.ser.read(4), encoding="ascii")
        iface_graphique.textInfos.insert(0.0, information)
    note: n'oubliez pas que contrairement à des programmes plus basiques, impossible de faire tourner le votre sans disposer du matériel ad hoc. Si vous n'êtes pas plus précis sur les problèmes rencontrés impossible de "voir" ce que vous ne dites pas.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  13. #13
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    Plusieurs cas sont à donné.

    1/ Je suis connecté à une interface qui envoie des datas au travers d'une liaison série.
    Si ma carte distance est disponible, elle envoie des datas de retour parfois, mais pas out le temps.
    Au retour, si j'exécute le code dit plus haut, s'affiche 'Ok[]'

    2/ Je suis connecté à une interface qui envoie des datas au travers d'une liaison série.
    Si ma carte distance n'est pas disponible, elle n'envoie de datas de retour !
    Au retour, si j'exécute le code dit plus haut, ça plante en boucle

    Du coup, par curiosité, avant de tout cassé je vais essayer de coder quelquechose de plus simple. Un bouton connexion, un autre pour donner un ordre, un autre pour déconnecter, et une fenêtre de log. Le tout en tkinter.

    Je me dis ceci:

    Faire un programme qui appelle une autre feuille ayant la partie graphique en tkinter.
    pouvoir faire appel à une autre page qui ferais ceci:
    - Lancer un treah d'écoute si la connexion est effectué et si on peut afficher des datas
    - Afficher des datas régulièrement
    Et enfin, coder proprement toutes mes fonctions comme il faut.

    C'est un vaste programme je sait, mais je m'y colle et tant pis si ça prends des jours. C'est comme ça que j'apprends.

  14. #14
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    Première étape, voir si j'étais capable de séparer les choses. Et bien oui !

    Voila donc mon programme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     
    # -- IMPORTATIONS
    import tkinter as tk
     
    from interface import interface
     
    # -- MAIN
    root = tk.Tk()
     
    iface = interface(root)
    iface.grid(column=0, row=0, sticky='NS')
     
    root.title('POSL Rover')    #Titre de l'application
    root.mainloop()             #Boucler
    Et ensuite, la classe interface graphique.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
     
    # -- IMPORTATIONS
    import tkinter as tk
     
    # -- CLASSE Interface graphique
    class interface(tk.Frame):
     
        #Initialisation
        def __init__(self, parent):
            #Variables
            self.parent = parent
     
            #Les frames
            tk.Frame.__init__(self, self.parent)
            self.boutons = tk.LabelFrame(self, text="Contoles", padx=20, pady=20)
            self.logs = tk.LabelFrame(self, text="Logs", padx=20, pady=20)
     
            #Images
            self.myImgAV = tk.PhotoImage(file="ressources/icones/av.gif")
            self.myImgCO = tk.PhotoImage(file="ressources/icones/co.gif")
     
            #Boutons
            self.btAV = tk.Button(self.boutons, image=self.myImgAV, bg = "grey")
            self.btCO = tk.Button(self.boutons, image=self.myImgCO, bg = "green")
     
            #Textes
            self.textLogs = tk.Text(self.logs, width=50, height=20, wrap=tk.WORD)
     
            #Position des boutons
            self.boutons.grid(column=0, row=0, sticky='NS')
            self.btCO.grid(column=0, row=0)
            self.btAV.grid(column=2, row=1)
     
            #Position du log
            self.logs.grid(column=0, row=1, sticky='NS')
            self.textLogs.grid(column=0, row=0)
    Bon, j'ai juste mis un bouton de connexion et un bouton d'action pour les tests. Il y as une fenêtre de log pour y voir quelque-chose.

    Déjà est-ce que je ne me suis pas trop planté ?
    Même si ça fonctionne est-ce que c'est encore améliorable ?
    Et enfin, si vous me permettez le pas à pas, je me demande maintenant comment implémenter mon thread qui va se réveillé à chaque fois que je reçois des datas ?Il il faut gérer la non connexion puisqu'on ne l'as pas encore implémenté. Ou bien faut-t-il faire l'inverse ?

    Merci en tout cas pour vos aides.

  15. #15
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Salut,

    Citation Envoyé par Caxton Voir le message
    Première étape, voir si j'étais capable de séparer les choses. Et bien oui !
    ...

    Déjà est-ce que je ne me suis pas trop planté ?
    Même si ça fonctionne est-ce que c'est encore améliorable ?
    Et enfin, si vous me permettez le pas à pas, je me demande maintenant comment implémenter mon thread qui va se réveillé à chaque fois que je reçois des datas ?Il il faut gérer la non connexion puisqu'on ne l'as pas encore implémenté. Ou bien faut-t-il faire l'inverse ?
    Il y a pleins de façons pour distribuer les instructions d'un programme.
    Ce n'est pas arbitraire, çà répond à des choix quant à la distribution des rôles et des responsabilités.
    Vous avez 2 composants dans votre application: celui qui s'occupe de l'interface graphique et celui qui s'occupe d'expédier des commandes au robot et d'en récupérer l'état côté port série.
    Relisez votre (ancien) code, par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        def av(self):
            speed = self.scaleSpeed.get() #Mise à jour de la vitesse
            distance = self.distanceRoute() #Récupère le temps demandé par rapport à une distance demandé
            #Si on utilise l'interface 1 
            if self.currentInterface == 1:
                #Si on a la connection lancé
                if self.serialConnection == 1:
                    self.serialOrdre("!,1,1," + str(speed) + ",1," + str(speed) + ",1," + str(speed) + ",1," + str(speed) + "," + str(distance) + ",*")
    Cette méthode est appelée lorsqu'on clique sur le Button qui affiche MyImgAV.
    Pour fonctionner, il lui faut récupérer des informations côté utilisateur - speed, distance - dans des Entry puis dialoguer avec l'interface série...
    Imaginez une class Robot qui ait sa méthode avance.
    Elle pourrait prendre en paramètre speed et distance:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class Robot:
         ...
           def avancer(self, speed, distance):
                # on expédie la commande sur le port série
    Côté interface graphique, on a dégagé la gestion du port série et on connaît le robot via un attribut, donc la fonction/méthode/callback appelée lorsqu'on clique sur le bouton peut se contenter de récupérer ce qu'il faut côté Entry et dans un premier temps se résumer à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class IHM:
        ...
        def do_avancer(self):
            speed = self.scaleSpeed.get() #Mise à jour de la vitesse
            distance = self.distanceRoute() #Récupère le temps demandé par rapport à une distance demandé
            self.robot.avancer(speed, distance)
    Après vous avez:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        #Ecrire sur le port série l'ordre
        def serialOrdre(self, ordre):
            data = bytearray(ordre, 'ascii')
            self.ser.write(data)
     
            information = "Envoyé -> " + ordre + "...\n"
            self.textInfos.insert(0.0, information)
            #print("Ordre envoyé",ordre)
    Les deux premières instructions ont leur place dans "Robot" par contre, il faut éventuellement remonter quelque chose (la commande expédiée sur le port série) pour mettre à jour le "log".
    Séparons!
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Robot:
        ...
        def _send(self, command):
            data = bytearray(command, 'ascii')
            self.ser.write(data)
            self.last_command = command    
        def avancer(self, speed, distance):
            fmt= "!,1,1,%(speed)s,1,%(speed)s,1,%(speed)s,1,%(speed)s,1,%(distance)s,*"
            self._send(fmt % vars())
    Maintenant que le Robot enregistre la dernière commande expédiée, on peut compléter do_avancer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class IHM:
        ...
        def do_avancer(self):
            speed = self.scaleSpeed.get() #Mise à jour de la vitesse
            distance = self.distanceRoute() #Récupère le temps demandé par rapport à une distance demandé
            self.robot.avancer(speed, distance)
            sel.log_sent(self.robot.last_command)
     
        def log_sent(self, ordre):
            information = "Envoyé -> " + ordre + "...\n"
            self.textInfos.insert(0.0, information)
            #print("Ordre envoyé",ordre)
    C'est juste pour vous donnez des idées côté découpage. Après à vous de voir mais pas la peine regrouper des opérations dans des boîtes si vous ne pouvez pas la fermer et placer des ouvertures (fonctions, attributs) qui vont "simplifier" la construction de chaque boîte indépendamment. Pire, si la séparation n'est pas bien faites placer un "thread" entre les deux sera "sportif".

    Bon courage,

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  16. #16
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    Oui, je comprends le principe évoqué.

    Ça va un peu vite pour moi

    Est-ce que je part de ce nouveau code déjà ?
    Ne faut-il pas ensuite que je crée une classe qui envoie les datas et gère le thread sérial (retour d'infos) ?
    Enfin, je terminerais entre les deux par les fonctionnalités découpés comme décrite au dessus.

  17. #17
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    Bon, Je répond un peu à moi même

    Voila donc comment je découpe les choses. M'arrêter si je me trompe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
     
    POSL-Rover.py
    	-Import ihm
    	-Import rover
     
    	Crée un objet root tkinter
     
    	Créer un objet l'ihm (root)
    	Place l'ihm dans la fenêtre
     
    	Créer un objet rover(root)
     
    	Créer un objet communication(root)
     
    	Placer le titre sur root
    	mainloop sur root
     
     
    ihm.py
    	-Import tkinter
     
    	Initialiser la fenêtre
    	Initialiser les frames
    	Initialiser les boutons
    	Placer les boutons sur les frames
    	Placer les frames sur la fenêtre
     
    rover.py
    	Initialiser la classe
     
    	Avance (vitesse, nombre de pas)
    		datas avance
     
    	Recule (vitesse, nombre de pas)
    		datas recule
     
    communication.py
    	Initialiser la classe
     
    	Connexion()
    		Connecter
    		Placer un flag connecter
     
    	Deconnexion()
    		Si flag connecter = 1
    			Déconnecter
     
    	SendDatas(datas)
    		Si connecter
    			Envoyer datas
     
    	receivDatas()
    		Si connecter
    			Si in a reçu des datas
    				Récuperer les datas
    				Mise en forme des datas
    				Retourner des datas
    			Retourner none
    En gros.

  18. #18
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Citation Envoyé par Caxton Voir le message
    Ne faut-il pas ensuite que je crée une classe qui envoie les datas et gère le thread sérial (retour d'infos) ?
    Enfin, je terminerais entre les deux par les fonctionnalités découpés comme décrite au dessus.
    Je dirais que tant que vous n'avez pas complété la class Robot (qui représente la chose qui bouge derrière le port série) vous pouvez réfléchir à ce que pourrait ressembler une interface graphique et à la façon de l'interfacer avec Robot.

    Vous avez écrit:
    Citation Envoyé par Caxton Voir le message
    Plusieurs cas sont à donné.

    1/ Je suis connecté à une interface qui envoie des datas au travers d'une liaison série.
    Si ma carte distance est disponible, elle envoie des datas de retour parfois, mais pas out le temps.
    Au retour, si j'exécute le code dit plus haut, s'affiche 'Ok[]'

    2/ Je suis connecté à une interface qui envoie des datas au travers d'une liaison série.
    Si ma carte distance n'est pas disponible, elle n'envoie de datas de retour !
    Au retour, si j'exécute le code dit plus haut, ça plante en boucle
    Si vous expédiez une commande il faudra peut être attendre une réponse avant de lancer la commande suivante. De plus, si vous récupérez l'état du robot sans avoir attendu une éventuelle réponse, vous risquez de lire tout d'un coup et avoir des "OK[]" de la commande précédente à trier.
    Ce n'est pas sans implications sur les fonctionnalités (et la quantité de code à réaliser et à mettre au point) côté Robot.
    Dans un premier temps, vous devriez pouvoir écrire des petits scripts du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    robot = Robot()
    robot.avancer(2, 10)
    robot.roues_gauche()
    robot.avancer(2, 10)
    ...
    La question est de savoir si l'appel à avancer doit revenir et permettre l'exécution de l'instruction suivante (.roues_gauche) sans attendre ou pas (et si on attend, çà attend quoi?)

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  19. #19
    Membre régulier Avatar de Caxton
    Homme Profil pro
    Sans
    Inscrit en
    Janvier 2005
    Messages
    586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Corrèze (Limousin)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Janvier 2005
    Messages : 586
    Points : 123
    Points
    123
    Par défaut
    En fait le retour d'info doit être asynchrone. Il n'est pas obligatoire. Et c'est voulue.

    Il se passe réelement ceci:
    1/ On a une image du lieu (devant le robot)
    2/ L'homme décide d'aller en avant de, mettons, 30 cm
    3/ L'homme rempli le champ distance et tape 30
    4/ L'homme appuie sur le bouton Avancer
    5/ L'infos est transmise via Python -> USb -> Série -> XBee -> UART au rover (déjà géré)
    6/ On récupère une image après l'acton. Et c'est reparti pour un tour.

    Le retour d'image passe par une autre voie que la connexion de pilotage. On a s donc pas à la gérer

    Enfin, pour terminer, le retour d'info n'intervient que pour tester un composant interne à l'étude. Exemple des impulsions sur une roue par voie analogique et en temps décalé. Une fois l'ordre reçu sur le rover, il ne fait que son travail en toute autonomie. Le pilotage interviens que pour la prise de décision.

    De cette façon, on s'affranchi des perturbations au pilotage. On doit recevoir une trame complète sur le rover avant qu'il fasse quelque-chose. Et ceci est déjà géré.

  20. #20
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 241
    Points : 36 698
    Points
    36 698
    Par défaut
    Salut,

    Citation Envoyé par Caxton Voir le message
    En fait le retour d'info doit être asynchrone. Il n'est pas obligatoire. Et c'est voulue.
    Dois je comprendre que lorsque vous expédiez des bytes sur le port série pour dire "avance" vous ne savez même pas si la commande a bien été reçue?

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 3 123 DernièreDernière

Discussions similaires

  1. Tkinter Bind et boucle infinie
    Par lilly74 dans le forum Tkinter
    Réponses: 3
    Dernier message: 13/03/2010, 00h41
  2. symptome de la boucle infinie dans une requete
    Par ouam81 dans le forum Langage SQL
    Réponses: 8
    Dernier message: 27/05/2005, 13h10
  3. Réponses: 15
    Dernier message: 24/05/2005, 09h34
  4. [Socket] Pb de boucle infinie
    Par Myogtha dans le forum Entrée/Sortie
    Réponses: 12
    Dernier message: 10/06/2004, 15h10
  5. [C#] Comment eviter les boucles infinies ?
    Par Thomas Lebrun dans le forum C#
    Réponses: 12
    Dernier message: 09/06/2004, 01h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo