1

this question has been asked many times but to be honest I don't quite understand why do I get this error "RuntimeError: main thread is not in main loop". I have the following code to read data from Serial and draw some graph with it. My problem is that my program is working if I do not try to draw in real time the data (so if I only keep take_measure() inside the plotter function). But if I add the part for the graph drawing as shown bellow my code is returning this mainloop error. What is happening ? I know I should use the after command but for me after command was a total disaster (a lots of bugs). So do you know what is the cause of my error and how to solve it/how to implement the after method ?

Sorry for my nasty code....

from tkinter import * 
from random import randint 
import numpy as np
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure 
import time 
import threading
import serial
from tkinter.filedialog import asksaveasfile
import csv
from skimage.restoration import (denoise_tv_chambolle, denoise_bilateral, denoise_wavelet, estimate_sigma)

continuePlotting = False 
 
def change_state(): 
    global continuePlotting
    global serial_port
    global i
    global b_start
    global mid
    global label
    if continuePlotting == True: 
        continuePlotting = False
        serial_port.close()
        i = 2
        label.pack_forget()
    else: 
        continuePlotting = True
        label.pack(in_=mid)

t, p1 = [], []
i = 0

def take_measure():
    global t
    global p1
    global t_temp
    global p1_temp
    global i
    global serial_port
    if i == 0:
        serial_port =serial.Serial('COM5', 2000000)
        serial_port.setDTR(False)
        time.sleep(0.1)
        serial_port.setDTR(True)
        serial_port.flushInput()
        p1_temp = []
        t_temp = []
        i = 1
    elif i == 2:
        serial_port =serial.Serial('COM5', 2000000)
        serial_port.setDTR(False)
        time.sleep(0.1)
        serial_port.setDTR(True)
        serial_port.flushInput()
        p1_temp = []
        t_temp = []
        i = 1
    try:
        temp=serial_port.readline()[:-2].decode()
        a = temp.index(";")
        t_val = float(temp[:a])
        p1_val = (float(temp[a+1:])-2640)*20/3520
        t.append(t_val)
        p1.append(p1_val)
        t_temp.append(t_val)
        p1_temp.append(p1_val)
    except:
        pass

def app():
    global t_temp
    global p1_temp
    global i
    global serial_port
    global t
    global p1
    global b_start
    global mid
    global label

root = Tk() 
root.config(background='white') 
root.geometry("1000x700")

top = Frame(root)
bottom = Frame(root)
mid = Frame(root)
top.pack(side="top")
mid.pack(side="top")
bottom.pack(side="bottom", fill="both", expand=True) 
 
fig = Figure() 
 
ax = fig.add_subplot(111) 
ax.set_xlabel("X axis") 
ax.set_ylabel("Y axis") 
ax.grid()

graph = FigureCanvasTkAgg(fig, master=root) 
graph.get_tk_widget().pack(in_=bottom, side="top",fill='both',expand=True)
graph.draw()

toolbar = NavigationToolbar2Tk(graph, root, pack_toolbar=False)
toolbar.update()

def plotter():
    while continuePlotting:
        take_measure()
        ax.clear()
        ax.plot(t_temp, p1_temp)
        graph.draw()

        


def gui_handler(): 
    change_state() 
    t=threading.Thread(target=plotter, daemon=True)
    t.start()

def Save():
    files = [('CSV', '*.csv')]
    file_name = asksaveasfile(filetypes = files, defaultextension = files)
    with open(str(file_name.name),"w", newline='') as file:
        Writer=csv.writer(file)
        Writer.writerow(["temps en ms", "pression en V"]) 
        for elt in range(len(t)):
            Writer.writerow([t[elt], p1[elt]])
    file.close()

def Clear():
    global t
    global p1
    serial_port.close()
    i = 0
    ax.clear()
    graph.draw()
    t = []
    p1 = []

def Draw():
    l_t = [t[0]]
    l_p1 = [p1[0]]
    ax.cla() 
    ax.grid()
    for elt in range(1,len(t)):
        if t[elt] == 0:
            l_p = denoise_wavelet(np.array(l_p1), method="VisuShrink", mode="hard", wavelet_levels=3, wavelet='haar', rescale_sigma='True')
            print("max", max(l_p1))
            print("min", min(l_p1))
            ax.plot(l_t, l_p)
            l_t, l_p1 = [], []
        l_t.append(t[elt])
        l_p1.append(p1[elt])
    l_p = denoise_wavelet(np.array(l_p1), method="VisuShrink", mode="hard", wavelet_levels=3, wavelet='haar', rescale_sigma='True')
    ax.plot(l_t, l_p)
    graph.draw()
    print("max", max(l_p1))
    print("min", min(l_p1))

b_start = Button(root, text="Start/Stop", command=gui_handler, bg="red", fg="white") 
b_start.pack(in_=top, side = LEFT) 

button_quit = Button(master=root, text="Quit", command=root.destroy)
button_save = Button(root,text="Save", command=Save)
button_clear = Button(root,text="Clear graph", command=Clear)
button_draw = Button(root,text="Show graphs", command=Draw)

button_draw.pack(in_=top, side = LEFT)
button_clear.pack(in_=top, side = LEFT)
button_save.pack(in_=top, side = LEFT)
button_quit.pack(in_=top, side=LEFT)

label = Label(root, text = "WAIT")

toolbar.pack(in_=bottom, fill=X)

root.mainloop() 


if __name__ == '__main__': 
    app() 

The error is the following:

Exception ignored in: <function Variable.__del__ at 0x000001CCB0F3E3E0>
Traceback (most recent call last):
  File "c:\Users\cbroggi\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 410, in __del__
    if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: main thread is not in main loop
Exception ignored in: <function Image.__del__ at 0x000001CCB1098D60>
Traceback (most recent call last):
  File "c:\Users\cbroggi\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 4083, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <function Variable.__del__ at 0x000001CCB0F3E3E0>
Traceback (most recent call last):
  File "c:\Users\cbroggi\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 410, in __del__
    if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: main thread is not in main loop
Exception ignored in: <function Variable.__del__ at 0x000001CCB0F3E3E0>
Traceback (most recent call last):
  File "c:\Users\cbroggi\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 410, in __del__
    if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: main thread is not in main loop
2
  • 2
    Post traceback error. Commented Feb 16, 2023 at 9:08
  • You cannot update the UI from outside the main loop thread. so t=threading.Thread(target=plotter, daemon=True) with graph.draw() is trying to update outside of mainloop() Commented Feb 16, 2023 at 16:23

1 Answer 1

0

Since you cannot update the UI from a thread your graph.draw() method will cause this type of error.

As a solution try using the .after() method:

def update():
    global graph
    graph.draw()
    root.after(1000, update) ##after 1000ms run update()
    ##WARNING: this will require some additoinal code to stop when you exit the code.
def plotter():
    while continuePlotting:
        take_measure()
        ax.clear()
        ax.plot(t_temp, p1_temp)
        #graph.draw() #removed as its in a thread
update() ##triggers the update loop that re-draws the graph every 1000ms
root.mainloop()
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, unfortunatly I'm still having the same issue with this method. If I understand your explaination, I have to remove everything that is trying to modifiy the main thread (root) while not being part of it. In that case it means that if I call the update function inside the main thread even if it is declared ouside of it it will still execute itself only whre I call it ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.