derricw
derricw

Reputation: 7036

Running a Tkinter form in a separate thread

I have written a short module that can be passed an image and simply creates a Tkinter window and displays it. The problem that I am having is that even when I instantiate and call the method that displays the image in a separate thread, the main program will not continue until the Tkinter window is closed.

Here is my module:

import Image, ImageTk
import Tkinter


class Viewer(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

    def show(self,img):
        self.to_display = ImageTk.PhotoImage(img)
        self.label_image = Tkinter.Label(self,image=self.to_display)
        self.label_image.grid(column = 0, row = 0, sticky = "NSEW")
        self.mainloop()

It seems to work fine, except when I call it from my test program like the one below, it will not seem to allow my test program to continue, even when started in a different thread.

import Image
from viewer import Viewer
import threading

def showimage(im):
    view = Viewer(None)
    view.show(im)

if __name__ == "__main__":
    im = Image.open("gaben.jpg")
    t = threading.Thread(showimage(im))
    t.start()
    print "Program keeps going..."

I think that perhaps my problem is that I should be creating a new thread within the module itself, but I was wanting to just try and keep it simple, as I am new to Python.

Anyway, thanks in advance for any assistance.

edit: To clarity, I am just trying to make a module that will display an image in a Tkinter window, so that I can use this module any time I want to display an image. The problem that I am having is that any time a program uses this module, it cannot resume until the Tkinter window is closed.

Upvotes: 13

Views: 35027

Answers (5)

crs
crs

Reputation: 71

Sounds like you should take the solution above, using subprocess as a suggestion to write a program, could be tkinter based, to run as a separate process, displaying an image stored in a file. This avoids threading and facilitates a modeless (non-blocking) display. Note that this solution avoids threading or other inconvenient interactions between your display operation and your main program. I found this solution helpful when part of my program used turtle (tkinter based graphics) but not being screen reader friendly, and another part of my program used wxPython (c++ based graphics frame) being much more screen reader friendly. The target users are blind individuals highly dependent on screen reader friendly applications.

Upvotes: 0

Gonzo
Gonzo

Reputation: 2153

From your comments it sound's like you do not need a GUI at all. Just write the image to disk and call an external viewer.

On most systems it should be possible to launch the default viewer using something like this:

import subprocess 

subprocess.Popen("yourimage.png")

Upvotes: 5

Aftershock
Aftershock

Reputation: 5351

I have tried to run tkinter from a separate thread, not a good idea, it freezes. There is one solution that worked. Run the gui in the main thread, and send events to the main gui. This is similar example, it just shows a label.

import Tkinter as t
global root;
root = t.Tk()
root.title("Control center")
root.mainloop()

def new_window(*args):
    global root
    print "new window"
    window = t.Toplevel(root)
    label = t.Label(window, text="my new window")
    label.pack(side="top", fill="both", padx=10, pady=10)
    window.mainloop()

root.bind("<<newwin>>",new_window)

#this can be run in another thread
root.event_generate("<<newwin>>",when="tail")

Upvotes: 2

Bryan Oakley
Bryan Oakley

Reputation: 386382

Tkinter isn't thread safe, and the general consensus is that Tkinter doesn't work in a non-main thread. If you rewrite your code so that Tkinter runs in the main thread, you can have your workers run in other threads.

The main caveat is that the workers cannot interact with the Tkinter widgets. They will have to write data to a queue, and your main GUI thread will have to poll that queue.

If all you're doing is showing images, you probably don't need threading at all. Threading is only useful when you have a long running process that would otherwise block the GUI. Tkinter can easily handle hundreds of images and windows without breaking a sweat.

Upvotes: 22

parselmouth
parselmouth

Reputation: 1668

From what I can tell, Tkinter doesn't like playing in other threads. See this post...I Need a little help with Python, Tkinter and threading

The work around is to create a (possibly hidden) toplevel in your main thread, spawn a separate thread to open images, etc - and use a shared queue to send messages back to the Tk thread.

Are you required to use Tkinter for your project? I like Tkinter. It's "quick and dirty." - but there are (many) cases where other GUI kits are the way to go.

Upvotes: 3

Related Questions