Thread.tkinter

yazchapi21 Messages postés 6 Statut Membre -  
yazchapi21 Messages postés 6 Statut Membre -
Bonjour, j'ai un problème. Un thread gère la communication client-serveur. Il envoie la variable globale ''msg'' et reçoit les informations du serveur. Le clic sur un bouton d'une interface graphique tkinter change la valeur de la variable globale ''msg''. Mais lorsque je clique sur l'un des boutons le thread n'envoie pas le message. Aidez-moi s'il vous plaît.

10 réponses

Résumé de la discussion

Problème central : une application tkinter utilise un processus en arrière-plan pour gérer une communication client-serveur et la variable globale msg changée par les boutons ne transmet pas l'information. Des échanges soulignent l'intérêt d'une file d'attente (Queue) pour transférer les données entre le processus et l'interface tkinter, et d'éviter les boucles bloquantes qui empêchent l'envoi. D'autres proposent une pause courte par time.sleep(0,1 seconde) pour laisser passer les échanges, et certains envisagent d'intégrer l'interface Tkinter avec des appels déclenchés par les boutons tout en utilisant une queue centralisée. En cas de complexité avec plusieurs boutons, l'essentiel consiste à décharger le traitement des données sur un consommateur dédié alimenté par une queue, afin de préserver la réactivité de l'interface.

