Reputation: 53
I am using tkinter to display some labels based on voltages I am reading. However, it stops executing after one read. I have found that it is due to root.mainloop(). But I am not able to fix it. I have included my code. The root.mainloop() is located right at the end of the while True.
from Tkinter import *
import spidev
import time
import os
import RPi.GPIO as GPIO
import numpy
GPIO.cleanup()
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7, GPIO.OUT)
GPIO.setup(11, GPIO.OUT)
GPIO.setup(13, GPIO.OUT)
GPIO.setup(15, GPIO.OUT)
GPIO.setup(12, GPIO.OUT)
adcvolt = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
batvolt = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
root = Tk() #Makes the window
w,h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0"%(w,h))
labelFont = ("times",15 ,"bold", "italic")
labelText1 = StringVar()
labelText2 = StringVar()
labelText3 = StringVar()
labelText4 = StringVar()
labelText5 = StringVar()
labelText6 = StringVar()
labelText1.set("Battery Management System")
label1 = Label(root, textvariable=labelText1, height=3, anchor="center")
label1.config(font=labelFont)
label1.pack()
labelText2.set("Wait")
label2 = Label(root, textvariable=labelText2, height=3, anchor="center")
label2.config(font=labelFont)
label2.pack()
labelText3.set("Wait")
label3 = Label(root, textvariable=labelText3, height=3, anchor="center")
label3.config(font=labelFont)
label3.pack()
labelText4.set("Wait")
label4 = Label(root, textvariable=labelText4, height=3, anchor="center")
label4.config(font=labelFont)
label4.pack()
labelText5.set("Wait")
label5 = Label(root, textvariable=labelText5, height=3, anchor="center")
label5.config(font=labelFont)
label5.pack()
labelText6.set("Wait")
label6 = Label(root, textvariable=labelText6, height=3, anchor="center")
label6.config(font=labelFont)
label6.pack()
#root.wm_title("Window Title") #Makes the title that will appear in the top left
#root.config(background = "#FFFFFF") #sets background color to white
# Open SPI bus
spi = spidev.SpiDev()
spi.open(0,0)
# Function to read SPI data from MCP3008 chip
# Channel must be an integer 0-7
def ReadChannel(channel):
adc = spi.xfer2([1,(8+channel)<<4,0])
data = ((adc[1]&3) << 8) + adc[2]
return data
#Function to select multiplexer channel on 74HCT4067 chip
#Channel must be an integer 0-15
def MuxSelect(muxchannel):
if muxchannel == 0:
GPIO.output(7,0)
GPIO.output(11,0)
GPIO.output(13,0)
GPIO.output(15,0)
elif muxchannel == 1:
GPIO.output(7,0)
GPIO.output(11,0)
GPIO.output(13,0)
GPIO.output(15,1)
elif muxchannel == 2:
GPIO.output(7,0)
GPIO.output(11,0)
GPIO.output(13,1)
GPIO.output(15,0)
elif muxchannel == 3:
GPIO.output(7,0)
GPIO.output(11,0)
GPIO.output(13,1)
GPIO.output(15,1)
elif muxchannel == 4:
GPIO.output(7,0)
GPIO.output(11,1)
GPIO.output(13,0)
GPIO.output(15,0)
elif muxchannel == 5:
GPIO.output(7,0)
GPIO.output(11,1)
GPIO.output(13,0)
GPIO.output(15,1)
elif muxchannel == 6:
GPIO.output(7,0)
GPIO.output(11,1)
GPIO.output(13,1)
GPIO.output(15,0)
elif muxchannel == 7:
GPIO.output(7,0)
GPIO.output(11,1)
GPIO.output(13,1)
GPIO.output(15,1)
elif muxchannel == 8:
GPIO.output(7,1)
GPIO.output(11,0)
GPIO.output(13,0)
GPIO.output(15,0)
elif muxchannel == 9:
GPIO.output(7,1)
GPIO.output(11,0)
GPIO.output(13,0)
GPIO.output(15,1)
elif muxchannel == 10:
GPIO.output(7,1)
GPIO.output(11,0)
GPIO.output(13,1)
GPIO.output(15,0)
elif muxchannel == 11:
GPIO.output(7,1)
GPIO.output(11,0)
GPIO.output(13,1)
GPIO.output(15,1)
elif muxchannel == 12:
GPIO.output(7,1)
GPIO.output(11,1)
GPIO.output(13,0)
GPIO.output(15,0)
elif muxchannel == 13:
GPIO.output(7,1)
GPIO.output(11,1)
GPIO.output(13,0)
GPIO.output(15,1)
elif muxchannel == 14:
GPIO.output(7,1)
GPIO.output(11,1)
GPIO.output(13,1)
GPIO.output(15,0)
elif muxchannel == 15:
GPIO.output(7,1)
GPIO.output(11,1)
GPIO.output(13,1)
GPIO.output(15,1)
# Function to convert data to voltage level,
# rounded to specified number of decimal places.
def ConvertVolts(data,places):
volts = (data * 3.3) / 1023
volts = round(volts,places)
return volts
# Define sensor channels
voltage_channel = 0
# Define delay between readings
delay = 2
while True:
count = 0
while count<15:
MuxSelect(count)
# Read the voltage sensor data
voltage_level = ReadChannel(voltage_channel)
voltage_volts = ConvertVolts(voltage_level,2)
adcvolt[count] = voltage_volts
batvolt[count] = adcvolt[count] * 2.842
#print adcvolt[count]
#print batvolt[count]
#print count
count = count + 1
#time.sleep(delay)
labelText2.set('Cell1= '+str(batvolt[0])+'V Cell2= ' +str(batvolt[1])+'V Cell3= '+str(batvolt[2]))
#root.update()
labelText3.set('Cell4= '+str(batvolt[3])+'V Cell5= ' +str(batvolt[4])+'V Cell6= '+str(batvolt[5]))
# root.update()
labelText4.set('Cell7= '+str(batvolt[6])+'V Cell8= ' +str(batvolt[7])+'V Cell9= '+str(batvolt[8]))
# root.update()
labelText5.set('Cell10= '+str(batvolt[9])+'V Cell11= ' +str(batvolt[10])+'V Cell12= '+str(batvolt[11]))
# root.update()
labelText6.set('Cell13= '+str(batvolt[12])+'V Cell14= ' +str(batvolt[13])+'V Cell15= '+str(batvolt[14]))
root.update()
print "shit"
medianvolt = numpy.median(batvolt)
maxvolt = max(batvolt)
minvolt = min(batvolt)
diff1 = maxvolt-medianvolt
diff2 = medianvolt-minvolt
if diff1>0.02 or diff2>0.02:
GPIO.output (12,1)
time.sleep(120)
GPIO.output (12,0)
# Wait before repeating loop
time.sleep(delay)
root.update()
root.mainloop() #start monitoring and updating the GUI. Nothing below here runs.
Upvotes: 5
Views: 32209
Reputation: 142641
tkinter
needs mainloop()
to work correctly - it uses it to call all needed functions again and again - ie. to get key/mouse events from system, send them to widgets, updates values, and (re)draw widgets in window.
In tkinter
don't use while True
which runs forever (or runs longer time) because it blocks mainloop()
in tkinter
and it can't update items in window, and it looks like it freezes.
The same problem makes sleep()
- it can block mainloop()
in tkinter
.
You can use root.after(time_ms, function_name_without_brackets )
(before mainloop()
)
to run some function with delay - so it can be used like sleep()
. And if function will run the same after(...)
then it will work like loop. And this way tkinter
will have time to update widgets in window.
So put all code from your while True
(except mainloop()
) in function and call it using after()
.
And use second after()
in place of sleep()
.
import tkinter as tk
master = tk.Tk()
def my_mainloop():
print "Hello World!"
master.after(1000, my_mainloop) # run again after 1000ms (1s)
master.after(1000, my_mainloop) # run first time after 1000ms (1s)
master.mainloop()
And if you can't convert while
-loop and use after()
then you may have to run loop in separated thread. But tkinter
(like many other GUIs)` doesn't like to work in second thread and all changes in GUI you would have to do in main thread.
Upvotes: 27
Reputation: 53
Using the threading library:
this code creates two threads one which runs a tkinter program and the other thread will print numbers in a infinite loop
from threading import Thread
from time import sleep
from tkinter import *
root = Tk()
def k(arg):
global root
l = Label(root)
l.pack()
e = Entry(root)
e.pack()
def submit():
l.configure(text = e.get())
Button(text="Submit",command=submit).pack()
i = 0
def threaded_function(arg):
global i
while True:
print(i)
i+=1
sleep(1)
if __name__ == "__main__":
thread2 = Thread(target = k, args = (12,))
thread = Thread(target = threaded_function, args = (12,))
thread2.start()
thread.start()
root.mainloop()
Upvotes: 3
Reputation: 31
An alternative solution I found to work better - just start your while loop in another thread, and use global variables. My code worked perfectly doing it that way.
Upvotes: 2