Intermittent Python thread error, “main thread is not in main loop”

前端 未结 2 578
后悔当初
后悔当初 2021-01-13 01:01

Middle aged dad (electrical engineer not programmer by trade) trying to teach my 13 year old daughter electronics and programming. So far, I love Python. I am building a pro

相关标签:
2条回答
  • 2021-01-13 01:36

    You need to run the GUI code in the main thread, and your temperature reading code needs to be in the background thread. It's only safe to update the GUI in the main thread, so you can pass the temperature data you're reading from the background thread back to the main thread via a Queue, and have the main thread periodically check for data in the queue using self.root.after():

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

    Edit:

    After actually taking a look at the tkinter source code, as well as the Python bug tracker, it appears that unlike almost every other GUI library out there, tkinter is intended to be thread-safe, as long you run the mainloop in the main thread of the application. See the answer I added here for more info, or go straight to the resolved issue about tkinter's thread safety on the Python bug tracker here. If the tkinter source and Python's bug tracker are correct, that would mean that as long as you run the mainloop in the main thread, you can happily call gui.equipTemp.set() directly from your temperature reading thread - no Queue required. And in my testing, that did indeed work just fine.

    0 讨论(0)
  • 2021-01-13 01:43

    GUI toolkits are not threadsafe. You can only built and change your GUI from the main thread. Since reading the temperature does not take that long, you can remove all the threading code and use the after-method from Tk.

    Your read_temp_raw function is very complicated:

    def read_temp_raw():
        with open(device_file) as temp:
            return temp.read().split('\n')
    
    0 讨论(0)
提交回复
热议问题