# File: imageviewer.py
# References:
# http://www.tcl.tk/man/tcl8.5/TkCmd/ttk_labelframe.htm
# http://www.tcl.tk/man/tcl8.5/TkCmd/listbox.htm
# http://www.tcl.tk/man/tcl8.5/TkCmd/ttk_label.htm
# http://www.tcl.tk/man/tcl8.5/TkCmd/bind.htm
# http://infohost.nmt.edu/tcc/help/pubs/pil/index.html
# http://www.pythonware.com/library/pil/handbook/introduction.htm
import os
from os.path import basename
from glob import *
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from PIL import Image, ImageTk
from demopanels import MsgPanel, SeeDismissPanel
# file formats that can be 'read' by PIL
FILETYPES = ['.bmp', '.dib', '.dcx', '.gif', '.im', '.jpg',
'.jpe', '.jpeg', '.pcd', '.pcx', '.png', '.pbm',
'.pgm', '.ppm', '.psd', '.tif', '.tiff', '.xbm',
'.xpm']
class ImageViewerDemo(ttk.Frame):
def __init__(self, isapp=True, name='imageviewerdemo'):
ttk.Frame.__init__(self, name=name)
self.pack(expand=Y, fill=BOTH)
self.master.title('Image Viewer Demo')
self.isapp = isapp
self._create_widgets()
def _create_widgets(self):
if self.isapp:
MsgPanel(self,
["This demonstration allows you to view images using the Python Imaging Library (PIL).\n\n",
"To view an image, double-click on its name in the 'Files' list.\n\n",
"To view images in another directory, click on the ",
"'Directory...' button and select a new directory via a 'File Dialog'."])
SeeDismissPanel(self)
self._create_demo_panel()
def _create_demo_panel(self):
demoPanel = Frame(self)
demoPanel.pack(side=TOP, fill=BOTH, expand=Y)
dirPanel = self._create_dir_panel(demoPanel)
filePanel = self._create_file_panel(demoPanel)
imagePanel = self._create_image_panel(demoPanel)
# position panels
dirPanel.grid(in_=demoPanel, sticky='ew', padx='1m', pady='1m', columnspan=2)
filePanel.grid(in_=demoPanel, sticky='nw', padx='1m', pady='1m', row=1, column=0)
imagePanel.grid(in_=demoPanel, sticky='nw', padx='1m', pady='1m', row=1, column=1)
demoPanel.columnconfigure(1, weight=1)
self.entry.focus_set()
def _create_dir_panel(self, parent):
lf = ttk.Labelframe(parent, text='Directory')
lf.pack(side=TOP, fill=BOTH, expand=Y)
self.curpath = os.path.join(os.getcwd(),'images') # current path
self.dirName = StringVar() # entry control variable
self.dirName.set(self.curpath)
self.entry = ttk.Entry(lf, width=30, textvariable=self.dirName)
self.entry.bind('<Return>', self._load_dir)
btn = ttk.Button(lf, text='Directory...',
command=self._select_dir)
self.entry.pack(side=LEFT, fill=BOTH, padx='2m', pady='2m', expand=Y)
btn.pack(side=LEFT, fill=Y, padx=(0, '2m'), pady='2m')
return lf
def _create_file_panel(self, parent):
lf = ttk.Labelframe(parent, text='Files')
lf.pack(side=TOP, fill=BOTH, expand=Y)
# there is no ttk.Listbox; however, ttk.Scrollbar
# works fine with the tkinter Listbox
self.files = Listbox(lf, width=20, height=10)
sbar = ttk.Scrollbar(lf, command=self.files.yview, orient=VERTICAL)
self.files.configure(yscrollcommand=sbar.set)
self._load_dir()
self.files.pack(side=LEFT, fill=Y, expand=Y)
sbar.pack(side=LEFT, fill=Y, expand=Y)
self.files.bind('<Double-1>', self._load_image)
return lf
def _create_image_panel(self, parent):
lf = ttk.Labelframe(parent, text='Image')
# image handle to prevent garbage collection
# of current display image
self.imh = None
self.labelImage = ttk.Label(lf, image=self.imh, relief=SUNKEN, border=2)
self.labelImage.pack(side=TOP, padx='.5m', pady='.5m')
return lf
def _select_dir(self):
dir = filedialog.askdirectory(initialdir=self.dirName.get(),
parent=self,
title='Select a new files image directory',
mustexist=True) # only existing dirs
if dir:
self.dirName.set(dir)
self._load_dir()
def _load_dir(self, *args):
dn = os.path.join(self.dirName.get(), '*')
self.files.delete(0, END)
# populate files list with filenames from
# selected directory
for f in iglob(dn):
if os.path.splitext(f)[1] in FILETYPES:
self.files.insert(END, basename(f))
# prevent <Return> event from propagating to
# windows higher up in the hierarchy
return 'break'
def _load_image(self, event):
item = self.files.curselection()
fn = self.files.get(item)
imagePath = os.path.join(self.dirName.get(), fn)
try:
im = Image.open(imagePath)
if im.mode == '1':
self.imh = ImageTk.BitmapImage(im)
else:
self.imh = ImageTk.PhotoImage(im)
self.labelImage.configure(image=self.imh)
except Exception:
# mark non-image selections
self.files.itemconfig(item, background='red',
selectbackground='red')
if __name__ == '__main__':
ImageViewerDemo().mainloop()
Wednesday, July 25, 2012
Tkinter Image Viewer Demo
This code is based on the Tcl image2.tcl. I've made a few minor changes to the original; used PIL for the image handling and added a filetype filter to limit the types of files that will appear in the Files listbox.
Labels:
Tkinter Demos,
Tkinter Misc Examples