tkinter的root.mainloop与While True循环。

5

我正在使用tkinter来显示一些基于我读取的电压的标签。然而,它在一次读取后停止执行。我已经发现是由于root.mainloop()引起的。但是我无法解决这个问题。我已经包含了我的代码。root.mainloop()位于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.
3个回答

26

tkinter需要mainloop()来正确工作 - 它使用它重复调用所有所需的函数,例如从系统获取键盘/鼠标事件,将它们发送到小部件,更新值和在窗口中重新绘制小部件。

tkinter中不要使用永久运行(或运行时间较长)的while True,因为它会阻塞tkinter中的mainloop(),导致无法更新窗口中的项目,并且看起来像是冻结了。

相同的问题也会出现在sleep()中 - 它可以阻塞tkinter中的mainloop()

您可以使用root.after(time_ms, function_name_without_brackets )(在mainloop()之前)以延迟运行某些函数 - 这样它就像sleep()一样使用。如果函数将运行相同的after(...),则它将像循环一样工作。通过这种方式,tkinter将有时间更新窗口中的小部件。


因此,将while True中的所有代码(除了mainloop())放入函数中,并使用after()调用它。
并在sleep()位置使用第二个after()

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()

如果您无法将while-循环转换为使用after(),那么您可能需要在单独的线程中运行循环。但是,tkinter(像许多其他GUI一样)不喜欢在第二��线程中工作,您必须在主线程中进行GUI中的所有更改。


3
使用线程库:
这段代码创建了两个线程,一个运行tkinter程序,另一个线程将在无限循环中打印数字。
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()
    

2
我发现一个更好的替代方案 - 只需在另一个线程中启动while循环,并使用全局变量。我的代码以这种方式完美地工作。

1
如果您能提供一个代码示例,那将不胜感激。 - mccurcio
1
你如何将变量从线程传递到tk? - DagicCross

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接