Kjetil's Information Center: A Blog About My Projects

Python Image Viewer Improved

This is a new version of the Python Image Viewer posted earlier. It has now been updated to be Python 3 compatible and there is a couple of new features. The first feature is resizing of all images to the same resolution. The resolution is provided by an argument or will be 800x600 by default. The second feature is an extra button to browse to a random image.

The updated code:

#!/usr/bin/python3

from tkinter import *
from PIL import ImageTk, Image
import subprocess
import os
import random

class ImageView(Frame):
    def __init__(self, images, command, resolution, master=None):
        Frame.__init__(self, master)
        self.master.bind("<Key>", self._key_press)
        self.grid()
        self._create_widgets()
        self._images = images
        self._command = command
        self._resolution = tuple(map(int, resolution.split("x")))
        self._offset = 0
        self._browse(0) # Load first image!

    def _browse(self, offset):
        if offset == None:
            self._offset = random.randint(0, len(self._images))
        else:
            self._offset += offset

        if self._offset < 0:
            self._offset = 0
        elif self._offset >= len(self._images):
            self._offset = (len(self._images) - 1)

        self.master.title("%s (%d/%d)" % (os.path.basename(self._images[self._offset]), self._offset + 1, len(self._images)))

        self._image_data = ImageTk.PhotoImage(Image.open(self._images[self._offset]).resize(self._resolution))
        self._image = Button(image=self._image_data, bd=0, relief=FLAT, command=lambda: self._browse(1))
        self._image.grid(column=0, row=0, columnspan=8, sticky="news")

    def _exec(self):
        subprocess.call([self._command, self._images[self._offset]])

    def _create_widgets(self):
        self._random   = Button(text="Rnd", command=lambda: self._browse(None))
        self._prev_1   = Button(text="<",   command=lambda: self._browse(-1))
        self._prev_10  = Button(text="<<",  command=lambda: self._browse(-10))
        self._prev_100 = Button(text="<<<", command=lambda: self._browse(-100))
        self._next_1   = Button(text=">",   command=lambda: self._browse(1))
        self._next_10  = Button(text=">>",  command=lambda: self._browse(10))
        self._next_100 = Button(text=">>>", command=lambda: self._browse(100))
        self._go       = Button(text="Go!", command=self._exec)

        self._random.grid  (column=0, row=1, sticky="ew")
        self._prev_100.grid(column=1, row=1, sticky="ew")
        self._prev_10.grid (column=2, row=1, sticky="ew")
        self._prev_1.grid  (column=3, row=1, sticky="ew")
        self._go.grid      (column=4, row=1, sticky="ew")
        self._next_1.grid  (column=5, row=1, sticky="ew")
        self._next_10.grid (column=6, row=1, sticky="ew")
        self._next_100.grid(column=7, row=1, sticky="ew")
    
    def _key_press(self, e):
        if e.keysym == "Right":
            self._browse(1)
        elif e.keysym == "Left":
            self._browse(-1)
        elif e.keysym == "Up":
            self._browse(10)
        elif e.keysym == "Down":
            self._browse(-10)
        elif e.keysym == "Prior":
            self._browse(100)
        elif e.keysym == "Next":
            self._browse(-100)
        elif e.keysym == "Return":
            self._exec()
        elif e.keysym == "Escape":
            self.master.destroy()
        else:
            pass

if __name__ == "__main__":
    import sys

    if len(sys.argv) < 3:
        print("Usage: %s <image directory> <command> ['width'x'height']" % (sys.argv[0]))
        sys.exit(1)

    images = list()
    for root, dirs, files in os.walk(sys.argv[1]):
        for filename in files:
            if filename.endswith(".jpg"):
                images.append(os.path.join(root, filename))

    if len(images) == 0:
        print("No images found :-(")
        sys.exit(1)

    if len(sys.argv) > 3:
        resolution = sys.argv[3]
    else:
        resolution = "800x600" # Default

    iw = ImageView(sorted(images), sys.argv[2], resolution)
    iw.master.resizable(0, 0)
    iw.mainloop()

    sys.exit(0)
          


Topic: Scripts and Code, by Kjetil @ 12/12-2025, Article Link