I am trying to launch multiple instances of a tkinter class in separate threads. Everytime the second screen appears, the following error is thrown: "_tkinter.TclError: image "pyimage2" doesn't exist". I expected them to be two separate instances, but according to internet searches this is not the case.
Here's a simplified PoC:
import subprocess, threading, time
import tkinter
from PIL import Image, ImageTk
class App:
def __init__(self, display: str):
self.root = tkinter.Tk(screenName=display)
self.background_image = Image.new("RGB", (200, 200), (200, 200, 200))
self.background = ImageTk.PhotoImage(self.background_image)
self.label_background = tkinter.Label(
self.root, image=self.background, borderwidth=0
)
self.root.mainloop()
class X(threading.Thread):
def __init__(self, display):
super().__init__()
self.display = display
subprocess.Popen(["Xephyr", self.display])
# wait for X server to become available
while (
subprocess.run(
["xset", "-q", "-display", self.display],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
).returncode
!= 0
):
time.sleep(0.01)
def run(self):
# blocking call
self.app = App(self.display)
x1 = X(":1")
x1.start()
x2 = X(":2")
x2.start()
x2.join()
And the according output:
$ python3 tkinter_test.py
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
self.run()
File "/home/user/pyrdp/tkinter_test.py", line 35, in run
self.app = App(self.display)
File "/home/user/pyrdp/tkinter_test.py", line 11, in __init__
self.label_background = tkinter.Label(
File "/usr/lib/python3.10/tkinter/__init__.py", line 3187, in __init__
Widget.__init__(self, master, 'label', cnf, kw)
File "/usr/lib/python3.10/tkinter/__init__.py", line 2601, in __init__
self.tk.call(
_tkinter.TclError: image "pyimage2" doesn't exist
Is it even possible to run multiple tkinter instances in different threads or do I need a different approach here? These instances will have to run in two separate X servers and do not need to share any resources or communicate among each other.
CodePudding user response:
Everytime the second screen appears, the following error is thrown: "_tkinter.TclError: image "pyimage2" doesn't exist". I expected them to be two separate instances, but according to internet searches this is not the case.
They are separate instances, which is why you're seeing the error. You didn't specify a master for each image so each one defaults to the first instance of Tk
that was created. Thus, all of your images belong to the first instance and are not visible in the second instance. Thus, image "pyimage2" doesn't exist
. The second image has an internal name of pyimage2
, but there's only an image with that name in the first instance of Tk
.
The solution is to pass a value for master
when creating the image so that the image is created in the appropriate tkinter window.
self.background = ImageTk.PhotoImage(self.background_image, master=self.root)