#!/usr/bin/env python import PIL from PIL import Image from sys import exit import PySimpleGUI as sg import os import io import base64 """ Demo PNG Thumbnail Viewer Displays PNG files from a folder. OK, so... this isn't the best Demo Program, that's for sure. It's one of the older demos in the repo. There are likely better ones to use. The convert_to_bytes function is the best thing in this demo. Copyright 2021 PySimpleGUI.org """ thumbnails = {} ROWS = 8 COLUMNS = 8 sg.set_options(border_width=0) # Get the folder containing the images from the user folder = sg.popup_get_folder('Image folder to open') if folder is None: sg.popup_cancel('Cancelling') exit(0) def convert_to_bytes(file_or_bytes, resize=None): """ Will convert into bytes and optionally resize an image that is a file or a base64 bytes object. Turns into PNG format in the process so that can be displayed by tkinter :param file_or_bytes: either a string filename or a bytes base64 image object :type file_or_bytes: (Union[str, bytes]) :param resize: optional new size :type resize: (Tuple[int, int] or None) :param fill: If True then the image is filled/padded so that the image is not distorted :type fill: (bool) :return: (bytes) a byte-string object :rtype: (bytes) """ if isinstance(file_or_bytes, str): img = PIL.Image.open(file_or_bytes) else: try: img = PIL.Image.open(io.BytesIO(base64.b64decode(file_or_bytes))) except Exception as e: dataBytesIO = io.BytesIO(file_or_bytes) img = PIL.Image.open(dataBytesIO) cur_width, cur_height = img.size if resize: new_width, new_height = resize scale = min(new_height / cur_height, new_width / cur_width) img = img.resize((int(cur_width * scale), int(cur_height * scale)), PIL.Image.LANCZOS) with io.BytesIO() as bio: img.save(bio, format="PNG") del img return bio.getvalue() # # old, original PIL code. # def image_file_to_bytes(filename, size): # try: # image = Image.open(filename) # image.thumbnail(size, Image.LANCZOS) # bio = io.BytesIO() # a binary memory resident stream # image.save(bio, format='PNG') # save image as png to it # imgbytes = bio.getvalue() # except: # imgbytes = None # return imgbytes def set_image_to_blank(key): img = PIL.Image.new('RGB', (100, 100), (255, 255, 255)) img.thumbnail((1, 1), PIL.Image.LANCZOS) bio = io.BytesIO() img.save(bio, format='PNG') imgbytes = bio.getvalue() window[key].update(image_data=imgbytes) # get list of PNG files in folder png_files = [os.path.join(folder, f) for f in os.listdir(folder) if f.endswith('.png')] filenames_only = [f for f in os.listdir(folder) if f.endswith('.png')] if len(png_files) == 0: sg.popup('No PNG images in folder') exit(0) # define menu layout menu = [['&File', ['&Open Folder', 'E&xit']], ['&Help', ['&About', ]]] buttons = [] for display_index in range(ROWS): row = [] for j in range(COLUMNS): row.append(sg.Button('', border_width=0, button_color=sg.COLOR_SYSTEM_DEFAULT, key=(display_index, j))) buttons.append(row) col_buttons = [[]] # define layout, show and read the window col = [[sg.Text(png_files[0], size=(80, 3), key='filename')], [sg.Image(data=convert_to_bytes(png_files[0], (500, 500)), key='image')], ] layout = [ [sg.Menu(menu)], [sg.Col(buttons), sg.Col([[sg.Slider((len(png_files), 0), default_value=0, size=(38, 20), orientation='v', key='-slider-', change_submits=True)]]), sg.Col(col)] ] window = sg.Window('Image Browser', layout, return_keyboard_events=True, use_default_focus=False, finalize=True) # -------========= Event Loop =========-------- display_index = 0 while True: for x in range(ROWS): # update thumbnails for y in range(COLUMNS): cur_index = display_index + (x * COLUMNS) + y if cur_index < len(png_files): filename = png_files[cur_index] if filename not in thumbnails: imgbytes = convert_to_bytes(filename, (100, 100)) thumbnails[filename] = imgbytes else: imgbytes = thumbnails[filename] button_elem = window[(x, y)] button_elem.update(image_data=imgbytes) else: set_image_to_blank((x, y)) event, values = window.read() display_index = int(values['-slider-']) # --------------------- Button & Keyboard --------------------- if event in (sg.WIN_CLOSED, 'Exit'): break elif event in ('MouseWheel:Down', 'Down:40',) and display_index < len(png_files)-1: display_index += 4 elif event in ('MouseWheel:Up', 'Up:38',) and display_index > 0: display_index -= 4 elif event in ('Prior:33', 'Prev'): display_index -= 16 elif event in ('Next:34', 'Next'): display_index += 16 window['-slider-'].update(display_index) # ----------------- Menu choices ----------------- if event == 'Open Folder': newfolder = sg.popup_get_folder('New folder', no_window=True) if newfolder is None: continue folder = newfolder png_files = [os.path.join(folder, f) for f in os.listdir(folder) if '.png' in f] filenames_only = [f for f in os.listdir(folder) if '.png' in f] display_index = 0 thumbnail = {} for j in range(ROWS): for i in range(COLUMNS): set_image_to_blank((i, j)) elif event == 'About': sg.popup('Demo PNG Viewer Program', 'Please give PySimpleGUI a try!') elif type(event) is tuple: x, y = event image_index = display_index + (x * COLUMNS) + y if image_index < len(png_files): filename = png_files[image_index] imgbytes = convert_to_bytes(filename, (500, 500)) window['image'].update(data=imgbytes) window['filename'].update(filename) window.close()