Thread.tkinter

Fermé
yazchapi21 Messages postés 6 Date d'inscription samedi 19 juin 2021 Statut Membre Dernière intervention 27 juin 2021 - 20 juin 2021 à 12:45
yazchapi21 Messages postés 6 Date d'inscription samedi 19 juin 2021 Statut Membre Dernière intervention 27 juin 2021 - 27 juin 2021 à 18:08
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

yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025 Ambassadeur 1 568
20 juin 2021 à 13:33
bonjour,
peut-être faut-il adapter ton code?
0
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
yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025 1 568
20 juin 2021 à 17:19
pour que le code soit lisible, et pour en préserver l'indentation, il est nécessaire d'utiliser les balises de code, comme expliqué ici: https://codes-sources.commentcamarche.net/faq/11288-les-balises-de-code
0
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
Phil_1857 Messages postés 1872 Date d'inscription lundi 23 mars 2020 Statut Membre Dernière intervention 28 février 2024 168
22 juin 2021 à 13:38
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

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025 Ambassadeur 1 568
22 juin 2021 à 14:34
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
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
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
yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025 1 568
22 juin 2021 à 15:10
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
yazchapi21 > yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025
25 juin 2021 à 18:11
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
yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025 1 568 > yazchapi21
25 juin 2021 à 18:19
le but est justement de montrer que le thread ne se termine pas, qu'il continue à travailler.
0
yazchapi21 > yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025
26 juin 2021 à 13:57
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
yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025 1 568 > yazchapi21
26 juin 2021 à 14:08
est-ce dans consume() que tu souhaites récupérer tous les messages?
0
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
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
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
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
yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025 1 568
27 juin 2021 à 11:59
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
yazchapi21 Messages postés 6 Date d'inscription samedi 19 juin 2021 Statut Membre Dernière intervention 27 juin 2021 > drepo
27 juin 2021 à 15:05
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
yg_be Messages postés 23474 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 19 février 2025 1 568 > yazchapi21 Messages postés 6 Date d'inscription samedi 19 juin 2021 Statut Membre Dernière intervention 27 juin 2021
27 juin 2021 à 15:30
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
yazchapi21 > drepo
27 juin 2021 à 16:17
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