PySimpleGUI/DemoPrograms/Demo_Desktop_Widget_Digital...

272 lines
13 KiB
Python
Raw Normal View History

import PySimpleGUI as sg
import datetime
import PIL
from PIL import Image
import random
import os
import io
import base64
"""
Another simple Desktop Widget using PySimpleGUI
This one shows images from a folder of your choosing.
You can change the new "Standard Desktop Widget Settings"
* Theme, location, alpha channel, refresh info,
Specific to this Widget are
* Image size
* How long to show the image and if you wnt this time to vary semi-randomly
2021-04-01 23:14:20 +00:00
* Folder containing your images
Copyright 2021, 2023 PySimpleGUI
"""
ALPHA = 0.9 # Initial alpha until user changes
refresh_font = sg.user_settings_get_entry('-refresh font-', 'Courier 8')
def make_square(im, fill_color=(0, 0, 0, 0)):
x, y = im.size
size = max(x, y)
new_im = Image.new('RGBA', (size, size), fill_color)
new_im.paste(im, (int((size - x) / 2), int((size - y) / 2)))
return new_im
def get_image_size(source):
if isinstance(source, str):
image = PIL.Image.open(source)
elif isinstance(source, bytes):
image = PIL.Image.open(io.BytesIO(base64.b64decode(source)))
else:
image = PIL.Image.open(io.BytesIO(source))
width, height = image.size
return (width, height)
def convert_to_bytes(source, size=(None, None), subsample=None, zoom=None, fill=False):
"""
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 source: either a string filename or a bytes base64 image object
:type source: (Union[str, bytes])
:param size: optional new size (width, height)
:type size: (Tuple[int, int] or None)
:param subsample: change the size by multiplying width and height by 1/subsample
:type subsample: (int)
:param zoom: change the size by multiplying width and height by zoom
:type zoom: (int)
:param fill: If True then the image is filled/padded so that the image is square
:type fill: (bool)
:return: (bytes) a byte-string object
:rtype: (bytes)
"""
# print(f'converting {source} {size}')
if isinstance(source, str):
image = PIL.Image.open(source)
elif isinstance(source, bytes):
image = PIL.Image.open(io.BytesIO(base64.b64decode(source)))
else:
image = PIL.Image.open(io.BytesIO(source))
width, height = image.size
scale = None
if size != (None, None):
new_width, new_height = size
scale = min(new_height/height, new_width/width)
elif subsample is not None:
scale = 1/subsample
elif zoom is not None:
scale = zoom
resized_image = image.resize((int(width * scale), int(height * scale)), Image.ANTIALIAS) if scale is not None else image
if fill and scale is not None:
resized_image = make_square(resized_image)
# encode a PNG formatted version of image into BASE64
with io.BytesIO() as bio:
resized_image.save(bio, format="PNG")
contents = bio.getvalue()
encoded = base64.b64encode(contents)
return encoded
def choose_theme(location):
layout = [[sg.Text(f'Current theme {sg.theme()}')],
[sg.Listbox(values=sg.theme_list(), size=(20, 20), key='-LIST-')],
[sg.OK(), sg.Cancel()]]
event, values = sg.Window('Look and Feel Browser', layout, location=location, keep_on_top=True).read(close=True)
if event == 'OK' and values['-LIST-']:
sg.theme(values['-LIST-'][0])
sg.user_settings_set_entry('-theme-', values['-LIST-'][0])
return values['-LIST-'][0]
2021-04-01 23:14:20 +00:00
else:
return None
def reset_settings():
sg.user_settings_set_entry('-time per image-', 60)
sg.user_settings_set_entry('-random time-', False)
sg.user_settings_set_entry('-image size-', (None, None))
sg.user_settings_set_entry('-image_folder-', None)
sg.user_settings_set_entry('-location-', (None, None))
sg.user_settings_set_entry('-single image-', None)
sg.user_settings_set_entry('-alpha-', ALPHA)
def make_window(location):
alpha = sg.user_settings_get_entry('-alpha-', ALPHA)
# ------------------- Window Layout -------------------
# If this is a test window (for choosing theme), then uses some extra Text Elements to display theme info
# and also enables events for the elements to make the window easy to close
2021-06-23 22:03:23 +00:00
right_click_menu = [[''], ['Choose Image Folder', 'Choose Single Image', 'Edit Me', 'Change Theme', 'Set Image Size',
'Set Time Per Image','Save Location', 'Refresh', 'Show Refresh Info', 'Hide Refresh Info', 'Alpha',
[str(x) for x in range(1, 11)], 'Exit', ]]
refresh_info = [[sg.T(size=(25, 1), font=refresh_font, k='-REFRESHED-', justification='c')],
[sg.T(size=(40,1), justification='c', font=refresh_font, k='-FOLDER-')],
[sg.T(size=(40,1), justification='c', font=refresh_font, k='-FILENAME-')]]
2021-04-01 23:14:20 +00:00
layout = [[sg.Image(k='-IMAGE-', enable_events=True)],
[sg.pin(sg.Column(refresh_info, key='-REFRESH INFO-', element_justification='c', visible=sg.user_settings_get_entry('-show refresh-', True)))]]
window = sg.Window('Photo Frame', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_justification='c', element_padding=(0, 0), alpha_channel=alpha, finalize=True, right_click_menu=right_click_menu, keep_on_top=True, enable_close_attempted_event=True, enable_window_config_events=True)
return window
def main():
loc = sg.user_settings_get_entry('-location-', (None, None))
sg.theme(sg.user_settings_get_entry('-theme-', None))
time_per_image = sg.user_settings_get_entry('-time per image-', 60)
vary_randomly = sg.user_settings_get_entry('-random time-', False)
width, height = sg.user_settings_get_entry('-image size-', (None, None))
image_folder = sg.user_settings_get_entry('-image_folder-', None)
try:
os.listdir(image_folder) # Try reading the folder to check to see if it is read
except:
image_folder = None
sg.user_settings_set_entry('-image_folder-', None)
image_name = single_image = sg.user_settings_get_entry('-single image-', None)
2021-06-23 22:03:23 +00:00
if image_folder is None and single_image is None:
image_name = single_image = sg.popup_get_file('Choose a starting image', keep_on_top=True)
if not single_image:
if sg.popup_yes_no('No folder entered','Go you want to exit the program entirely?', keep_on_top=True) == 'Yes':
exit()
if image_folder is not None and single_image is None:
2021-06-23 22:03:23 +00:00
images = os.listdir(image_folder)
images = [i for i in images if i.lower().endswith(('.png', '.jpg', '.gif'))]
image_name = os.path.join(image_folder, random.choice(images))
2021-06-23 22:03:23 +00:00
else: # means single image is not none
images = None
image_name = single_image
window = make_window(loc)
window_size = window.size
image_data = convert_to_bytes(image_name, (width, height))
while True: # Event Loop
# -------------- Start of normal event loop --------------
2021-06-23 22:03:23 +00:00
timeout = time_per_image * 1000 + (random.randint(int(-time_per_image * 500), int(time_per_image * 500)) if vary_randomly else 0) if single_image is None else None
event, values = window.read(timeout=timeout)
if event == sg.WIN_CLOSED:
break
elif event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'):
sg.user_settings_set_entry('-location-', window.current_location()) # The line of code to save the position before exiting
break
# First update the status information
# for debugging show the last update date time
if event == sg.TIMEOUT_EVENT:
if single_image is None:
image_name =random.choice(images)
image_data = convert_to_bytes(os.path.join(image_folder, image_name))
window['-FOLDER-'].update(image_folder)
else:
image_name = single_image
image_data = convert_to_bytes(single_image, (width, height))
window['-FILENAME-'].update(image_name)
window['-IMAGE-'].update(data=image_data)
window['-REFRESHED-'].update(datetime.datetime.now().strftime("%m/%d/%Y %I:%M:%S %p"))
if event == sg.WINDOW_CONFIG_EVENT:
new_size = window.size
if new_size != window_size:
print(f'resizing {new_size}')
(width, height) = new_size
image_data = convert_to_bytes(image_data, (width, height))
window['-IMAGE-'].update(data=image_data)
window.size = get_image_size(image_data)
window_size = window.size
if event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Choose Image Folder':
folder = sg.popup_get_folder('Choose location of your images', default_path=image_folder, location=window.current_location(), keep_on_top=True)
if folder is not None:
image_folder = folder
window['-FOLDER-'].update(image_folder)
sg.user_settings_set_entry('-image_folder-', image_folder)
images = os.listdir(image_folder)
images = [i for i in images if i.lower().endswith(('.png', '.jpg', '.gif'))]
2021-06-23 22:03:23 +00:00
sg.user_settings_set_entry('-single image-', None)
single_image = None
elif event == 'Set Time Per Image':
layout = [[sg.T('Enter number of seconds each image should be displayed')],
[sg.I(time_per_image, size=(5,1),k='-TIME PER IMAGE-')],
[sg.CB('Use some randomness', vary_randomly, k='-RANDOM TIME-')],
[sg.Ok(), sg.Cancel()]]
event, values = sg.Window('Display duration',layout, location=window.current_location(), keep_on_top=True, no_titlebar=True ).read(close=True)
if event == 'Ok':
try:
time_per_image = int(values['-TIME PER IMAGE-'])
vary_randomly = values['-RANDOM TIME-']
sg.user_settings_set_entry('-time per image-', time_per_image)
sg.user_settings_set_entry('-random time-', values['-RANDOM TIME-'])
except:
sg.popup_error('Bad number of seconds entered', location=window.current_location(), keep_on_top=True)
elif event == 'Set Image Size':
layout = [[sg.T('Enter size should be shown at in pixels (width, height)')],
[sg.I(width, size=(4,1),k='-W-'), sg.I(height, size=(4,1),k='-H-')],
[sg.Ok(), sg.Cancel()]]
event, values = sg.Window('Image Dimensions',layout, location=window.current_location(), keep_on_top=True, no_titlebar=True ).read(close=True)
if event == 'Ok':
try:
w,h = int(values['-W-']), int(values['-H-'])
sg.user_settings_set_entry('-image size-', (w,h))
width, height = w,h
except:
sg.popup_error('Bad size specified. Use integers only', location=window.current_location(), keep_on_top=True)
elif event == 'Show Refresh Info':
window['-REFRESH INFO-'].update(visible=True)
sg.user_settings_set_entry('-show refresh-', True)
elif event == 'Save Location':
sg.user_settings_set_entry('-location-', window.current_location())
elif event == 'Hide Refresh Info':
window['-REFRESH INFO-'].update(visible=False)
sg.user_settings_set_entry('-show refresh-', False)
elif event in [str(x) for x in range(1, 11)]:
window.set_alpha(int(event) / 10)
sg.user_settings_set_entry('-alpha-', int(event) / 10)
elif event == 'Change Theme':
loc = window.current_location()
if choose_theme(loc) is not None:
window.close()
window = make_window(loc)
2021-06-23 22:03:23 +00:00
elif event == 'Choose Single Image':
image_name = single_image = sg.popup_get_file('Choose single image to show', history=True)
2021-06-23 22:03:23 +00:00
sg.user_settings_set_entry('-single image-', single_image)
(width, height) = get_image_size(single_image)
sg.user_settings_set_entry('-image size-', (width, height))
image_data = convert_to_bytes(image_name, (width, height))
window['-IMAGE-'].update(data=image_data)
window.size = window_size = (width, height)
window.close()
if __name__ == '__main__':
# reset_settings() # if get corrupted problems, uncomment this
main()