Intermittent Python thread error, "main thread not in main loop"

My middle-aged father (an electrical engineer is not a programmer by profession) is trying to teach my 13-year-old daughter electronics and programming. I still love Python. I am creating a program to display temperature throughout our home using the tkinter GUI and DS18B20 sensors.

We compiled the program below by reading books, online research and using Qaru to correct errors (this site is rocky!).

Now we are at a dead end, we continue to receive an intermittent error, when we run the program for the first time after loading inactivity on our raspberries, it works fine.

The second time and all subsequent moments we get this error message:

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 

Please note that we understand that in order to update the static window labels and updates, the updated rates read from our sensor (DS18B20), we needed to use a stream. The sample code we started with contains _init_ instructions with one underscore preceding and ending - I don’t know why, if I add a second underscore, I get error messages. The update window code that we used as the basis is obtained from the Raspberry Pi Forum

Here is our code:

 from Tkinter import * import tkFont import os import glob import time import subprocess import re import sys import time import threading import Image import ImageTk os.system('modprobe w1-gpio') os.system('modprobe w1-therm') #28-000005c6ba08 sensors = ['28-000005c6ba08'] sensors1 = ['28-000005c70f69'] def read_temp_raw(): catdata = subprocess.Popen(['cat',device_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out,err = catdata.communicate() out_decode = out.decode('utf-8') lines = out_decode.split('\n') return lines def read_temp(): lines = read_temp_raw() while lines[0].strip()[-3:] != 'YES': time.sleep(0.2) lines = read_temp_raw() equals_pos = lines[1].find('t=') if equals_pos != -1: temp_string = lines[1][equals_pos+2:] temp_c = float(temp_string) / 1000.0 temp_f = temp_c * 9.0 / 5.0 + 32.0 return temp_f ########### build window ################### bground="grey" class App(threading.Thread): def _init_(self): threading.Thread._init_(self) self.start() def callback(self): self.root.quit() def run(self): #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" self.root.mainloop() #start monitoring and updating the GUI ########### Start Loop ################### print "starting app" app = App() app.start() print "app started" ################### Begin ds18b20 function ############## 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) app.equipTemp.set(tempread) time.sleep(5) ##################### END ds18b20 Function ###### 
+6
source share
2 answers

You need to run the GUI code in the main thread, and your temperature reading code should be in the background thread. It is safe to update the GUI in the main thread, so you can transfer the temperature data that you are reading from the background stream back to the main stream through Queue , and have the main stream periodically check the 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 ok if there 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 studying the tkinter source code, as well as tracking Python errors, it seems that unlike almost any other GUI library, tkinter is designed to be thread safe since you will run mainloop in the main application thread. See the Answer I added here for more information, or go to the resolved tkinter thread safety issue on the Python tracker here . If the tkinter source and the Python error tracker are correct, this will mean that as long as you run mainloop in the main thread, you can happily call gui.equipTemp.set() directly from the temperature read stream - there is no Queue . And in my testing, it really works great.

+3
source

GUI tools are not thread safe. You can only create and modify your GUI from the main thread. Since reading the temperature is not so long, you can delete the entire stream code and use the after method from Tk.

Your read_temp_raw function is very complex:

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

Source: https://habr.com/ru/post/973936/


All Articles