Creating an Image Viewer with PySimpleGUI

PySimpleGUI makes creating applications easy. In this tutorial, you will learn how to use PySimpleGUI to create a simple Image Viewer. You will be using the regular version of PySimpleGUI, which wraps Tkinter, rather than its wxPython or PyQt variants.

Let's get started!

Getting Started

You need to install PySimpleGUI as it is not included with Python. You will also need Pillow because Tkinter only supports GIF and PGM/PPM image types.

Fortunately, you can install both of these packages easily with pip:

python3 -m pip install PySimpleGUI Pillow

Now that you have your dependencies installed, you can create a brand new application!

Creating an Image Viewer

PySimpleGUI lets you create a simple image viewer in less than 50 lines. To see how, create a new file and name it image_viewer.py. Then add this code to the file:

# image_viewer.py

import io
import os
import PySimpleGUI as sg
from PIL import Image


file_types = [("JPEG (*.jpg)", "*.jpg"),
              ("All files (*.*)", "*.*")]

def main():
    layout = [
        [sg.Image(key="-IMAGE-")],
        [
            sg.Text("Image File"),
            sg.Input(size=(25, 1), key="-FILE-"),
            sg.FileBrowse(file_types=file_types),
            sg.Button("Load Image"),
        ],
    ]

    window = sg.Window("Image Viewer", layout)

    while True:
        event, values = window.read()
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event == "Load Image":
            filename = values["-FILE-"]
            if os.path.exists(filename):
                image = Image.open(values["-FILE-"])
                image.thumbnail((400, 400))
                bio = io.BytesIO()
                image.save(bio, format="PNG")
                window["-IMAGE-"].update(data=bio.getvalue())

    window.close()


if __name__ == "__main__":
    main()

This is a decent chunk of code. Let's break it down into a couple of smaller pieces:

# image_viewer.py

import io
import os
import PySimpleGUI as sg
from PIL import Image


file_types = [("JPEG (*.jpg)", "*.jpg"),
              ("All files (*.*)", "*.*")]

This is your initial setup code. You import PySimpleGUI and the modules you need from PIL, and set file_types to the file selection choices for the Browse button in the form, which will default to JPEG.

Now you're ready to learn about the main() function:

def main():
    elements = [
        [sg.Image(key="-IMAGE-")],
        [
            sg.Text("Image File"),
            sg.Input(size=(25, 1), enable_events=True, key="-FILE-"),
            sg.FileBrowse(file_types=file_types),
        ],
    ]

    window = sg.Window("Image Viewer", elements)

This is your main() function. These 11 lines of code define how your Elements are laid out. PySimpleGUI uses Python lists to lay out the user interface. In this case, you are telling it that you want to create an Image widget at the top of your Window. Then you want to add three more widgets underneath it. These three widgets are lined up horizontally in the form from left-to-right. The reason they are lined up horizontally is because they are in a nested list.

These three widgets are as follows:

  • Text - A label Element
  • Input - A text entry Element
  • FileBrowse - A button that opens a file browser dialog

To enable events for an Element, you set the enable_events argument to True -- this will submit an Event whenever the Element changes. You disable the Input Element to make it read-only and prevent typing into it -- each keypress would be an individual Event, and your loop is not prepared for that. Any Element you need to access later should also be given a name, which is the key argument. These have to be unique.

The last piece of code to cover are these lines:

    while True:
        event, values = window.read()
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event == "Load Image":
            filename = values["-FILE-"]
            if os.path.exists(filename):
                image = Image.open(values["-FILE-"])
                image.thumbnail((400, 400))
                bio = io.BytesIO()
                image.save(bio, format="PNG")
                window["-IMAGE-"].update(data=bio.getvalue())

    window.close()


if __name__ == "__main__":
    main()

This is how you create the event loop in PySimpleGUI. You read() the window object for events and values. You check for the Exit event, which occurs when you close the application. You also check for the file event. This is the key name you defined earlier for the Input Element. When an event occurs with that Element, it will be added to the window using that Element's key or name.

This is where the meat of the program is. When the file event is fired, you will grab the image that was chosen by doing a lookup using the key on the values dictionary. Now you have the path to the image! You can open that image using Pillow, then resize it using thumbnail(). To display the image, you convert the file into an byte stream using io.BytesIO, which lets you save the image in memory. Then you pull the byte data from the in-memory file and pass that to the sg.Image object in the window.update() method at the end.

Finally you show the image by calling update() on the Image widget and passing in the PhotoImage object. You can do that by using the image key that is contained in the window object.

When you run this code, you will end up with something like this:

PySimpleGUI Image Viewer

That is how the Image Viewer looks when an image is not loaded. If you load an image, it will look like this:

PySimpleGUI Image Viewer with Photo

Doesn't that look nice? Give it a try by opening up a photo on your computer!

Creating an Image Browser

Your application makes you keep opening new images by browsing for a file one at a time. If would be nice if you could make the viewer load up a directory of images and cycle through them instead.

Take what you learned in the previous section and rewrite it in a new file named image_browser.py:

# image_browser.py

import glob
import PySimpleGUI as sg

from PIL import Image, ImageTk


def parse_folder(path):
    images = glob.glob(f'{path}/*.jpg') + glob.glob(f'{path}/*.png')
    return images

def load_image(path, window):
    try:
        image = Image.open(path)
        image.thumbnail((400, 400))
        photo_img = ImageTk.PhotoImage(image)
        window["image"].update(data=photo_img)
    except:
        print(f"Unable to open {path}!")
        

def main():
    elements = [
        [sg.Image(key="image")],
        [
            sg.Text("Image File"),
            sg.Input(size=(25, 1), enable_events=True, key="file"),
            sg.FolderBrowse(),
        ],
        [
            sg.Button("Prev"),
            sg.Button("Next")
        ]
    ]

    window = sg.Window("Image Viewer", elements, size=(475, 475))
    images = []
    location = 0

    while True:
        event, values = window.read()
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event == "file":
            images = parse_folder(values["file"])
            if images:
                load_image(images[0], window)
        if event == "Next" and images:
            if location == len(images) - 1:
                location = 0
            else:
                location += 1
            load_image(images[location], window)
        if event == "Prev" and images:
            if location == 0:
                location = len(images) - 1
            else:
                location -= 1
            load_image(images[location], window)

    window.close()


if __name__ == "__main__":
    main()

The images are loaded by using Python's glob module to search a folder for JPG and PNG files after a user selects a folder. Next, you check if the user is pressing "Next" or "Prev" and that images have been loaded. If they are, you check where in the list of paths you are and update it accordingly before loading the next image.

When you run this code, your application will look like this:

PySimpleGUI Image Browser

Now you can browse an entire folder easily with PySimpleGUI.

Wrapping Up

PySimpleGUI makes creating an image application very simple. You can write a pretty nice little image viewer with very few lines of code. With a little more polish, you could make this application more useful. Try adding a menu bar or a toolbar to make opening files / folders simpler. You could also make an application that supports opening both files AND folders. There are many other features you could add, but those are some simple ones to get your creative juices flowing. Have fun and happy coding!

Copyright © 2024 Mouse Vs Python | Powered by Pythonlibrary