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!
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!
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 ElementInput
- A text entry ElementFileBrowse
- A button that opens a file browser dialogTo 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:
That is how the Image Viewer looks when an image is not loaded. If you load an image, it will look like this:
Doesn't that look nice? Give it a try by opening up a photo on your computer!
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:
Now you can browse an entire folder easily with PySimpleGUI.
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