Merge pull request #4966 from PySimpleGUI/Dev-latest
psgresizer demo updated both in Demo Programs and in the new standalo…
This commit is contained in:
		
						commit
						1ed6e56c86
					
				
					 1 changed files with 152 additions and 51 deletions
				
			
		|  | @ -1,96 +1,197 @@ | |||
| import PySimpleGUI as sg | ||||
| from PIL import Image | ||||
| import PIL | ||||
| import os | ||||
| import base64 | ||||
| import io | ||||
| import webbrowser | ||||
| 
 | ||||
| """ | ||||
|     Demo Image Resize and Base64 Encode | ||||
| 
 | ||||
|     A quick little utility that will resize an image and also Base64 Encode it. | ||||
|     This demo has been released to PyPI as the commnand `psgresizer`.  It is also in in this repo: | ||||
|         https://github.com/PySimpleGUI/psgresizer | ||||
|     A quick little utility that will resize an image, convert between formats, and also Base64 Encode it. | ||||
| 
 | ||||
|     Base64 is particularly good to use to make icons or other images that you include in your sourcecode. | ||||
|      | ||||
| 
 | ||||
|     Use this Demo to help you code your PySimpleGUI programs.  Here's how: | ||||
|     1. Resize your image | ||||
|     2. Paste the base64 encoded byte-string into your code as a variable | ||||
|     1. Select PNG as the output format | ||||
|     2. Set "Encode to Base64" checkbox to True | ||||
|     2. Click resize button | ||||
|     2. Paste the base64 encoded byte-string left on the clipboard into your code as a variable | ||||
|     3. Use your variable for things like an icon, an image for buttons, etc. | ||||
| 
 | ||||
|     Copyright 2021 PySimpleGUI | ||||
| """ | ||||
| 
 | ||||
| def resize(input_file, output_file, size): | ||||
| version = '1.3.1' | ||||
| __version__ = version.split()[0] | ||||
| 
 | ||||
| ''' | ||||
| Change log | ||||
| 
 | ||||
|     1.3.1   16-Nov-2021 | ||||
|         Added correct readme to PyPI | ||||
|     1.3.0   16-Nov-2021 | ||||
|         Fixed bug - MUST always include the icon in the main function for these psg commands | ||||
|     1.2.0   16-Nov-2021 | ||||
|         Somewhat extensive reworking of the functionality | ||||
|         Added a drop-down list of formats to convert to | ||||
|         Automatically save all the settings | ||||
|         And more I'm sure! | ||||
| ''' | ||||
| 
 | ||||
| 
 | ||||
| def resize(input_file, size, output_file=None, encode_format='PNG'): | ||||
|     image = Image.open(input_file) | ||||
|     width, height = image.size | ||||
|     print(f"The original image size is {width} wide x {height} high") | ||||
|     new_width, new_height = size | ||||
|     scale = min(new_height / height, new_width / width) | ||||
|     resized_image = image.resize((int(width * scale), int(height * scale)), Image.ANTIALIAS) | ||||
|     # resized_image = image.resize(size) | ||||
|     width, height = resized_image.size | ||||
|     print(f"The resized image size is {width} wide x {height} high") | ||||
|     resized_image.save(output_file) | ||||
|     if new_width != width or new_height != height:  # if the requested size is different than original size | ||||
|         scale = min(new_height / height, new_width / width) | ||||
|         resized_image = image.resize((int(width * scale), int(height * scale)), Image.ANTIALIAS) | ||||
|     else: | ||||
|         resized_image = image | ||||
| 
 | ||||
| def convert_file_to_base64(filename): | ||||
|     try: | ||||
|         contents = open(filename, 'rb').read() | ||||
|     if output_file is not None: | ||||
|         resized_image.save(output_file) | ||||
| 
 | ||||
