You have several problems in the code that I will try to explain:
❶ The main problem that causes the GUI to crash is the use of an infinite cycle ( while 1
). When you call the function for the first time (and last) you enter the cycle taking control of the main thread and no longer leaves it so the GUI is not given the opportunity to update and freezes. Also, you should never use sleep()
in the main thread of an application Tkinter
since it is also blocking.
The solution is to use after()
and call the function recursively. On the other hand, if you only need to update the label
, you can apply the after()
on it and not on the entire window. The general idea is something like this:
def funcion():
#hacer algo
label.after(1000, funcion)
sensor.after(1000, funcion)
In this way the same effect of an infinite cycle is created but in this case it is not blocking.
❷ On the other hand, the correct way to get the content of StringVar
is using the get()
method. However, it is much easier to tell the label
to take the text of a variable by means of the textvariable
attribute. In this way each time the variable changes, automatically (since it is actually an event) the content of Label
changes:
readingt = StringVar()
sensor = Label(ventana, textvariable = readingt)
❸ It is very common to do this:
sensor = Label(ventana, textvariable = readingt).place(x= 100, y = 100)
Which works but then we find errors when trying to do for example:
label.after(1000, funcion)
The problem of using the place()
method on the same line is that now sensor
is not a Label
object as we might think, it is actually the return of place()
, ie None
. For this reason it is always appropriate to apply place
on a different line:
sensor = Label(ventana, textvariable = readingt)
sensor.place(x= 100, y = 100)
❹ You are importing pylab
as ventana
and then you create an application with Tkinter
calling it also ventana
, which overwrites the above. This really does not cause problems for now but if you are going to use pylab
later you will have problems. For example, if you try to use the method linspace()
of pylabel
, you would call it like this:
t = ventana.linspace(-pl.pi, pl.pi, 10000)
this causes an error:
AttributeError: '_tkinter.tkapp' object has no attribute 'linspace'
because window is an instance of tkinter.Tk
actually. Change the import
to something like:
import pylab as pl
❺ Finally, the readline()
method is blocking, it does not return anything until there is something to read, which can never be. This blocks the GUI equally, so that this does not happen we specify that you do not wait for a reading to exist:
serie = serial.Serial('COM3', 9600, timeout=0, writeTimeout=0)
The code should look like this:
from tkinter import *
import serial
import sys
import numpy as np
import pylab as pl
serie = serial.Serial('COM3', 9600, timeout=0, writeTimeout=0)
def sensor_tem():
dato = serie.readline()
senso = dato[0:1]
readingt.set(senso)
sensor.after(1000, sensor_tem)
ventana = Tk()
ventana.geometry("600x300+0+0")
ventana.title("Sensor Temperatura")
tit_sensor = Label(ventana, text = "Lectura Sensor")
tit_sensor.place(x = 100, y = 70)
readingt = StringVar()
sensor = Label(ventana, textvariable = readingt)
sensor.place(x= 100, y = 100)
btn_salir = Button(ventana, text = " Salir", command = exit)
btn_salir.place(x = 100, y = 130)
sensor.after(1000, sensor_tem)
ventana.mainloop()
A note, after()
receives the wait time in milliseconds , I have put 1000 that would be a second. You can change it but keep in mind that 1 millisecond as you have it can be an important burden for the processor and may not provide much useful information.
There are other ways to address these problems such as the use of threads and queues but for your case this works correctly and is simpler.
EDIT:
I add example using threads and a tail:
import serial
import threading
import time
import queue
import tkinter as tk
class readerThread(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
serie = serial.Serial('COM3', 9600, timeout=0, writeTimeout=0)
while True:
datos = serie.readline()
senso = datos[0:1]
if senso:
self.queue.put(senso)
time.sleep(1)
class sensorGUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.geometry("600x300+0+0")
self.title("Sensor Temperatura")
self.tit_sensor = tk.Label(self, text = "Lectura Sensor")
self.tit_sensor.place(x = 100, y = 70)
self.readingt = tk.StringVar()
self.sensor = tk.Label(self, textvariable = self.readingt)
self.sensor.place(x= 100, y = 100)
self.btn_salir = tk.Button(self, text = " Salir", command = self.salir)
self.btn_salir.place(x = 100, y = 130)
self.queue = queue.Queue()
thread = readerThread(self.queue)
thread.start()
self.actualizar_datos()
def actualizar_datos(self):
while self.queue.qsize():
try:
self.readingt.set(self.queue.get())
except Queue.Empty:
pass
self.after(100, self.actualizar_datos)
def salir(self):
self.destroy()
if __name__ =='__main__':
app = sensorGUI()
app.mainloop()