Created
July 18, 2017 20:05
-
-
Save PM2Ring/467cf3d1e884ea508efd996f95fd871e to your computer and use it in GitHub Desktop.
A Tkinter viewer for named PIL Images
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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