|     # encode a PNG formatted version of image into BASE64 | ||||
|     with io.BytesIO() as bio: | ||||
|         resized_image.save(bio, format=encode_format) | ||||
|         contents = bio.getvalue() | ||||
|         encoded = base64.b64encode(contents) | ||||
|         sg.clipboard_set(encoded) | ||||
|         if not sg.user_settings_get_entry('-autoclose-'): | ||||
|             sg.popup('Copied to your clipboard!', 'Keep program open until you have pasted the base64 bytestring', auto_close=True, auto_close_duration=4) | ||||
|     except Exception as error: | ||||
|         sg.popup_error('Cancelled - An error occurred', error) | ||||
|     return encoded | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|     layout = [  [sg.Text('Image Resizer')], | ||||
|                 [sg.Frame('Input Image', [[sg.Input(key='-IN-', enable_events=True), sg.FileBrowse()], | ||||
|                 [sg.T('Original size'), sg.T(k='-ORIG WIDTH-'), sg.T('X'), sg.T(k='-ORIG HEIGHT-')]])], | ||||
|                 [sg.Frame('New Size', [[sg.In(50, s=4, k='-WIDTH-'), sg.T('X'), sg.In(50, s=4, k='-HEIGHT-')]])], | ||||
|                 [sg.CBox('Encode to Base64 and leave on Clipboard', default=True,k='-BASE64-')], | ||||
|                 [sg.CBox('Autoclose Immediately When Done', default=sg.user_settings_get_entry('-autoclose-', True if sg.running_windows() else False),k='-AUTOCLOSE-')], | ||||
|                 [sg.Button('Resize', bind_return_key=True), sg.Button('Exit')], | ||||
|                 [sg.T('Note - on some systems, autoclose cannot be used\nbecause the clipboard is cleared by tkinter')],] | ||||
|     image_resize_icon = b'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' | ||||
| 
 | ||||
|     window = sg.Window('Resize Image', layout, icon=image_resize_icon, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_LOC_EXIT, enable_close_attempted_event=True) | ||||
|     def update_outfilename(): | ||||
|         infile = values['-IN-'] | ||||
|         if os.path.isfile(infile): | ||||
| 
 | ||||
|             image = Image.open(infile) | ||||
|             width, height = image.size | ||||
|             window['-ORIG WIDTH-'].update(image.size[0]) | ||||
|             if not values['-WIDTH-']: | ||||
|                 window['-WIDTH-'].update(image.size[0]) | ||||
|             if not values['-HEIGHT-']: | ||||
|                 window['-HEIGHT-'].update(image.size[1]) | ||||
|             window['-ORIG HEIGHT-'].update(image.size[1]) | ||||
| 
 | ||||
|             infilename = os.path.basename(infile) | ||||
|             infilenameonly, infileext = os.path.splitext(infilename) | ||||
|             if values['-NEW FORMAT-']: | ||||
|                 outfileext = values['-NEW FORMAT-'].lower() | ||||
|                 if outfileext == 'jpeg': | ||||
|                     outfileext = 'jpg' | ||||
|             else: | ||||
|                 outfileext = infileext[1:]  # strip off the . | ||||
|             outfile = f'{infilenameonly}{width}x{height}.{outfileext}' | ||||
|             outfullfilename = os.path.join(os.path.dirname(infile), outfile) | ||||
| 
 | ||||
|             if values['-DO NOT SAVE-']: | ||||
|                 window['-NEW FILENAME-'].update('') | ||||
|                 window['-BASE64-'].update(True) | ||||
|             else: | ||||
|                 window['-NEW FILENAME-'].update(outfullfilename) | ||||
|         else: | ||||
|             window['-NEW FILENAME-'].update('') | ||||
|             window['-ORIG WIDTH-'].update('') | ||||
|             # window['-WIDTH-'].update('') | ||||
|             window['-ORIG HEIGHT-'].update('') | ||||
|             # window['-HEIGHT-'].update('') | ||||
|             window['-NEW FILENAME-'].update() | ||||
| 
 | ||||
