Thread.tkinter

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.

Bobot (l’IA à votre service)
  1. 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()
  2. 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.
    1. 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()
      
      1. Contributeur
        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.
        1. 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()
          1. 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.
            1. Contributeur
              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)
              
            2. @yg_beBonjour 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.
            3. Contributeur
              @yazchapi21le but est justement de montrer que le thread ne se termine pas, qu'il continue à travailler.
            4. @yg_beBonjour
              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.
            5. Contributeur
              @yazchapi21est-ce dans consume() que tu souhaites récupérer tous les messages?
          2. 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.
            1. 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.
              1. 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.
                1. 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.
                2. Contributeur
                  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é.
                3. @drepoBonjour
                  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.
                4. Contributeur
                  @yazchapi21le 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()
                5. @drepoLe 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.