Tkinter窗口不能很好地与线程一起播放

时间:2014-06-27 19:58:14

标签: python multithreading python-3.x tkinter python-3.3

我有一个程序最终将通过串口从外部源接收数据,但我首先尝试开发显示器。

我有这个"主要"具有模拟数据发送和接收的模块。它更新Matplotlib条形图使用的全局。所有这一切都有效。

#-------------------------------------------------------------------------------
# Name:        BBQData
# Purpose:  Gets the data from the Arduino, and runs the threads.
#-------------------------------------------------------------------------------
import time
import math
import random
from threading import Thread
import my_globals as bbq
import sys
import BBQStripChart as sc
import serial
import BBQControl as control


ser = serial.serial_for_url('loop://', timeout=10)

def simData():
    newTime = time.time()
    if not hasattr(simData, "lastUpdate"):
        simData.lastUpdate = newTime  # it doesn't exist yet, so initialize it
        simData.firstTime = newTime  # it doesn't exist yet, so initialize it
    if newTime > simData.lastUpdate:
        simData.lastUpdate = newTime
        return (140 + 0.05*(simData.lastUpdate - simData.firstTime), \
            145 + 0.022*(simData.lastUpdate - simData.firstTime), \
            210 + random.randrange(-10, 10))
    else:
        return None

def serialDataPump():
    testCtr = 0;
    while not bbq.closing and testCtr<100:
        newData = simData()
        if  newData != None:
            reportStr = "D " + "".join(['{:3.0f} ' for x in newData]) + '\n'
            reportStr = reportStr.format(*newData)
            ser.write(bytes(reportStr, 'ascii'))
            testCtr+=1
            time.sleep(1)
    bbq.closing = True

def serialDataRcv():
    while not bbq.closing:
        line = ser.readline()
        rcvdTime = time.time()
        temps = str(line, 'ascii').split(" ")
        temps = temps[1:-1]
        for j, x in enumerate(temps):
            bbq.temps[j].append(float(x))
            bbq.plotTimes.append(rcvdTime)

def main():
    sendThread = Thread(target = serialDataPump)
    receiveThread = Thread(target = serialDataRcv)
    sendThread.start()
    receiveThread.start()
#    sc.runUI() 
    control.runControl() #blocks until user closes window
    bbq.closing = True
    time.sleep(2)
    exit()


if __name__ == '__main__':
    main()
##    testSerMain()

但是,我想添加一个只包含最新数据的SEPARATE tkinter窗口,一个关闭按钮等。我可以启动该窗口,最初显示数据,但没有其他线程运行。 (当我尝试同时运行窗口和绘图时,没有任何作用。)

#-------------------------------------------------------------------------------
# Name:        BBQ Display/Control
# Purpose:      displays current temp data, and control options
#-------------------------------------------------------------------------------

import tkinter as tk
import tkinter.font
import my_globals as bbq
import threading

fontSize = 78

class BBQControl(tk.Tk):
    def __init__(self,parent):
        tk.Tk.__init__(self,parent)
        self.parent = parent
        self.labelFont = tkinter.font.Font(family='Helvetica', size=int(fontSize*0.8))
        self.dataFont = tkinter.font.Font(family='Helvetica', size=fontSize, weight = 'bold')
        self.makeWindow()


    def makeWindow(self):
        self.grid()

        btnClose = tk.Button(self,text=u"Close")
        btnClose.grid(column=1,row=5)

        lblFood = tk.Label(self,anchor=tk.CENTER, text="Food Temps", \
            font = self.labelFont)
        lblFood.grid(column=0,row=0)
        lblPit = tk.Label(self,anchor=tk.CENTER, text="Pit Temps", \
            font = self.labelFont)
        lblPit.grid(column=1,row=0)

        self.food1Temp = tk.StringVar()
        lblFoodTemp1 = tk.Label(self,anchor=tk.E, \
            textvariable=self.food1Temp, font = self.dataFont)
        lblFoodTemp1.grid(column=0,row=1)

        #spawn thread to update temps
        updateThread = threading.Thread(target = self.updateLoop)
        updateThread.start()

    def updateLoop(self):
        self.food1Temp.set(str(bbq.temps[1][-1]))

def runControl():
    app = BBQControl(None)
    app.title('BBQ Display')
    app.after(0, app.updateLoop)
    app.mainloop()
    bbq.closing = True

if __name__ == '__main__':
    runControl()

任何想法都会受到赞赏。

2 个答案:

答案 0 :(得分:1)

你的标题可以很好地总结问题:Tkinter在线程方面表现不佳。这不是问题,这就是答案。

您只能从创建窗口小部件的同一个线程中访问tkinter窗口小部件。如果你想使用线程,你需要你的非gui线程将数据放在一个队列上,并让gui线程定期轮询队列。

答案 1 :(得分:1)

使tkinter与线程一起运行的一种方法是修改库,以便所有方法调用都在单个线程上运行。另外两个问题涉及同一问题:Updating a TKinter GUI from a multiprocessing calculationPython GUI is not responding while thread is executing。反过来,给定的答案指向了几个有助于解决您所面临问题的模块。每当我使用tkinter时,我总是使用safetkinter模块,以防线程似乎对程序有帮助。