|     format_list = ('', 'PNG', 'JPEG', 'BMP', 'ICO', 'GIF', 'TIFF') | ||||
|     new_format_layout = [ | ||||
|         [sg.Combo(format_list, default_value=sg.user_settings_get_entry('-new format-', ''), readonly=True, enable_events=True, key='-NEW FORMAT-')]] | ||||
| 
 | ||||
|     layout = [[sg.Text('Image Resizer')], | ||||
|               [sg.Frame('Input Filename', [[sg.Input(key='-IN-', enable_events=True, s=80), sg.FileBrowse(), ], | ||||
|                                            [sg.T('Original size'), sg.T(k='-ORIG WIDTH-'), sg.T('X'), sg.T(k='-ORIG HEIGHT-')]])], | ||||
|               [sg.Frame('Output Filename', [[sg.In(k='-NEW FILENAME-', s=80), sg.FileBrowse(), ], | ||||
|                                             [sg.In(default_text=sg.user_settings_get_entry('-width-', ''), s=4, k='-WIDTH-'), sg.T('X'), | ||||
|                                              sg.In(default_text=sg.user_settings_get_entry('-height-', ''), s=4, k='-HEIGHT-')]])], | ||||
|               [sg.Frame('Convert To New Format', new_format_layout)], | ||||
|               [sg.CBox('Encode to Base64 and leave on Clipboard', k='-BASE64-', default=sg.user_settings_get_entry('-base64-', True))], | ||||
|               # [sg.CBox('Use PNG for all Base64 Encoding', default=True, k='-PNG CONVERT-')], | ||||
|               [sg.CBox('Do not save file - Only convert and Base64 Encode', k='-DO NOT SAVE-', enable_events=True, | ||||
|                        default=sg.user_settings_get_entry('-do not save-', False))], | ||||
|               [sg.CBox('Autoclose Immediately When Done', default=sg.user_settings_get_entry('-autoclose-', True if sg.running_windows() else False), | ||||
|                        k='-AUTOCLOSE-')], | ||||
|               [sg.Button('Resize', bind_return_key=True), sg.Button('Exit')], | ||||
|               [sg.T('Note - on some systems, autoclose cannot be used because the clipboard is cleared by tkinter')], | ||||
|               [sg.T('Your settings are automatically saved between runs')], | ||||
|               [sg.T(f'Version {version}'), sg.T('Go to psgresizer GitHub Repo', font='_ 8', enable_events=True, k='-PSGRESIZER-'), | ||||
|                sg.T('A PySimpleGUI Application - Go to PySimpleGUI home', font='_ 8', enable_events=True, k='-PYSIMPLEGUI-')], | ||||
|               ] | ||||
| 
 | ||||
