I want to implement GUI using threading and tkinter (python 3.6).
When I run GUIExecution.py, the following error occurs.
"RuntimeError: Calling Tcl from different appartment" on self.root.mainloop() in base_gui_class.py
I am implementing it on a class basis, and the three code files are as follows.
The executable file is GUIExecution.py.
I spent a lot of time trying to fix the error, but I have not been able to fix it yet.
Please give a lot of advice.
Additionally, if I run the following code in a python2 environment, it works fine without error.
GUIExecution.py
from base_gui_class import *
from base_class import *
speed = 1000
height = 500
width = 700
base_model = base_class()
gui = base_gui_class(base_model, speed, height, width)
base_model.visualize()
base_class.py
class base_class():
genes = []
dicLocations = {}
gui = ''
best = ''
time = 0
def __init__(self):
pass
def visualize(self):
if self.gui != '':
self.gui.start()
def registerGUI(self, gui):
self.gui = gui
base_gui_class.py
import threading
import tkinter as tk
import math
import threading
import time
class base_gui_class(threading.Thread):
root = ''
canvas = ''
speed = 0
base_model = ''
def __init__(self, base_model, speed, h, w):
threading.Thread.__init__(self)
self.base_model = base_model
base_model.registerGUI(self)
self.root = tk.Tk()
self.canvas = tk.Canvas(self.root, height=h, width=w)
self.canvas.pack()
self.root.title("Test")
self.speed = 1 / speed
def run(self):
self.root.mainloop()
def update(self):
time.sleep(self.speed)
width = int(self.canvas.cget("width"))
height = int(self.canvas.cget("height"))
self.canvas.create_rectangle(0, 0, width, height, fill='white')
def stop(self):
self.root.quit()
To a very good first and second approximation, the core of Tk is single threaded. It can be used from multiple threads, but only by initialising it separately in each of those threads. Internally, it uses thread-specific variables extensively to avoid the need for major locking (that is, it has nothing like a big Global Interpreter Lock) but that means you must not cheat. Whatever thread initialises a Tk context must be the only thread that interacts with that Tk context. This includes loading the Tkinter module so you are effectively restricted to using Tkinter from your main thread only; working around this is serious expert's-only stuff.
I recommend that you make your worker threads make changes to your GUI by posting events to it using a queue (or otherwise interlock with critical sections and condition variables, though I find queues easier in practice).