断断续续的Python线程错误;“主线程不在主循环中”;

断断续续的Python线程错误;“主线程不在主循环中”;,python,multithreading,raspberry-pi,Python,Multithreading,Raspberry Pi,中年爸爸(电气工程师而非程序员)试图教我13岁的女儿电子和编程。到目前为止,我喜欢Python。我正在建立一个程序,用tkinter GUI和DS18B20传感器显示整个房子的温度 我们通过阅读书籍、在线研究和使用堆栈溢出来解决问题,拼凑了下面的程序(这个网站棒极了!) 现在我们被难住了,我们不断得到一个间歇性错误,当我们在Raspberry上加载idle后第一次运行程序时,它工作正常 第二次以及随后的所有时间,我们都会收到以下错误消息: Traceback (most recent call

中年爸爸(电气工程师而非程序员)试图教我13岁的女儿电子和编程。到目前为止,我喜欢Python。我正在建立一个程序,用tkinter GUI和DS18B20传感器显示整个房子的温度

我们通过阅读书籍、在线研究和使用堆栈溢出来解决问题,拼凑了下面的程序(这个网站棒极了!)

现在我们被难住了,我们不断得到一个间歇性错误,当我们在Raspberry上加载idle后第一次运行程序时,它工作正常

第二次以及随后的所有时间,我们都会收到以下错误消息:

Traceback (most recent call last):
  File "/home/pi/Code-working-library/stackoverflow-paste.py", line 140, in <module>
    app.equipTemp.set(tempread)
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 203, in set
    return self._tk.globalsetvar(self._name, value)
RuntimeError: main thread is not in main loop

GUI工具包不是线程安全的。您只能从主线程构建和更改GUI。 由于读取温度不会花费那么长时间,因此可以删除所有线程代码,并使用Tk中的
after
-方法

您的
read\u temp\u raw
功能非常复杂:

def read_temp_raw():
    with open(device_file) as temp:
        return temp.read().split('\n')

您需要在主线程中运行GUI代码,温度读取代码需要在后台线程中。只在主线程中更新GUI是安全的,因此您可以通过
队列将正在从后台线程读取的温度数据传递回主线程,并让主线程使用
self.root.after()定期检查队列中的数据。

编辑:


在实际查看了tkinter源代码以及Python bug跟踪器之后,看起来tkinter与几乎所有其他GUI库不同,它是线程安全的,只要您在应用程序的主线程中运行mainloop。更多信息,请参阅我添加的答案,或者直接转到Python bug跟踪器上关于tkinter线程安全的已解决问题。如果tkinter源代码和Python的bug跟踪器是正确的,那就意味着只要在主线程中运行mainloop,就可以直接从温度读取线程调用
gui.equipTemp.set()
——无需
Queue
。在我的测试中,这确实工作得很好。

tkinter没有做太多工作,但gui工具包通常只在主线程上运行。显而易见的解决方案:将主循环留在主线程上,将计算放在另一个线程中!是的,请将您的计算放在App class.thx中以便快速响应,但请像对待白痴一样对待我。当你说把我的计算放在app类中时,你是指我在注释后面的代码:###Begin ds18b20 function###还是我代码开头的两个read temp函数?好吧,现在你在主线程中进行计算(在Begin ds18b20 function之后的东西),在一个额外的线程中进行gui。你应该反过来做!顺便说一句,您肯定应该使用
\uuuu init\uuu
,而不是
\uu init
。您在使用
\uuuuu init\uuuu
时遇到的任何错误都可能是由于您所犯的合法编码错误造成的,该错误只有在实际调用
\uuuuuu init\uuu
时才会发生。
def read_temp_raw():
    with open(device_file) as temp:
        return temp.read().split('\n')
from Tkinter import *
import tkFont
import os
import glob
import time
import threading
import Image 
import Queue


def update_temp(queue):
    """ Read the temp data. This runs in a background thread. """
    while True:
        #   28-000005c6ba08
        i = "28-000005c6ba08"
        base_dir = '/sys/bus/w1/devices/'
        device_folder = glob.glob(base_dir + i)[0]
        device_file = device_folder + '/w1_slave'

        tempread=round(read_temp(),1)

        # Pass the temp back to the main thread.
        queue.put(tempread)
        time.sleep(5)

class Gui(object):
    def __init__(self, queue):
        self.queue = queue

        #Make the window
        self.root = Tk() 
        self.root.wm_title("Home Management System")
        self.root.minsize(1440,1000)

        self.equipTemp = StringVar()   
        self.equipTemp1 = StringVar()
        self.equipTemp2 = StringVar()       

        self.customFont = tkFont.Font(family="Helvetica", size=16)

        #   1st floor Image
        img = Image.open("HOUSE-PLANS-01.png") 
        photo = ImageTk.PhotoImage(img)

        Label1=Label(self.root, image=photo)
        Label1.place(x=100, y=100)

        #   2nd floor
        img2 = Image.open("HOUSE-PLANS-02.png")
        photo2 = ImageTk.PhotoImage(img2)

        Label1=Label(self.root, image=photo2)
        Label1.place(x=600, y=100)

        #   Basement image
        img3 = Image.open("HOUSE-PLANS-03.png")
        photo3 = ImageTk.PhotoImage(img3)

        Label1=Label(self.root, image=photo3)
        Label1.place(x=100, y=500)

        #   Attic Image
        img4 = Image.open("HOUSE-PLANS-04.png")
        photo4 = ImageTk.PhotoImage(img4)

        Label1=Label(self.root, image=photo4)
        Label1.place(x=600, y=500)

        #   House Isometric Image
        img5 = Image.open("house-iso.png")
        photo5 = ImageTk.PhotoImage(img5)

        Label1=Label(self.root, image=photo5)
        Label1.place(x=1080, y=130)

        #Garage Temp Label
        Label2=Label(self.root, textvariable=self.equipTemp, width=6, justify=RIGHT, font=self.customFont)
        Label2.place(x=315, y=265)

        print "start monitoring and updating the GUI"

        # Schedule read_queue to run in the main thread in one second.
        self.root.after(1000, self.read_queue)

    def read_queue(self):
        """ Check for updated temp data"""
        try:
            temp = self.queue.get_nowait()
            self.equipTemp.set(temp)
        except Queue.Empty:
            # It's ok if there's no data to read.
            # We'll just check again later.
            pass
        # Schedule read_queue again in one second.
        self.root.after(1000, self.read_queue)

if __name__ == "__main__":
    queue = Queue.Queue()
    # Start background thread to get temp data
    t = threading.Thread(target=update_temp, args=(queue,))
    t.start()
    print "starting app"
    # Build GUI object
    gui = Gui(queue)
    # Start mainloop
    gui.root.mainloop()