|     window = sg.Window('Resize Image', layout, icon=image_resize_icon, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_LOC_EXIT, | ||||
|                        enable_close_attempted_event=True, finalize=True) | ||||
|     window['-PSGRESIZER-'].set_cursor('hand1') | ||||
|     window['-PYSIMPLEGUI-'].set_cursor('hand1') | ||||
|     while True: | ||||
|         event, values = window.read() | ||||
|         # print(event, values) | ||||
|         if event in (sg.WIN_CLOSED, sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'): | ||||
|             sg.user_settings_set_entry('-autoclose-', values['-AUTOCLOSE-']) | ||||
|             break | ||||
|         sg.user_settings_set_entry('-autoclose-', values['-AUTOCLOSE-']) | ||||
|         infile = values['-IN-'] | ||||
|         if event == '-IN-': | ||||
|             if os.path.isfile(infile): | ||||
|                 image = Image.open(infile) | ||||
|                 window['-ORIG WIDTH-'].update(image.size[0]) | ||||
|                 window['-ORIG HEIGHT-'].update(image.size[1]) | ||||
|         update_outfilename() | ||||
| 
 | ||||
|         if event == '-DO NOT SAVE-': | ||||
|             if values['-DO NOT SAVE-']: | ||||
|                 window['-NEW FILENAME-'].update('') | ||||
|                 window['-BASE64-'].update(True) | ||||
|         if event == 'Resize': | ||||
|             if os.path.isfile(infile): | ||||
|                 infilename = os.path.basename(infile) | ||||
|                 infilenameonly, infileext = os.path.splitext(infilename) | ||||
|                 try: | ||||
|             try: | ||||
|                 if os.path.isfile(infile): | ||||
|                     update_outfilename() | ||||
|                     infilename = os.path.basename(infile) | ||||
|                     infilenameonly, infileext = os.path.splitext(infilename) | ||||
|                     if values['-NEW FORMAT-']: | ||||
|                         encode_format = values['-NEW FORMAT-'].upper() | ||||
|                     else: | ||||
|                         encode_format = infileext[1:].upper()  # strip off the . | ||||
|                     if encode_format == 'JPG': | ||||
|                         encode_format = 'JPEG' | ||||
|                     outfullfilename = values['-NEW FILENAME-'] | ||||
|                     width, height = int(values['-WIDTH-']), int(values['-HEIGHT-']) | ||||
|                     outfile = f'{infilenameonly}{width}x{height}{infileext}' | ||||
|                     outfullfilename = os.path.join(os.path.dirname(infile), outfile) | ||||
|                     resize(input_file=infile, output_file=outfullfilename, size=(width, height)) | ||||
|                     if values['-DO NOT SAVE-']: | ||||
|                         encoded = resize(input_file=infile, size=(width, height), output_file=None, encode_format=encode_format) | ||||
|                     else: | ||||
|                         encoded = resize(input_file=infile, size=(width, height), output_file=outfullfilename, encode_format=encode_format) | ||||
| 
 | ||||
|                     if values['-BASE64-']: | ||||
|                         convert_file_to_base64(outfullfilename) | ||||
|                 except Exception as e: | ||||
|                     sg.popup_error_with_traceback('Error resizing or converting', 'Error encountered during the resize or Base64 encoding', e) | ||||
|                 sg.popup_quick_message('DONE!', font='_ 40', background_color='red', text_color='white') | ||||
|                 if sg.user_settings_get_entry('-autoclose-'): | ||||
|                     break | ||||
|                         sg.clipboard_set(encoded) | ||||
| 
 | ||||
|                     sg.popup_quick_message('DONE!', font='_ 40', background_color='red', text_color='white') | ||||
| 
 | ||||
|             except Exception as e: | ||||
|                 sg.popup_error_with_traceback('Error resizing or converting', 'Error encountered during the resize or Base64 encoding', e) | ||||
|             if values['-AUTOCLOSE-']: | ||||
|                 break | ||||
|         elif event == 'Version': | ||||
|             sg.popup_scrolled(sg.get_versions(), non_blocking=True) | ||||
|         elif event == 'Edit Me': | ||||
|             sg.execute_editor(__file__) | ||||
|         elif event == 'File Location': | ||||
|             sg.popup_scrolled('This Python file is:', __file__) | ||||
|         elif event == '-PYSIMPLEGUI-': | ||||
|             webbrowser.open_new_tab(r'http://www.PySimpleGUI.com') | ||||
|         elif event == '-PSGRESIZER-': | ||||
|             webbrowser.open_new_tab(r'https://github.com/PySimpleGUI/psgresizer') | ||||
| 
 | ||||
|     if event != sg.WIN_CLOSED: | ||||
|         sg.user_settings_set_entry('-autoclose-', values['-AUTOCLOSE-']) | ||||
|         sg.user_settings_set_entry('-new format-', values['-NEW FORMAT-']) | ||||
|         sg.user_settings_set_entry('-do not save-', values['-DO NOT SAVE-']) | ||||
|         sg.user_settings_set_entry('-base64-', values['-BASE64-']) | ||||
|         sg.user_settings_set_entry('-width-', values['-WIDTH-']) | ||||
|         sg.user_settings_set_entry('-height-', values['-HEIGHT-']) | ||||
|     window.close() | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     image_resize_icon = b'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' | ||||
| 
 | ||||
|     main() | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue