Threading a Tkinter GUI - Victor95 - Nov-09-2018
Hi all! I'm working on a GUI to control pulses of alternating current. I've seen many posts that talk about Tkinter being bad in the thread and I've been doing tests and yes, I really can not make it work the right way.
I have a raspberry connected to 5 AD converters( 3 writing and 2 reading), and I want my GUI continues working at the same time that analog values are read and written on the screen through label.
This is my code. I'm not very experienced in python but I'm doing my bests.
The two functions that I want to run on different threads are ValorAnalog and ConfirmarOna.
from tkinter import *
import RPi.GPIO as GPIO
import smbus2 as smbus
import sys, os
import time
from tk_tools import *
import threading
#Modo; Si Modo=1=P, Modo=2=N, Modo=3=PN, Modo=0=0;
bus = smbus.SMBus(1)
ModoTrama1=0
ModoTrama2=0
ModoTrama3=0
FaseActual=1
Angulo=0
t=1
AnguloFase1int=0
AnguloFase2int=0
AnguloFase3int=0
Tiempo = 0
Tiempo1 = 0
Tiempo2 = 0
Tiempo3 = 0
def ventanaPrincipal():
root = Tk()
root.attributes("-fullscreen",True)
root.title("Panel de control")
def P():
global FaseActual
global ModoTrama1,ModoTrama2,ModoTrama3
if FaseActual==1:
AnguloFase1.config(text='0°')
BotonFase1.config(image=Pmode)
ModoTrama1=1
elif FaseActual==2:
AnguloFase2.config(text='0°')
BotonFase2.config(image=Pmode)
ModoTrama2=1
else:
AnguloFase3.config(text='0°')
BotonFase3.config(image=Pmode)
ModoTrama3=1
def val0():
global FaseActual
global ModoTrama1,ModoTrama2,ModoTrama3
if FaseActual==1:
AnguloFase1.config(text='0°')
BotonFase1.config(image=mode0)
ModoTrama1=0
elif FaseActual==2:
AnguloFase2.config(text='0°')
BotonFase2.config(image=mode0)
ModoTrama2=0
else:
AnguloFase3.config(text='0°')
BotonFase3.config(image=mode0)
ModoTrama3=0
def PN():
global FaseActual
global ModoTrama1,ModoTrama2,ModoTrama3
if FaseActual==1:
AnguloFase1.config(text='0°')
BotonFase1.config(image=PNmode)
ModoTrama1=3
elif FaseActual==2:
AnguloFase2.config(text='0°')
BotonFase2.config(image=PNmode)
ModoTrama2=3
else:
AnguloFase3.config(text='0°')
BotonFase3.config(image=PNmode)
ModoTrama3=3
def N():
global FaseActual
global ModoTrama1,ModoTrama2,ModoTrama3
if FaseActual==1:
AnguloFase1.config(text='0°')
BotonFase1.config(image=Nmode)
ModoTrama1=2
elif FaseActual==2:
AnguloFase2.config(text='0°')
BotonFase2.config(image=Nmode)
ModoTrama2=2
else:
AnguloFase3.config(text='0°')
BotonFase3.config(image=Nmode)
ModoTrama3=2
def Fase1(event):
global FaseActual
FaseActual=1
t=1
LedFase1.to_green(on=True)
LedFase2.to_red(on=True)
LedFase3.to_red(on=True)
def Fase2(event):
global FaseActual
FaseActual=2
LedFase2.to_green(on=True)
LedFase1.to_red(on=True)
LedFase3.to_red(on=True)
def grados0():
Slider.set(0)
def grados90():
Slider.set(90)
def grados180():
Slider.set(180)
def Fase3(event):
global FaseActual
FaseActual=3
LedFase3.to_green(on=True)
LedFase2.to_red(on=True)
LedFase1.to_red(on=True)
def ConfirmarAngulo():
global AnguloFase1int,AnguloFase2int,AnguloFase3int, FaseActual, ModoTrama1,ModoTrama2,ModoTrama3, Tiempo
Angulo=Slider.get()
Angulostr=str(Angulo)
Tiempo=SliderTiempo.get()
if FaseActual==1:
Tiempo1=Tiempo
if Tiempo>=1000:
Tiempo=Tiempo/1000
Tiempostr=str(Tiempo)
TiempoFase1.config(text=Tiempostr+'s')
else:
Tiempostr=str(Tiempo)
TiempoFase1.config(text=Tiempostr+'ms')
Tiempo1=Tiempo1/1000
AnguloFase1int=Angulo
AnguloFase1.config(text=Angulostr+'°')
if ModoTrama1==1:
if Angulo==90:
BotonFase1.config(image=PhotoP90)
elif Angulo == 0:
BotonFase1.config(image=Pmode)
elif Angulo<90:
BotonFase1.config(image=PhotoPmen90)
elif Angulo>90 and Angulo<=179:
BotonFase1.config(image=PhotoPmay90)
else:
BotonFase1.config(image=mode0)
elif ModoTrama1==2:
if Angulo ==90:
BotonFase1.config(image=PhotoN90)
elif Angulo == 0:
BotonFase1.config(image=Nmode)
elif Angulo<90:
BotonFase1.config(image=PhotoNmen90)
elif Angulo>90 and Angulo<=179:
BotonFase1.config(image=PhotoNmay90)
else:
BotonFase1.config(image=mode0)
elif ModoTrama1==3:
if Angulo==90:
BotonFase1.config(image=PhotoPN90)
elif Angulo == 0:
BotonFase1.config(image=PNmode)
elif Angulo<90:
BotonFase1.config(image=PhotoPNmen90)
elif Angulo>90 and Angulo<=179:
BotonFase1.config(image=PhotoPNmay90)
else:
BotonFase1.config(image=mode0)
else:
BotonFase1.config(image=mode0)
if FaseActual==2:
Tiempo2=Tiempo
if Tiempo>=1000:
Tiempo=Tiempo/1000
Tiempostr=str(Tiempo)
TiempoFase2.config(text=Tiempostr+'s')
else:
Tiempostr=str(Tiempo)
TiempoFase2.config(text=Tiempostr+'ms')
Tiempo2=Tiempo2/1000
AnguloFase2int=Angulo
AnguloFase2.config(text=Angulostr+'°')
if ModoTrama2==1:
if Angulo==90:
BotonFase2.config(image=PhotoP90)
elif Angulo == 0:
BotonFase2.config(image=Pmode)
elif Angulo<90:
BotonFase2.config(image=PhotoPmen90)
elif Angulo>90 and Angulo<=179:
BotonFase2.config(image=PhotoPmay90)
else:
BotonFase2.config(image=mode0)
elif ModoTrama2==2:
if Angulo ==90:
BotonFase2.config(image=PhotoN90)
elif Angulo == 0:
BotonFase2.config(image=Nmode)
elif Angulo<90:
BotonFase2.config(image=PhotoNmen90)
elif Angulo>90 and Angulo<=179:
BotonFase2.config(image=PhotoNmay90)
else:
BotonFase2.config(image=mode0)
elif ModoTrama2==3:
if Angulo==90:
BotonFase2.config(image=PhotoPN90)
elif Angulo == 0:
BotonFase2.config(image=PNmode)
elif Angulo<90:
BotonFase2.config(image=PhotoPNmen90)
elif Angulo>90 and Angulo<=179:
BotonFase2.config(image=PhotoPNmay90)
else:
BotonFase2.config(image=mode0)
else:
BotonFase2.config(image=mode0)
if FaseActual==3:
Tiempo3=Tiempo
if Tiempo>=1000:
Tiempo=Tiempo/1000
Tiempostr=str(Tiempo)
TiempoFase3.config(text=Tiempostr+'s')
else:
Tiempostr=str(Tiempo)
TiempoFase3.config(text=Tiempostr+'ms')
Tiempo3=Tiempo3/1000
AnguloFase3int=Angulo
AnguloFase3.config(text=Angulostr+'°')
if ModoTrama3==1:
if Angulo==90:
BotonFase3.config(image=PhotoP90)
elif Angulo == 0:
BotonFase3.config(image=Pmode)
elif Angulo<90:
BotonFase3.config(image=PhotoPmen90)
elif Angulo>90 and Angulo<=179:
BotonFase3.config(image=PhotoPmay90)
else:
BotonFase3.config(image=mode0)
elif ModoTrama3==2:
if Angulo ==90:
BotonFase3.config(image=PhotoN90)
elif Angulo == 0:
BotonFase3.config(image=Nmode)
elif Angulo<90:
BotonFase3.config(image=PhotoNmen90)
elif Angulo>90 and Angulo<=179:
BotonFase3.config(image=PhotoNmay90)
else:
BotonFase3.config(image=mode0)
elif ModoTrama3==3:
if Angulo==90:
BotonFase3.config(image=PhotoPN90)
elif Angulo == 0:
BotonFase3.config(image=PNmode)
elif Angulo<90 and Angulo>=1:
BotonFase3.config(image=PhotoPNmen90)
elif Angulo>90 and Angulo<=179:
BotonFase3.config(image=PhotoPNmay90)
else:
BotonFase3.config(image=mode0)
else:
BotonFase3.config(image=mode0)
def ExecutaOna():
global t, Tiempo1, Tiempo2, Tiempo3
t=1
while(t==1):
x = threading.Thread(target=ConfirmarOna)
f = threading.Thread(target=ValorAnalog)
Tiempototal=Tiempo1+Tiempo2+Tiempo3
time.sleep(Tiempototal)
def ParaOna():
global t
t=0
def ConfirmarOna():
global Tiempo1,Tiempo2,Tiempo3, AnguloFase1int,AnguloFase2int,AnguloFase3int, t
date = AnguloFase1int*0.55
date = int(date)
date = date * 819 / 20
dat1 = date/256.0
dat1int= int(date/256)
dat2 = ((dat1-dat1int)*256)
dat2 = int(dat2)
dat1 = int(dat1)
dato=[0xc0,dat1,dat2]
bus.write_i2c_block_data(0x62,0x00,dato)
time.sleep(Tiempo1)
date = AnguloFase2int*0.55
date = int(date)
date = date * 819 / 20
dat1 = date/256.0
dat1int= int(date/256)
dat2 = ((dat1-dat1int)*256)
dat2 = int(dat2)
dat1 = int(dat1)
dato=[0xc0,dat1,dat2]
bus.write_i2c_block_data(0x62,0x00,dato)
time.sleep(Tiempo2)
date = AnguloFase3int*0.55
date = int(date)
date = date * 819 / 20
dat1 = date/256.0
dat1int= int(date/256)
dat2 = ((dat1-dat1int)*256)
dat2 = int(dat2)
dat1 = int(dat1)
dato=[0xc0,dat1,dat2]
bus.write_i2c_block_data(0x62,0x00,dato)
time.sleep(Tiempo3)
if(t==1):
root.after(0,ConfirmarOna)
def Salir():
root.destroy()
PhotoP90=PhotoImage(file='/home/pi/Desktop/prstep/P90.png')
PhotoN90=PhotoImage(file='/home/pi/Desktop/prstep/N90.png')
PhotoPN90=PhotoImage(file='/home/pi/Desktop/prstep/PN90.png')
PhotoPmen90=PhotoImage(file='/home/pi/Desktop/prstep/Pmen90.png')
PhotoNmen90=PhotoImage(file='/home/pi/Desktop/prstep/Nmen90.png')
PhotoPNmen90=PhotoImage(file='/home/pi/Desktop/prstep/PNmen90.png')
PhotoPmay90=PhotoImage(file='/home/pi/Desktop/prstep/Pmay90.png')
PhotoNmay90=PhotoImage(file='/home/pi/Desktop/prstep/Nmay90.png')
PhotoPNmay90=PhotoImage(file='/home/pi/Desktop/prstep/PNmay90.png')
PhotoP=PhotoImage(file='/home/pi/Desktop/prstep/P.png')
PhotoN=PhotoImage(file='/home/pi/Desktop/prstep/N.png')
PhotoPN=PhotoImage(file='/home/pi/Desktop/prstep/PN.png')
Photo0=PhotoImage(file='/home/pi/Desktop/prstep/0.png')
Pmode=PhotoImage(file='/home/pi/Desktop/prstep/Pmode.png')
mode0=PhotoImage(file='/home/pi/Desktop/prstep/0mode.png')
Nmode=PhotoImage(file='/home/pi/Desktop/prstep/Nmode.png')
PNmode=PhotoImage(file='/home/pi/Desktop/prstep/PNmode.png')
btnSalir= PhotoImage(file = '/home/pi/Desktop/prstep/salir.png')
PhotoEjecutar=PhotoImage(file='/home/pi/Desktop/prstep/ejecutar.png')
PhotoParar=PhotoImage(file='/home/pi/Desktop/prstep/parar.png')
bSalir=Button(root, image=btnSalir,command=Salir, relief=FLAT, activebackground='#dcdcdc')
BotonFase1=Label(root,image=mode0)
BotonFase1.bind('<Button-1>',Fase1)
BotonFase2=Label(root,image=mode0)
BotonFase2.bind('<Button-1>',Fase2)
BotonFase3=Label(root,image=mode0)
BotonFase3.bind('<Button-1>',Fase3)
BotonEjecutar=Button(root,image=PhotoEjecutar,command=ExecutaOna,relief=FLAT,activebackground='#dcdcdc')
BotonParar=Button(root,image=PhotoParar,command=ParaOna,relief=FLAT,activebackground='#dcdcdc')
BotonP=Button(root,image=PhotoP,command=P,relief=FLAT, activebackground='#dcdcdc')
BotonN=Button(root,image=PhotoN,command=N,relief=FLAT, activebackground='#dcdcdc')
BotonPN=Button(root,image=PhotoPN,command=PN,relief=FLAT, activebackground='#dcdcdc')
Boton0=Button(root,image=Photo0,command=val0,relief=FLAT, activebackground='#dcdcdc')
Slider= Scale(root,from_=0, to=180, resolution=1,length=225,orient=HORIZONTAL)
SliderTiempo=Scale(root,from_=0, to=2000,resolution=20,length=225,orient=HORIZONTAL)
LedFase1= Led(root,size=25)
LedFase2= Led(root,size=25)
LedFase3= Led(root,size=25)
LedFase1.to_green(on=True)
LedFase2.to_red(on=True)
LedFase3.to_red(on=True)
lblFase1=Label(root,text='Trama 1',font=('Calibri',12,'bold'))
lblFase2=Label(root,text='Trama 2',font=('Calibri',12,'bold'))
lblFase3=Label(root,text='Trama 3',font=('Calibri',12,'bold'))
Confirmar=PhotoImage(file='/home/pi/Desktop/prstep/botonconfirmar.png')
BotonConfirmar=Button(root,command=ConfirmarAngulo,image=Confirmar,relief=FLAT,activebackground='#dcdcdc')
AnguloFase1=Label(root,text='0°',font=('Calibri',12,'bold'))
AnguloFase2=Label(root,text='0°',font=('Calibri',12,'bold'))
AnguloFase3=Label(root,text='0°',font=('Calibri',12,'bold'))
TiempoFase1=Label(root,text='0ms',justify=RIGHT,font=('Calibr',12,'bold'))
TiempoFase2=Label(root,text='0ms',justify=RIGHT,font=('Calibr',12,'bold'))
TiempoFase3=Label(root,text='0ms',justify=RIGHT,font=('Calibr',12,'bold'))
Boton0gradosImagen=PhotoImage(file='/home/pi/Desktop/prstep/Boton0.png')
Boton90gradosImagen=PhotoImage(file='/home/pi/Desktop/prstep/Boton90.png')
Boton180gradosImagen=PhotoImage(file='/home/pi/Desktop/prstep/Boton180.png')
Boton0grados=Button(root,command=grados0,image=Boton0gradosImagen,relief=FLAT,activebackground='#dcdcdc')
Boton90grados=Button(root,command=grados90,image=Boton90gradosImagen,relief=FLAT,activebackground='#dcdcdc')
Boton180grados=Button(root,command=grados180,image=Boton180gradosImagen,relief=FLAT,activebackground='#dcdcdc')
TextoAngulo=Label(root,text='ANGULO DISPARO',font=('Calibri',12,'bold'))
TextoTiempo=Label(root,text='TIEMPO DE TRAMA ( ms )',font=('Calibri',12,'bold'))
LabelVoltage=Label(root,text='0 V',font=('Calibri',12,'bold'))
LabelIntensidad=Label(root,text='0 A',font=('Calibri',12,'bold'))
LabelVoltage.place(x=1,y=100)
LabelIntensidad.place(x=1,y=150)
lblFase1.place(x=250,y=177)
lblFase2.place(x=450,y=177)
lblFase3.place(x=650,y=177)
LedFase1.place(x=260,y=200)
LedFase2.place(x=460,y=200)
LedFase3.place(x=660,y=200)
BotonFase1.place(x=195,y=10)
BotonFase2.place(x=395,y=10)
BotonFase3.place(x=595,y=10)
BotonP.place(x=200,y=250)
BotonN.place(x=350,y=250)
BotonPN.place(x=500,y=250)
Boton0.place(x=650,y=250)
bSalir.place(x=1,y=1)
SliderTiempo.place(x=550,y=400)
Slider.place(x=195,y=400)
BotonConfirmar.place(x=445,y=405)
Boton0grados.place(x=195,y=440)
Boton90grados.place(x=290,y=440)
Boton180grados.place(x=385,y=440)
AnguloFase1.place(x=296,y=207)
AnguloFase2.place(x=496,y=207)
AnguloFase3.place(x=696,y=207)
TiempoFase1.place(x=200,y=207)
TiempoFase2.place(x=400,y=207)
TiempoFase3.place(x=600,y=207)
BotonEjecutar.place(x=2,y=300)
BotonParar.place(x=2,y=390)
TextoAngulo.place(x=225,y=380)
TextoTiempo.place(x=550,y=380)
def ValorAnalog():
data = [0xC0,0x83]
bus.write_i2c_block_data(0x48,0x01,data)
time.sleep(0.1)
data = bus.read_i2c_block_data(0x48, 0x00, 2)
raw=(data[0] * 256 + data[1])
rawstr=str(raw)
LabelVoltage.config(text=rawstr+'V')
time.sleep(0.1)
data = [0xD0,0x83]
bus.write_i2c_block_data(0x48,0x01,data)
time.sleep(0.1)
data = bus.read_i2c_block_data(0x48, 0x00, 2)
raw2=(data[0] * 256 + data[1])
raw2str=str(raw2)
LabelIntensidad.config(text=raw2str+'A')
root.mainloop()
ventanaPrincipal()
Thanks!
RE: Threading a Tkinter GUI - nilamo - Nov-09-2018
Could you describe what those functions are supposed to do? If you're just writing a value out or reading something in, why is it so slow that it needs to be threaded? Or is it doing something else?
Also, this is terrifying:Quote: t=1
while(t==1):
x = threading.Thread(target=ConfirmarOna)
f = threading.Thread(target=ValorAnalog)
Tiempototal=Tiempo1+Tiempo2+Tiempo3
time.sleep(Tiempototal)
So you're creating an infinite number of threads, but never start any of them...
RE: Threading a Tkinter GUI - Victor95 - Nov-09-2018
Hi nilamo, first of all, I worked with root.after() and what I see is the following:
more focused:
This is what my write function does.
The time that the reading function is taken, is the continuous state of the wave. And what I want is a wave that does not have these stops.
ConfirmarOna is the writer function and ValorAnalog is my reader function.
PD: sorry for that english, I'm spanish ^^'
RE: Threading a Tkinter GUI - nilamo - Nov-09-2018
Let's start with just the reader, ValorAnalog .
(Nov-09-2018, 04:07 PM)Victor95 Wrote: def ValorAnalog():
data = [0xC0,0x83]
bus.write_i2c_block_data(0x48,0x01,data)
time.sleep(0.1)
data = bus.read_i2c_block_data(0x48, 0x00, 2)
raw=(data[0] * 256 + data[1])
rawstr=str(raw)
LabelVoltage.config(text=rawstr+'V')
time.sleep(0.1)
data = [0xD0,0x83]
bus.write_i2c_block_data(0x48,0x01,data)
time.sleep(0.1)
data = bus.read_i2c_block_data(0x48, 0x00, 2)
raw2=(data[0] * 256 + data[1])
raw2str=str(raw2)
LabelIntensidad.config(text=raw2str+'A')
It looks like this function is doing four different things.
First, it writes to a pin: data = [0xC0,0x83]
bus.write_i2c_block_data(0x48,0x01,data) I'm guessing this is how you ask the sensor to give you data? It's almost identical (the only difference is the first element of data ) to what you do further in the function.
Second, it sleeps for a bit time.sleep(0.1) . Is this required? What purpose does this have?
Third, you read data from a pin:data = bus.read_i2c_block_data(0x48, 0x00, 2)
raw=(data[0] * 256 + data[1])
rawstr=str(raw) And then finally, you take that value that you read, and write it to some text field.
I think the function should be split into at least two smaller functions, to represent the different things it's doing. And all these magic numbers (0x48 , etc) should be constants with meaningful names so it's easier to understand what's going on.
But to your actual question, the time.sleep(0.1) is the important part. If you can remove that, you might not need threads. If you can't (maybe you have to wait for something else to provide data after you ask for it, or something), then I don't think this function should touch the ui at all, and should use a queue (https://docs.python.org/3/library/queue.html) to stream values to the main thread.
RE: Threading a Tkinter GUI - Victor95 - Nov-12-2018
I have deleted the time.sleep and the wave is not bad but the analog values are not refreshed in the GUI.
I do not care if the sliders do not work, as long as the stop button works while the wave writing process is running, it's enough for me.
But that the analog values are displayed in the GUI is indispensable.
Also, I have removed the time.sleep and it has not gone badly. But the labels where the readings are written are not updated.
Now I have it like that:
def ValorAnalog():
data = [0xC0,0x83]
bus.write_i2c_block_data(0x48,0x01,data)
data = bus.read_i2c_block_data(0x48, 0x00, 2)
raw=(data[0] * 256 + data[1])
rawstr=str(raw)
LabelVoltage.config(text=rawstr+'V')
data = [0xD0,0x83]
bus.write_i2c_block_data(0x48,0x01,data)
data = bus.read_i2c_block_data(0x48, 0x00, 2)
raw2=(data[0] * 256 + data[1])
raw2str=str(raw2)
LabelIntensidad.config(text=raw2str+'A')
if t==1:
root.after(0,ValorAnalog)
def ConfirmarOna():
global Tiempo1,Tiempo2,Tiempo3, AnguloFase1int,AnguloFase2int,AnguloFase3int, t
date = AnguloFase1int*0.55
date = int(date)
date = date * 819 / 20
dat1 = date/256.0
dat1int= int(date/256)
dat2 = ((dat1-dat1int)*256)
dat2 = int(dat2)
dat1 = int(dat1)
dato=[0xc0,dat1,dat2]
bus.write_i2c_block_data(0x62,0x00,dato)
time.sleep(Tiempo1)
date = AnguloFase2int*0.55
date = int(date)
date = date * 819 / 20
dat1 = date/256.0
dat1int= int(date/256)
dat2 = ((dat1-dat1int)*256)
dat2 = int(dat2)
dat1 = int(dat1)
dato=[0xc0,dat1,dat2]
bus.write_i2c_block_data(0x62,0x00,dato)
time.sleep(Tiempo2)
date = AnguloFase3int*0.55
date = int(date)
date = date * 819 / 20
dat1 = date/256.0
dat1int= int(date/256)
dat2 = ((dat1-dat1int)*256)
dat2 = int(dat2)
dat1 = int(dat1)
dato=[0xc0,dat1,dat2]
bus.write_i2c_block_data(0x62,0x00,dato)
time.sleep(Tiempo3)
if(t==1):
root.after(0,ConfirmarOna)
This is working, but I need to refresh the Labels at the same time that this functions are working.
PD: sorry for late reply.
RE: Threading a Tkinter GUI - nilamo - Nov-12-2018
Instead of root.after(0, function_name) , try root.after_idle(function_name) . Maybe the labels aren't being updated because they don't have a chance to, so hopefully by only running the callbacks when tk is idle, that sort of thing will get taken care of.
RE: Threading a Tkinter GUI - Victor95 - Nov-12-2018
Thanks for all Nilamo.
We did it!
At the time to see your post, my bulb went on. I put root.after(0,function) and labels can't update because it. I put root.after(1, function ) and for the reading I put root.after(900, function).
I don't care if I see the labels updating every 0.9 seconds. That's well at the end.
I'm so thanksfully with you!
If I could pay you I would :D
Thank you so much!
RE: Threading a Tkinter GUI - nilamo - Nov-12-2018
(Nov-12-2018, 05:43 PM)Victor95 Wrote: If I could pay you I would :D Well, actually... https://python-forum.io/misc.php?page=donations
|