Généré automatiquement par IA
sur la base des meilleures réponses
  1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 588
     
    bonjour,
    peut-être faut-il adapter ton code?
    0
  2. yazchapi21
     
    Voici le code:
    from PIL import Image, ImageTk
    import tkinter as tk
    from tkinter import simpledialog
    from tkinter import messagebox as mb
    from vidgear.gears import CamGear
    import argparse
    import datetime
    import cv2 as cv
    import cv2
    import os
    import csv
    import time
    import sys, socket
    from matplotlib.figure import Figure
    #import matplotlib.animation as animation
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    from matplotlib.animation import FuncAnimation
    import matplotlib.animation as animation
    from matplotlib import style
    import numpy as np
    from random import randint
    import utils
    import threading

    #url = 'http://192.168.43.199:8081'
    #cap = CamGear(source=url).start()

    #Lib install

    #sudo apt-get install python3-pil.imagetk
    #sudo apt-get install libopencv-dev python-opencv

    #Test Tkinter install
    #python3 -m tkinter

    white = "#ffffff"
    lightBlue2 = "#adc5ed"
    font = "Constantia"
    fontButtons = (font, 12)
    maxWidth = 1100
    maxHeight = 700
    temps =[]
    vitesses_droites = np.array([])
    vitesses_gauches = np.array([])
    tps = np.array([])
    xMax = 10
    Distance_ultrason = []
    vitesse_droite = []
    vitesse_gauche = []
    Latitude = []
    Longitude = []
    temps = []
    msg = None

    def demarrer():
    global msg
    msg = str(1)

    def arreter():
    global msg
    msg = str(4)

    def devier_a_gauche():
    global msg
    msg = str(2)

    def devier_a_droite():
    global msg
    msg = str(3)

    HOST = sys.argv[-1] if len(sys.argv) > 1 else '192.168.43.199'
    PORT = utils.PORT
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((HOST, PORT))
    print('\nConnected to {}:{}'.format(HOST, PORT))
    print("Type message, enter to send. 'q' to quit")

    class ThreadCommunication(threading.Thread):
    """objet thread gérant la communication"""
    def __init__(self, sock):
    threading.Thread.__init__(self)
    self.connexion = sock # réf. du socket de connexion
    def run(self):
    global msg
    #while True:
    # if (not(msg == None)):
    if(msg):
    utils.send_msg(sock, msg)
    print(msg)
    #print(msg)
    msg = None
    msgFromArduino = utils.recv_msg(sock)
    msgFromArduino = msgFromArduino.split(':')[1].split(';')
    print(msgFromArduino)
    if (not(len(msgFromArduino) < 6)):
    data1 = msgFromArduino[0]
    data2 = msgFromArduino[1]
    data3 = msgFromArduino[2]
    data4 = msgFromArduino[3]
    data5 = msgFromArduino[4]
    start_time = time.time()
    secondes = time.time() - start_time
    Distance_ultrason.append(data1)
    vitesse_droite.append(data2)
    vitesse_gauche.append(data3)
    Latitude.append(data4)
    Longitude.append(data5)
    temps.append(round(secondes, 2))
    now = datetime.datetime.now()
    c = open('fichierrobot_car_' + '_' + str(now.strftime("%Y-%m-%d-%H-%M")) + '.csv', "w")
    c = csv.writer(c, delimiter="\t")
    c.writerow (("Distance_ultrason","vitesse_droite","vitesse_gauche","Latitude","Longitude","temps"))
    fin1, fin2 = len(Distance_ultrason), len(vitesse_droite)
    if fin1 == fin2:
    for i in range (fin1):
    c.writerow((Distance_ultrason[i],vitesse_droite[i],vitesse_gauche[i],Latitude[i],Longitude[i],temps[i]))
    return vitesse_droite, vitesse_gauche, temps
    thrd = ThreadCommunication(sock)
    thrd.start()
    class Application:
    def __init__(self, output_path = "./"):
    """ Initialize application which uses OpenCV + Tkinter. It displays
    a video stream in a Tkinter window """
    #replace with your video URL
    #url = 'http://192.168.43.199:8081'
    #cap = CamGear(source=url).start()
    self.vs = cv2.VideoCapture('my_input.mp4') # capture video frames, 0 is your default video camera
    self.vs2 = cv2.VideoCapture('my_input.mp4') # capture video frames, camVideo is your out video
    self.output_path = output_path # store output path if want todo a snapshot
    self.current_image = None # current image from the camera
    self.current_image2 = None # current image from the camVideo

    self.root = tk.Tk() # initialize root window

    self.root.resizable(width=False, height=False) #block auto-resizable
    self.root.geometry('%dx%d+%d+%d' % (maxWidth,maxHeight,0,0)) #window size
    self.root.title("Project Interface") # set window title
    self.root['bg'] = 'purple'

    # self.destructor function gets fired when the window is closed
    self.root.protocol('WM_DELETE_WINDOW', self.destructor)

    self.panel = tk.Label(self.root, width=500,height=250) # initialize image panel
    self.panel.pack(padx=5, pady=0, side=tk.LEFT)
    self.panel.place(x=10, y=160)
    self.img = cv2.imread("000.jpg")
    self.resize_img =cv2.resize(self.img, (500, 250))
    self.current_img = Image.fromarray(self.resize_img)
    img_tk = ImageTk.PhotoImage(image=self.current_img)
    self.panel.imgtk = img_tk
    self.panel.config(image=img_tk)

    self.panel1 = tk.Label(self.root, width=500,height=250) # initialize image panel1
    self.panel1.pack(padx=5, pady=0, side=tk.RIGHT)
    self.panel1.place(x=570, y=160)
    self.img1 = cv2.imread("001.jpg")
    self.resize_img1 =cv2.resize(self.img1, (500, 250))
    self.current_img1 = Image.fromarray(self.resize_img1)
    img_tk1 = ImageTk.PhotoImage(image=self.current_img1)
    self.panel1.imgtk1= img_tk1
    self.panel1.config(image=img_tk1)

    self.panel2 = tk.Label(self.root, width=150,height=150)
    self.panel2.pack(padx=5, pady=0, side=tk.LEFT)
    self.panel2.place(x=10, y=10)
    self.img = cv.imread("uam.jpg")
    self.resize_img =cv.resize(self.img, (150, 150))
    self.current_img = Image.fromarray(self.resize_img)
    img_tk = ImageTk.PhotoImage(image=self.current_img)
    self.panel2.imgtk = img_tk
    self.panel2.config(image=img_tk)

    self.panel3 = tk.Label(self.root, width=150,height=150)
    self.panel3.pack(padx=5, pady=0, side=tk.LEFT)
    self.panel3.place(x=930, y=10)
    self.img = cv.imread("fast.jpg")
    self.resize_img =cv.resize(self.img, (150, 150))
    self.current_img = Image.fromarray(self.resize_img)
    img_tk = ImageTk.PhotoImage(image=self.current_img)
    self.panel3.imgtk = img_tk
    self.panel3.config(image=img_tk)

    #graphique courbe
    #print parameters values using a listbox
    self.liste = tk.Listbox(self.root, width=35,height=15)
    self.liste.insert(1, "Distance Ultrason : "+ str(100))
    self.liste.insert(2, "Vitesse roues droites : "+ str(65))
    self.liste.insert(3, "Vitesse roues gauches : "+ str(65))
    self.liste.insert(4, "Latitude : "+ str(10.4))
    self.liste.insert(5, "Longitude : "+ str(51.5))
    self.liste.pack()
    self.liste.place(x=550, y=600)
    '''
    def clearlist():
    listb2.delete(0,END)
    for nom in cur.fetchall():
    listb.insert(END,nom)

    self.liste = tk.Listbox(self.root, width=35,height=15)
    self.liste.insert(1, print('Distance_ultrason[i]=, Distance_ultrason[i]'))
    self.liste.insert(2, print('vitesse_droite[i]=, vitesse_droite[i]'))
    self.liste.insert(3, print('vitesse_gauche[i]=, vitesse_gauche[i]'))
    self.liste.insert(4, print('Latitude[i]=, Latitude[i]'))
    self.liste.insert(5, print('Latitude[i]=, Latitude[i]'))
    self.liste.pack()
    self.liste.place(x=550, y=600)
    '''

    #
    #create a button, that when pressed, start a car driving
    #Todo : define the function
    btn = tk.Button(self.root, text="Demarrer", bg='red', command=demarrer)
    btn.pack()
    btn.place(x=580, y=550)

    #create a button, that when pressed, stop a car driving
    #Todo : define the function
    btn1 = tk.Button(self.root, text="arreter", bg='cyan', command=arreter)
    btn1.pack()
    btn1.place(x=680, y=550)

    #create a button, that when pressed, change a car direction
    #Todo : define the function
    btn2 = tk.Button(self.root, text="devier_a_gauche", bg='green', command=devier_a_gauche)
    btn2.pack()
    btn2.place(x=780, y=550)

    # create a button, that when pressed, will take the current frame and save it to file
    btn3 = tk.Button(self.root, text="devier_a_droite", bg='orange', command=devier_a_droite)
    btn3.pack()
    btn3.place(x=950, y=550)

    btn4 = tk.Button(self.root, text="Quitter", bg='yellow', command=self.root.quit)
    btn4.pack()
    btn4.place(x=500, y=550)

    # start a self.video_loop and self.video2_loop that constantly pools the video sensor
    # for the most recently read frame
    #self.video_loop()
    #self.video2_loop()

    def video_loop(self):
    """ Get frame from the video stream and show it in Tkinter """
    ok, frame = self.vs.read() # read frame from video stream
    if ok: # frame captured without any errors
    cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) # convert colors from BGR to RGBA
    self.current_image = Image.fromarray(cv2image) # convert image for PIL
    imgtk = ImageTk.PhotoImage(image=self.current_image) # convert image for tkinter
    self.panel.imgtk = imgtk # anchor imgtk so it does not be deleted by garbage-collector
    self.panel.config(image=imgtk) # show the image
    self.root.after(30, self.video_loop) # call the same function after 30 milliseconds

    def video2_loop(self):
    """ Get frame from the video stream and show it in Tkinter """
    ok, frame = self.vs2.read() # read frame from video stream
    if ok: # frame captured without any errors
    cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) # convert colors from BGR to RGBA
    self.current_image2 = Image.fromarray(cv2image) # convert image for PIL
    imgtk = ImageTk.PhotoImage(image=self.current_image2) # convert image for tkinter
    self.panel1.imgtk = imgtk # anchor imgtk so it does not be deleted by garbage-collector
    self.panel1.config(image=imgtk) # show the image
    self.root.after(30, self.video2_loop) # call the same function after 30 milliseconds

    def destructor(self):
    """ Destroy the root object and release all resources """
    print("[INFO] closing...")
    self.root.destroy()
    self.vs.release() # release
    self.vs2.release() # release
    cv2.destroyAllWindows() # it is not mandatory in this application

    # construct the argument parse and parse the arguments
    ap = argparse.ArgumentParser()
    ap.add_argument("-o", "--output", default="./",
    help="path to output directory to store snapshots (default: current folder")
    args = vars(ap.parse_args())

    # start the app
    print("[INFO] starting...")
    pba = Application(args["output"])

    def get_back_values():
    global vitesses_droites, vitesses_gauches, xMax, tps
    # vitesse_droite, vitesse_gauche, temps = connexion()
    "Dummy function"
    if (len(vitesses_droites) < xMax):
    vitesses_droites = np.append(vitesses_droites, vitesse_droite)
    vitesses_gauches = np.append(vitesses_gauches, vitesse_gauche)
    tps = np.append(tps, temps)
    else:
    vitesses_droites[0:10] = vitesses_droites[0:xMax]
    vitesses_droites[9] = vitesse_droite
    vitesses_gauches[0:10] = vitesses_gauches[0:xMax]
    vitesses_gauches[9] = vitesse_gauche
    tps[0:10] = tps[0:xMax]
    tps[9] = temps
    x = [tps[i] for i in range(10)]
    print(temps[i])
    y1 = [vitesses_droites[i] for i in range(10)]
    print(vitesse_droite[i])
    y2 = [vitesses_gauches[i] for i in range(10)]
    print(vitesse_gauche[i])
    return x, y1, y2
    def update_graph(event=True):
    global vitesses_droites
    if (not(len(vitesses_droites) < 10)):
    x, y1, y2 = get_back_values()
    ax1.clear()
    ax2.clear()
    ax1.set_ylim(0, 10, auto=False)
    #ax1.xaxis.set_ticks(range(6))
    #ax1.xaxis.set_ticklabels(['0', '2', '4', '6', '8', '10'])
    ax2.set_ylim(0, 10,auto=False)
    #ax2.xaxis.set_ticks(range(6))
    #ax2.xaxis.set_ticklabels(['0', '2', '4', '6', '8', '10'])
    ax2.set_xlabel('Temps')
    ax1.set_ylabel('vitesse_droite', color='g', fontsize = 22)
    ax2.set_ylabel('vitesse_gauche', color='r', fontsize = 22)
    ax1.plot(x, y1, 'g-o')
    ax2.plot(x, y2, 'r-o')

    style.use("ggplot")

    fig = Figure(figsize=(8, 5), dpi=50)
    ax1 = fig.add_subplot(211)
    ax2 = fig.add_subplot(212, sharex=ax1)
    ax2.set_xlabel('Temps')
    ax1.set_ylim(0, 10, auto=False)
    #ax1.xaxis.set_ticks(range(6))
    #ax1.xaxis.set_ticklabels(['0', '2', '4', '6', '8', '10'])
    ax1.set_ylabel('vitesse_droite', color='g', fontsize = 22)
    ax2.set_ylabel('vitesse_gauche', color='r', fontsize = 22)
    ax2.set_ylim(0, 10, auto=False)
    #ax2.xaxis.set_ticks(range(6))
    #ax2.xaxis.set_ticklabels(['0', '2', '4', '6', '8', '10'])
    fig.tight_layout()

    graph = FigureCanvasTkAgg(fig, master=pba.root)
    #graph = FigureCanvasTkAgg(fig, height= 100, width=100; background='red', itmaster=app)
    canvas = graph.get_tk_widget()
    canvas.grid(row=0, column=0)

    FuncAnimation(fig, update_graph, interval=2000)
    ani = animation.FuncAnimation(fig, update_graph, interval=500)
    canvas.pack()
    canvas.place(x=45, y=440)
    pba.root.mainloop()

    #if __name__ == "__main__":
    #fen = Application()
    #fen.start()
    0
  3. yazchapi21
     
    Bonjour, j'ai testé plusieurs fois ces balises pour préserver l'indentation mais ça ne marche pas toujours. Le problème du code est que le thread envoie la variable globale msg au serveur en tenant compte de ses variations lorsqu'on clique sur les boutons de tkinter. Merci.
    0
  4. Phil_1857 Messages postés 1883 Date d'inscription   Statut Membre Dernière intervention   169
     
    Bonjour,

    C'est pourtant simple et ça marche toujours:

    on fait un copier/coller du code ici, on le sélectionne à la souris, on clique sur la petite icone

    "flèche vers le bas" dans la barre d'icones au dessus, on choisit Python dans la liste déroulante

    et cela met le code entre balises

    Ensuite, lorsque le message est validé, le code apparait comme ceci:

    def test():
        print('test')
    
    test()
    
    0
  5. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 588
     
    tu écris "Le clic sur un bouton d'une interface graphique tkinter change la valeur de la variable globale ''msg''.".
    En es-tu certain? Comment l'as-tu vérifié?

    Ton code contient plein de choses qui n'ont rien à voir avec ton soucis. Simplifie-le pour n'en garder que le minimum qui montre le soucis.
    0
  7. yazchapi21
     
    msg = None   
        
    def demarrer():
        global msg
        msg = str(1)
    
    def arreter():
        global msg
        msg = str(4)
    
    def devier_a_gauche():
        global msg
        msg = str(2)
    
    def devier_a_droite():
        global msg
        msg = str(3)
    
    HOST = sys.argv[-1] if len(sys.argv) > 1 else '192.168.43.199'
    PORT = utils.PORT
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((HOST, PORT))
    print('\nConnected to {}:{}'.format(HOST, PORT))
    print("Type message, enter to send. 'q' to quit")
    
    class ThreadCommunication(threading.Thread):
        """objet thread gérant la communication"""
        def __init__(self, sock):
            threading.Thread.__init__(self)
            self.connexion = sock           # réf. du socket de connexion
        def run(self):
            global msg
            if(msg):
                utils.send_msg(sock, msg)
                print(msg)
            #print(msg)
            msg = None
            msgFromArduino = utils.recv_msg(sock)
            msgFromArduino = msgFromArduino.split(':')[1].split(';')
            print(msgFromArduino)
            if (not(len(msgFromArduino) < 6)):
                data1 = msgFromArduino[0]
                data2 = msgFromArduino[1]
                data3 = msgFromArduino[2]
                data4 = msgFromArduino[3]
                data5 = msgFromArduino[4]
                start_time = time.time()
                secondes = time.time() - start_time
                Distance_ultrason.append(data1)
                vitesse_droite.append(data2)
                vitesse_gauche.append(data3)
                Latitude.append(data4)
                Longitude.append(data5)
                temps.append(round(secondes, 2))
                now = datetime.datetime.now()
                c = open('fichierrobot_car_' +  '_' + str(now.strftime("%Y-%m-%d-%H-%M")) + '.csv', "w")
                c = csv.writer(c, delimiter="\t")
                c.writerow (("Distance_ultrason","vitesse_droite","vitesse_gauche","Latitude","Longitude","temps"))
                fin1, fin2 = len(Distance_ultrason), len(vitesse_droite)
                if fin1 == fin2: 
                    for i in range (fin1):
                        c.writerow((Distance_ultrason[i],vitesse_droite[i],vitesse_gauche[i],Latitude[i],Longitude[i],temps[i]))
                        return vitesse_droite, vitesse_gauche, temps
    thrd = ThreadCommunication(sock)
    thrd.start()
    0
  8. yazchapi21
     
    Merci, voilà le bout de code géré par le thread. Mais ça ne marche pas à cause de la variable globale msg. J'attend votre aide s'il vous plait. Merci d'avance.
    0
    1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       
      ce n'est pas la variable global qui pose problème.
      que penses-tu faire en ligne 33? quand penses-tu que cette ligne est exécutée?

      exemple qui utilise bien cette variable globale:
      import time
      import threading
      msg = None   
          
      def demarrer():
          global msg
          msg = "1"
      def arreter():
          global msg
          msg = "4"
      def devier_a_gauche():
          global msg
          msg = "2"
      
      def devier_a_droite():
          global msg
          msg = "3"
      
      class ThreadCommunication(threading.Thread):
          def __init__(self):
              threading.Thread.__init__(self)
          def run(self):
              global msg
              print("run",msg)
              while True:
                  if(msg):
                      print(msg)
                      msg = None
                  time.sleep(0.1)
              print("thread fini")
      thrd = ThreadCommunication()
      thrd.start()
      demarrer()
      time.sleep(1)
      arreter()
      time.sleep(4)
      
      0
    2. yazchapi21 > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
       
      Bonjour yg_be, j'ai pas reçu la notification voilà pourquoi j'ai pas répondu.
      Je vais mettre mon code client intégral à jour, tester et je vous tiendrai au courant. Mais quand j'ai testé votre code à la ligne 30 le code n'affiche pas ("thread fini") ? Merci.
      0
    3. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588 > yazchapi21
       
      le but est justement de montrer que le thread ne se termine pas, qu'il continue à travailler.
      0
    4. yazchapi21 > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
       
      Bonjour
      comme les fonctions modifiant la variable globale doivent être lancées par des boutons, il me faut donc une interface tkinter avec les boutons. J'étais sur la proposition d'utiliser la queue quand j'ai vu votre message. J'ai un code de test avec un seul bouton, l’affichage se fait dans le terminal et non dans le label affichant None lorsqu'on quitte l'interface (c'est pas ça l'essentiel). (Je n'arrige pas à mieux faire les lignes 40, 41, 42).
      import sys, threading
      import numpy as np
      import tkinter as tk
      import time
      from queue import Queue
      
      Resultat = None
      class Fenetre():
          def __init__(self):
              threading.Thread.__init__(self)
              self.q = Queue()
              self.root = tk.Tk()
              self.root.geometry("450x500")
              label = tk.Label(self.root, text="Resultat: {}".format(Resultat))
              label.pack(side="top", fill="both", expand=True)
              btn1 = tk.Button(self.root, text="commande", bg='cyan', command=self.calcul)
              btn1.pack()
              btn1.place(x=20, y=200)
              btn2 = tk.Button(self.root, text="Quitter", bg='yellow', command=self.root.quit)
              btn2.pack()
              btn2.place(x=350, y=200)
              
          def calcul(self):
              global Resultat
              x = np.arange(4)
              y = np.arange(10,16)
              A = np.arange(2, 3, 0.1)
              B = np.append(x, y)
              Resultat = A+B
              self.q.put(Resultat)
              time.sleep(1)
      class Consumer:
          def __init__(self, prod):
              self.prod = prod
      
          def consume(self):
              msg = self.prod.q.get()
              print('Item receved', msg)
                       
      Fenetre().root.mainloop()
      p = Fenetre()
      c = Consumer(p)
      t1 = threading.Thread(target=p.calcul)
      t2 = threading.Thread(target=c.consume)
      t1.start()
      t2.start()
      t1.join()
      t2.join()
      
      

      Mais lorsque j'augmente des boutons (comme j'en ai 4 dans mon vrai code) je ne sais vraiment pas comment créer un seul thread qui vas récupérer le msg comme ce n'est pas une seule fonction qui génère le msg. Si on peut intégrer l'interface tkinter dans votre code avec les boutons appelant les fonctions ça serait mieux. Merci.
      0
    5. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588 > yazchapi21
       
      est-ce dans consume() que tu souhaites récupérer tous les messages?
      0
  9. yazchapi21
     
    Je l'ai vérifié parce que lorsque je clique sur un bouton et que ça n'a pas marché, je relance le code et là ça marche mais lorsque je clique sur un autre bouton ça ne marche pas, le thread n'envoie pas.
    0
  10. drepo
     
    Salut,

    Que doit faire ton script en fait ?
    Quel travail doit faire le thread ?

    Parce d'après tes dernières réponses, je crois qu'utiliser un thread n'a pas d'intérêt sachant que tkinter peut faire la même chose naturellement.

    Par exemple, quelques retouches de ton code en utilisant une simple variable tkinter à laquelle on définit une fonction de callback laquelle sera exécutée chaque fois que celle-ci est modifiée.

    import tkinter as tk
    
    class Commandes():
        def __init__(self, communication):
            self._com = communication
    
        def demarrer(self):
            self._com.send("1")
    
        def arreter(self):
            self._com.send("4")
    
        def devier_a_gauche(self):
            self._com.send("2")
    
        def devier_a_droite(self):
            self._com.send("3")
    
    Resultat = None
    class Fenetre():
        def __init__(self):
            self.root = tk.Tk()
            self.root.geometry("450x500")
            self.communication = Communication(self.root)
            self.communication.run()
            self.commandes = Commandes(self.communication)
            label = tk.Label(self.root, text="Resultat: {}".format(Resultat))
            label.pack(side="top", fill="both", expand=True)
            btn1 = tk.Button(self.root, text="commande1", bg='cyan', command=self.commandes.demarrer)
            btn1.pack()
            btn1.place(x=20, y=200)
            btn2 = tk.Button(self.root, text="Quitter", bg='yellow', command=self.root.quit)
            btn2.pack()
            btn2.place(x=150, y=200)
            btn3 = tk.Button(self.root, text="commande2", bg='orange', command=self.commandes.devier_a_gauche)
            btn3.pack()
            btn3.place(x=320, y=200)
            btn4 = tk.Button(self.root, text="commande3", bg='yellow', command=self.commandes.devier_a_droite)
            btn4.pack()
            btn4.place(x=20, y=300)
            btn5 = tk.Button(self.root, text="commande4", bg='orange', command=self.commandes.arreter)
            btn5.pack()
            btn5.place(x=320, y=300)
    
    class Communication():
        def __init__(self, window):
            self._win = window
            self._msg = tk.Variable(self._win, '', 'msg')
            self._msg.trace('w', self._manage)
            self._stopped = False
    
        def _manage(self, *args):
            if self._stopped:
                print("Can't send message, communication is not running")
                return
            print(self._msg.get())
            if self._msg.get() == "4":
                self._stopped = True
    
        def run(self):
            self._stopped = False
            print('Ready !')
    
        def send(self, msg):
            self._msg.set(msg)
    
    fenetre = Fenetre()
    fenetre.root.mainloop()


    Ce n'est qu'un simple exemple, maintenant si ce n'est pas possible de gérer ça de cette façon, regarde alors du côté de la méthode after de tkinter, dans 99,99% des cas il n'y a pas besoin d'utiliser un thread avec tkinter.
    0
  11. yazchapi21
     
    En résumé le script permet de commander un robot-car à distance. Le thread est nécessaire pour la communication client-serveur et l'interface pour la commande. Les deux doivent être indépendants.
    0
    1. drepo
       
      Dans ce cas, il faut revoir quelques parties de ton code et t'inspirer un peu de ce que j'ai rapidement fait.
      Première chose, tu travailles avec des objets, quand on a progressé à en arriver là, on n'utilise plus jamais ce global et on s'arrange pour passer les objets aux autres instances qui auront besoin d'agir dessus.

      On arrive rapidement a

      import time
      import threading
      import tkinter as tk
      
      class Commandes:
          def __init__(self, thread):
              self.thread = thread
      
          def demarrer(self):
              self.thread.message = "1"
      
          def arreter(self):
              self.thread.message = "4"
      
          def devier_a_gauche(self):
              self.thread.message = "2"
      
          def devier_a_droite(self):
              self.thread.message = "3"
      
      
      Resultat = None
      class Fenetre(tk.Tk):
          def __init__(self, thread, commandes):
              super().__init__()
              self.thread = thread
              self.commandes = commandes
              self.geometry("450x500")
              label = tk.Label(self, text="Resultat: {}".format(Resultat))
              label.pack(side="top", fill="both", expand=True)
              btn1 = tk.Button(self, text="commande1", bg='cyan', command=self.commandes.demarrer)
              btn1.pack()
              btn1.place(x=20, y=200)
              btn2 = tk.Button(self, text="Quitter", bg='yellow', command=self.quit)
              btn2.pack()
              btn2.place(x=150, y=200)
              btn3 = tk.Button(self, text="commande2", bg='orange', command=self.commandes.devier_a_gauche)
              btn3.pack()
              btn3.place(x=320, y=200)
              btn4 = tk.Button(self, text="commande3", bg='yellow', command=self.commandes.devier_a_droite)
              btn4.pack()
              btn4.place(x=20, y=300)
              btn5 = tk.Button(self, text="commande4", bg='orange', command=self.commandes.arreter)
              btn5.pack()
              btn5.place(x=320, y=300)
      
          def quit(self):
              self.wait_thread_end()
              super().quit()
      
          def destroy(self):
              self.wait_thread_end()
              super().destroy()
      
          def wait_thread_end(self):
              # Cette fonction attendra que le thread soit bien fini avant de se fermer
              self.thread.running = False
              if self.thread.is_alive:
                  self.after(100, self.wait_thread_end)
      
      
      class ThreadCommunication(threading.Thread):
          def __init__(self):
              threading.Thread.__init__(self)
              self.message = 'ready'
              self.running = True
      
          def run(self):
              while self.running:
                  if(self.message):
                      print(self.message)
                      self.message = None
                  time.sleep(5)
              print("thread fini")
      
      thrd = ThreadCommunication()
      thrd.start()
      commandes = Commandes(thrd)
      fenetre = Fenetre(thrd, commandes)
      fenetre.mainloop()


      Et tu peux bien voir que tout est un peu une question de logique et de propreté du code, on peut faire autrement, mais il n'y avait pas grand-chose à faire pour en arriver là, bon y a encore des choses à revoir dans ton code.

      Ne sachant pas du tout à quoi sert cette variable Resultat (j'imagine un retour du résultat des commandes ?), j'ai laissé en l'état, mais nul doute qu'il faudra procéder de la même façon.

      J'ai viré les time.sleep, si tu dois les remettre, il faut qu'il soient effectués dans le run, sinon tu vas geler tkinter chaque fois. Bref en fonction du message reçu tu fais le sleep adéquat, pas compliqué à faire.
      0
    2. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       
      Le thread de communication ne fait qu'attendre que la variable msg change, il est donc dépendant, et je ne vois pas de raison d'en faire un thread séparé.
      0
    3. yazchapi21 Messages postés 6 Statut Membre > drepo
       
      Bonjour
      En touchant juste sur time.sleep (0.1) j'ai eu l'effet souhaité. Je vais mettre mon vrai code à jour et après le test avec le robot (demain) je vous tiendrai au courant. Merci.
      0
    4. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588 > yazchapi21 Messages postés 6 Statut Membre
       
      le même, sans thread:
      import tkinter as tk
      
      class Commandes:
          def __init__(self, reseau):
              self.reseau = reseau
      
          def demarrer(self):
              self.reseau.envoi("1")
      
          def arreter(self):
              self.reseau.envoi("4")
      
          def devier_a_gauche(self):
              self.reseau.envoi("2")
      
          def devier_a_droite(self):
              self.reseau.envoi("3")
      
      Resultat = None
      class Fenetre(tk.Tk):
          def __init__(self, commandes):
              super().__init__()
              self.commandes = commandes
              self.geometry("450x500")
              label = tk.Label(self, text="Resultat: {}".format(Resultat))
              label.pack(side="top", fill="both", expand=True)
              btn1 = tk.Button(self, text="commande1", bg='cyan', command=self.commandes.demarrer)
              btn1.pack()
              btn1.place(x=20, y=200)
              btn2 = tk.Button(self, text="Quitter", bg='yellow', command=self.quit)
              btn2.pack()
              btn2.place(x=150, y=200)
              btn3 = tk.Button(self, text="commande2", bg='orange', command=self.commandes.devier_a_gauche)
              btn3.pack()
              btn3.place(x=320, y=200)
              btn4 = tk.Button(self, text="commande3", bg='yellow', command=self.commandes.devier_a_droite)
              btn4.pack()
              btn4.place(x=20, y=300)
              btn5 = tk.Button(self, text="commande4", bg='orange', command=self.commandes.arreter)
              btn5.pack()
              btn5.place(x=320, y=300)
          def quit(self):
              self.destroy()
              super().quit()
      
      class Communication():
          def __init__(self):
              pass
          def envoi(self,message):
              print(message)
      
      res = Communication()
      commandes = Commandes(res)
      fenetre = Fenetre( commandes)
      fenetre.mainloop()
      0
    5. yazchapi21 > drepo
       
      Le thread est très nécessaire. Pouvez-vous me vérifier le code ci-dessous ?
      class ThreadCommunication(threading.Thread):
          def __init__(self):
              threading.Thread.__init__(self)
            def run(self):
                  global msg
                  print("run",msg)
                  while True:
                      if(msg):
                          print(msg)
                          utils.send_msg(sock, msg)
                          msg = None
                    time.sleep(0.1)
      		msgFromArduino = utils.recv_msg(sock)
      		msgFromArduino = msgFromArduino.split(':')[1].split(';')
      		print(msgFromArduino)
      		if (not(len(msgFromArduino) < 6)):
      			data1 = msgFromArduino[0]
      			data2 = msgFromArduino[1]
      			data3 = msgFromArduino[2]
      			data4 = msgFromArduino[3]
      			data5 = msgFromArduino[4]
      			start_time = time.time()
      			secondes = time.time() - start_time
      			Distance_ultrason.append(data1)
      			vitesse_droite.append(data2)
      			vitesse_gauche.append(data3)
      			Latitude.append(data4)
      			Longitude.append(data5)
      			temps.append(round(secondes, 2))
      			now = datetime.datetime.now()
      			c = open('fichierrobot_car_' +  '_' + str(now.strftime("%Y-%m-%d-%H-%M")) + '.csv', "w")
      			c = csv.writer(c, delimiter="\t")
      			c.writerow (("Distance_ultrason","vitesse_droite","vitesse_gauche","Latitude","Longitude","temps"))
      			fin1, fin2 = len(Distance_ultrason), len(vitesse_droite)
      			if fin1 == fin2:  
      				for i in range (fin1):
      					c.writerow((Distance_ultrason[i],vitesse_droite[i],vitesse_gauche[i],Latitude[i],Longitude[i],temps[i]))
      					return vitesse_droite, vitesse_gauche, temps 
                      
      thrd = ThreadCommunication(sock)
      thrd.start()

      Qu'est-ce qu'il faut mettre après utils.send_msg(sock, msg) pour que la réception soit faite à la ligne suivante (ligne msgFromArduino = utils.recv_msg(sock)) soit faite. Le but est que l'envoi (utils.send_msg(sock, msg)) et la réception (msgFromArduino = utils.recv_msg(sock)) soient indépendants malgré que les deux sont dans un seul thread. La boucle while lançant l'envoi étant infinie. Merci.
      0