Skip to content

Instantly share code, notes, and snippets.

@PM2Ring
Created July 18, 2017 20:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PM2Ring/467cf3d1e884ea508efd996f95fd871e to your computer and use it in GitHub Desktop.
Save PM2Ring/467cf3d1e884ea508efd996f95fd871e to your computer and use it in GitHub Desktop.
A Tkinter viewer for named PIL Images
#!/usr/bin/env python3
''' A Tkinter viewer for named PIL Images
Image windows can be iconified / deiconified individually via buttons
in the main viewer window, or iconified / deiconified en masse by
iconifying / deiconifying the main viewer window.
Written by PM 2Ring 2017.07.16
'''
from time import sleep
from PIL import Image, ImageTk
import tkinter as tk
class Viewer(tk.Tk):
''' View named PIL Images in separate Toplevel windows '''
def __init__(self):
super().__init__()
self.title('Viewer')
tk.Label(self, text='Viewer main window').pack()
self.winset = set()
self.bind("<Map>", lambda e: self.all_cb(e, 1))
self.bind("<Unmap>", lambda e: self.all_cb(e, 0))
self.update_idletasks()
# Create an image window
def show(self, img, title):
win = tk.Toplevel(self)
win.title(title)
self.winset.add(win)
win.photo = ImageTk.PhotoImage(img)
tk.Label(win, image=win.photo).pack()
tk.Label(win, text=title).pack()
# Add a map / unmap button for this window to the main window
win.var = tk.IntVar()
win.var.set(1)
win.button = tk.Checkbutton(self, text=title, anchor='nw',
variable=win.var,
command=lambda w=win: self.win_show_hide(win, win.var.get()))
win.button.pack(fill=tk.X, expand=tk.TRUE)
# Intercept map / unmap events so we can keep win.var in sync
win.bind("<Map>", lambda e, w=win: self.icon_cb(e, w, 1))
win.bind("<Unmap>", lambda e, w=win: self.icon_cb(e, w, 0))
# Intercept close events so we can remove the winset entry
# and the map / unmap button
win.protocol("WM_DELETE_WINDOW", lambda w=win: self.close(w))
# Allow Tkinter to add the button to the main window
self.update_idletasks()
# In case Tkinter has other stuff to do, like responding to a
# close event for another window
self.update()
return win
# Close an image window
def close(self, win):
win.button.destroy()
self.winset.discard(win)
win.destroy()
def win_show_hide(self, win, state):
(win.deiconify if state else win.iconify)()
# iconify / deiconify all image windows when
# the main window is iconified / deiconified
def all_cb(self, event, state):
if event.widget == self:
for win in self.winset:
self.win_show_hide(win, state)
# Set an image window's var to reflect its current map / unmap state
def icon_cb(self, event, win, state):
if event.widget == win:
win.var.set(state)
# Test
def main():
# Set up image viewer
viewer = Viewer()
#Make a square image that fades diagonally from black to white
size = 128
m = 255.0 / (2 * size - 2)
r = range(size)
fade = Image.new('L', (size, size), color=None)
fade.putdata([round(m * (x + y)) for y in r for x in r])
fade = fade.convert('RGB')
# Make some shaded color squares
colors = ('red', 'yellow', 'green', 'cyan', 'blue', 'magenta')
windows = {}
for c in colors:
# Simulate complex image creation that takes a long time
sleep(0.5)
img = Image.new('RGB', (size, size), color=c)
img = Image.blend(img, fade, 0.5)
win = viewer.show(img, title=c)
# Save the window by title for future reference
windows[c] = win
sleep(2)
# Iconify a window
win = windows['yellow']
win.iconify()
viewer.update_idletasks()
sleep(2)
# Close a window by its title
win = windows['cyan']
viewer.close(win)
viewer.update()
sleep(2)
# Deiconify a window
win = windows['yellow']
win.deiconify()
# We don't need `viewer.update_idletasks()` here because it'll
# get updated when `viewer.mainloop()` gets called.
# Wait for the main viewer window to be closed. Alternatively,
# the viewer can be closed by calling `viewer.destroy()`; otherwise
# it will be automatically destroyed when the program ends.
print('waiting')
viewer.mainloop()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment