'RuntimeError: main thread is not in main loop when tkinter GUI is closed
I'm trying to create a tkinter GUI that presents the 10 processes that are taking up the cpu, refreshing it every 2 seconds, similar to top, on the linux command line. My code is:
import time
import threading
import logging
import psutil
from functools import cmp_to_key
from tkinter import *
import tkinter.scrolledtext as ScrolledText
class TextHandler(logging.Handler):
def __init__(self, text):
logging.Handler.__init__(self)
self.text = text
def emit(self, record):
msg = self.format(record)
def append():
self.text.configure(state='normal')
self.text.insert(END, msg + '\n')
self.text.configure(state='disabled')
# Autoscroll to the bottom
self.text.yview(END)
# This is necessary because we can't modify the Text from other threads
self.text.after(0, append)
class myGUI(Frame):
def __init__(self):
self.window = Tk()
self.window.title("TOP GUI")
self.window.configure(bg='#999999')
self.window.minsize(width=400, height=400)
self.window.resizable(0, 0)
self.lable_title = Label(self.window,
text=" Table of Processes ",
font=("Arial", 20, "bold"),
bg='#4F4F4F', fg='#E6BA00', highlightthickness=2)
self.lable_title.pack(pady=10)
self.frame_label1 = Label(self.window,
text='\n Below are listed the 10 processes with the highest \n'
+ 'CPU consumption, with 5 second refresh. \n',
bg='#E6BA00',
highlightthickness=0,
width=43,
borderwidth=2,
relief='ridge')
self.frame_label1.pack(pady=20)
self.frame_top_message = Frame(self.window, bg='#999999')
self.frame_botao_go = Frame(self.window, bg='#999999')
self.frame_top_message.pack(pady=20)
self.frame_botao_go.pack(pady=20)
self.button_completed = Button(self.frame_botao_go,
font='Arial 14 bold',
text='GO',
width=10,
command=self.window.destroy)
self.button_completed.pack(side='left', padx=50)
self.button_completed.configure(bg='#E6BA00')
# Add text widget to display logging info
st = ScrolledText.ScrolledText(self.frame_top_message)
st.configure(font='TkFixedFont',
width=50,
height=15)
st.pack(padx=30)
# Create textLogger
text_handler = TextHandler(st)
# Logging configuration
logging.basicConfig(filename='test.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
# Add the handler to logger
logger = logging.getLogger()
logger.addHandler(text_handler)
trheading()
self.window.mainloop()
def trheading():
t2 = threading.Thread(target=worker, args=[])
t2.start()
def worker():
def cmpCpu(a, b):
a = a['cpu']
b = b['cpu']
if a > b:
return -1
elif a == b:
return 0
else:
return 1
while True:
timestamp = time.asctime()
logging.info(timestamp)
# Collect information for each process
processes = []
for proc in psutil.process_iter(attrs=['name', 'cpu_percent']):
processes.append({'name': proc.info['name'], 'cpu': proc.info['cpu_percent']})
# Sort by cpu usage
logging.info("CPU:")
processes.sort(key=cmp_to_key(cmpCpu))
for i in range(10):
info = processes[i]
info = info['name'] + ", " + str(info['cpu']) + "%"
logging.info(info)
logging.info("\n")
time.sleep(2)
t1 = threading.Thread(target=myGUI, args=[])
t1.start()
It will be used as a module of another larger program and will allow the user to follow a series of processes launched through subprocess.run. It was written by putting together several examples from the internet. It works very well, but when I click the GO button I get the error message:
Exception in thread Thread-2:
Traceback (most recent call last):
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/threading.py", line 973, in _bootstrap_inner
self.run()
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/threading.py", line 910, in run
self._target(*self._args, **self._kwargs)
File "/home/arymaia/PycharmProjects/TopIso3D_Viewer_vbeta/Table_of_Processes_2.py", line 116, in worker
logging.info(timestamp)
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/logging/__init__.py", line 2097, in info
root.info(msg, *args, **kwargs)
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/logging/__init__.py", line 1446, in info
self._log(INFO, msg, args, **kwargs)
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/logging/__init__.py", line 1589, in _log
self.handle(record)
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/logging/__init__.py", line 1599, in handle
self.callHandlers(record)
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/logging/__init__.py", line 1661, in callHandlers
hdlr.handle(record)
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/logging/__init__.py", line 952, in handle
self.emit(record)
File "/home/arymaia/PycharmProjects/TopIso3D_Viewer_vbeta/Table_of_Processes_2.py", line 27, in emit
self.text.after(0, append)
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/tkinter/__init__.py", line 821, in after
name = self._register(callit)
File "/home/arymaia/.pyenv/versions/3.9-dev/lib/python3.9/tkinter/__init__.py", line 1528, in _register
self.tk.createcommand(name, f)
RuntimeError: main thread is not in main loop
Tcl_AsyncDelete: async handler deleted by the wrong thread
I researched several answers that explain that tkinter has limitations with the use of trheading, but what I found different was that the code I made works without error, until the moment of being closed. Could someone explain to me why this is and how I can solve this problem?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|