[Python] Calling ImageTk.PhotoImage() in a thread causing deadlock

Discussão em 'Python' iniciado por Stack, Maio 22, 2025.

  1. Stack

    Stack Membro Participativo

    I am working on a camera applicaiton where I need to get images from camera buffer and display it in a TK label live:

    def display_thread_run(self):
    while self.is_acquiring:
    image_data = self.camera.get_next_image()
    # --- some image conversion methods omitted ---
    pil_image = Image.fromarray(image_data)
    photo = ImageTk.PhotoImage(image=pil_image)
    self.image_frame.after(0, self._create_photo_and_update_gui, photo)
    time.sleep(0.01)


    When the application exits, I need to close the camera stream, join the thread and at the very end, release camera related resources:

    def stop_and_cleanup(self):
    camera.stop_acquisition()
    self.is_acquiring = False
    if self.display_thread and self.display_thread.is_alive():
    self.display_thread.join()
    camera.release()
    logger.info("Display resources released")


    However, after I click the close window button (cross button), photo = ImageTk.PhotoImage(image=pil_image) causes deadlock and the thread won't join.

    Setting thread.daemon=True and removing display_thread.join() won't work because camera.release() must be called after the thread is properly cleaned.

    I'm using:

    • Python 3.10.11
    • pillow 11.2.1

    A minimum code example:

    import tkinter as tk
    from PIL import Image, ImageTk
    import threading
    import time


    IMG_PATH = 'test_img.jpg'

    class App:
    def __init__(self, root):
    self.root = root
    self.label = tk.Label(root)
    self.label.pack()
    self.running = True
    self.img = Image.open(IMG_PATH)

    self.display_thread= threading.Thread(target=self.update_image_loop, daemon=True)
    self.display_thread.start()

    self.root.protocol("WM_DELETE_WINDOW", self.on_close)

    def update_image_loop(self):
    while self.running:
    tk_img = ImageTk.PhotoImage(image=self.img)
    self.label.after(0, self.set_image, tk_img)
    time.sleep(0.001)

    def set_image(self, tk_img):
    self.label.img = tk_img # keep reference
    self.label.config(image=tk_img)

    def on_close(self):
    print("on_close")
    self.running = False
    self.display_thread.join()
    self.root.destroy()

    if __name__ == '__main__':
    root = tk.Tk()
    app = App(root)
    root.mainloop()

    Continue reading...

Compartilhe esta Página