diff --git a/Demo_All_Widgets.py b/Demo_All_Widgets.py index 7a4d5a6e..51d70237 100644 --- a/Demo_All_Widgets.py +++ b/Demo_All_Widgets.py @@ -33,7 +33,7 @@ layout = [ [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))], [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), sg.Frame('Labelled Group',[[ - sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25, tick_interval=25), sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), sg.Column(column1, background_color='lightblue')]])], diff --git a/Demo_Buttons_Mac.py b/Demo_Buttons_Mac.py new file mode 100644 index 00000000..b605923b --- /dev/null +++ b/Demo_Buttons_Mac.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +import sys +import time +if sys.version_info[0] >= 3: + import PySimpleGUI as sg +else: + import PySimpleGUI27 as sg + +def show_win(): + sg.SetOptions(border_width=0, margins=(0,0), element_padding=(5,3)) + + + frame_layout = [ [sg.Button('', image_data=mac_red, button_color=('white', sg.COLOR_SYSTEM_DEFAULT), key='_exit_'), + sg.Button('', image_data=mac_orange, button_color=('white', sg.COLOR_SYSTEM_DEFAULT)), + sg.Button('', image_data=mac_green, button_color=('white', sg.COLOR_SYSTEM_DEFAULT), key='_minimize_'), + sg.Text(' '*40)],] + + layout = [[sg.Frame('',frame_layout)], + [sg.T('')], + [ sg.Text(' My Mac-alike window', size=(25,2)) ],] + + window = sg.Window('My new window', + no_titlebar=True, + grab_anywhere=True, + alpha_channel=0, + ).Layout(layout).Finalize() + + for i in range(100): + window.SetAlpha(i/100) + time.sleep(.01) + + while True: # Event Loop + event, values = window.Read() + if event is None or event == '_exit_': + break + if event == '_minimize_': + # window.Minimize() # cannot minimize a window with no titlebar + pass + print(event, values) + + + +mac_red = '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' + +mac_green = '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' + +mac_orange = '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' + + +show_win() \ No newline at end of file diff --git a/Demo_Buttons_Nice_Graphics.py b/Demo_Buttons_Nice_Graphics.py new file mode 100644 index 00000000..3242239f --- /dev/null +++ b/Demo_Buttons_Nice_Graphics.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +import sys +if sys.version_info[0] >= 3: + import PySimpleGUI as sg +else: + import PySimpleGUI27 as sg +import io +from PIL import Image +import base64 + + +def image_file_to_bytes(image64, size): + image_file = io.BytesIO(base64.b64decode(image64)) + img = Image.open(image_file) + img.thumbnail(size, Image.ANTIALIAS) + bio = io.BytesIO() + img.save(bio, format='PNG') + imgbytes = bio.getvalue() + return imgbytes + + +def ShowMeTheButtons(): + bcolor = ('black', 'black') + wcolor = ('white', 'black') + + sg.ChangeLookAndFeel('Black') + sg.SetOptions(auto_size_buttons=True, border_width=0, button_color=sg.COLOR_SYSTEM_DEFAULT) + + toolbar_buttons = [ [sg.Text('Who says Windows have to be ugly when using tkinter?', size=(45,3))], + [sg.Text('All of these buttons are part of the code itself', size=(45,2))], + + [sg.Button('Next', image_data=image_file_to_bytes(button64, (100,50)),button_color=wcolor, font='Any 15', pad=(0,0), key='_close_'), + # [sg.Button('Exit', image_data=image_file_to_bytes(black64, (100,50)),button_color=bcolor, font='Any 15', pad=(0,0), key='_close_'),], + sg.Button('Submit', image_data=image_file_to_bytes(red_pill64, (100,50)),button_color=wcolor, font='Any 15', pad=(0,0), key='_close_'), + sg.Button('OK', image_data=image_file_to_bytes(green_pill64, (100,50)),button_color=bcolor, font='Any 15', pad=(0,0), key='_close_'), + sg.Button('Exit', image_data=image_file_to_bytes(orange64, (100,50)),button_color=bcolor, font='Any 15', pad=(0,0), key='_close_'),], + ] + + # layout = toolbar_buttons + layout = [[sg.Frame('Nice Buttons', toolbar_buttons, font=('any 18'), background_color='black')]] + + window = sg.Window('Demo of Nice Looking Buttons', + no_titlebar=False, + grab_anywhere=True, + keep_on_top=True, + use_default_focus=False, + font='any 15', + background_color='black').Layout(layout).Finalize() + + # ---===--- Loop taking in user input --- # + while True: + button, value = window.Read() + print(button) + if button == '_close_' or button is None: + break # exit button clicked + +if __name__ == '__main__': + + # To convert your PNG into Base 64: + # Go to https://www.base64-image.de/ + # Drag and drop your PNG image onto the webpage + # Choose "Copy image" + # Create a string variable name to hold your image + # Paste data from webpage as a string + # Delete the "header" stuff - up to the data portion (data:image/png;base64,) + orange64 = 'iVBORw0KGgoAAAANSUhEUgAAAiIAAADLCAMAAABkvgh7AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAANtvJ99sId5tIt5uI91tJNxuJNxuJttvKN90NN91Ntt3PN52ONx3Otx3PNt4Pdp4Ptx4PepfD+pfEOtgD+piD+tkD+ttD+dnFOdoE+doFOVoFuNqG+JrHOFrHuFsHuRpGORpGuRqGORqGupgEOpiEOpkEOlmEOhnEupoEOpoEupqFettEetsF+tuFOxqFetrGOtsGetvHOxrGOxsGOxtGuxuG+xuHexxFutwH+xxGOxyGOxyGuxxHuBsIOJvJ+NvKORvKONwKeNwKuJxLONyLeJyLuRwKeRwKuRxLOtxIexyIexzJOx0JOx1Ju11KOx2KOx2Kux5Iux4Kux5Le1/LeFyMOFzMuB0NOB1Nux7MOx8MOx8Mu19NO1+NO1+Ntd7RdV8RtN+S9J/TdF/TtV9SNR9Stl5QNl6Qdh6Qth7RK+Tfa+Tfq6Ufr+JZb+JZr+KZ7uMbr6KaL6LaryMbLyNb7yOb7WPdLuNcLqOcLmOcrmPdLOQd7SQdrKReLKRerGSerCTfLCTfrmQde2AN+2AOO2BOu2COu6DPO6EPe6EPsWDV8OFXcOFXsOGXcOGXsaDWMSEWsqCVs+AUM2AUs6CU82CVMqDWMmEWtCAT9CAUMKHYMCIYsCIZMCKZu6FQO6GQe6GQu6HRO6IRe6JRu6KR+6KSO+LSu+MS++MTO+OTe+OTu+OUO+RUu+TVfCNTvCOTvCPUPCQUfCQUvCSU/CRVPCSVPCSVvCUV/CUWPCVWvGWWvGXXPGYXvCWYPGaYfGcYvGcZPKdZvKeZ/GdavKeaPKebfKebvKgavKhbPKibfGibvKjcPGkcfKmdPKiePOod/OpePSpePSqevSrfPSsfPSsfvSqg/SugfWwgvWwhPWyhfWyhvWxi/Wyj/W0ifW2jfa4j/a1kPW2kva2lfW2lvW4kPa6kfa7kva5lvW7lPa8lPW8lve+l/a4mva5nPa6nPa7nva+mPa+mva/nPa8oPa9ovbAm/bAnfXAnvbCofbEo/XGp/bGqPbIqQAAAC/NnaUAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjFjKpxLAAAJXElEQVR4Xu3de3zVdR3H8Xb9/sJh60LlOTtubdIZEyckSdedMzkdZyRGIePmZdqki8s0KllJmtGCTAXRlUAyrireUeZga2JRkooYRWR0JbXMLsIqZ5fT7/L5nRvj9/md0/fhw/Px/fyHP+D33fb4vPj+LvsxXpMA8IREgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgJF7Ih1Xf3bVsuu/AYXu+mUrL+u8iqbqIcdErvhme0V8SjQCIpwWe+0nli+k2R5NTolc0jYzqpQBcigV+UDltZ57SQ6JfLqyRRXRyiCIis7u7qAhj8B3Ip1tLdg/pFLR0Z+hOR/JbyIrWxGIZCp289E2Ep+J3HzEFqLKy0qhUJUfeUkZnf8lGnYWX4l0tDfTOg5VEqoJNNTXh6EgmaMLBKuaymmcJFLRSfPO5CuRtgitYisPBehDQUFrqC3O2EtUxRdo4Bn8JHJhlNawlNZh85Aj2JQeiRp9JY08nY9Euk+nFUwq1ECLgww1ZTRaS+R8mnk6PpEF8dTDkNIgLQxiHFdNw7U0d9PU07CJdLw+tRUVpy5Cxo19+bz9lYI+n5cNfdhcjWGZf2jsCTTJcDiUmnBR/HKaewqbSHfqQqSJrkLGvuuctZu3jOgBD1u99B7Fg6a+Vwrrk8kNfRX+0XE2+qg527adsc38M723d01yK3lbqhH1SZp7CpfIolnJw4udQk44ddPOn/3q6b+M6K8+/c2vF+jXVw29X/ALI7N+Z3jome+f/QZ7pOG6VCMtl9Dkk7hElifvd0uPs1cbs/SJ5/4+9OLw8ItQ2IaH//vvJ6Y7O0mIhmxuI/Np8klMIhdXuH0p50r1zVue+8fQ0GEQYeg/w32TrLHWF9OYDSO2gGbvYhJZfRodaFTZhYzpe4lWBxmGfzBnnDnYQPJRa+RjNHsXk8hF7iZSYl+IvPGefx0+BJIc/v2OudZoq2jQhhr1FRo+8U7kU6MoEVVjLROedujQn0GYfd99nznahlJn0oYxJetM453IpTE6rNgu5E0/fOlPIMzzf9zVY51q6mjURiTr8Zl3Ite5D0Vq7UQ+8jwtC5Ls7W00h9vgXo2oNpo+8U7kBjrPlDnfmdnwzz+APAf632NN93hn1oYaTdMn3om0USLVdiFje59+9hmQ5tmDg3YiQfe6c1bm287eiVTSUc555i0DB2lVkIQSSZ5pZl5B43d4J3KMk4hyvn3XOHjwtyDPwR3vtefrPj6LZ7595p3IaCeRMue7M+MHfvcbkOfXg04i7lP4WB6JlNgrhBsHzOVAGjMR68FI6rY3n0Sa7BXMRA7QqiDJgQEnkRp72Pkl4tzQhBv7f0mLgiS/oF0kSK8X5pPI8fYK4cbtPz8A8jxFu4h71/v/7CLb9j8F8uzXmEjffpBIYyIP7qM1QZT+99vz1ZFI7959IFC/vl1kKxIRabu+XQSJiPTTbdp2kfH3//gnIM/ePn2JbNmzF+R5sm+qPV8tiex+EuTZ06svkTt27wF5dm/VmMjju0GgB/Qlsvmxx0GgO7Xd9I6/7VFaE0S5T2Miux57FOTRmMitj/wI5Nl1j75ENu18BAS6W18iGx/eCQLdqi+RDTu+9zDIs2myPV8diawf3AECbdSXyDokItIGfYmsHXxoEORZry2RiT0DtCaIsk5jIv0DIFDPO+z5aklkez8ItGaiPV8diazpox//C6Is1beL3NJLP0IaRFmibRc5aemdd4E8W7t0JgISaUxkyZb7QaDF2hI58ev33gcC6UtkQtdmkOi8k+z56thFum4Dic450Z6vjkQWbwKJ5ulL5KvrN4JAGhM5d916EGiOxkR61vaAOGtnaEzkljUg0Aznf4HQkci8JUtBoOn6EpnbBRJ9WF8ic74GEk3Tl8iMxSDRh6z/+ExTIueBRFM17iLngkRT9e0iH50HEulMZC5INFlfItNngETOP6PRkUjDWbQkyOK8UaQlkWnTQSKdiZwFEiERYGhM5MxpIJHz6qqWRKaeCRIhEWA4bxRpSeSDIBISAYbGRN5NS4IsOhMBkTQmMhlEQiLAQCLAcF4605LIO08FifQlMm4iiKQxkQm0JMiicxeZAALpTOSUSSCRxkROPuVkEEhnIiCS8wI8EoGj0pdIuAFEQiLAoPHqSGQciETj1ZEIiIZEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgIFEgBG0h51fIk20BohWaw87v0RKaA0Qrcoedn6JlLs/xwYkq7aHnV8iKkiLgGQl9rANI55LIq9zEjGqaBEQLEDDNuJfpPE7vBP5OB1VTKuAYO7VatHMRTR+h3ci7ZSICtAyIFeTM2tDzbqYxu/wTmRFhA4L0TIgVvI8oypp+sQ7kVVROqwM9zTShWjUhrqApk+8E+mM02G4YJUuUE6TNiLX0PSJdyLuXa9hlONqRDb3oYhhxBbS8AmTyA3uxQi+TyNbrbsXGOpYmr2LSWRBjA40VB0tBgIFymjM5nlmOc3exSSSqEzVhUesYtW7D1atpyKZD874RFaeToealyNoRKgG95GIKft+hk8kcWxyGzHK0IhI9WmFFMUvp8EnsYmsbqGDTeW1tCgIEiim8Voi36a5p7CJJNqTNzXmLhSqp3VBiprUlao54FFfprGn8Iksmp061RhGKTYSUQLV6dM1Yt+hqafhE0lcFi+iBSyqCVckYrw1lHymamu+iWaezkciiWvSLkdMqqQqgPNN4QvUNmXsIOaFyEU08Qx+Ekl0t6TvIyZVWl1VEwwGA1CIgsGaulBJ5gZiirR10MAz+Eok8a1YVm8WBQUr66+8rbl9xEJ8JpJY1TpCIyCIOmMFzTqbz0QSnfObEYlgkdmradJH8JuIebJpjSASoVTswsz3VdP5TySxaEVrFJHIoyLxtktpxiPJIREzkmWV8ah5sUNrQ6ErUioypeLGrHeIsuSUiKnzuguOmRWPTYHCF4u3VrR1Lxj5PiYl10QsV1298HOfh4K3sPNKmqinfBKBVxUkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAgwkAp4Sif8BKbOKvRIFiXEAAAAASUVORK5CYII=' + + green_pill64 = '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' + + red_pill64 = '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' + + button64 = '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' + + ShowMeTheButtons() + diff --git a/Demo_Change_Submits_InputText.py b/Demo_Change_Submits_InputText.py index 079495ea..2bb513df 100644 --- a/Demo_Change_Submits_InputText.py +++ b/Demo_Change_Submits_InputText.py @@ -31,7 +31,7 @@ layout = [[ sg.Text('Test of reading input field') ], key='_in5'), sg.FileBrowse()], [sg.T('This input clears after submit'),sg.In(change_submits=True, key='_in6_'), sg.FileBrowse()], - [ sg.RButton('Read')]] + [ sg.Button('Read')]] window = sg.Window('Demonstration of InputText with change_submits', auto_size_text=False, diff --git a/Demo_Chatterbot.py b/Demo_Chatterbot.py index 4bd8d355..6a5c1964 100644 --- a/Demo_Chatterbot.py +++ b/Demo_Chatterbot.py @@ -19,7 +19,7 @@ to collect user input that is sent to the chatbot. The reply is displayed in th # Create the 'Trainer GUI' # The Trainer GUI consists of a lot of progress bars stacked on top of each other sg.ChangeLookAndFeel('GreenTan') -sg.DebugWin() +# sg.DebugWin() MAX_PROG_BARS = 20 # number of training sessions bars = [] texts = [] @@ -39,8 +39,8 @@ def print_progress_bar(description, iteration_counter, total_items, progress_bar global texts global training_window # update the window and the bars - button, values = training_window.ReadNonBlocking() - if button is None and values is None: # if user closed the window on us, exit + button, values = training_window.Read(timeout=0) + if button is None: # if user closed the window on us, exit sys.exit(69) if bars[current_bar].UpdateBar(iteration_counter, max=total_items) is False: sys.exit(69) @@ -60,7 +60,7 @@ chatbot.train("chatterbot.corpus.english") layout = [[sg.Output(size=(80, 20))], [sg.Multiline(size=(70, 5), enter_submits=True), - sg.ReadButton('SEND', bind_return_key=True), sg.ReadButton('EXIT')]] + sg.Button('SEND', bind_return_key=True), sg.Button('EXIT')]] window = sg.Window('Chat Window', auto_size_text=True, default_element_size=(30, 2)).Layout(layout) diff --git a/Demo_Color.py b/Demo_Color.py index 11cd4c6b..ead1245f 100644 --- a/Demo_Color.py +++ b/Demo_Color.py @@ -1649,8 +1649,8 @@ def show_all_colors_on_buttons(): row_len = 20 for i, c in enumerate(colors): hex = get_hex_from_name(c) - button1 = sg.Button(button_text=c, button_color=(get_complementary_hex(hex), hex), size=(8, 1)) - button2 = sg.Button(button_text=c, button_color=(hex, get_complementary_hex(hex)), size=(8, 1)) + button1 = sg.CButton(button_text=c, button_color=(get_complementary_hex(hex), hex), size=(8, 1)) + button2 = sg.CButton(button_text=c, button_color=(hex, get_complementary_hex(hex)), size=(8, 1)) row.append(button1) row.append(button2) if (i+1) % row_len == 0: @@ -1717,8 +1717,8 @@ def main(): complementary_color = get_name_from_hex(complementary_hex) layout = [[sg.Text('That color and it\'s compliment are shown on these buttons. This form auto-closes')], - [sg.Button(button_text=color_name, button_color=(color_hex, complementary_hex))], - [sg.Button(button_text=complementary_hex + ' ' + complementary_color, button_color=(complementary_hex , color_hex), size=(30, 1))], + [sg.CloseButton(button_text=color_name, button_color=(color_hex, complementary_hex))], + [sg.CloseButton(button_text=complementary_hex + ' ' + complementary_color, button_color=(complementary_hex , color_hex), size=(30, 1))], ] sg.Window('Color demo', default_element_size=(100, 1), auto_size_text=True, auto_close=True, auto_close_duration=5, icon=MY_WINDOW_ICON).Layout(layout).Read() diff --git a/Demo_Color_Names.py b/Demo_Color_Names.py index e310d427..a81aaa36 100644 --- a/Demo_Color_Names.py +++ b/Demo_Color_Names.py @@ -686,7 +686,7 @@ layout = [[sg.Text('Hover mouse to see RGB value, click for white & black text', # -- Create primary color viewer window by building rows and appending to layout -- row = [] for i, color in enumerate(color_map): - row.append(sg.RButton(color, button_color=('black', color), key=color, tooltip=color_map[color])) + row.append(sg.Button(color, button_color=('black', color), key=color, tooltip=color_map[color])) if (i+1) % 15 == 0: # every 15 buttons make a new row layout.append(row) row = [] @@ -700,4 +700,4 @@ while True: break # -- Create a secondary window that shows white and black text on chosen color layout2 =[[sg.DummyButton(event, button_color=('white', event), tooltip=color_map[event]), sg.DummyButton(event, button_color=('black', event), tooltip=color_map[event])] ] - sg.Window('Buttons with white and black text', keep_on_top=True).Layout(layout2).ReadNonBlocking() \ No newline at end of file + sg.Window('Buttons with white and black text', keep_on_top=True).Layout(layout2).Read(timeout=0) \ No newline at end of file diff --git a/Demo_Color_Names_Smaller_List.py b/Demo_Color_Names_Smaller_List.py index 1cfaa34a..c4c2e69b 100644 --- a/Demo_Color_Names_Smaller_List.py +++ b/Demo_Color_Names_Smaller_List.py @@ -96,7 +96,7 @@ layout = [[sg.Text('Click on a color square to see both white and black text on row = [] # -- Create primary color viewer window -- for i, color in enumerate(COLORS): - row.append(sg.RButton(color, button_color=('black', color), key=color)) + row.append(sg.Button(color, button_color=('black', color), key=color)) if (i+1) % 12 == 0: layout.append(row) row = [] @@ -110,4 +110,4 @@ while True: break # -- Create a secondary window that shows white and black text on chosen color layout2 =[[sg.DummyButton(event, button_color=('white', event)), sg.DummyButton(event, button_color=('black', event))]] - sg.Window('Buttons with white and black text', keep_on_top=True).Layout(layout2).ReadNonBlocking() \ No newline at end of file + sg.Window('Buttons with white and black text', keep_on_top=True).Layout(layout2).Read(timeout=0) \ No newline at end of file diff --git a/Demo_Cookbook_Browser.py b/Demo_Cookbook_Browser.py new file mode 100644 index 00000000..fe141133 --- /dev/null +++ b/Demo_Cookbook_Browser.py @@ -0,0 +1,793 @@ + + +# import PySimpleGUI as sg +import inspect + +def SimpleDataEntry(): + """Simple Data Entry - Return Values As List + Same GUI screen except the return values are in a list instead of a dictionary and doesn't have initial values. + """ + import PySimpleGUI as sg + # Very basic window. Return values as a list + window = sg.Window('Simple data entry form') # begin with a blank form + + layout = [ + [sg.Text('Please enter your Name, Address, Phone')], + [sg.Text('Name', size=(15, 1)), sg.InputText()], + [sg.Text('Address', size=(15, 1)), sg.InputText()], + [sg.Text('Phone', size=(15, 1)), sg.InputText()], + [sg.Submit(), sg.Cancel()] + ] + + button, values = window.Layout(layout).Read() + + print(button, values[0], values[1], values[2]) + +def SimpleReturnAsDict(): + """ + Simple data entry - Return Values As Dictionary + A simple form with default values. Results returned in a dictionary. Does not use a context manager + """ + import PySimpleGUI as sg + + # Very basic window. Return values as a dictionary + window = sg.Window('Simple data entry form') # begin with a blank form + + layout = [ + [sg.Text('Please enter your Name, Address, Phone')], + [sg.Text('Name', size=(15, 1)), sg.InputText('name', key='name')], + [sg.Text('Address', size=(15, 1)), sg.InputText('address', key='address')], + [sg.Text('Phone', size=(15, 1)), sg.InputText('phone', key='phone')], + [sg.Submit(), sg.Cancel()] + ] + + button, values = window.Layout(layout).Read() + + print(button, values['name'], values['address'], values['phone']) + +def FileBrowse(): + """ + Simple File Browse + Browse for a filename that is populated into the input field. + """ + import PySimpleGUI as sg + + with sg.Window('SHA-1 & 256 Hash') as form: + form_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')], + [sg.InputText(), sg.FileBrowse()], + [sg.Submit(), sg.Cancel()]] + (button, (source_filename,)) = window.LayoutAndRead(form_rows) + + print(button, source_filename) + +def GUIAddOn(): + """ + Add GUI to Front-End of Script + Quickly add a GUI allowing the user to browse for a filename if a filename is not supplied on the command line using this 1-line GUI. It's the best of both worlds. + """ + import PySimpleGUI as sg + import sys + + if len(sys.argv) == 1: + button, (fname,) = sg.Window('My Script').Layout([[sg.T('Document to open')], + [sg.In(), sg.FileBrowse()], + [sg.Open(), sg.Cancel()]]).Read() + else: + fname = sys.argv[1] + + if not fname: + sg.Popup("Cancel", "No filename supplied") + # raise SystemExit("Cancelling: no filename supplied") + +def Compare2Files(): + """ + Compare 2 Files + Browse to get 2 file names that can be then compared. Uses a context manager + """ + import PySimpleGUI as sg + + with sg.Window('File Compare') as form: + form_rows = [[sg.Text('Enter 2 files to comare')], + [sg.Text('File 1', size=(8, 1)), sg.InputText(), sg.FileBrowse()], + [sg.Text('File 2', size=(8, 1)), sg.InputText(), sg.FileBrowse()], + [sg.Submit(), sg.Cancel()]] + + button, values = window.Layout(form_rows).Read() + + print(button, values) + +def AllWidgetsWithContext(): + """ + Nearly All Widgets with Green Color Theme with Context Manager + Example of nearly all of the widgets in a single window. Uses a customized color scheme. This recipe uses a context manager, the preferred method. + """ + import PySimpleGUI as sg + # Green & tan color scheme + sg.ChangeLookAndFeel('GreenTan') + + + # sg.ChangeLookAndFeel('GreenTan') + + with sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False) as form: + + column1 = [[sg.Text('Column 1', background_color='#F7F3EC', justification='center', size=(10, 1))], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]] + + layout = [ + [sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))], + [sg.Text('Here is some text.... and a place to enter text')], + [sg.InputText('This is my text')], + [sg.Checkbox('Checkbox'), sg.Checkbox('My second checkbox!', default=True)], + [sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], + [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)), + sg.Multiline(default_text='A second multi-line', size=(35, 3))], + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)), + sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], + [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), + sg.Column(column1, background_color='#F7F3EC')], + [sg.Text('_' * 80)], + [sg.Text('Choose A Folder', size=(35, 1))], + [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), + sg.InputText('Default Folder'), sg.FolderBrowse()], + [sg.Submit(), sg.Cancel()] + ] + + button, values = window.LayoutAndRead(layout) + +def AllWidgetsNoContext(): + """ + All Widgets No Context Manager + """ + import PySimpleGUI as sg + + sg.ChangeLookAndFeel('GreenTan') + + window = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False) + + column1 = [[sg.Text('Column 1', background_color='#F7F3EC', justification='center', size=(10, 1))], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]] + + layout = [ + [sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))], + [sg.Text('Here is some text.... and a place to enter text')], + [sg.InputText('This is my text')], + [sg.Checkbox('Checkbox'), sg.Checkbox('My second checkbox!', default=True)], + [sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], + [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)), + sg.Multiline(default_text='A second multi-line', size=(35, 3))], + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)), + sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], + [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), + sg.Column(column1, background_color='#F7F3EC')], + [sg.Text('_' * 80)], + [sg.Text('Choose A Folder', size=(35, 1))], + [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), + sg.InputText('Default Folder'), sg.FolderBrowse()], + [sg.Submit(), sg.Cancel()] + ] + + button, values = window.LayoutAndRead(layout) + +def NonBlockingWithUpdates(): + """ + Non-Blocking Form With Periodic Update + An async form that has a button read loop. A Text Element is updated periodically with a running timer. There is no context manager for this recipe because the loop that reads the form is likely to be some distance away from where the form was initialized. + """ + import PySimpleGUI as sg + import time + + window = sg.Window('Running Timer') + # create a text element that will be updated periodically + + form_rows = [[sg.Text('Stopwatch', size=(20,2), justification='center')], + [ sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center', key='output')], + [sg.T(' ' * 5), sg.ReadButton('Start/Stop', focus=True), sg.Quit()]] + + window.LayoutAndRead(form_rows, non_blocking=True) + + timer_running = True + i = 0 + # loop to process user clicks + while True: + i += 1 * (timer_running is True) + button, values = window.ReadNonBlocking() + if values is None or button == 'Quit': # if user closed the window using X or clicked Quit button + break + elif button == 'Start/Stop': + timer_running = not timer_running + window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) + + time.sleep(.01) + # if the loop finished then need to close the form for the user + window.CloseNonBlocking() + +def NonBlockingWithContext(): + """ + Async Form (Non-Blocking) with Context Manager + Like the previous recipe, this form is an async window. The difference is that this form uses a context manager. + """ + import PySimpleGUI as sg + import time + + with sg.Window('Running Timer') as form: + layout = [[sg.Text('Non blocking GUI with updates', justification='center')], + [sg.Text('', size=(10, 2), font=('Helvetica', 20), text_color='red', justification='center', key='output')], + [sg.T(' ' * 15), sg.Quit()]] + window.LayoutAndRead(layout, non_blocking=True) + + for i in range(1, 500): + window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) + button, values = window.ReadNonBlocking() + if values is None or button == 'Quit': # if user closed the window using X + break + time.sleep(.01) + else: + # if the loop finished then need to close the form for the user + window.CloseNonBlocking() + +def CallbackSimulation(): + """ + Callback Function Simulation + The architecture of some programs works better with button callbacks instead of handling in-line. While button callbacks are part of the PySimpleGUI implementation, they are not directly exposed to the caller. The way to get the same result as callbacks is to simulate them with a recipe like this one. + """ + import PySimpleGUI as sg + + # This design pattern simulates button callbacks + # Note that callbacks are NOT a part of the package's interface to the + # caller intentionally. The underlying implementation actually does use + # tkinter callbacks. They are simply hidden from the user. + + # The callback functions + def button1(): + print('Button 1 callback') + + def button2(): + print('Button 2 callback') + + # Create a standard form + window = sg.Window('Button callback example') + # Layout the design of the GUI + layout = [[sg.Text('Please click a button')], + [sg.ReadButton('1'), sg.ReadButton('2'), sg.Quit()]] + # Show the form to the user + window.Layout(layout) + + # Event loop. Read buttons, make callbacks + while True: + # Read the form + button, value = window.Read() + # Take appropriate action based on button + if button == '1': + button1() + elif button == '2': + button2() + elif button =='Quit' or button is None: + break + + # All done! + sg.PopupOK('Done') + +def RealtimeButtons(): + """ + Realtime Buttons (Good For Raspberry Pi) + This recipe implements a remote control interface for a robot. There are 4 directions, forward, reverse, left, right. When a button is clicked, PySimpleGUI immediately returns button events for as long as the buttons is held down. When released, the button events stop. This is an async/non-blocking window. + """ + import PySimpleGUI as sg + + # Make a form, but don't use context manager + window = sg.Window('Robotics Remote Control') + + form_rows = [[sg.Text('Robotics Remote Control')], + [sg.T(' ' * 10), sg.RealtimeButton('Forward')], + [sg.RealtimeButton('Left'), sg.T(' ' * 15), sg.RealtimeButton('Right')], + [sg.T(' ' * 10), sg.RealtimeButton('Reverse')], + [sg.T('')], + [sg.Quit(button_color=('black', 'orange'))] + ] + + window.LayoutAndRead(form_rows, non_blocking=True) + + # + # Some place later in your code... + # You need to perform a ReadNonBlocking on your form every now and then or + # else it won't refresh. + # + # your program's main loop + while (True): + # This is the code that reads and updates your window + button, values = window.ReadNonBlocking() + if button is not None: + print(button) + if button is 'Quit' or values is None: + break + + window.CloseNonBlocking() + +def EasyProgressMeter(): + """ + Easy Progress Meter + This recipe shows just how easy it is to add a progress meter to your code. + """ + import PySimpleGUI as sg + + for i in range(1000): + sg.EasyProgressMeter('Easy Meter Example', i+1, 1000) + +def TabbedForm(): + """ + Tabbed Form + Tabbed forms are easy to make and use in PySimpleGUI. You simple may your layouts for each tab and then instead of LayoutAndRead you call ShowTabbedwindow. Results are returned as a list of form results. Each tab acts like a single window. + """ + import PySimpleGUI as sg + + with sg.Window('') as form: + with sg.Window('') as form2: + + layout_tab_1 = [[sg.Text('First tab', size=(20, 1), font=('helvetica', 15))], + [sg.InputText(), sg.Text('Enter some info')], + [sg.Submit(button_color=('red', 'yellow')), sg.Cancel(button_color=('white', 'blue'))]] + + layout_tab_2 = [[sg.Text('Second Tab', size=(20, 1), font=('helvetica', 15))], + [sg.InputText(), sg.Text('Enter some info')], + [sg.Submit(button_color=('red', 'yellow')), sg.Cancel(button_color=('white', 'blue'))]] + + results = sg.ShowTabbedForm('Tabbed form example', (form, layout_tab_1, 'First Tab'), + (form2, layout_tab_2,'Second Tab')) + + sg.Popup(results) + +def MediaPlayer(): + """ + Button Graphics (Media Player) + Buttons can have PNG of GIF images on them. This Media Player recipe requires 4 images in order to function correctly. The background is set to the same color as the button background so that they blend together. + """ + import PySimpleGUI as sg + + background = '#F0F0F0' + # Set the backgrounds the same as the background on the buttons + sg.SetOptions(background_color=background, element_background_color=background) + # Images are located in a subfolder in the Demo Media Player.py folder + image_pause = './ButtonGraphics/Pause.png' + image_restart = './ButtonGraphics/Restart.png' + image_next = './ButtonGraphics/Next.png' + image_exit = './ButtonGraphics/Exit.png' + + # Open a form, note that context manager can't be used generally speaking for async forms + window = sg.Window('Media File Player', default_element_size=(20, 1), + font=("Helvetica", 25)) + # define layout of the rows + layout = [[sg.Text('Media File Player', size=(17, 1), font=("Helvetica", 25))], + [sg.Text('', size=(15, 2), font=("Helvetica", 14), key='out')], + [sg.ReadButton('Restart Song', button_color=(background, background), + image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0), + sg.Text(' ' * 2), + sg.ReadButton('Pause', button_color=(background, background), + image_filename=image_pause, image_size=(50, 50), image_subsample=2, border_width=0), + sg.Text(' ' * 2), + sg.ReadButton('Next', button_color=(background, background), + image_filename=image_next, image_size=(50, 50), image_subsample=2, border_width=0), + sg.Text(' ' * 2), + sg.Text(' ' * 2), sg.Button('Exit', button_color=(background, background), + image_filename=image_exit, image_size=(50, 50), image_subsample=2, + border_width=0)], + [sg.Text('_' * 20)], + [sg.Text(' ' * 30)], + [sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', + font=("Helvetica", 15)), + sg.Text(' ' * 2), + sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', + font=("Helvetica", 15)), + sg.Text(' ' * 8), + sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', + font=("Helvetica", 15))], + [sg.Text('Bass', font=("Helvetica", 15), size=(6, 1)), + sg.Text('Treble', font=("Helvetica", 15), size=(10, 1)), + sg.Text('Volume', font=("Helvetica", 15), size=(7, 1))] ] + + # Call the same LayoutAndRead but indicate the form is non-blocking + window.LayoutAndRead(layout, non_blocking=True) + # Our event loop + while (True): + # Read the form (this call will not block) + button, values = window.ReadNonBlocking() + if button == 'Exit' or values is None: + break + # If a button was pressed, display it on the GUI by updating the text element + if button: + window.FindElement('out').Update(button) + +def ScriptLauncher(): + """ + Script Launcher - Persistent Form + This form doesn't close after button clicks. To achieve this the buttons are specified as sg.ReadButton instead of sg.Button. The exception to this is the EXIT button. Clicking it will close the window. This program will run commands and display the output in the scrollable window. + """ + import PySimpleGUI as sg + import subprocess + + def Launcher(): + + window = sg.Window('Script launcher') + + layout = [ + [sg.Text('Script output....', size=(40, 1))], + [sg.Output(size=(88, 20))], + [sg.ReadButton('script1'), sg.ReadButton('script2'), sg.Button('EXIT')], + [sg.Text('Manual command', size=(15,1)), sg.InputText(focus=True), sg.ReadButton('Run', bind_return_key=True)] + ] + + window.Layout(layout) + + # ---===--- Loop taking in user input and using it to query HowDoI --- # + while True: + (button, value) = window.Read() + if button == 'EXIT' or button is None: + break # exit button clicked + if button == 'script1': + ExecuteCommandSubprocess('pip','list') + elif button == 'script2': + ExecuteCommandSubprocess('python', '--version') + elif button == 'Run': + ExecuteCommandSubprocess(value[0]) + + + def ExecuteCommandSubprocess(command, *args): + try: + expanded_args = [] + for a in args: + expanded_args += a + sp = subprocess.Popen([command,expanded_args], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = sp.communicate() + if out: + print(out.decode("utf-8")) + if err: + print(err.decode("utf-8")) + except: pass + + Launcher() + +def MachineLearning(): + """ + Machine Learning GUI + A standard non-blocking GUI with lots of inputs. + """ + import PySimpleGUI as sg + + sg.ChangeLookAndFeel('LightGreen') + + sg.SetOptions(text_justification='right') + + window = sg.Window('Machine Learning Front End', font=("Helvetica", 12)) # begin with a blank form + + layout = [[sg.Text('Machine Learning Command Line Parameters', font=('Helvetica', 16))], + [sg.Text('Passes', size=(15, 1)), sg.Spin(values=[i for i in range(1, 1000)], initial_value=20, size=(6, 1)), + sg.Text('Steps', size=(18, 1)), sg.Spin(values=[i for i in range(1, 1000)], initial_value=20, size=(6, 1))], + [sg.Text('ooa', size=(15, 1)), sg.In(default_text='6', size=(10, 1)), sg.Text('nn', size=(15, 1)), sg.In(default_text='10', size=(10, 1))], + [sg.Text('q', size=(15, 1)), sg.In(default_text='ff', size=(10, 1)), sg.Text('ngram', size=(15, 1)), sg.In(default_text='5', size=(10, 1))], + [sg.Text('l', size=(15, 1)), sg.In(default_text='0.4', size=(10, 1)), sg.Text('Layers', size=(15, 1)), sg.Drop(values=('BatchNorm', 'other'),auto_size_text=True)], + [sg.Text('_' * 100, size=(65, 1))], + [sg.Text('Flags', font=('Helvetica', 15), justification='left')], + [sg.Checkbox('Normalize', size=(12, 1), default=True), sg.Checkbox('Verbose', size=(20, 1))], + [sg.Checkbox('Cluster', size=(12, 1)), sg.Checkbox('Flush Output', size=(20, 1), default=True)], + [sg.Checkbox('Write Results', size=(12, 1)), sg.Checkbox('Keep Intermediate Data', size=(20, 1))], + [sg.Text('_' * 100, size=(65, 1))], + [sg.Text('Loss Functions', font=('Helvetica', 15), justification='left')], + [sg.Radio('Cross-Entropy', 'loss', size=(12, 1)), sg.Radio('Logistic', 'loss', default=True, size=(12, 1))], + [sg.Radio('Hinge', 'loss', size=(12, 1)), sg.Radio('Huber', 'loss', size=(12, 1))], + [sg.Radio('Kullerback', 'loss', size=(12, 1)), sg.Radio('MAE(L1)', 'loss', size=(12, 1))], + [sg.Radio('MSE(L2)', 'loss', size=(12, 1)), sg.Radio('MB(L0)', 'loss', size=(12, 1))], + [sg.Submit(), sg.Cancel()]] + + button, values = window.LayoutAndRead(layout) + +def CustromProgressMeter(): + """" + Custom Progress Meter / Progress Bar + Perhaps you don't want all the statistics that the EasyProgressMeter provides and want to create your own progress bar. Use this recipe to do just that. + """ + import PySimpleGUI as sg + + def CustomMeter(): + # create the progress bar element + progress_bar = sg.ProgressBar(10000, orientation='h', size=(20,20)) + # layout the form + layout = [[sg.Text('A custom progress meter')], + [progress_bar], + [sg.Cancel()]] + + # create the form + window = sg.Window('Custom Progress Meter') + # display the form as a non-blocking form + window.LayoutAndRead(layout, non_blocking=True) + # loop that would normally do something useful + for i in range(10000): + # check to see if the cancel button was clicked and exit loop if clicked + button, values = window.ReadNonBlocking() + if button == 'Cancel' or values == None: + break + # update bar with loop value +1 so that bar eventually reaches the maximum + progress_bar.UpdateBar(i+1) + # done with loop... need to destroy the window as it's still open + window.CloseNonBlocking() + + CustomMeter() + +def OneLineGUI(): + """ + The One-Line GUI + For those of you into super-compact code, a complete customized GUI can be specified, shown, and received the results using a single line of Python code. The way this is done is to combine the call to Window and the call to LayoutAndRead. Window returns a Window object which has the LayoutAndRead method. + """ + import PySimpleGUI as sg + + layout = [[sg.Text('Filename')], + [sg.Input(), sg.FileBrowse()], + [sg.OK(), sg.Cancel()] ] + + button, (number,) = sg.Window('Get filename example').LayoutAndRead(layout) + + """ + you can write this line of code for the exact same result (OK, two lines with the import): + """ + # import PySimpleGUI as sg + + button, (filename,) = sg.Window('Get filename example'). LayoutAndRead([[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ]) + +def MultipleColumns(): + """ + Multiple Columns + Starting in version 2.9 (not yet released but you can get from current GitHub) you can use the Column Element. A Column is required when you have a tall element to the left of smaller elements. + + This example uses a Column. There is a Listbox on the left that is 3 rows high. To the right of it are 3 single rows of text and input. These 3 rows are in a Column Element. + + To make it easier to see the Column in the window, the Column background has been shaded blue. The code is wordier than normal due to the blue shading. Each element in the column needs to have the color set to match blue background. + """ + import PySimpleGUI as sg + + # Demo of how columns work + # Form has on row 1 a vertical slider followed by a COLUMN with 7 rows + # Prior to the Column element, this layout was not possible + # Columns layouts look identical to form layouts, they are a list of lists of elements. + + # sg.ChangeLookAndFeel('BlueMono') + + # Column layout + col = [[sg.Text('col Row 1', text_color='white', background_color='blue')], + [sg.Text('col Row 2', text_color='white', background_color='blue'), sg.Input('col input 1')], + [sg.Text('col Row 3', text_color='white', background_color='blue'), sg.Input('col input 2')]] + + layout = [[sg.Listbox(values=('Listbox Item 1', 'Listbox Item 2', 'Listbox Item 3'), select_mode=sg.LISTBOX_SELECT_MODE_MULTIPLE, size=(20,3)), sg.Column(col, background_color='blue')], + [sg.Input('Last input')], + [sg.OK()]] + + # Display the form and get values + # If you're willing to not use the "context manager" design pattern, then it's possible + # to collapse the form display and read down to a single line of code. + button, values = sg.Window('Compact 1-line form with column').LayoutAndRead(layout) + + sg.Popup(button, values, line_width=200) + +def PersistentForm(): + """ + Persistent Form With Text Element Updates + This simple program keep a form open, taking input values until the user terminates the program using the "X" button. + """ + import PySimpleGUI as sg + + window = sg.Window('Math') + + output = sg.Txt('', size=(8,1)) + + layout = [ [sg.Txt('Enter values to calculate')], + [sg.In(size=(8,1), key='numerator')], + [sg.Txt('_' * 10)], + [sg.In(size=(8,1), key='denominator')], + [output], + [sg.ReadButton('Calculate', bind_return_key=True)]] + + window.Layout(layout) + + while True: + button, values = window.Read() + + if button is not None: + try: + numerator = float(values['numerator']) + denominator = float(values['denominator']) + calc = numerator / denominator + except: + calc = 'Invalid' + + output.Update(calc) + else: + break + +def CanvasWidget(): + """ + tkinter Canvas Widget + The Canvas Element is one of the few tkinter objects that are directly accessible. The tkinter Canvas widget itself can be retrieved from a Canvas Element like this: + """ + + import PySimpleGUI as gui + + canvas = gui.Canvas(size=(100,100), background_color='red') + + layout = [ + [canvas], + [gui.T('Change circle color to:'), gui.ReadButton('Red'), gui.ReadButton('Blue')] + ] + + window = gui.Window('Canvas test', grab_anywhere=True) + window.Layout(layout) + window.ReadNonBlocking() + + cir = canvas.TKCanvas.create_oval(50, 50, 100, 100) + + while True: + button, values = window.Read() + if button is None: + break + if button is 'Blue': + canvas.TKCanvas.itemconfig(cir, fill = "Blue") + elif button is 'Red': + canvas.TKCanvas.itemconfig(cir, fill = "Red") + +def InputElementUpdate(): + """ + Input Element Update + This Recipe implements a Raspberry Pi touchscreen based keypad entry. As the digits are entered using the buttons, the Input Element above it is updated with the input digits. There are a number of features used in this Recipe including: Default Element Size auto_size_buttons ReadButton Dictionary Return values Update of Elements in form (Input, Text) do_not_clear of Input Elements + """ + import PySimpleGUI as g + + # Demonstrates a number of PySimpleGUI features including: + # Default element size + # auto_size_buttons + # ReadButton + # Dictionary return values + # Update of elements in form (Text, Input) + # do_not_clear of Input elements + + layout = [[g.Text('Enter Your Passcode')], + [g.Input(size=(10, 1), do_not_clear=True, key='input')], + [g.ReadButton('1'), g.ReadButton('2'), g.ReadButton('3')], + [g.ReadButton('4'), g.ReadButton('5'), g.ReadButton('6')], + [g.ReadButton('7'), g.ReadButton('8'), g.ReadButton('9')], + [g.ReadButton('Submit'), g.ReadButton('0'), g.ReadButton('Clear')], + [ g.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='output')], + ] + + window = g.Window('Keypad', default_element_size=(5, 2), auto_size_buttons=False) + window.Layout(layout) + + # Loop forever reading the form's values, updating the Input field + keys_entered = '' + while True: + button, values = window.Read() # read the form + if button is None: # if the X button clicked, just exit + break + if button is 'Clear': # clear keys if clear button + keys_entered = '' + elif button in '1234567890': + keys_entered = values['input'] # get what's been entered so far + keys_entered += button # add the new digit + elif button is 'Submit': + keys_entered = values['input'] + window.FindElement('output').Update(keys_entered) # output the final string + + window.FindElement('input').Update(keys_entered) # change the form to reflect current key string + + +def TableSimulation(): + """ + Display data in a table format + """ + import PySimpleGUI as sg + sg.ChangeLookAndFeel('Dark1') + + layout = [[sg.T('Table Test')]] + + for i in range(20): + layout.append([sg.T('{} {}'.format(i,j), size=(4, 1), background_color='black', pad=(1, 1)) for j in range(10)]) + + sg.Window('Table').LayoutAndRead(layout) + + +def TightLayout(): + """ + Turn off padding in order to get a really tight looking layout. + """ + import PySimpleGUI as sg + + sg.ChangeLookAndFeel('Dark') + sg.SetOptions(element_padding=(0, 0)) + layout = [[sg.T('User:', pad=((3, 0), 0)), sg.OptionMenu(values=('User 1', 'User 2'), size=(20, 1)), + sg.T('0', size=(8, 1))], + [sg.T('Customer:', pad=((3, 0), 0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20, 1)), + sg.T('1', size=(8, 1))], + [sg.T('Notes:', pad=((3, 0), 0)), sg.In(size=(44, 1), background_color='white', text_color='black')], + [sg.ReadButton('Start', button_color=('white', 'black')), + sg.ReadButton('Stop', button_color=('white', 'black')), + sg.ReadButton('Reset', button_color=('white', '#9B0023')), + sg.ReadButton('Submit', button_color=('white', 'springgreen4')), + sg.Button('Exit', button_color=('white', '#00406B')), + ] + ] + + window = sg.Window("Time Tracker", default_element_size=(12, 1), text_justification='r', auto_size_text=False, + auto_size_buttons=False, no_titlebar=True, + default_button_element_size=(12, 1)) + window.Layout(layout) + while True: + button, values = window.Read() + if button is None or button == 'Exit': + return + +# -------------------------------- GUI Starts Here -------------------------------# +# fig = your figure you want to display. Assumption is that 'fig' holds the # +# information to display. # +# --------------------------------------------------------------------------------# + + +import PySimpleGUI as sg + +fig_dict = {'Simple Data Entry':SimpleDataEntry, 'Simple Entry Return Data as Dict':SimpleReturnAsDict, 'File Browse' : FileBrowse, + 'GUI Add On':GUIAddOn, 'Compare 2 Files':Compare2Files, 'All Widgets With Context Manager':AllWidgetsWithContext, 'All Widgets No Context Manager':AllWidgetsNoContext, + 'Non-Blocking With Updates':NonBlockingWithUpdates, 'Non-Bocking With Context Manager':NonBlockingWithContext, 'Callback Simulation':CallbackSimulation, + 'Realtime Buttons':RealtimeButtons, 'Easy Progress Meter':EasyProgressMeter, 'Tabbed Form':TabbedForm, 'Media Player':MediaPlayer, 'Script Launcher':ScriptLauncher, + 'Machine Learning':MachineLearning, 'Custom Progress Meter':CustromProgressMeter, 'One Line GUI':OneLineGUI, 'Multiple Columns':MultipleColumns, + 'Persistent Form':PersistentForm, 'Canvas Widget':CanvasWidget, 'Input Element Update':InputElementUpdate, + 'Table Simulation':TableSimulation, 'Tight Layout':TightLayout} + + +# define the form layout +listbox_values = [key for key in fig_dict.keys()] + +while True: + sg.ChangeLookAndFeel('Dark') + # sg.SetOptions(element_padding=(0,0)) + + col_listbox = [[sg.Listbox(values=listbox_values, size=(max(len(x) for x in listbox_values),min(len(listbox_values), 20)), change_submits=False, key='func')], + [sg.ReadButton('Run', pad=(0,0)), sg.ReadButton('Show Code', button_color=('white', 'gray25'), pad=(0,0)), sg.Exit(button_color=('white', 'firebrick4'), pad=(0,0))]] + + layout = [[sg.Text('PySimpleGUI Coookbook', font=('current 18'))], + [sg.Column(col_listbox), sg.Multiline(size=(50,min(len(listbox_values), 20)), do_not_clear=True, key='multi')], + ] + +# create the form and show it without the plot +# window.Layout(layout) + + window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI', default_button_element_size=(9,1),auto_size_buttons=False, grab_anywhere=False) + window.Layout(layout) + # show it all again and get buttons + while True: + button, values = window.Read() + + if button is None or button == 'Exit': + exit(69) + try: + choice = values['func'][0] + func = fig_dict[choice] + except: + continue + + if button == 'Show Code' and values['multi']: + window.FindElement('multi').Update(inspect.getsource(func)) + elif button is 'Run' and values['func']: + # sg.ChangeLookAndFeel('SystemDefault') + window.CloseNonBlocking() + func() + break + else: + print('ILLEGAL values') + break + diff --git a/Demo_Design_Patterns.py b/Demo_Design_Patterns.py index dfe3ff35..b4b3114c 100644 --- a/Demo_Design_Patterns.py +++ b/Demo_Design_Patterns.py @@ -52,6 +52,8 @@ layout = [[ sg.Text('My layout') ]] window = sg.Window('My new window').Layout(layout).Finalize() +# if you have operations on elements that must take place before the event loop, do them here + while True: # Event Loop event, values = window.Read() if event is None: diff --git a/Demo_Desktop_Floating_Toolbar.py b/Demo_Desktop_Floating_Toolbar.py index 204e0561..3648d9cd 100644 --- a/Demo_Desktop_Floating_Toolbar.py +++ b/Demo_Desktop_Floating_Toolbar.py @@ -29,6 +29,9 @@ def Launcher(): namesonly = [f for f in os.listdir(ROOT_PATH) if f.endswith('.py') ] + if len(namesonly) == 0: + namesonly = ['test 1', 'test 2', 'test 3'] + sg.SetOptions(element_padding=(0,0), button_element_size=(12,1), auto_size_buttons=False) layout = [[sg.Combo(values=namesonly, size=(35,30), key='demofile'), @@ -39,7 +42,7 @@ def Launcher(): sg.Button('EXIT', button_color=('white','firebrick3'))], [sg.T('', text_color='white', size=(50,1), key='output')]] - window = sg.Window('Floating Toolbar', no_titlebar=True, keep_on_top=True).Layout(layout) + window = sg.Window('Floating Toolbar', no_titlebar=True, grab_anywhere=True, keep_on_top=True).Layout(layout) # ---===--- Loop taking in user input and executing appropriate program --- # diff --git a/Demo_Desktop_Widget_CPU_Dashboard.py b/Demo_Desktop_Widget_CPU_Dashboard.py index af4cb57a..0512f4d6 100644 --- a/Demo_Desktop_Widget_CPU_Dashboard.py +++ b/Demo_Desktop_Widget_CPU_Dashboard.py @@ -17,7 +17,7 @@ GRAPH_WIDTH = 120 # each individual graph size in pixels GRAPH_HEIGHT = 40 TRANSPARENCY = .8 # how transparent the window looks. 0 = invisible, 1 = normal window NUM_COLS = 4 -POLL_FREQUENCY = 400 # how often to update graphs in milliseconds +POLL_FREQUENCY = 500 # how often to update graphs in milliseconds colors = ('#23a0a0', '#56d856', '#be45be', '#5681d8', '#d34545', '#BE7C29') @@ -59,7 +59,7 @@ def main(): sg.SetOptions(element_padding=(0,0), margins=(1,1), border_width=0) # ---------------- Create Layout ---------------- - layout = [[ sg.RButton('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window'), + layout = [[ sg.Button('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window'), sg.Text(' CPU Core Usage')] ] # add on the graphs @@ -100,7 +100,7 @@ def main(): for i in range(num_cores): graphs[i].graph_percentage_abs(stats[i]) graphs[i].text_display('{} CPU {:2.0f}'.format(i, stats[i])) - + window.Close() if __name__ == "__main__": # the clever Red X graphic diff --git a/Demo_Desktop_Widget_CPU_Graph.py b/Demo_Desktop_Widget_CPU_Graph.py index 1af8fd2a..801cd61e 100644 --- a/Demo_Desktop_Widget_CPU_Graph.py +++ b/Demo_Desktop_Widget_CPU_Graph.py @@ -54,8 +54,8 @@ def main(): prev_x, prev_y = 0, 0 while True: # the Event Loop time.sleep(.5) - event, values = window.ReadNonBlocking() - if event == 'Quit' or values is None: # always give ths user a way out + event, values = window.Read(timeout=0) + if event == 'Quit' or event is None: # always give ths user a way out break # do CPU measurement and graph it current_cpu = int(g_cpu_percent*10) @@ -73,5 +73,7 @@ def main(): i += STEP_SIZE if i < SAMPLES else 0 last_cpu = current_cpu + g_exit = True + window.Close() if __name__ == '__main__': main() diff --git a/Demo_Desktop_Widget_CPU_Utilization.py b/Demo_Desktop_Widget_CPU_Utilization.py index 92d37d6e..39aefb5d 100644 --- a/Demo_Desktop_Widget_CPU_Utilization.py +++ b/Demo_Desktop_Widget_CPU_Utilization.py @@ -54,6 +54,7 @@ def main(): window = sg.Window('CPU Utilization', no_titlebar=True, keep_on_top=True, + alpha_channel=.8, grab_anywhere=True).Layout(layout) # start cpu measurement thread diff --git a/Demo_Desktop_Widget_Email_Notification.py b/Demo_Desktop_Widget_Email_Notification.py index 5896231a..de4264d8 100644 --- a/Demo_Desktop_Widget_Email_Notification.py +++ b/Demo_Desktop_Widget_Email_Notification.py @@ -30,9 +30,9 @@ def gui(): sg.SetOptions(border_width=0, margins=(0, 0), element_padding=(4, 0)) layout = [[sg.T('Email New Mail Notification' + 48 * ' '), - sg.RButton('', image_data=refresh, button_color=('#282923', '#282923'), key='_refresh_', + sg.Button('', image_data=refresh, button_color=('#282923', '#282923'), key='_refresh_', tooltip='Refreshes Email'), - sg.RButton('', image_data=red_x, button_color=('#282923', '#282923'), key='_quit_', + sg.Button('', image_data=red_x, button_color=('#282923', '#282923'), key='_quit_', tooltip='Closes window')], [sg.T('', key='_status_', size=(25, 1))], ] diff --git a/Demo_Desktop_Widget_Timer.py b/Demo_Desktop_Widget_Timer.py index 72d0800a..e5a06939 100644 --- a/Demo_Desktop_Widget_Timer.py +++ b/Demo_Desktop_Widget_Timer.py @@ -26,8 +26,8 @@ sg.SetOptions(element_padding=(0, 0)) layout = [[sg.Text('')], [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], - [sg.ReadButton('Pause', key='button', button_color=('white', '#001480')), - sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'), + [sg.Button('Pause', key='button', button_color=('white', '#001480')), + sg.Button('Reset', button_color=('white', '#007339'), key='Reset'), sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]] window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout) diff --git a/Demo_Desktop_Widget_psutil_Dashboard.py b/Demo_Desktop_Widget_psutil_Dashboard.py index 5a8157f9..3d1df605 100644 --- a/Demo_Desktop_Widget_psutil_Dashboard.py +++ b/Demo_Desktop_Widget_psutil_Dashboard.py @@ -18,6 +18,7 @@ import psutil GRAPH_WIDTH = 120 # each individual graph size in pixels GRAPH_HEIGHT = 40 +ALPHA = .7 class DashGraph(object): def __init__(self, graph_elem, starting_count, color): @@ -47,7 +48,7 @@ class DashGraph(object): self.graph_current_item += 1 -def human_size(bytes, units=(' bytes','KB','MB','GB','TB', 'PB', 'EB')): +def human_size(bytes, units=[' bytes','KB','MB','GB','TB', 'PB', 'EB']): """ Returns a human readable string reprentation of bytes""" return str(bytes) + units[0] if bytes < 1024 else human_size(bytes>>10, units[1:]) @@ -63,17 +64,19 @@ def main(): sg.ChangeLookAndFeel('Black') sg.SetOptions(element_padding=(0,0), margins=(1,1), border_width=0) - layout = [[sg.Text('System Status Dashboard'+' '*18), sg.RButton('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window')], - [sg.Column([[Txt('Net Out ', key='_NET_OUT_'), ], - [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_OUT_')]], pad=(2, 2)), - sg.Column([[Txt('Net In', key='_NET_IN_'),], - [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_IN_')]], pad=(0, 2))], - [sg.Column([[Txt('Disk Read', key='_DISK_READ_')], - [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_DISK_READ_')]], pad=(2,2)), - sg.Column([[Txt('Disk Write', key='_DISK_WRITE_')], - [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, GRAPH_HEIGHT), background_color='black', key='_GRAPH_DISK_WRITE_')]], pad=(0, 2))], - [sg.Column([[Txt('CPU Usage', key='_CPU_USAGE_')], [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_CPU_USAGE_')]], pad=(2,2)), - sg.Column([[Txt('Memory Usage', key='_MEM_USAGE_')], [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key='_GRAPH_MEM_USAGE_')]], pad=(2, 2))]] + def GraphColumn(name, key): + col = sg.Column([[Txt(name, key=key+'TXT_'), ], + [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', + key=key+'GRAPH_')]], pad=(2, 2)) + return col + + layout = [[sg.Text('System Status Dashboard'+' '*18), sg.Button('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window')], + [GraphColumn('Net Out', '_NET_OUT_'), + GraphColumn('Net In', '_NET_IN_')], + [GraphColumn('Disk Read', '_DISK_READ_'), + GraphColumn('Disk Write', '_DISK_WRITE_')], + [GraphColumn('CPU Usage', '_CPU_'), + GraphColumn('Memory Usage', '_MEM_')],] window = sg.Window('PSG System Dashboard', keep_on_top=True, @@ -82,22 +85,24 @@ def main(): no_titlebar=True, default_button_element_size=(12, 1), return_keyboard_events=True, - alpha_channel=.9, + alpha_channel=ALPHA, use_default_focus=False, - ).Layout(layout) + ).Layout(layout).Finalize() # setup graphs & initial values netio = psutil.net_io_counters() - net_graph_in = DashGraph(window.FindElement('_GRAPH_IN_'), netio.bytes_recv, '#23a0a0') - net_graph_out = DashGraph(window.FindElement('_GRAPH_OUT_'), netio.bytes_sent, '#56d856') + net_graph_in = DashGraph(window.FindElement('_NET_IN_GRAPH_'), netio.bytes_recv, '#23a0a0') + net_graph_out = DashGraph(window.FindElement('_NET_OUT_GRAPH_'), netio.bytes_sent, '#56d856') + diskio = psutil.disk_io_counters() - disk_graph_write = DashGraph(window.FindElement('_GRAPH_DISK_WRITE_'), diskio.write_bytes, '#be45be') - disk_graph_read = DashGraph(window.FindElement('_GRAPH_DISK_READ_'), diskio.read_bytes, '#5681d8') + disk_graph_write = DashGraph(window.FindElement('_DISK_WRITE_GRAPH_'), diskio.write_bytes, '#be45be') + disk_graph_read = DashGraph(window.FindElement('_DISK_READ_GRAPH_'), diskio.read_bytes, '#5681d8') - cpu_usage_graph = DashGraph(window.FindElement('_GRAPH_CPU_USAGE_'), 0, '#d34545') - mem_usage_graph = DashGraph(window.FindElement('_GRAPH_MEM_USAGE_'), 0, '#BE7C29') + cpu_usage_graph = DashGraph(window.FindElement('_CPU_GRAPH_'), 0, '#d34545') + mem_usage_graph = DashGraph(window.FindElement('_MEM_GRAPH_'), 0, '#BE7C29') + print(psutil.cpu_percent(percpu=True)) # ---------------- main loop ---------------- while (True): # --------- Read and update window once a second-------- @@ -108,22 +113,22 @@ def main(): netio = psutil.net_io_counters() write_bytes = net_graph_out.graph_value(netio.bytes_sent) read_bytes = net_graph_in.graph_value(netio.bytes_recv) - Txt_Update(window, '_NET_OUT_', 'Net out {}'.format(human_size(write_bytes))) - Txt_Update(window, '_NET_IN_', 'Net In {}'.format(human_size(read_bytes))) + Txt_Update(window, '_NET_OUT_TXT_', 'Net out {}'.format(human_size(write_bytes))) + Txt_Update(window, '_NET_IN_TXT_', 'Net In {}'.format(human_size(read_bytes))) # ----- Disk Graphs ----- diskio = psutil.disk_io_counters() write_bytes = disk_graph_write.graph_value(diskio.write_bytes) read_bytes = disk_graph_read.graph_value(diskio.read_bytes) - Txt_Update(window, '_DISK_WRITE_', 'Disk Write {}'.format(human_size(write_bytes))) - Txt_Update(window, '_DISK_READ_', 'Disk Read {}'.format(human_size(read_bytes))) + Txt_Update(window, '_DISK_WRITE_TXT_', 'Disk Write {}'.format(human_size(write_bytes))) + Txt_Update(window, '_DISK_READ_TXT_', 'Disk Read {}'.format(human_size(read_bytes))) # ----- CPU Graph ----- cpu = psutil.cpu_percent(0) cpu_usage_graph.graph_percentage_abs(cpu) - Txt_Update(window, '_CPU_USAGE_', '{0:2.0f}% CPU Used'.format(cpu)) + Txt_Update(window, '_CPU_TXT_', '{0:2.0f}% CPU Used'.format(cpu)) # ----- Memory Graph ----- mem_used = psutil.virtual_memory().percent mem_usage_graph.graph_percentage_abs(mem_used) - Txt_Update(window, '_MEM_USAGE_', '{}% Memory Used'.format(mem_used)) + Txt_Update(window, '_MEM_TXT_', '{}% Memory Used'.format(mem_used)) if __name__ == "__main__": # the clever Red X graphic diff --git a/Demo_Disable_Elements.py b/Demo_Disable_Elements.py index 61c6dd30..f4c4e372 100644 --- a/Demo_Disable_Elements.py +++ b/Demo_Disable_Elements.py @@ -33,7 +33,7 @@ for key in key_list: window.FindElement(key).Update(disabled=True) # don't do while True: event, values = window.Read() - if event in (None, 'Exit'): + if event in (None, 'exit'): break elif event == 'Disable': for key in key_list: window.FindElement(key).Update(disabled=True) diff --git a/Demo_DuplicateFileFinder.py b/Demo_DuplicateFileFinder.py index eebe5ee1..7e7045c6 100644 --- a/Demo_DuplicateFileFinder.py +++ b/Demo_DuplicateFileFinder.py @@ -54,8 +54,8 @@ def FindDuplicatesFilesInFolder(path): if __name__ == '__main__': source_folder = None - rc, source_folder = sg.PopupGetFolder('Duplicate Finder - Count number of duplicate files', 'Enter path to folder you wish to find duplicates in') - if rc is True and source_folder is not None: + source_folder = sg.PopupGetFolder('Duplicate Finder - Count number of duplicate files', 'Enter path to folder you wish to find duplicates in') + if source_folder is not None: FindDuplicatesFilesInFolder(source_folder) else: sg.PopupCancel('Cancelling', '*** Cancelling ***') diff --git a/Demo_Floating_Toolbar.py b/Demo_Floating_Toolbar.py index cf8a7c35..b7da1182 100644 --- a/Demo_Floating_Toolbar.py +++ b/Demo_Floating_Toolbar.py @@ -52,10 +52,10 @@ def ShowMeTheButtons(): sg.SetOptions(auto_size_buttons=True, margins=(0,0), button_color=sg.COLOR_SYSTEM_DEFAULT) - toolbar_buttons = [[sg.RButton('', image_data=get_image_bytes(close64),button_color=('white', 'black'), pad=(0,0), key='_close_'), - sg.RButton('', image_data=get_image_bytes(timer64), button_color=('white', 'black'), pad=(0, 0), key='_timer_'), - sg.RButton('', image_data=get_image_bytes(house64), button_color=('white', 'black'), pad=(0, 0), key='_house_'), - sg.RButton('', image_data=get_image_bytes(cpu64), button_color=('white', 'black'), pad=(0,0), key='_cpu_'),]] + toolbar_buttons = [[sg.Button('', image_data=get_image_bytes(close64),button_color=('white', 'black'), pad=(0,0), key='_close_'), + sg.Button('', image_data=get_image_bytes(timer64), button_color=('white', 'black'), pad=(0, 0), key='_timer_'), + sg.Button('', image_data=get_image_bytes(house64), button_color=('white', 'black'), pad=(0, 0), key='_house_'), + sg.Button('', image_data=get_image_bytes(cpu64), button_color=('white', 'black'), pad=(0,0), key='_cpu_'),]] # layout = toolbar_buttons layout = [[sg.Frame('Launcher', toolbar_buttons,title_color='white', background_color='black')]] diff --git a/Demo_Font_Previewer.py b/Demo_Font_Previewer.py index a97b2909..1c7eeb63 100644 --- a/Demo_Font_Previewer.py +++ b/Demo_Font_Previewer.py @@ -12,7 +12,7 @@ fonts = list(font.families()) fonts.sort() root.destroy() -sg.ChangeLookAndFeel('Dark') +sg.ChangeLookAndFeel('Black') layout = [[ sg.Text('My Text Element', size=(20,1), @@ -20,7 +20,7 @@ layout = [[ sg.Text('My Text Element', click_submits=True, relief=sg.RELIEF_GROOVE, font = 'Courier` 25', - text_color='red', + text_color='#FF0000', background_color='white', justification='center', pad=(5,3), @@ -29,7 +29,7 @@ layout = [[ sg.Text('My Text Element', ) ], [sg.Listbox(fonts, size=(30,20), change_submits=True, key='_list_')], [sg.Input(key='_in_')], - [ sg.RButton('Read', bind_return_key=True), sg.Exit()]] + [ sg.Button('Read', bind_return_key=True), sg.Exit()]] window = sg.Window('My new window', # grab_anywhere=True, diff --git a/Demo_Font_String.py b/Demo_Font_String.py index b32ef663..b97c0f67 100644 --- a/Demo_Font_String.py +++ b/Demo_Font_String.py @@ -12,7 +12,7 @@ layout = [[sg.Text('This is my sample text',size=(20,1), key='_text_') ], [sg.Slider((6,50), default_value=12, size=(14,20), orientation='h', key='_slider_', change_submits=True), sg.Text('Font size')], [sg.Text('Font string = '), sg.Text('', size=(25,1), key='_fontstring_')], - [ sg.RButton('Exit')]] + [ sg.Button('Exit')]] window = sg.Window('Font string builder').Layout(layout) diff --git a/Demo_Func_Callback_Simulation.py b/Demo_Func_Callback_Simulation.py deleted file mode 100644 index d2996c4a..00000000 --- a/Demo_Func_Callback_Simulation.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -import sys -if sys.version_info[0] >= 3: - import PySimpleGUI as sg -else: - import PySimpleGUI27 as sg - -layout = [[sg.Text('Filename', )], - [sg.Input(), sg.FileBrowse()], - [sg.OK(), sg.Cancel()]] - -event, (number,) = sg.Window('Get filename example').LayoutAndRead(layout) - - - -import PySimpleGUI as sg - -event, (filename,) = sg.Window('Get filename example').LayoutAndRead( - [[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()]]) \ No newline at end of file diff --git a/Demo_Google_TTS.py b/Demo_Google_TTS.py index c3863fa8..ee8557bb 100644 --- a/Demo_Google_TTS.py +++ b/Demo_Google_TTS.py @@ -20,7 +20,7 @@ import os layout = [[sg.Text('What would you like me to say?')], [sg.Multiline(size=(60,10), enter_submits=True)], - [sg.RButton('Speak', bind_return_key=True), sg.Exit()]] + [sg.Button('Speak', bind_return_key=True), sg.Exit()]] window = sg.Window('Google Text to Speech').Layout(layout) diff --git a/Demo_Graph_Element.py b/Demo_Graph_Element.py index 51aa7363..67f28ac5 100644 --- a/Demo_Graph_Element.py +++ b/Demo_Graph_Element.py @@ -43,10 +43,8 @@ def main(): i=0 prev_x, prev_y = 0, 0 while True: - time.sleep(.2) - - event, values = window.ReadNonBlocking() - if event == 'Quit' or values is None: + event, values = window.Read(timeout=200) + if event == 'Quit' or event is None: break if g_response_time is None or prev_response_time == g_response_time: continue diff --git a/Demo_Graph_Noise.py b/Demo_Graph_Noise.py index 2ed56a32..c1893514 100644 --- a/Demo_Graph_Noise.py +++ b/Demo_Graph_Noise.py @@ -33,7 +33,7 @@ def main(): sg.ChangeLookAndFeel('Black') sg.SetOptions(element_padding=(0,0)) - layout = [ [sg.Quit( button_color=('white','black'))], + layout = [ [sg.Button('Quit', button_color=('white','black'))], [sg.Graph(CANVAS_SIZE, (0,0), (SAMPLES,SAMPLE_MAX),background_color='black', key='graph')],] window = sg.Window('Canvas test', grab_anywhere=True, background_color='black', no_titlebar=False, use_default_focus=False).Layout(layout).Finalize() @@ -45,9 +45,8 @@ def main(): graph_value = 250 while True: # time.sleep(.2) - event, values = window.ReadNonBlocking() - print(event, values) - if event == 'Quit' or values is None: + event, values = window.Read(timeout=0) + if event == 'Quit' or event is None: break graph_offset = random.randint(-10, 10) graph_value = graph_value + graph_offset diff --git a/Demo_Keyboard_Realtime.py b/Demo_Keyboard_Realtime.py index 6143bf87..cc50054e 100644 --- a/Demo_Keyboard_Realtime.py +++ b/Demo_Keyboard_Realtime.py @@ -11,15 +11,15 @@ layout = [[sg.Text("Hold down a key")], window = sg.Window("Realtime Keyboard Test", return_keyboard_events=True, use_default_focus=False).Layout(layout) while True: - event, values = window.ReadNonBlocking() + event, values = window.Read(timeout=0) if event == "OK": print(event, values, "exiting") break - if event is not None: + if event is not sg.TIMEOUT_KEY: if len(event) == 1: print('%s - %s' % (event, ord(event))) else: print(event) - elif values is None: + elif event is None: break diff --git a/Demo_Keypad.py b/Demo_Keypad.py index c5cc4ff2..31194fbc 100644 --- a/Demo_Keypad.py +++ b/Demo_Keypad.py @@ -8,7 +8,7 @@ else: # Demonstrates a number of PySimpleGUI features including: # Default element size # auto_size_buttons -# ReadButton +# Button # Dictionary return values # Update of elements in form (Text, Input) # do_not_clear of Input elements @@ -17,10 +17,10 @@ else: layout = [[sg.Text('Enter Your Passcode')], [sg.Input(size=(10, 1), do_not_clear=True, key='input')], - [sg.ReadButton('1'), sg.ReadButton('2'), sg.ReadButton('3')], - [sg.ReadButton('4'), sg.ReadButton('5'), sg.ReadButton('6')], - [sg.ReadButton('7'), sg.ReadButton('8'), sg.ReadButton('9')], - [sg.ReadButton('Submit'), sg.ReadButton('0'), sg.ReadButton('Clear')], + [sg.Button('1'), sg.Button('2'), sg.Button('3')], + [sg.Button('4'), sg.Button('5'), sg.Button('6')], + [sg.Button('7'), sg.Button('8'), sg.Button('9')], + [sg.Button('Submit'), sg.Button('0'), sg.Button('Clear')], [sg.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='out')], ] diff --git a/Demo_LCD_Clock.py b/Demo_LCD_Clock.py deleted file mode 100644 index 86e1aa90..00000000 --- a/Demo_LCD_Clock.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python -import sys -if sys.version_info[0] >= 3: - import PySimpleGUI as sg -else: - import PySimpleGUI27 as sg - -import os -import datetime - - -def lcd_clock(): - lcd_digits = [lcd0, lcd1, lcd2, lcd3, lcd4, lcd5, lcd6, lcd7, lcd8, lcd9] - sg.SetOptions(border_width=0) - - layout = [ - [sg.T(' '*15, background_color='black'), - sg.Image(data=lcdblank[22:], key='_hour1_'), - sg.Image(data=lcdblank[22:] ,key='_hour2_'), - sg.Image(data=lcdblank[22:] ,key='_colon_'), - sg.Image(data=lcdblank[22:] ,key='_min1_'), - sg.Image(data=lcdblank[22:] ,key='_min2_')], - [sg.Image(data=w1[22:] , background_color='black', key='_w1_'), - sg.Image(data=w2[22:] , background_color='black',key='_w2_'), - sg.Image(data=w3[22:] , background_color='black',key='_w3_'), - sg.Image(data=w4[22:] , background_color='black',key='_w4_'), - sg.Image(data=w5[22:] , background_color='black',key='_w5_'),], - [ sg.RButton('Exit', button_color=('white', 'black'), image_data=orangeround[22:])]] - - window = sg.Window('My new window', - background_color='black', - grab_anywhere=True, - use_default_focus=False, - no_titlebar=True - ).Layout(layout).Finalize() - - colon_elem = window.FindElement('_colon_') - hour1 = window.FindElement('_hour1_') - hour2 = window.FindElement('_hour2_') - min1 = window.FindElement('_min1_') - min2 = window.FindElement('_min2_') - - i = 0 - while True: # Event Loop - event, values = window.Read(timeout=500) - if event in (None, 'Exit'): - break - - now = datetime.datetime.now() - real_hour = now.hour-12 if now.hour > 12 else now.hour - hour1_digit = lcd_digits[real_hour//10] - hour1.Update(data=hour1_digit[22:]) - hour2.Update(data=lcd_digits[real_hour%10][22:]) - min2.Update(data=lcd_digits[int(now.minute)%10][22:]) - min1.Update(data=lcd_digits[int(now.minute)//10][22:]) - - if i % 2: - colon_elem.Update(data=lcdcolon[22:]) - else: - colon_elem.Update(data=lcdblank[22:]) - i += 1 - -lcd0 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAAEb0lEQVRoge2Zz4scRRTHP/WqunsDSQjiBvFHSBC8eRJRFAXFgCIYRVBPgrkoiBgU9U/w4iEgHjwEBT2IKHiIiBfBg4oB8SJ4CgHjIXpSE5Ls9HR9PUz1ZGa6dnZ2dmdnlXzh0TPV1a+/9erVq/eqHSB2GWzZBHK4TmpW7EpSIdfokuwE4jrv12QDk40LhNElNmapluFNZtws8bdEsQCCrb69zvEL0FP3DQLk0nUf6P6y1A8hSKAeKG6jNEmnQG8Xhe5cWZE5p5ZHy3mM1GHQz2Z6YGVF33ovgeqkZDukn65vVpVeDUFfr6wIM9k0UgdBa6DfvNc9VaWvErFeGuW80h8Z3Imi0PGqkkCny1KYyY+Q6qw+A64AtzUNn0k8GwI9M56oayLzxxABzjleCgFvxqleb3DDddd5NiT4dL01Rr6oa54pS9aKgqMSl+YgFoG9wFtm7JE42evRkyjJh54sqRZ9YFXi07rmLjMuJEWbXY0B+Ac4Dpysa2qmD6xDavSFnsEoVyVWm4bzEg35gLcRKYA7wuDXRsE561M28R8GVptFYQ5t/yYTj6aSagPnJKlJDNfsJtD2n3Uw3fdnVsNOo0PKwdKJ/YcstSDM71ObeHhR6FpqN04fsHS/2lGfmhU76lNbcvRF5eez7pkbOnpcr+McaPe03KCV6TdsvApccY4KqIESOGvG7yOb6bzT+6MEZvgYqRlkIRdTojypc5iGtnnysbLUFTMJ9Kv3OhLCIGV1Tm5O8c4JMz1fluon3WdC0KrZWDre2kc5Yk+GoJ+KQoeKQjg3mdjPJQGE9zpeFPq+KHRDCDlC6hSjMJjT6BzmPcSIYqSZc8omUQDRDOccagZaJ3V3/NcYJHT3ec8nzrHfORqzbXP02jke854PzSicoyHvo52pu9d7/ZlKq++81w1FIcw6Zt6MeBBmejQEXUw+ddp7VdN8qm280XtdSISupjrtTAja5/3At+Zw8uCccE5Hi0I9M8UR3aeqSjg3VvcNZ6U14T7nOJCWf8lgvu9uGm4fqXO1SWmfe9iMIkb6XItFtwA4N+bYHVfRxBy3NeCsSf809JOOUf2tk4+2zey/25HSWEZHTuvST/Jy9l86qRyWTuq6pWaFMqt6+aQybUsnlcPSSf3/LLUdxUTOqXN6px4vAsPD17bjVg7NQtpmRvfX4YY9K6kmdTjrPefSg3FiR58FbUV0JkZwDpOm6lj3g5HSzfPe82JR8EFdc8iMNTZvqQjsBz4GXi5L3kunw7C+W4xlnYdDGFYyfzinB8tS36Skr2H614Rp0j77elXpRDrYF+jzVJhkk7xRhgG45BzPlSWvNQ0PNQ29dK+ZU8Qg939nbY3LMfJGOinuzxLRA3DZjKfKkhdi5FjTcJVryd5WUQPv1zXnvOfdEDiwTr+xHP1ICHp6zx59lKasv4UpmyrO6ZWq0uNV1Skcxuq+9s9BMx6Jkb+20UKjiAzy/55zfAmdkJAtRrONC8KGX0ZHsQgL5ZCrvHfSKDNjV27I/wIpihClbBNQxAAAAABJRU5ErkJggg==' - - -lcd1 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAACD0lEQVRoge2Yv27UQBCHv9kkRCAhXgAkSh6AmoKCFFBENLwBFQ0PQIPES0BJky4NDR0lJT28A4ICQc7nH4V3FccZ+9zcrAv/pJVP3jntp525+XMGiIUp1QbwtELN1Qo1V1WhLK+hqkEVIC8fVYFKdDB3gLvW3ZUN9qsAmRlnKfEoQ6WBTShQCxybcW7Gk7blt2N3GAVkGegmcA6ctC0AB45tyE2VgL5txifgROLfhH0YFMBH4DGwYdpFIVBtft5XlwA8l/UVGujtbhMgGMrL3p7W2rcrlor2DtV32dHM7+wdSiOfp7TG1FytUHO1Qnllpko7bL3n3N4pJE+VFvhLftf09n/Kz16KXO/NJJBAr8wEKF23i4GxvDDTh5T0OqUxII2NXntRiavGDJM4pOtChwqfZhrgDfDCjA3jnUOY+zDT2xxTf0Gnea9KTJVD36UkgS5ALWgDeugEe+jg8DTPegnY0k009/JetbF92wMoEFu73rmHB/oVOUCuXbS2zrvqUF6RrgIlLmPKy9zrTXlaJNQi3bc8KGmBUCwgphYzOPRVeqdd/1OFQJVDvuVa1y8tcgaHEKhy7EvgsxlHXE401dxXysqFxHO6UevGhH1YTCkf9kfiFPiaXdk4tuG/vgT8Ap4B31PilmMXOmIVHdAF+wMzGuCHdAWkClQ5mJHDq2X0/q0MVe2mplS99nlaJNR/DW4BrSasyZkAAAAASUVORK5CYII=' - -lcd2 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAADmElEQVRoge2Zz4scRRiGn7d6NlHJQaKoKHgwxGgiIpiTegrI4kEUkdwEQcRF8pdI0INIrorkoqB48SB4EhQUvSoIIkjixaiExdnNTtfrYaqyPZmamZ6e7GQC88JHM71dVU999ev7tgSYFVO41QAlraHaaiWheqWXSrYMxQnt39LVVwLolT64T+Jem22gOgAQM5w3hyV+8bhPisN3GPhU4qTNf5M+WgBIwCFgS+KqxKU4Oohj3ssvHpL4UuLJGKm5OR7LDQl4NQQeBp4A3oiRCqjT38dWX3btJZszwA8SFbCXCnW1AfteerGqOCLxboz00/A1F1ZxS4gMPXMlRjaB7yQ2UsGqgwWGU0DACxKPxMiHdT0GkzVxutSpsn9sNiU+riqetulP6skM9YA303Q4b7MD3DEvFAw9JmDb5pU07l3UA/rAOeC8zd6MhmcurDwPSJV1UZ7gpwrzpxNUrvQ6XAflcqXdu6S5tqCuW39zK2ijlTyQ11BttYZqq6VCSe3W39pTbbWSUK139EWSidzz0jFTOiVaQYVUuO3ZdaNyuRxlNM/RuUOXDBSBu4GjDOOseT3WA/4CvgbeSnA5fPl3QhlPspCexyX/KjmC++DdOa0PNnhL8supHoM/l3xIMmCNtl0GqtLz8RD8u2SD61RZF8sgWyH4tRB8MQRXZaAyVAaS5O8T0G6quKvVqQ6DXwrBgDfKQONQecjOSH5P8mnJlxue6go1SEDvSw6pnQlAo1AZ6JkQfCVV8kEIfqyqfDkEW3KU7DmtTs93koemwBjw9dWXV9mDEl/YHAV2gbdj5M4QOC1xXOoUpwu4BvwUY+uVO+KlU8kb2e0Gb4MfyPNsRi+n2YwhG/dUVt7omvl8c2+qGu/nVT37E6CweZaOkyZIvQBUW63kgbyGaqvbG+qgJ3dTt7enlqk1VFu1guoam3dVq8Qh/wMW9pOILhLtzr+pUANgA/hR4k8Am7gAFOyHSJ2g6gbQuRD4LEbuSfHUvNlMBI4AH0lciJEeww5P00g8dVLyIIW/P4NPhOBvQlg4ccj2eoo+e9NjqlGoEyl0/U3yoyH4q0biMFjA9nKnJJ9NWcwUsFGopyT/EYKPVZU/SUA7qcJFbS8BXgNvJo+FaVA5TD0m+bmq8sU0ZDkLuVmWQ+wdyc9PABu5xco/7geeZZhSH8R9Xw3cBfwt8a09dpVWvFrjxpdLVvG6Vizn/DEreodc0koeyP8DbP/uIBO2okgAAAAASUVORK5CYII=' - -lcd3 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAADoUlEQVRoge2ZzascRRTFf6d7TCLBaALiImAIRoObJzqu3JiVCzH/QEACLlyI4sdCiArqwp2gG/8BcavgB7gRsgquRRBc+lwEBTExEMzLdB8XVTWv01090zPznPcgc6FopvtO1al7q+qee0uAOWBS7DeAnGxADZUDCWrU92EdaE1+l6nn/dokB2CUUzguccLmJlD+D0Aq4BBQS/zurk267pM4AnwFbAH/ZJVWB1QB54GbwF/cabGO9UqJSuJUXfMDcAao2Zs15jjgBHgWeA44DFwiTHwS9TpjVTalzW8S54Cfo9JOnN2y7XYEdAN4WuIJ4H2CJ/omMG2KzxKM5OOSr4ANruJz0VbH5zXwGcmvN769G8cbNTB0lksycQWUNn9LvAB8ATwK/Jsz7wxx1K+AF4ELwIexnyNxrLmWarcCXEgzdea1ZIV3onV2YjP4vYyl5k66js9VFnraSY8r2EX0WwgG7va+k3eoJACTmVq7stbYV2YOypwcyIB894Jqho8hcvdaalHZgBoqG1BDpRNmJOHWyZuYw9At3ZaSwBLqzLfcGT8o9pXxz7lOh0g1Y7BcDpAF1bRWmuWx2JYBNgL+BL6XuGBP+RrA1dz4ZCyoRDFsaomzwNc2pwm0eBE3JtePgOcljgLfxAl/ArzVAyJLzErJFIXH4KstWrssHTb4PPhl8Gct+t1q3ZdJ8RD4p9jZrdj5sm3SaFux/8M9oLJHggmp1k5R8ApwjZCrrUr0SuAl4KTEm8At+s+krqUkSwoulPwM+PoKLkz/uQg+1+Dnr0XuX3Q9lV/o090nUQEPAw8SKO2i59UI+APYsvmOsIMr4B7gIvA5u7s86fcCgpicxpx/e0EwTVAT4CMJ7OngAKfTuC39uVKz2ome1k1KHJr93M7oD65dLHuaN/+bW9RD3+27bEANlQ2oobJWUEPD1MZSQ2XjvlxUyL0bFGYSF1qWT5WEuJcb7Ebm3VxLNYPpsuXqFIh/zBTNFk6xSqCSeMTmYylcXbA4W6iB+4APbN4APiVUh2cN3mGdsFtHPwn+lb2po++An5J8SZp+e5UhdXQ71L0l7ge+BR4jpFYFy1MYE5jmZZuxxAPA2+SLsx1QhUQtcW9d8yXwZAS0F5dGE0JCe8VmDJwCTsyYxG5qVRQ+Kvnyii6b58pt8FjyWe5M62gnDunHQ8AYuM7e3/eJQIGPAdvAL3Qz5H29GU27uA0gX+BgfXfIuY2z73fIOTmQLOE/hw5ngBNVOEQAAAAASUVORK5CYII=' - -lcd4 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAADdElEQVRoge2Zu4tkRRSHv3OqukdFxd0FUQRdxESMFgQDBWFBQ/P1LzBREHSNzAwNRPwbVkMDQUMVdIKNfIAYimAgsivjY3fsufUzmKrZftSdvn3t7tvC/OBw+z6q6runHufUbQPEjsmHBqjpDKqrzqC6qgq1TdJaW9X21XZjzXIgtVyf0f3uXAyBBIQtAF0ajYhmC/cFyPLxwRD0/Xisp0MQoJivr9MiCDO9HqO+Go+F2Un72WahHghBN0PQgbueyWCjNQKVul4JQTLT5zEKM/lpUOdD0K8xSqDfzfScmQCFNQCF7KHX3CWQQF+MRsuhLoSgGxlKoD/MdDlGAfMFVzLPQFdz3Ye5/i+7eOpCCLoRggSa5IIH7nrcvTdY8fKV3GWTqbprUAuzz7gzJQPHM+S+lDif0sn9VVXKPJL9ryX1VKFq5+vIb8qatOzFqlC1Qn081LeOzlDbVDWabAqqa71xoaA2mB1XwklN/5/UZWgtQm2y+zqq7qkNgfVeEnZBOwm1sCQox6Y2TcfGrirP1+qtXVuAcmb7vqTF0zGwllefpvJ8Sa81BeqVtWsB6hbwpxn3SkyAMfCDGb+Z8URKOKsH5wAcANeBf8wYSxwCe8DNlkk1m4iBLrvrwEwCfWemSzHq63w+ATUrWsmd3nPXCyHo73z+jbseypltNUefB3vRXfsx6vnRSPs56WtyZX2slH0/Rr0co/Zj1MM5cZwDWoQqYA7CXZ9loNug9B/tVgZ7KwThrj3quX91IiXyQEyJqynxszt7QFN7g47WAHcB10LgW+AaMDKjob6onp7sg550108xSu5q3KUVrZT5MEY9Ox7rl7yb+dRdd1e60Eoftinkt3w0BJ5yZ9LyZqfJgb8Al/g4Jc6lxO3suY9i5ErT4NLMUtNpe1QZjJ2t7LLfyB46nBr4+3k3Y7PPL1cZY31jUgCOuLOITq91TT5Od1knqALWVzZ3XKbBA3KnT0Hbliqxb3ioyrXBoXaz+yrXzqBq2kmoszFVU80rg0PVdjXlt1We26hKwz+agdlMQB7MU01u6JOUeNXsJEcbFAry/lHig5R40529HPMGH1MNx7nSuxJvx+Os6aiy7+ucT61LDRAl3jk64mIIPNbyda93mtvXSup7D+il/DfI3P21fCLvpc7/921TagEY1FNtGjz21bSTUP8C1hqqTm9IJgsAAAAASUVORK5CYII=' - - -lcd5 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAADyUlEQVRoge2Zv4tcVRTHP99zZ6NEggg2iYgWpghKEGNlYaGNYifBgAGbNFaCkCJ/haAiiGDAgOkFKxtttrGKFlaCbJFlwVVEo+5m5t2vxdy3+3bmzcybmXVnA/uFw5v35r37Pu/cX+fcK8AcM8WqAdp0AtVVxxKqN+mPo6I14z1NLdfaLx6hWj0VwLMS/9iYIeRhawCcAbYkfrXHHFF70CrHsxG+npIN3gX3wYNDtB2wwXcifDalA+8uv/cBE1ABNyXuSPwlcbOqyMVbh+GxqrznB4nXgC2bkMje99OB9ly/dAB8mDODnHlHIoBcCsxLWL8ArUfwisSWTYIDQGNQo3C3bE5LvJUSSSKV/2IBA1gDvpV4Hfg9572aGa2BiQ0dYBf4NGfeS4nLEXxs829LIbOUgdPA9zZXgb9zJgoQjPf0ieNUDdcHPqoqrqXEuVL4YE6oWgPApaflKfdNhWr2gvMSVBX3l4Cqa2DWGNgKJWnYaWk0/sb5or2wbfTuDNWm5lf+36P91N63Kh3LKOHBgTqpvhaNQ0lDW6FOqq+rjqWnOo/o9QTaDEXm1ULTTP2AGscaJjXgps3w0xQjZXaCEoDNn+V8wDAwQ2IdeBx4jP04qKvM8KO2Jf6w9yLZWc8cCN7PSP4mwgbfB7+Rkm+U8zqRmMd2S6Lwk+SnSqIQjfe22MELNdjDEf46wlcjfEOywVUpfBGrn/05whckA+51harB1sBIfrukWjvgvKTVHluXjGRN9lg7bUhOkk9F+PMC1i9fvIj1C9AG+MUIf5KSX4qYVJXtUCpfApgIfxZhS64ke07L5XhX8nOSb5fmsBnhF1rA2tcSJDwSDq9JXIqg1/ZAB/WAX2w+sHnTZhd4CNiUuAj8NpK6T+sFe20slca5iNVeOM8wbW+2sz74mZH7Oo3oZjiupFk3TlA9Lpn9tN3s18Jced+o5h00azWrZHSKaptnH5woYdU6geqqlUNVjK9NrByqLT5bOVSbTqC66sihDn19atF1qXru63Usp5On6kXTRZeq6zlzA/hRQkyfR2d6KgGVxPUIruTMva5f0pCBU8AmcC2CL3PmQtlimfZMq/VKnP5+yWQyyycO30m+mJI3SvR5D/zkSDzVDiXtAb1bgOp0aZk9mTpx+CrCz0d4G7wr+VwJIKdCpQJ0OSVn6VCARjeLvpB8KcJ3I/z0LKgoQFfqwH/JamuzQTneivDLvZ6fKFB1ojJxv/FV4BFgh8XD4GmqgEcZ7tVsjyQNq94E3Ru3mhBT95CPYp2qTiiaWrmn2nQsJ+T/AHYfURS2h8j3AAAAAElFTkSuQmCC' - -lcd6 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAAEBUlEQVRoge2ZP4gdVRTGf9+Z3c1qZJFUChJWLBQigmLQiJYhIkQLwcLWwkYrGy1s0sRC0gQUIljYWCRaaKONJoKNIoJKwEgaBcEuJuwmu/vefBZz7+68P/N23p91X7EfHGbmzsy935x777nfuSPAzBlivwkMwwGptphLUgvDCv8vpmVDueibfQMF+4BtT2UyhyUeBP61Kfao0RK4B7gO3Gl4xtkOgY8WhT+RbPAaeGPGtg42+EKEkVxvv2ZY6WJJ8jcRfjrCFxKxTqpkFpbr+qAojGTV2m4kheRrEb4l+VhR+FyEnb6wM6VtJkLnJCM5mgn1kpLk3xKRG5KPR/i95LHuFB4q0/Fs6rJiNCEPhIQ8uFdsrticjGBD4nXgJuOHizyoz0ucLUsWbLrZHQ1Q8hQGQuKqxMNlSYdqaq5LHAeuAodoji27YQsIu9X7g8HT1TcE0AHutllNpDpAd0JSxRjvDpBS7Tx3Vad2T0yGcTzcQ2q3Rrenxh5jLhfkHlKyJ+6eWWLQU97v5XgIqbnz1DSza5aY/4E+Lzgg1RY9EX23iB273B+FcVaDgWWmft5NRPJDJZOrhNwlbYj1kNoCbkpgs0klVf4Erkk8YLPYstJ+CPhbYiOtGG3qMOBIx2OS/0pK8XfJj0T4UlKft0ckBE12O9X1qeQiJQp1pQsNcrif2GMpgXiiKHwxyeNp5HB+91KElyNcgKOZUC8pKn3sJaok4sOah8opLXvsTNLpi6N1+qCnjkr+WPLjki/XUq1JCeXU6mKEn4zwR5KP9HXlUFL55n0R/il12ffg1Qh/G2FL7kr2mJbf+SzCq0XhX9NHXpa80kBsYDJ8J/GczR1gGfhR4hWJ+6WJ4pSoZvUtmy9tHqrV/XkEL9tDE4qdDBl8nZ1cbSudP5/uF81jYFQOZ8CvJu9v1gb+D2l89XuqJ07Vg6PZCXi5bJINj4Iq8cgernu7KRAPRPRRi2GX8VMs9R09pKwfc7kg7yuppklzQKotDjzVFgeeaou58FR/EG1Faha7C3lJaZOBD/0NUq8ogKV0PYlKyF+dG6ovM2OTKtPNfyR+AUgbqONmM3kX8GdgDThMJWUKmj+wkdQCcEPixQjeKkueklhjst3hFeAL4KUIvirLVnVs65lF8B9J66yDT0T4fNJB0/x5yPrp3Qifiti+vpL0VJbh2XpI1/v7lMRp4I2yZDOVdSc0U3XZmbLkUZvTRaXM3LBBF/0XAl4oCk4A75QlG1RdqSktgE3gfZsjNq9FcJe07YyB7st2L/jZovDbM8j1mqwEW/KbEX4mokrndkscliVOJnHfZvqOC1PNvAL4muFKdoBUMPkmxrhoamvofsNe/RHtR5Pen4dfxgOYS5XwH7eZhqnTFNxUAAAAAElFTkSuQmCC' - -lcd7 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAACtElEQVRoge2ZwWoUQRCGv+ruFRPx4iOIGPEuETyLGDH4AqII4tN49KQg+AB6jAjiOeBFg5gHEA+KHiQmJpme38P0hN3ZSZysm949zA9NQ2/t9LfdtV1V0waIOZObNUCbeqiumkuocNgHuWjF+D/NWsayylI/DHHoSi2ZUUgUTH/VDNgHzgK/zPgijazOGJQDSmDZjOdAlIhTBiuARWATuG/GjsQPRrdNzeZSfy8ERTMJVII0hRZTv2mmJee0Brqe5vNDLjYGBSikftVMuwlqPz100raXgDa81+XBQK+9l0BXukIBGqT+pnPadu6/VqxeoXXQee/1Lj1PoKsNqEMdHSpnDMBaWXInBJ46R6DyOTvqiw2VwAKwIfGwLHkRI9fS8wct9kdCAQf/vjdFwUXvCUx2hhjwR+ItHAAdNvk/oaD6pQ7Yi5HdCYEEnAEupDF/hH0nqBrMON62NaFKIHaw7wwF0zn6u/youQzIPVRX9VBd1UN1VQ/VVVmh6lDTNj6srFCO0UCsofGm3YlLVDFvG1i3KvoVaUxm/E5janznxJul/pSZXqU0OIIemAmzg7qgZssCNQLmnF6GoEdmI4XKkF3eYtSlFp0DiSCx32KTTXUuVQCPgVWqtLgtC822fYAw05NUS26Z6XYIbVuY0Z/M9Cw5+S5VebXjnC41fOtY6fCkqh13UeJGrLL0ulQ7XZacG7KDzD7VLBxqiGYxkd3R2yZsFhN9QO6qHqqreqiu6qG6aj6gbPT4zF44dEneshcOYxPa+Bur7IXDR1dNORKEZ7l9AHfNeO89A6oMtE3ZoJQm+xkjKxIfzFpfV2eFgiqf8sD3smRF4nMaj7PevpjAvgK3nOOb9yw0bGZ23+epAJdDYAv4VBQHN2gzvYRsu4CEGZ/o9VHRPKlmfl3bpvmIfQ3NJdRfFS/ZxKoPEs0AAAAASUVORK5CYII=' - -lcd8 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAAEi0lEQVRoge2ZT4gcRRTGf+9Vd/VkIRjxIqggJF5E8JSDF/EQjKAICflDQjAnUdGD6EEQDHrRmwdBD6IIweQkiiIevOTgKSAiiiAiBDxpomhiJLvTVf08TPVkZqdmMz272Z2FfPCoobrr1dev/r1vSgBjwaBbTSCHW6RmxUKSKnKVkuxmw8ivMlld35LZjCU5ra8JUgD3qyJNQ5+bM74R2AGsqPJL02RJGGCSyru9t5NVZf+JmIEFsGYDrQYzsL9U7diOHXavc2P9AzYMhEvlURH2x8iBsuSaKi69KRtgxmAS/y3CwbLkZAg8FuNY/5AZncvAsRA41DQ86T1XRFAgAM06LKTOflflibLkuRjZX9f8I/klZYAVqXy2qsxSiM+UpT3ivV1KQ9mk+q7WtvvNOXuoquwL54bPToiM9Z9+57EMHK9ryqLgcFnyoRlLZkS6bRcGlMCfwDPO8XoIPBojy0BvSpsJUjLyoAYOh0C/LNltxs5EqisUuAp8mQjVieg0TI0UXJ9896lCCFyNsfP+1U5wBfaM+G26kBodmpZAYzbxrCuxBmaO8pqRGnUK04+FWTHrR01sCZtx5t0IC5klbI9ILSSpjYZM+b0WNi1Syvihe6N3Fw6bOqcWbvi6YKYdvT2nlO47envEqMjAj417yPmbIJWbZC4lYs2cpNrjKec7NzITpK6YgQiNGcJgxZwz43Yz7mSQQXZFAfwhwjlV9oRw/WAW4V8RMMsLh6GJ2HtFYZayzTe9tyPeDwVEAIsdLKQM85qqPV5V9s5I1vmW94bImGhgICImdZ+J8EFR0Ae+UeVMv4+kr5lnIbTtVkQ4WhQcEuFi0/ByjEO/uTajLM2DoWoPem8rqhY2QGq17a+o2p6qMkTMr5JWUyOlQCPCKeeIqlw04/0QhonePGgnuQAnvOcBMy4Bb4eAmmWz0LEoIWKnynIw7iL2Qlna096bqVqjatbRRtsc895e837gW9WeL8tWfOYj5Rikq8erijN1TZ3kdAG8UpZ8JcI9ZvTprmYq4IIZJ0R4NQTqtLILVfaL8HWMw/7HItXqrpeS7qtHVs557w3nsuN/I5OR8ufkL3Jdvj+VZPuaui83d9ovaCX8PDDye1yuv0k1k5HR7U4c10Fq1M8YMqS2R+qy2YiLFKl2kuQE6pZHKre9zERqs68ktjxSOdwiNSu2L6mNmuhr/VE2ipnUjKajZ56sc6hmpnRmKUcf628th+2XnY8RzBARrKM1IgN5pcq3xYDW6hRlNaZGKjD49/Z0UXBOle+ahmWRucWqA153jiXgQAgspzoykZpKqgd8UpacLQo+XVlhqWnWLRxO9/scLAp2Ose+GEFktki1Oc/nzvGuCJ8tL7NkRpiTUIsGuMOMszFy1Hs88HCMBJ2cQRM1twHfO8cbzvFxXbPLjHqdhNqO+sBdTcNHdc2LZcmvRcHOKe+PJe/7ej070uvZhSQa5736mGYxlT+q2sFez/YWxYR4GJNYrRDdC+w24zIz7hkdIAxuMnYBP6nyQ+a+b/vcjLbC8WbDyO/yWVJbjYU8kP8HKX/sOnQ6GT4AAAAASUVORK5CYII=' - -lcd9 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAAEYklEQVRoge2ZT2gkRRTGf+91VXeQJcp6kAVBEFn/ggh7ET0IggjBGBNW1+Bpc1E8uDfPIgqC4OLNu6IL4oJ48+RR0LMXvQgii4d1E4PZdFc9D1M9yWRqJjPpbGbAfFBM0l3d7+tXr+q9r0oAY86gsyaQwympSTGXpFzuoqR2p2HkZ5kcvN6SOYkpOcrWECmAh1XxMbIDFHeATAQqIKjyS4zZPgaYpN9zInapquymqhlYDRaPsTVgBrYlYm+UpT1YlgP2AesHeuuRJTPWQ2CtLLklgks95RiaJTvbqqyWJa/EyEXVAfuQmX03gZeaho0QWPaev0RQoEluP2prkrFbIiyXJZdCYLVp+Nvy0WuAufT7WnKvgV0vCnumLO2PojBL7rcjtPa5G6r2bFnaV973771dVQP209957AArIbAAvO49n4lwFqiZbrkweuvOlggbIlwJgbUQ2AEWRjwzRKp1pk8EXgyBWoRHzThDbyimRQFsAdeAtRCoc4bHkWqDrA1KgIdiRMz4xyy/hkwCVc471yc57uOGSDmR3mjvQ2BvBsL0q70kEmFEUB9KKmew6yrfPjdpoh3uJyeR9cZjiNTsKZ16anLMZZE37Kl5HL55wGlMTYrhvJiJqTZPdfmCtq46iFyWGJmQc50iI4r6CZGrDDTjhH6/1tAmgAhmRkgkf1DlbIzcZ0ZzRDI3gO9FeJJegifZ2RwnHGiLdxH7uCzNRMzArjpny2VpO0lINGBhitakyrMWsVXv7aOy7FWdIvap94bIgGhIPIZ1n6nySVGwKMJ3wLW6xpsNlC/ToH0uAutVxfNmRDPeDAGJMRsOB1n26+XHqsq2i6IvjbpKqwD2r6o9UVWGqpUHpNVIT7XecqrUIrxTFFyta+IxeEpF2PCe+4EmRj5sGpTRs3KgFUVhImIKhnP2lvdmqhZVzTq0y1VlV1olI2LvOme0dsZ5SpM4jGlWFCIEVS6IcK8IzZTeMqAEfhfhZeCD3V3qdM+LcNF7vt7dpWBvVmZFRdw3TYMZzoyfY8QmrLGzUOXzJBw0kcWMc8kJ+z90nNLZI0a31VzoKeQmfdT+4cmJiYlItdO56/bQ0YXDHGAiUp1i6bB3Z67N3FNzSSqHmZOaS0/NjNTYDJCZRP9fT/UxoaY8UVIxM1QTCYccNNXsXVFkPHWk4VMgivRyn8iRWrsV8GP6sLDfwKQJuc3iDmhUWSoK3gd20p7ntDB6xx7vqbLoPet1zU56/yj/D1R9qrq3r61qz3lv20nJdN1H31K1FxYW7Bvn+vcuO3f4PrrR24xtRLjgHNebhrtinLrizL33TIx8efs2K95TqrIUY78K3Y8hUgXQiPCIc3wbAvfEyC7dT7ME2AXOmvFF07DiPQ80DYvjjkFo5Y6qPe6c/dlxyA4byt+Kwl5dWLCnve+FzSjh0P7zlCrnY2ST3snDcVZTQu8k427gVxF+yhwYdNmv6IxR+/PZJUE5mc0zIy9EZ+qpUZh5lZDDfw9J5SDpQQEIAAAAAElFTkSuQmCC' - - -lcdcolon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAABdklEQVRoge2YQU7DMBBFn9MEVVBYcADEmiVH4BScD3EL7gIcAYEEFSQdFrZRCJGYeBa1qnlSZSuVJz8/k/EkARAqo9m3gDlclBYXpcVFaXFRWlyUFhelpUpRrWVxAFaTYwIMlqApbnX9VJFT+UpOgRugA3ZE195C4EHE7JYs/TVpvAJ5BxGQPo1PTSObEASQUBA7xS/nE/hI8+yMEJ20YBK1G81zwnfWoKXr85MxAF9p3o/+24tT45N2k/EYOLIoovDpy069AvfAOdGxDngR+cmzUg6nTv0XoJ85toQqnapyQ3ZRWlyUFhelpUpR5oo+1xFYq3GVFd3Uo18Cd8CGuN+tgcem4VaErUjxFZtErYFr4ITY8K2AM2ILsy0JnDD36Pnk49a4mh49B2qtQUvX5zzZ8TdnVqVBR5jWD/x2C6Ko6av8UkwloQUuiImd32J64Bnb94TDqVNj5u7/9JYupUqnqtyQXZQWF6XFRWlxUVpclBYXpeUbS+1qOYf8HRgAAAAASUVORK5CYII=' - - -lcdblank = 'data:image/png;base64,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/GMzLdsAADpEaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0NSA3OS4xNjIzMTksIDIwMTgvMDIvMTUtMjA6Mjk6NDMgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICAgICAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIFBob3Rvc2hvcCBFbGVtZW50cyAxNy4wIChXaW5kb3dzKTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAxOC0xMC0yMFQxNjowOToyNS0wNDowMDwveG1wOkNyZWF0ZURhdGU+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE4LTEwLTIwVDE2OjM2OjMyLTA0OjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxOC0xMC0yMFQxNjozNjozMi0wNDowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8L2RjOmZvcm1hdD4KICAgICAgICAgPHBob3Rvc2hvcDpDb2xvck1vZGU+MzwvcGhvdG9zaG9wOkNvbG9yTW9kZT4KICAgICAgICAgPHBob3Rvc2hvcDpJQ0NQcm9maWxlPnNSR0IgSUVDNjE5NjYtMi4xPC9waG90b3Nob3A6SUNDUHJvZmlsZT4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+eG1wLmlpZDoxYTc5NDQ5ZC05ZmM0LWZiNGItOTNjYy0xNTcyMjU1MGU5OTk8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPmFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkMGIzZGFmNS1kNGE3LTExZTgtOGI3ZC1jYmQxMWFlODBkMGE8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+eG1wLmRpZDowZDlkYjE1OS1lYThmLWQxNDEtYWE4MS04YTY4NzFkNTI4YTc8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkhpc3Rvcnk+CiAgICAgICAgICAgIDxyZGY6U2VxPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jcmVhdGVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6MGQ5ZGIxNTktZWE4Zi1kMTQxLWFhODEtOGE2ODcxZDUyOGE3PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE4LTEwLTIwVDE2OjA5OjI1LTA0OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgRWxlbWVudHMgMTcuMCAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjFhNzk0NDlkLTlmYzQtZmI0Yi05M2NjLTE1NzIyNTUwZTk5OTwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxOC0xMC0yMFQxNjozNjozMi0wNDowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIEVsZW1lbnRzIDE3LjAgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6U2VxPgogICAgICAgICA8L3htcE1NOkhpc3Rvcnk+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjMxNDAwMDAvMTAwMDA8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOllSZXNvbHV0aW9uPjMxNDAwMDAvMTAwMDA8L3RpZmY6WVJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+Mzc8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+NjA8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PvnJsBAAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAEZJREFUeNrszkEBACAMAKHT/p1nDPeABJxqWua2kJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSU1G8PAAD//wMAGdkBdyV2S1QAAAAASUVORK5CYII=' - - - - -w1 = 'data:image/png;base64,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' - - -w2 = 'data:image/png;base64,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' - -w3 = 'data:image/png;base64,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' - -w4 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABGCAYAAABxLuKEAAAACXBIWXMAAAsSAAALEgHS3X78AAAUt0lEQVR4nO2ce5TdV3XfP/v8Hvc1d+48LI1GoweSZVvGsuWHsOLK0NjEJBTbgbQYcFoSQ9oVAmQloXFqFoUmNMkioV0JDU1CWroocZPlOLGpsSl+Fdu4ED+IZWzLlrHQ+zGa152Z+/g9ztn94/e7M6PHMDOSLJO1tNe6msf53XP2+Z79+O595kpUlXNyopg3WoEfVzkHzDzyBgOjZh4dBNQ729oco8A/whgjwOuu9Fm0GJUTvk8PbSA9uj7/5ey4a3UR774SnH+SOc7KSZ4lYGy+6Q44+YaT/dfReupX8zEDLgAgHb6U9jO3g3HgZBYLUTTxUfu66/06L6DZ/O0Xr6f9wk+BKDgv+wqKTmn7hRsAD8QCFoB45/WkIz7gsrHcmlxzGY3HfguRE5c6w/I6AKNyrNsApjtl8v7P4Zo1MBbX7AEQRCTedSHJvstx45dp/NQnAbT93M2qreVAAGmKxiGANh76JHb8gtyS5gRnPeP7OLMTalxB02JmEWo6lkEw9Bx2eC2T934GgOSZO0ie+n2cXY6d9Gm/+G7S7/+KJN+9nejlfyPxvk2iUsKOXEzz7nsgGiA9cplMPfhRwvMfzhbL50YNqKBJcCa34i/8yGJEDYjDNZdp48lflK7rP48pTWcKR2WkMEV40QM6+fVfp7ztTtHJDRo9czNpeT9SgtZ3bhezSqDgafv//JnYSNRNDEn7G1/BHrgcO3y31r/x86IuIDz/W2CLqPMQvwniaHzng/i9r1G4+MkZXU5TzpDFSGbaXs9u0bbo0T+8B9ccAFHiZ28jevgv8PsjsS1k9L9+HTv6T0W6kMCskuIQ4hPi6j52UkQS1UIvUqgOQmMz/nkw/fU/ovH0O/EHd+P2v4fmXY+gjZUgqlMPfFqbj3+U8IJncmXOSNY6TR6jAkh+QlkqVVvQAx95TUzXUVb8znWkr95C694/R2qW9ohBY6HYB/FhxdbBJYKqgopiEDTbmwSKV1SCAYOUIKorxWUgVjDVI3R96Bomv/khrf/1J2XZ7T9H6cqvZXHH2OP0eiOAycFR5yHGZop5KY2//wCHP/m/KF7xNLWfeAm39xcwJUe839B4UdXFYGoiXh+YCkgIksdSdaAp0ELtFGLHQduqxUGR8iYFXyAc0YTvyeSD76Cy7W6W/cZ7MzCEPOvZ2Rj0RgATvbwV0z1KsPIHgKCxR/TtT0HoMX7vv6KxfS1db1ZKQ0L9cUim0MI6xF9GloVdBgSdg1WyzQlgUDEIgroGxHvBTiDVS6GwDiaecxhj6Hv/FzFBhL/mUYIL7wfAxTXiHW8lXP8kpjqeg7akjZ5i8O0sVEg4+sd/oqUrH5XaTX+OKddxjVXafvDDlJZbSm930twpDN8N/iCUNiPqIJ2cA8L8ko0qIgbCdeCmYfI58F5Fqz8BftURP/tRhDEJL/sqAK1nbtGJr/0GXdvuleLm+7Npls57TsNinAHjaO+4jn0f+wZB3zC9t32K0rqAxt99Sb2KMP2cyOR2KG4ErwKula+61JivmWWZAAjRaBdChPa/04lg8NY+ib/pD7V+zx00vrtV+n/x0/R84LOnk6FOM8akPvgp7ZevYv+v30M6vJrC+WP0Xt3D1HOGyX9AK5sQ8rgxA8iprCm52wmYEsSHwE7BwLshnoiZ3mEg8bX/Q5+Rnvf9TnZwojmnWrIrnQowgtqMpouXYEcHSfdeTvPpm5h8+CN4RYezhsnnoLQOsKDKGaXx6sAUIR5GJYDaFSrphBCs3kntpk9hug4Rvvn/gTMoihj3OgOj+SYNM6egSY2JLzyk9shb8M6zYhtGD94lUugH482e8hkXB14RmgehchHaf62STjvcqCfVf/lhild/eQ7x7EL8NhKmi519CcDkHKHx1JXU//fHKV7yPOXLv0u4/nu4kW3Uv/wAIgWOPKCaDIsUzgMX8/qAMkfEQOswDLwnpdDtE26+h66bbyPeu5HolS20nn8r3sBr9L3vP2K6WouedskWgwu0/sDPc+Q//weiH66lcP4wwdpdUuq5DNso64G/Velakc/7erdOFEyAtEYhHFIGf0ZoDu/GTvnEu1ZBME3/B79Ezy2fw+seXkqsOYUYk0/umt0c/W//lrEv34Fr+3RtQif3IK0fQGUI3KKt9jRFssDeGEVX3ojYUSWti1bedr8s++XbCde8dLZ4TE7922UqF5SUf9YScV04i0w8JBTLYBNQewpTn6KIB0kTaeyDFW9X4lGV4vkl3NgGdOVOxJubshcF0CkUkfm8UnCa7N6MTlbVFNHp10TjSVQlA0XdWXop2DTLC5O70LQhKr4heulKNBXET3Oll+Tbi7cYTQLUCqaQgBYxpRGp3fZ+nfjqA+J2baVxwOEwxG3wgkzhsyEiGTBWkPYYxBNCWLPa9Z6PS/HKh9B2D2qaaLsKWLzqxGISwiJiTJ7y2jsvZPxvbqX54jXYsSHVNEs5dmo9lTXdcmi7Mvma4BWh0gWef3bAMQKNBiRtwKFr3wWmZUkao2KkpjAhpjpKeP4otZ/+U7p/5q8QzzBboJ1UFmExOaUurN1D3y13Udyxg6n/+3Ya39tGvPsiCgOeSBmiSQEDzkFjGsrlLJWqnmbGnudiQMlAaUUQtbO1bILE09C9zBAdXo45b4+Ur/gWXdc+QemSpwlXvZpX8QuWCaeelZpP3arjf/kF8f1+0lh5+U4haeTtAwVjoFTMvi5pjayyzpiy6yCQvWYqccnIYxxDFIEIikGSaRh6Gwxts0QjHuVr7qb27l/D9BxYamZaQlZSAIM6g7Z8ohfeIUT9WAcuFpzO6AyAtWCbEAYQZMz8+B75nGsROtW2IKBT4ByKj+IjJAgWTAH1Kkg6Ba0YTZipncVIdgCq4FJRfIhe3irRD6+guPkAoj54yWJ3uzSLUWfymicrzqJXr9WxL31FkpH1vHKfI5owGH9mkzOW4hkIJGvBnIBL3lwQg5DibExUfitp/01I5c3gVdC0DpPPEo78NYWpV4iCC0h63wnVjeAVIDqMN/YtiqMPIedvs/Sv9vA33cd5//o2vOWjs8po1hE448AAaOzTfGELU9/+aSaf+Ema/3ClVtZ1s+8Vlan9glfIdjy33aKa/eiRgWPyohfp1F2gMVaL1If+gGDdxygWBDEzpAmrQmtsH3b3X+K/6YMU+4YQ0ayRpRDFkO74PEV9VNPuq0Q575CEtWdLBX3Yr27+e4qXb0fMmSwJct9s71rL2L23Un/oZ2luv5B0tBcJobweereiux6Ho6+AX8xjwbzzZWAYg0gKmuAkIPb6mTzvlwgv+SxlaWCdRdXMgCs4MCUSAgKSvLeTX1KKIl5AlAjx9FHCriGMgE0bGo08IT3t/3S40LXq21q9+UFq77hLvGqdBe7AFw9MvH850d7LUNsHtoEyTuOJ9xO//HEKfU4PPGvY/R0Iwh8dbMUgzkKS4oxPHKxieOUdsOImCsUSxcDh8nB2ojgMDlVB5Ti/VIcIGC/EuRhAjQni2BYK8dSuv+vr5TNhsdo0wcBu8Bcke6eSlTJeY0f7dPjzX5No17UEPZbWuOeev5eZLCKGWV/KWaoYjI1ITR/t1b+EDL4Lrb4JDdcgtkG71SBOUjzPQzuBGKWjo0in2TmvbvmhSJbVNIOwHbvJ1MprIu5Ab638hd7e3oc4fYs5ZtF8twouChHxiPdcpwd/73/ijvbw2ovqJvaL+D4mTWbZggHnF5G0TVTaSPstd1EevBS/s31NUc0S29TUFBP1OmBwzmEEjOdlSzqLc2A8syA1mhvi8nyhzqlMTk41V61aeXNfX98jqmpETt76XEK6FhBxeVdM8Motbe3cyL7f/bCM3tdN9wbVgYvEO7qPhIBm99twpTXgYrzpFyhNb8eaCo3L/wfdg5dCu0kqGWqa3WIDUK1WsdYyMjJKrdZNpVLB931UFWstzWaTyckprHUYEfT4proq4nmICKouz+CqIuI8z0uCICiPjo69r6+v7xFmripOCxjI4o1x2HpND37ht9j3uU9gG6GW11gpX2iENpOrbqG9+hMEA5fjeSGqlnYS0X7tq6STOykt24JtTeMwyMyfu8xabZqmlMtlVqzwKRQKiIBziogQhgFh2EOpVOLAgYO0ogTP91HnZtzN8zxa01OMjY3R29uXzyGiqp6i9vChQwwNrTy6oBkswZWy2DL51NUc+Pxvk+7vpnrN43RtfYb4e79M+5mfqttrbLP333ndXSU0mZ6jrE8sJaLmNOWw0xpdQDERnDtRN1XF933a7Rbbtz+PAsViEWM8UGV6epqxsaNEUYTvh7ZYLKgxRpxzMjU5aXw/OHLjTTfeUKvVvn+mXMmhzhB2H3Fr/v1vts0GE9lSTZPx9YXW9qs8uZC6/3Om6gtxYwSV2amT1CK0KPgeSQqLKFXgeBeZI2maEoYhvu9z+PAhSqUSExN1ms3mTM/d933XbNbNyEgsAJ7vucEVK57atu3aT9Rqte8D84ICS7AYVYwIrtmya44eHf0srvkBz5MAwKohTR3FQkihEJz0pBfa7FJFREjTFGMEVaFeH+f555/nwIEDhIGnjWZbli9f/v0VKwZ/UKt171q5cuWDQ0NDj3meF/0oS5mZf5HACKCtVnPd3r17H66Uy+uDoDB3+6Lq5EzfkixOrUx8PyCO2zzyyKPu1V37zSUXrX3gXTfedFu1Wh3OYq+oanbZvxAosEhXUlVfRJKDBw/d4fvF9Ygft6M4nKX85FmAM2UQS5Z2u02xVLUXXzDobVpX/9Mr3nrrrwhqVNXLAVERsSyyi7cgMKrqiYhN05R2O9paKpWJougM/cHR4kRg1hTzuusEUaXdappS9xDrWr93o4wPPKq9771bxBkRs+QG9EIbFBGx9Xr90sOHj9wcx/HKIAhRdYu+KNfcvxZrSKo6w2k64pw7pkFhzIlZTVXVUyetVhK79X/2EZJHLqS9byPF1S9zCnfY8wLTCVB79uz+F/v3HfjvxXKlOwxDjeI402+BDWv+r8m7eG5OADppVyYHxJiM8UIGiBghDEJ8PyNtSZoSR3EOoIdzaed94tQ4a1vh3iPFyy65+CO/j6aFbDlxzHS7WNS9zkmDbweUqamp87dvf/5b3bWeVagmqU19MivCGIOq4qxlplt0LCp4vkccxYgxhEGItWnesDoOFhE842Odpd1qUipXUFV6e7rp7++nUCjieWYGrFarzfDwUaanp+nr66VWqxIEPqqi1jo5sH9fOjg0+LO9PX0PWGuLgCcibWNMruzCcWY+YDwRsSMjI9c8/vgT3x5YscIWCgWRrE1m0sRKvT5GqdJFuVTBOTtbUUtG70WEdrvN3r27EWDFikHKla6MiAnHPI8qcRxxYP9+6vUJent72bz5ctauXYOqzryyx7NDSZKEVqtJtdqdB/6ZcSciZmKi/kMR7gSuBJar6r4wDO+sVqv3zqSoLDudFKT50rXkvu49/PDDX9nx0ku3Vms1FwaBCYJAo3ZEs9WUQqFIb29vVs8EYcZW1ZHEcVYMjo9h7WzcK5VKlEsVwkIhLwyVJE1ot9tM1sfp6qpy6aWXUi6X6e3txfd9nLN5zJlNgZpnwYwdnxg6cqA0SRIJggCRrOicbkwThuHH+vv7v5hPeGJNsgAwdN5krS2/9NJLv7B37553NZvNoeHh4fWqdBcKBZumqXHWivE8jDEYMTh12DTFqeIZg+QuB8yANDfAat7U8v2A66+/nsHBlaRpmj/7IxoMWWA6IVDPjCMqSApqFBVRsWIkqNcndnR3Vz9TLJZ2VyqVZ405eatzIYI325zM4o7s2LHjvQ8++OBfxHHcVSwWnTHGqqpnbefzAlkMyhV2IuKccyZf66S7cM4RhiE33PAOKpUqaZock3kWIo4Lj2vHxdU5SxzHYtO0bYz527Vr1/xaoVAY4bjYsyDzzce9/HtnjNFDhw5teeyxx3579+7dNyRJEvi+j5dZjcv9F+cc1lpJ0yxrBEGA53l27gkrSIcuW2v1ggsuYNOlm71juUre1yVvVOnMO5l1K2bca7bfLDOWaUSwuctJTvSMMTQaDb9UKvyXDRs2/Gonri4amOPE5Is5a62/Z8+et73yyivv27t379UTExOroyjq7/h8EASuVqu9unr16qfr9fENBw8evqzZbJY77iQiSMcq8uCapglbtrxFN226TNI0naED1lmM8fJ4MnsDIcZk8cRZpDM+BynjZb9rt1uUy5UcWO1U6EkURUEUtb951VVX3uR5XueO+5SAyfeRfaghj+oSRVFPs9nsbTQay6MoUs/zpFKpxF1dXftLpdJwkiS16enpgaNHj1505MjwFePjY0ONRrMnbk+tszZ1fthlisXCzq5KxRsbH39PtbsW1LprxjOGqcYU01OTDAyspFKpHOMz1lrGx8eYmqwzMDBIuVxmhjooxEnM0eHDTNQn6OnpdV1dVc2aXmgctfXgwQPB5s2bP3vFFVd8+nQt5hhxzvmA5vxgXhBFZJ7m8wyf7fh3eN99931z586dP9nT05OoqnHOdeKVlEolCcMCYgSbpLSjSJMkUlWMMcYVi0UThKEApHFCq91S55wzxkir1TIda85TNRdddNGd11133ccKhUK9U2ieEWDmSCewHk/zNF9sZnxORlIk7+Fl5u0bY5KRkZGr7r///q8ePnz4Yub0KXzfn5PJMp1d3rkzxqiqiu/7zpic+iuIMZIkiZemKWvXrv1uuVze5ft+2Nvbu3fNmjVfHxwcfMIYc1Im/GP1mcjOqSVJUtu5c+c/HxkZ2aiq8dTU1Jv27Nlz9fR0YyBNEwEIgkCr1eqhjRs33r169epnnnzyyd/cs2fPP7HWHtPm7O/v/8HWrVu/uGXLlj85CQj5bd/SeMwbIvO5XqPRGJiYmFgbx1lHrlAo0NfX92qxWBwDiKKotm/fvq2HDh16S7vd7g/DsDEwMPDcqlWrnuzq6jp8nEXnBfvSme8bLdLhPvnP88WxznOdUuCEzaiqqKrJidyiN/vjCswJoqpmLkHsxKlONy4H4ITPaouIPRlgC8k/GmDOtpz7LwzmkXPAzCPngJlHzgEzj5wDZh45B8w8cg6YeeQcMPPIOWDmkf8P1g4l0uBhGEEAAAAASUVORK5CYII=' - -w5 = 'data:image/png;base64,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' - -orangeround = 'data:image/png;base64,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' - -if __name__ == '__main__': - lcd_clock() diff --git a/Demo_LED_Clock_Weather.py b/Demo_LED_Clock_Weather.py index dd4e3f07..cde7115e 100644 --- a/Demo_LED_Clock_Weather.py +++ b/Demo_LED_Clock_Weather.py @@ -50,7 +50,7 @@ class GUI(): layout = [[sg.Column(clock, background_color='black')], [sg.Column(weather_cols[x], background_color='black') for x in range(NUM_COLS)], - [sg.RButton('Exit', button_color=('black', 'black'), + [sg.Button('Exit', button_color=('black', 'black'), image_data=orangeround[22:], tooltip='close window', pad=((450,0),(10,0)))]] # Create the window diff --git a/Demo_LED_Indicators.py b/Demo_LED_Indicators.py index be9417dd..57eb84b2 100644 --- a/Demo_LED_Indicators.py +++ b/Demo_LED_Indicators.py @@ -33,7 +33,7 @@ layout = [[sg.Text('My LED Status Indicators', size=(20,1))], [sg.Text('RAM'), LEDIndicator('_ram_')], [sg.Text('Temperature'), LEDIndicator('_temp_')], [sg.Text('Server 1'), LEDIndicator('_server1_')], - [sg.RButton('Exit')]] + [sg.Button('Exit')]] window = sg.Window('My new window', default_element_size=(12, 1), auto_size_text=False).Layout(layout).Finalize() diff --git a/Demo_MIDI_Player.py b/Demo_MIDI_Player.py index 79c73e3d..11b4445c 100644 --- a/Demo_MIDI_Player.py +++ b/Demo_MIDI_Player.py @@ -66,14 +66,14 @@ class PlayerGUI(): [sg.T('MIDI File Player', size=(30, 1), font=("Helvetica", 25))], [self.TextElem], [self.SliderElem], - [sg.ReadFormButton('PAUSE', button_color=sg.TRANSPARENT_BUTTON, - image_filename=image_pause, image_size=(50,50), image_subsample=2, border_width=0), sg.T(' '), - sg.ReadFormButton('NEXT', button_color=sg.TRANSPARENT_BUTTON, - image_filename=image_next, image_size=(50,50), image_subsample=2, border_width=0), sg.T(' '), - sg.ReadFormButton('Restart Song', button_color=sg.TRANSPARENT_BUTTON, - image_filename=image_restart, image_size=(50,50), image_subsample=2, border_width=0), sg.T(' '), - sg.SimpleButton('EXIT', button_color=sg.TRANSPARENT_BUTTON, - image_filename=image_exit, image_size=(50,50), image_subsample=2, border_width=0, )] + [sg.Button('', button_color=sg.TRANSPARENT_BUTTON, + image_filename=image_pause, image_size=(50,50), image_subsample=2, border_width=0, key='PAUSE'), sg.T(' '), + sg.Button('', button_color=sg.TRANSPARENT_BUTTON, + image_filename=image_next, image_size=(50,50), image_subsample=2, border_width=0, key='NEXT'), sg.T(' '), + sg.Button('', button_color=sg.TRANSPARENT_BUTTON, + image_filename=image_restart, image_size=(50,50), image_subsample=2, border_width=0, key='Restart Song'), sg.T(' '), + sg.Button('', button_color=sg.TRANSPARENT_BUTTON, + image_filename=image_exit, image_size=(50,50), image_subsample=2, border_width=0,key='EXIT')] ] window = sg.Window('MIDI File Player', default_element_size=(30, 1), font=("Helvetica", 25)).Layout(layout).Finalize() @@ -90,8 +90,8 @@ class PlayerGUI(): if 'window' not in locals() or window is None: # if the widnow has been destoyed don't mess with it return PLAYER_COMMAND_EXIT self.TextElem.Update(DisplayString) - event, (values) = window.ReadNonBlocking() - if values is None: + event, (values) = window.Read(timeout=0) + if event is None: return PLAYER_COMMAND_EXIT if event == 'PAUSE': return PLAYER_COMMAND_PAUSE diff --git a/Demo_Machine_Learning.py b/Demo_Machine_Learning.py index 3ef1743a..0b5881f1 100644 --- a/Demo_Machine_Learning.py +++ b/Demo_Machine_Learning.py @@ -43,17 +43,17 @@ def MachineLearningGUI(): def CustomMeter(): # layout the form layout = [[sg.Text('A custom progress meter')], - [sg.ProgressBar(10000, orientation='h', size=(20,20), key='progress')], + [sg.ProgressBar(1000, orientation='h', size=(20,20), key='progress')], [sg.Cancel()]] # create the form` window = sg.Window('Custom Progress Meter').Layout(layout) progress_bar = window.FindElement('progress') # loop that would normally do something useful - for i in range(10000): + for i in range(1000): # check to see if the cancel button was clicked and exit loop if clicked - event, values = window.ReadNonBlocking() - if event == 'Cancel' or values == None: + event, values = window.Read(timeout=0, timeout_key='timeout') + if event == 'Cancel' or event == None: break # update bar with loop value +1 so that bar eventually reaches the maximum progress_bar.UpdateBar(i+1) diff --git a/Demo_Matplotlib_Animated.py b/Demo_Matplotlib_Animated.py index 764e3e76..20db3496 100644 --- a/Demo_Matplotlib_Animated.py +++ b/Demo_Matplotlib_Animated.py @@ -36,8 +36,8 @@ def main(): dpts = [randint(0, 10) for x in range(10000)] for i in range(len(dpts)): - event, values = window.ReadNonBlocking() - if event is 'Exit' or values is None: + event, values = window.Read(timeout=10) + if event is 'Exit' or event is None: exit(69) slider_elem.Update(i) diff --git a/Demo_Matplotlib_Animated_Scatter.py b/Demo_Matplotlib_Animated_Scatter.py index 98cb7bb4..5d87bbd1 100644 --- a/Demo_Matplotlib_Animated_Scatter.py +++ b/Demo_Matplotlib_Animated_Scatter.py @@ -26,8 +26,8 @@ def main(): canvas = canvas_elem.TKCanvas while True: - event, values = window.ReadNonBlocking() - if event is 'Exit' or values is None: + event, values = window.Read(timeout=10) + if event is 'Exit' or event is None: exit(69) def PyplotScatterWithLegend(): diff --git a/Demo_Matplotlib_Ping_Graph.py b/Demo_Matplotlib_Ping_Graph.py index 7532bb04..bec2c4b0 100644 --- a/Demo_Matplotlib_Ping_Graph.py +++ b/Demo_Matplotlib_Ping_Graph.py @@ -646,7 +646,7 @@ def main(): global g_my_globals # define the form layout - layout = [[ sg.Canvas(size=SIZE, background_color='white',key='canvas') , sg.ReadButton('Exit', pad=(0, (210, 0)))]] + layout = [[ sg.Canvas(size=SIZE, background_color='white',key='canvas') , sg.Button('Exit', pad=(0, (210, 0)))]] # create the form and show it without the plot window = sg.Window('Ping Graph', background_color='white', grab_anywhere=True).Layout(layout).Finalize() @@ -662,8 +662,8 @@ def main(): plt.tight_layout() while True: - event, values = window.ReadNonBlocking() - if event is 'Exit' or values is None: + event, values = window.Read(timeout=0) + if event is 'Exit' or event is None: exit(0) run_a_ping_and_graph() diff --git a/Demo_Matplotlib_Ping_Graph_Large.py b/Demo_Matplotlib_Ping_Graph_Large.py index f693e109..d6be561a 100644 --- a/Demo_Matplotlib_Ping_Graph_Large.py +++ b/Demo_Matplotlib_Ping_Graph_Large.py @@ -82,7 +82,7 @@ def main(): # define the form layout layout = [[sg.Text('Animated Ping', size=(40, 1), justification='center', font='Helvetica 20')], [sg.Canvas(size=(640, 480), key='canvas')], - [sg.ReadButton('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]] + [sg.Button('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]] # create the form and show it without the plot window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize() @@ -96,8 +96,8 @@ def main(): plt.tight_layout() while True: - event, values = window.ReadNonBlocking() - if event is 'Exit' or values is None: + event, values = window.Read(timeout=0) + if event is 'Exit' or event is None: break run_a_ping_and_graph() diff --git a/Demo_Media_Player.py b/Demo_Media_Player.py index d6bb2915..4ccd84bd 100644 --- a/Demo_Media_Player.py +++ b/Demo_Media_Player.py @@ -27,17 +27,16 @@ def MediaPlayerGUI(): # define layout of the rows layout= [[sg.Text('Media File Player',size=(17,1), font=("Helvetica", 25))], [sg.Text('', size=(15, 2), font=("Helvetica", 14), key='output')], - [sg.ReadButton('Restart Song', button_color=(background,background), - image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0), + [sg.Button('', button_color=(background,background), + image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0, key='Restart Song'), sg.Text(' ' * 2), - sg.ReadButton('Pause', button_color=(background,background), - image_filename=image_pause, image_size=(50, 50), image_subsample=2, border_width=0), + sg.Button('', button_color=(background,background), + image_filename=image_pause, image_size=(50, 50), image_subsample=2, border_width=0, key='Pause'), sg.Text(' ' * 2), - sg.ReadButton('Next', button_color=(background,background), - image_filename=image_next, image_size=(50, 50), image_subsample=2, border_width=0), + sg.Button('', button_color=(background,background), image_filename=image_next, image_size=(50, 50), image_subsample=2, border_width=0, key='Next'), sg.Text(' ' * 2), - sg.Text(' ' * 2), sg.Button('Exit', button_color=(background,background), - image_filename=image_exit, image_size=(50, 50), image_subsample=2, border_width=0)], + sg.Text(' ' * 2), sg.Button('', button_color=(background,background), + image_filename=image_exit, image_size=(50, 50), image_subsample=2, border_width=0, key='Exit')], [sg.Text('_'*20)], [sg.Text(' '*30)], [ @@ -53,15 +52,14 @@ def MediaPlayerGUI(): # Open a form, note that context manager can't be used generally speaking for async forms window = sg.Window('Media File Player', auto_size_text=True, default_element_size=(20, 1), - font=("Helvetica", 25), no_titlebar=True).Layout(layout) + font=("Helvetica", 25)).Layout(layout) # Our event loop while(True): - # Read the form (this call will not block) - event, values = window.ReadNonBlocking() - if event == 'Exit': + event, values = window.Read(timeout=100) # Poll every 100 ms + if event == 'Exit' or event is None: break # If a button was pressed, display it on the GUI by updating the text element - if event: + if event != sg.TIMEOUT_KEY: window.FindElement('output').Update(event) MediaPlayerGUI() diff --git a/Demo_Menu_With_Toolbar.py b/Demo_Menu_With_Toolbar.py index 78a3999d..9a9afa94 100644 --- a/Demo_Menu_With_Toolbar.py +++ b/Demo_Menu_With_Toolbar.py @@ -15,18 +15,18 @@ def ShowMeTheButtons(): sg.SetOptions(auto_size_buttons=True, margins=(0,0), button_color=sg.COLOR_SYSTEM_DEFAULT) - toolbar_buttons = [[sg.RButton('', image_data=close64[22:],button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0,0), key='_close_'), - sg.RButton('', image_data=timer64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_timer_'), - sg.RButton('', image_data=house64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_house_'), - sg.RButton('', image_data=cpu64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_cpu_'), - sg.RButton('', image_data=camera64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_camera_'), - sg.RButton('', image_data=checkmark64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_checkmark_'), - sg.RButton('', image_data=cookbook64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_cookbook_'), - sg.RButton('', image_data=download64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_download_'), - sg.RButton('', image_data=github64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_github_'), - sg.RButton('', image_data=psg64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_psg_'), - sg.RButton('', image_data=run64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_run_'), - sg.RButton('', image_data=storage64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_storage_'), + toolbar_buttons = [[sg.Button('', image_data=close64[22:],button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0,0), key='_close_'), + sg.Button('', image_data=timer64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_timer_'), + sg.Button('', image_data=house64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_house_'), + sg.Button('', image_data=cpu64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_cpu_'), + sg.Button('', image_data=camera64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_camera_'), + sg.Button('', image_data=checkmark64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_checkmark_'), + sg.Button('', image_data=cookbook64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_cookbook_'), + sg.Button('', image_data=download64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_download_'), + sg.Button('', image_data=github64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_github_'), + sg.Button('', image_data=psg64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_psg_'), + sg.Button('', image_data=run64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_run_'), + sg.Button('', image_data=storage64[22:], button_color=('white', sg.COLOR_SYSTEM_DEFAULT), pad=(0, 0), key='_storage_'), ]] # layout = toolbar_buttons diff --git a/Demo_Menus.py b/Demo_Menus.py index e916563f..779843c2 100644 --- a/Demo_Menus.py +++ b/Demo_Menus.py @@ -33,13 +33,16 @@ def TestMenus(): # ------ GUI Defintion ------ # layout = [ - [sg.Menu(menu_def, tearoff=True)], + [sg.Menu(menu_def, tearoff=False, pad=(20,1))], [sg.Output(size=(60,20))], - [sg.In('Test', key='input', do_not_clear=True)] + [sg.Text('Status Bar', relief=sg.RELIEF_SUNKEN, size=(55,1), pad=(0,3),key='_status_')] ] - window = sg.Window("Windows-like program", default_element_size=(12, 1), auto_size_text=False, - auto_size_buttons=False, default_button_element_size=(12, 1)).Layout(layout) + window = sg.Window("Windows-like program", + default_element_size=(12, 1), + auto_size_text=False, + auto_size_buttons=False, + default_button_element_size=(12, 1)).Layout(layout) # ------ Loop & Process button menu choices ------ # while True: @@ -49,9 +52,9 @@ def TestMenus(): print('Event = ', event) # ------ Process menu choices ------ # if event == 'About...': - # window.Hide() + window.Disappear() sg.Popup('About this program','Version 1.0', 'PySimpleGUI rocks...', grab_anywhere=True) - # window.UnHide() + window.Reappear() elif event == 'Open': filename = sg.PopupGetFile('file to open', no_window=True) print(filename) diff --git a/Demo_Multiple_Windows_Experimental.py b/Demo_Multiple_Windows_Experimental.py index 73339720..a5902d60 100644 --- a/Demo_Multiple_Windows_Experimental.py +++ b/Demo_Multiple_Windows_Experimental.py @@ -7,27 +7,27 @@ else: layout1 = [[ sg.Text('Window 1') ], [sg.Input(do_not_clear=True)], - [ sg.RButton('Read')]] + [ sg.Button('Read')]] window1 = sg.Window('My new window', location=(800,500)).Layout(layout1) layout2 = [[ sg.Text('Window 2') ], - [sg.Input(do_not_clear=False)], - [ sg.RButton('Read')]] + [sg.Input(do_not_clear=True)], + [ sg.Button('Read')]] -window2 = sg.Window('My new window', location=(800, 625)).Layout(layout2) +window2 = sg.Window('My new window', location=(800, 625), return_keyboard_events=True).Layout(layout2) layout3 = [[ sg.Text('Window 3') ], [sg.Input(do_not_clear=False)], - [ sg.RButton('Read')]] + [ sg.Button('Read')]] window3 = sg.Window('My new window', location=(800,750), return_keyboard_events=True).Layout(layout3) while True: # Event Loop - event, values = window1.Read(timeout=100) + event, values = window1.Read(timeout=50) if event is None: break elif event != '__timeout__': diff --git a/Demo_NonBlocking_Form.py b/Demo_NonBlocking_Form.py index fc5273e0..5fe714c0 100644 --- a/Demo_NonBlocking_Form.py +++ b/Demo_NonBlocking_Form.py @@ -13,22 +13,22 @@ def StatusOutputExample(): # Create the rows layout = [[sg.Text('Non-blocking GUI with updates')], [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='output')], - [sg.ReadButton('LED On'), sg.ReadButton('LED Off'), sg.ReadButton('Quit')]] + [sg.Button('LED On'), sg.Button('LED Off'), sg.Button('Quit')]] # Layout the rows of the Window and perform a read. Indicate the Window is non-blocking! window = sg.Window('Running Timer', auto_size_text=True).Layout(layout) # # Some place later in your code... - # You need to perform a ReadNonBlocking on your window every now and then or + # You need to perform a Read on your window every now and then or # else it won't refresh. # # your program's main loop i=0 while (True): # This is the code that reads and updates your window - event, values = window.ReadNonBlocking() + event, values = window.Read(timeout=10) window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) - if event == 'Quit' or values is None: + if event == 'Quit' or event is None: break if event == 'LED On': print('Turning on the LED') @@ -37,10 +37,9 @@ def StatusOutputExample(): i += 1 # Your code begins here - time.sleep(.01) # Broke out of main loop. Close the window. - window.CloseNonBlocking() + window.Close() def RemoteControlExample(): @@ -63,14 +62,13 @@ def RemoteControlExample(): # your program's main loop while (True): # This is the code that reads and updates your window - button, values = window.ReadNonBlocking() - if button is not None: - print(button) - if button == 'Quit' or values is None: + event, values = window.Read(timeout=0, timeout_key='timeout') + if event is not 'timeout': + print(event) + if event == 'Quit' or event is None: break - # time.sleep(.01) - window.CloseNonBlocking() + window.Close() def main(): diff --git a/Demo_Notification_Window_Alpha_Channel.py b/Demo_Notification_Window_Alpha_Channel.py index 4975a3d6..ea645eae 100644 --- a/Demo_Notification_Window_Alpha_Channel.py +++ b/Demo_Notification_Window_Alpha_Channel.py @@ -21,7 +21,7 @@ bcolor=('black', '#282923') sg.SetOptions(border_width=0, margins=(0,0)) layout = [[sg.T('Notification'+' '*14), - sg.Button('', image_data=red_x, button_color=('#282923', '#282923'))], + sg.CloseButton('', image_data=red_x, button_color=('#282923', '#282923'))], [sg.T('')], [sg.T('You have 6 new emails')],] diff --git a/Demo_OpenCV.py b/Demo_OpenCV.py index 3e1b0ec6..6e0db558 100644 --- a/Demo_OpenCV.py +++ b/Demo_OpenCV.py @@ -32,7 +32,7 @@ def main(): layout = [[sg.Text('OpenCV Demo', size=(15, 1),pad=((510,0),3), justification='center', font='Helvetica 20')], [sg.Image(filename='', key='image')], [sg.Slider(range=(0, num_frames), size=(115, 10), orientation='h', key='slider')], - [sg.ReadButton('Exit', size=(10, 2), pad=((600, 0), 3), font='Helvetica 14')]] + [sg.Button('Exit', size=(10, 2), pad=((600, 0), 3), font='Helvetica 14')]] # create the window and show it without the plot window = sg.Window('Demo Application - OpenCV Integration', no_titlebar=False, location=(0,0)).Layout(layout) @@ -40,8 +40,8 @@ def main(): # ---===--- LOOP through video file by frame --- # i = 0 while vidFile.isOpened(): - event, values = window.ReadNonBlocking() - if event is 'Exit' or values is None: + event, values = window.Read(timeout=0) + if event is 'Exit' or event is None: exit(69) ret, frame = vidFile.read() if not ret: # if out of data stop looping diff --git a/Demo_OpenCV_Simple_GUI.py b/Demo_OpenCV_Simple_GUI.py index ff051d32..7399dc96 100644 --- a/Demo_OpenCV_Simple_GUI.py +++ b/Demo_OpenCV_Simple_GUI.py @@ -1,8 +1,9 @@ import sys + if sys.version_info[0] >= 3: - import PySimpleGUI as sg + import PySimpleGUI as sg else: - import PySimpleGUI27 as sg + import PySimpleGUI27 as sg import cv2 import numpy as np from sys import exit as exit @@ -20,57 +21,68 @@ blur: simple Gaussian blur, slider sets the sigma, i.e. the amount of blur hue: moves the image hue values by the amount selected on the slider enhance: applies local contrast enhancement on the luma channel to make the image fancier - slider controls fanciness. """ + + def main(): + sg.ChangeLookAndFeel('LightGreen') - sg.ChangeLookAndFeel('LightGreen') + # define the window layout + layout = [[sg.Text('OpenCV Demo', size=(40, 1), justification='center')], + [sg.Image(filename='', key='image')], + [sg.Radio('None', 'Radio', True, size=(10, 1))], + [sg.Radio('threshold', 'Radio', size=(10, 1), key='thresh'), + sg.Slider((0, 255), 128, 1, orientation='h', size=(40, 15), key='thresh_slider')], + [sg.Radio('canny', 'Radio', size=(10, 1), key='canny'), + sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='canny_slider_a'), + sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='canny_slider_b')], + [sg.Radio('contour', 'Radio', size=(10, 1), key='contour'), + sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='contour_slider'), + sg.Slider((0, 255), 80, 1, orientation='h', size=(20, 15), key='base_slider')], + [sg.Radio('blur', 'Radio', size=(10, 1), key='blur'), + sg.Slider((1, 11), 1, 1, orientation='h', size=(40, 15), key='blur_slider')], + [sg.Radio('hue', 'Radio', size=(10, 1), key='hue'), + sg.Slider((0, 225), 0, 1, orientation='h', size=(40, 15), key='hue_slider')], + [sg.Radio('enhance', 'Radio', size=(10, 1), key='enhance'), + sg.Slider((1, 255), 128, 1, orientation='h', size=(40, 15), key='enhance_slider')], + [sg.Button('Exit', size=(10, 1))]] - # define the window layout - layout = [[sg.Text('OpenCV Demo', size=(40, 1), justification='center')], - [sg.Image(filename='', key='image')], - [sg.Radio('None', 'Radio', True, size=(10, 1))], - [sg.Radio('threshold', 'Radio', size=(10, 1),key='thresh'),sg.Slider((0,255),128,1,orientation='h', size=(40, 15),key='thresh_slider')], - [sg.Radio('canny', 'Radio', size=(10, 1), key='canny'),sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='canny_slider_a'),sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='canny_slider_b')], - [sg.Radio('contour', 'Radio', size=(10, 1), key='contour'),sg.Slider((0, 255), 128, 1, orientation='h', size=(20, 15), key='contour_slider'),sg.Slider((0, 255), 80, 1, orientation='h', size=(20, 15), key='base_slider')], - [sg.Radio('blur', 'Radio', size=(10, 1),key='blur'),sg.Slider((1,11),1,1,orientation='h', size=(40, 15),key='blur_slider')], - [sg.Radio('hue', 'Radio', size=(10, 1), key='hue'),sg.Slider((0, 225), 0, 1, orientation='h', size=(40, 15), key='hue_slider')], - [sg.Radio('enhance', 'Radio', size=(10, 1),key='enhance'),sg.Slider((1,255),128,1,orientation='h', size=(40, 15),key='enhance_slider')], - [sg.ReadButton('Exit', size=(10, 1))]] + # create the window and show it without the plot + window = sg.Window('Demo Application - OpenCV Integration', + location=(800, 400)) + window.Layout(layout).Finalize() - # create the window and show it without the plot - window = sg.Window('Demo Application - OpenCV Integration', - location=(800,400)) - window.Layout(layout).Finalize() + cap = cv2.VideoCapture(0) + while True: + event, values = window.Read(timeout=0, timeout_key='timeout') + if event == 'Exit' or event is None: + sys.exit(0) + ret, frame = cap.read() + if values['thresh']: + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)[:, :, 0] + _, frame = cv2.threshold(frame, values['thresh_slider'], 255, cv2.THRESH_BINARY) + if values['canny']: + frame = cv2.Canny(frame, values['canny_slider_a'], values['canny_slider_b']) + if values['blur']: + frame = cv2.GaussianBlur(frame, (21, 21), values['blur_slider']) + if values['hue']: + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) + frame[:, :, 0] += values['hue_slider'] + frame = cv2.cvtColor(frame, cv2.COLOR_HSV2BGR) + if values['enhance']: + enh_val = values['enhance_slider'] / 40 + clahe = cv2.createCLAHE(clipLimit=enh_val, tileGridSize=(8, 8)) + lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) + lab[:, :, 0] = clahe.apply(lab[:, :, 0]) + frame = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR) + if values['contour']: + hue = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) + hue = cv2.GaussianBlur(hue, (21, 21), 1) + hue = cv2.inRange(hue, np.array([values['contour_slider'], values['base_slider'], 40]), + np.array([values['contour_slider'] + 30, 255, 220])) + _, cnts, _ = cv2.findContours(hue, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + cv2.drawContours(frame, cnts, -1, (0, 0, 255), 2) + imgbytes = cv2.imencode('.png', frame)[1].tobytes() # ditto + window.FindElement('image').Update(data=imgbytes) - cap = cv2.VideoCapture(0) - while True: - event, values = window.ReadNonBlocking() - if event == 'Exit' or values is None: - sys.exit(0) - ret, frame = cap.read() - if values['thresh']: - frame=cv2.cvtColor(frame,cv2.COLOR_BGR2LAB)[:,:,0] - _,frame=cv2.threshold(frame,values['thresh_slider'],255,cv2.THRESH_BINARY) - if values['canny']: - frame=cv2.Canny(frame,values['canny_slider_a'],values['canny_slider_b']) - if values['blur']: - frame=cv2.GaussianBlur(frame,(21,21),values['blur_slider']) - if values['hue']: - frame=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV) - frame[:,:,0]+=values['hue_slider'] - frame=cv2.cvtColor(frame,cv2.COLOR_HSV2BGR) - if values['enhance']: - enh_val=values['enhance_slider']/40 - clahe=cv2.createCLAHE(clipLimit=enh_val, tileGridSize=(8,8)) - lab=cv2.cvtColor(frame,cv2.COLOR_BGR2LAB) - lab[:,:,0]=clahe.apply(lab[:,:,0]) - frame=cv2.cvtColor(lab,cv2.COLOR_LAB2BGR) - if values['contour']: - hue=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV) - hue=cv2.GaussianBlur(hue,(21,21),1) - hue=cv2.inRange(hue,np.array([values['contour_slider'],values['base_slider'],40]),np.array([values['contour_slider']+30,255,220])) - _,cnts,_=cv2.findContours(hue,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) - cv2.drawContours(frame,cnts,-1,(0,0,255),2) - imgbytes=cv2.imencode('.png', frame)[1].tobytes() #ditto - window.FindElement('image').Update(data=imgbytes) main() diff --git a/Demo_OpenCV_Webcam.py b/Demo_OpenCV_Webcam.py index c6fb89c3..6efb97b2 100644 --- a/Demo_OpenCV_Webcam.py +++ b/Demo_OpenCV_Webcam.py @@ -18,10 +18,10 @@ def main(): # define the window layout layout = [[sg.Text('OpenCV Demo', size=(40, 1), justification='center', font='Helvetica 20')], [sg.Image(filename='', key='image')], - [sg.ReadButton('Record', size=(10, 1), font='Helvetica 14'), - sg.RButton('Stop', size=(10, 1), font='Any 14'), - sg.ReadButton('Exit', size=(10, 1), font='Helvetica 14'), - sg.RButton('About', size=(10,1), font='Any 14')]] + [sg.Button('Record', size=(10, 1), font='Helvetica 14'), + sg.Button('Stop', size=(10, 1), font='Any 14'), + sg.Button('Exit', size=(10, 1), font='Helvetica 14'), + sg.Button('About', size=(10,1), font='Any 14')]] # create the window and show it without the plot window = sg.Window('Demo Application - OpenCV Integration', @@ -32,9 +32,8 @@ def main(): cap = cv2.VideoCapture(0) recording = False while True: - event, values = window.ReadNonBlocking() - - if event == 'Exit' or values is None: + event, values = window.Read(timeout=0, timeout_key='timeout') + if event == 'Exit' or event is None: sys.exit(0) elif event == 'Record': recording = True @@ -56,3 +55,4 @@ def main(): window.FindElement('image').Update(data=imgbytes) main() +exit() \ No newline at end of file diff --git a/Demo_PDF_Viewer.py b/Demo_PDF_Viewer.py index 39351a76..48671c6d 100644 --- a/Demo_PDF_Viewer.py +++ b/Demo_PDF_Viewer.py @@ -44,8 +44,8 @@ from binascii import hexlify sg.ChangeLookAndFeel('GreenTan') if len(sys.argv) == 1: - rc, fname = sg.PopupGetFile('PDF Browser', 'PDF file to open', file_types=(("PDF Files", "*.pdf"),)) - if rc is False: + fname = sg.PopupGetFile('PDF Browser', 'PDF file to open', file_types=(("PDF Files", "*.pdf"),)) + if fname is None: sg.PopupCancel('Cancelling') exit(0) else: @@ -99,17 +99,17 @@ goto = sg.InputText(str(cur_page + 1), size=(5, 1), do_not_clear=True) layout = [ [ - sg.ReadButton('Next'), - sg.ReadButton('Prev'), + sg.Button('Prev'), + sg.Button('Next'), sg.Text('Page:'), goto, ], [ sg.Text("Zoom:"), - sg.ReadButton('Top-L'), - sg.ReadButton('Top-R'), - sg.ReadButton('Bot-L'), - sg.ReadButton('Bot-R'), + sg.Button('Top-L'), + sg.Button('Top-R'), + sg.Button('Bot-L'), + sg.Button('Bot-R'), ], [image_elem], ] @@ -124,13 +124,11 @@ old_zoom = 0 # used for zoom on/off # the zoom buttons work in on/off mode. while True: - event, values = window.ReadNonBlocking() + event, values = window.Read(timeout=100) zoom = 0 force_page = False - if event is None and values is None: - break if event is None: - continue + break if event in ("Escape:27",): # this spares me a 'Quit' button! break diff --git a/Demo_PNG_Thumbnail_Viewer.py b/Demo_PNG_Thumbnail_Viewer.py index 197fbb24..b11a71bd 100644 --- a/Demo_PNG_Thumbnail_Viewer.py +++ b/Demo_PNG_Thumbnail_Viewer.py @@ -57,7 +57,7 @@ buttons = [] for display_index in range(ROWS): row = [] for j in range(COLUMNS): - row.append(sg.RButton('',border_width=0,button_color=sg.COLOR_SYSTEM_DEFAULT, key=(display_index, j))) + row.append(sg.Button('',border_width=0,button_color=sg.COLOR_SYSTEM_DEFAULT, key=(display_index, j))) buttons.append(row) col_buttons = [[]] @@ -117,12 +117,6 @@ while True: for j in range(ROWS): for i in range(COLUMNS): set_image_to_blank((i,j)) - # img = Image.new('RGB', (1,1), (255,255,255)) - # img.thumbnail((1,1), Image.ANTIALIAS) - # bio = io.BytesIO() - # img.save(bio, format='PNG') - # imgbytes = bio.getvalue() - # [window.FindElement((i,j)).Update(image_data=imgbytes) for j in range(ROWS) for i in range(COLUMNS)] elif event == 'About': sg.Popup('Demo PNG Viewer Program', 'Please give PySimpleGUI a try!') elif type(event) is tuple: diff --git a/Demo_PNG_Viewer.py b/Demo_PNG_Viewer.py index 807c89e7..4fba5b1a 100644 --- a/Demo_PNG_Viewer.py +++ b/Demo_PNG_Viewer.py @@ -30,11 +30,11 @@ menu = [['File', ['Open Folder', 'Exit']], ['Help', ['About',]]] # define layout, show and read the window col = [[sg.Text(png_files[0], size=(80, 3), key='filename')], [sg.Image(filename=png_files[0], key='image')], - [sg.ReadButton('Next', size=(8,2)), sg.ReadButton('Prev', size=(8,2)), + [sg.Button('Next', size=(8,2)), sg.Button('Prev', size=(8,2)), sg.Text('File 1 of {}'.format(len(png_files)), size=(15,1), key='filenum')]] col_files = [[sg.Listbox(values=filenames_only, size=(60,30), key='listbox')], - [sg.ReadButton('Read')]] + [sg.Button('Read')]] layout = [[sg.Menu(menu)], [sg.Column(col_files), sg.Column(col)]] window = sg.Window('Image Browser', return_keyboard_events=True, location=(0,0), use_default_focus=False ).Layout(layout) diff --git a/Demo_PSG_SDK_Quick_Ref.py b/Demo_PSG_SDK_Quick_Ref.py index 0698c641..299d63a4 100644 --- a/Demo_PSG_SDK_Quick_Ref.py +++ b/Demo_PSG_SDK_Quick_Ref.py @@ -376,9 +376,77 @@ Window( title text_justification=None no_titlebar=False grab_anywhere=False - keep_on_top=False) + keep_on_top=False + resizable=False) """ +desc_window_methods = """ +Layout(rows) +Call to set the window layout. Must be called prior to Read. Most likely "chained" in line with the Window creation. + +Finalize() +Call to force a window to go through the final stages of initialization. This will cause the tkinter resources to be allocated so that they can then be modified. + +Read() +Read the Window's input values and button clicks in a blocking-fashion +Returns event, values + +ReadNonBlocking() +Read the Window's input values and button clicks but without blocking. It will immediately return. + +Refresh() +Cause changes to the window to be displayed on the screen. Normally not needed unless the changes are immediately required or if it's going to be a while before another call to Read. + +SetIcon(icon) +Sets the window's icon that will be shown on the titlebar. + +Fill(values_dict) +Populates the windows fields with the values shown in the dictionary. + +FindElement(key) +Rerturns the Element that has a matching key. If the key is not found, an Error Element is returned so that the program will not crash should the user try to perform an "update". A Popup message will be shown + +SaveToDisk(filename) +Saves the window's values to disk + +LoadFromDisk(filename) +Fills in a window's fields based on previously saved file + +GetScreenDimensions() +Returns the size (w,h) of the screen in pixels + +Move(x, y) +Move window to (x,y) position on the screen + +Minimize() +Sends the window to the taskbar + +CloseNonBlocking() +Closes a non-blocking window + +Disable() +Stops a window from responding until Enable is called + +Enable() +Re-enables a previously disabled window + +Hide() +Completely hides a window, including removing from the taskbar + +UnHide() +Restores a window hidden using Hide + +Disappear() +Makes a window disappear while leaving the icon on the taskbar + +Reappear() +Makes a window reappear that was previously made to disappear using Disappear() + +SetAlpha(alpha) +Sets the window's transparency. 0 is completely transparent. 1 is fully visible, normal +""" + + desc_menu= """ Menu(menu_definition background_color=None @@ -390,8 +458,8 @@ desc_menu= """ desc_button_types = """ There are multiple button types / names to choose from -SimpleButton = Button -ReadFormButton = ReadButton = RButton +CloseButton = CButton = SimpleButton +Button = ReadFormButton = ReadButton = RButton RealtimeButton DummyButton FolderBrowse @@ -501,7 +569,7 @@ tab_multiline = [[sg.Column([[sg.Multiline(size=(15,1))],[sg.Text(desc_multiline tab_output= [[sg.Column([[sg.Text(desc_output, font=('Consolas 12'))]])]] tab_progressbar = [[sg.Column([[sg.Text(desc_progressbar, font=('Consolas 12'))]])]] tab_optionmenu = [[sg.Column([[sg.OptionMenu([1,2,3,4,5], size=(15,1))],[sg.Text(desc_inputoptionmenu, font=('Consolas 12'))]])]] -tab_combo = [[sg.Column([[sg.Combo([1,2,3,4,5], size=(15,1))],[sg.Text(desc_inputoptionmenu, font=('Consolas 12'))]])]] +tab_combo = [[sg.Column([[sg.Combo([1,2,3,4,5], size=(15,1))],[sg.Text(desc_inputcombo, font=('Consolas 12'))]])]] tab_frame = [[sg.Column([[sg.Frame('Frame',[[sg.T(' ')]], size=(15,1))],[sg.Text(desc_frame, font=('Consolas 12'))]])]] tab_column = [[sg.Text(desc_column, font=('Consolas 12'))]] tab_graph = [[sg.Text(desc_graph, font=('Consolas 12'))]] @@ -511,11 +579,14 @@ tab_image = [[sg.Text(desc_image, font=('Consolas 12'))]] tab_table = [[sg.Text(desc_table, font=('Consolas 12'))]] tab_tree = [[sg.Text(desc_tree, font=('Consolas 12'))]] tab_menu = [[sg.Text(desc_menu, font=('Consolas 12'))]] +tab_button = [[sg.Text(desc_button, font=('Consolas 12'))]] tab_button_types = [[sg.Text(desc_button_types, font=('Consolas 12'))]] tab_popup = [[sg.Text(desc_popup, font=('Consolas 12'))]] tab_popups = [[sg.Text(desc_popups, font=('Consolas 12'))]] tab_one_line_prog_meter = [[sg.Text(desc_one_line_progress_meter, font=('Consolas 12'))]] -tab_window = [[sg.Text(desc_window, font=('Consolas 12'))]] + +tab_window = [[ sg.TabGroup([[sg.Tab('Parms',[[sg.Text(desc_window, font=('Consolas 12'))]]), + sg.Tab('Methods', [[sg.Column([[sg.Text(desc_window_methods)]], size=(500,500), scrollable=True, )]])]])]] layout = [[sg.TabGroup([[sg.Tab('Window',tab_window), sg.Tab('Text',tab_text), @@ -539,6 +610,7 @@ layout = [[sg.TabGroup([[sg.Tab('Window',tab_window), sg.Tab('Tab', tab_tab), sg.Tab('TabGroup', tab_tabgroup), sg.Tab('Menu', tab_menu), + sg.Tab('Button', tab_button), sg.Tab('Button Types', tab_button_types), sg.Tab('Popup', tab_popup), sg.Tab('Popups', tab_popups), @@ -551,17 +623,20 @@ layout = [[sg.TabGroup([[sg.Tab('Window',tab_window), # sg.Text(desc_text, size=(55, 25), font=('Consolas 13'), text_color='darkblue', key='_out_')]] window = sg.Window('PySimpleGUI SDK Quick Reference', - font='Any 12', - grab_anywhere=True).Layout(layout) + font='Consolas 12', + ).Layout(layout) while True: event, values = window.Read() if event is None or event == 'Exit': break - element = values['_in_'][0] - try: - desc = descriptions[element] - except: - desc = '' - window.FindElement('_out_').Update(desc) + if event == 'Methods': + sg.PopupScrolled(desc_window_methods, size=(50,20)) + # element = values['_in_'][0] + # try: + # desc = descriptions[element] + # except: + # desc = '' + # window.FindElement('_out_').Update(desc) + # print(button, values) \ No newline at end of file diff --git a/Demo_Pi_LEDs.py b/Demo_Pi_LEDs.py index 7f0e6623..a2e73b04 100644 --- a/Demo_Pi_LEDs.py +++ b/Demo_Pi_LEDs.py @@ -34,8 +34,8 @@ def FlashLED(): layout = [[sg.T('Raspberry Pi LEDs')], [sg.T('', size=(14, 1), key='output')], - [sg.ReadButton('Switch LED')], - [sg.ReadButton('Flash LED')], + [sg.Button('Switch LED')], + [sg.Button('Flash LED')], [sg.Exit()]] window = sg.Window('Raspberry Pi GUI', grab_anywhere=False).Layout(layout) @@ -49,8 +49,9 @@ while True: window.FindElement('output').Update(SwitchLED()) elif event is 'Flash LED': window.FindElement('output').Update('LED is Flashing') - window.ReadNonBlocking() + window.Refresh() FlashLED() window.FindElement('output').Update('') +window.Close() sg.Popup('Done... exiting') diff --git a/Demo_Pi_Robotics.py b/Demo_Pi_Robotics.py index 511aa66b..0c2ea577 100644 --- a/Demo_Pi_Robotics.py +++ b/Demo_Pi_Robotics.py @@ -24,9 +24,10 @@ def RemoteControlExample(): layout = [[sg.Text('Robotics Remote Control')], [sg.T('', justification='center', size=(19,1), key='status')], - [ sg.RealtimeButton('Forward', image_filename=image_forward, pad=((50,0),0))], - [ sg.RealtimeButton('Left', image_filename=image_left), sg.RealtimeButton('Right', image_filename=image_right, pad=((50,0), 0))], - [ sg.RealtimeButton('Reverse', image_filename=image_backward, pad=((50,0),0))], + [ sg.RealtimeButton('', key='Forward', image_filename=image_forward, pad=((50,0),0))], + [ sg.RealtimeButton('', key='Left', image_filename=image_left), + sg.RealtimeButton('', key='Right', image_filename=image_right, pad=((50,0), 0))], + [ sg.RealtimeButton('', key='Reverse', image_filename=image_backward, pad=((50,0),0))], [sg.T('')], [sg.Quit(button_color=('black', 'orange'))]] @@ -40,16 +41,16 @@ def RemoteControlExample(): # your program's main loop while (True): # This is the code that reads and updates your window - event, values = window.ReadNonBlocking() + event, values = window.Read(timeout=0, timeout_key='timeout') if event is not None: window.FindElement('status').Update(event) - else: + elif event != 'timeout': window.FindElement('status').Update('') # if user clicked quit button OR closed the form using the X, then break out of loop if event == 'Quit' or values is None: break - window.CloseNonBlocking() + window.Close() def RemoteControlExample_NoGraphics(): @@ -67,25 +68,26 @@ def RemoteControlExample_NoGraphics(): # # Some place later in your code... - # You need to perform a ReadNonBlocking on your form every now and then or + # You need to perform a Read on your form every now and then or # else it won't refresh. + # Notice how the timeout is 100ms. You don't have to use a timeout = 0 for all of your hardware + # applications. Leave some CPU for other threads or for your GUI. The longer you are in the GUI, the more + # responsive the GUI itself will be Match your timeout with your hardware's capabilities # # your program's main loop while (True): # This is the code that reads and updates your window - event, values = window.ReadNonBlocking() - if event is not None: + event, values = window.Read(timeout=100, timeout_key='timeout') + # print(event, values) + if event != 'timeout': window.FindElement('status').Update(event) else: window.FindElement('status').Update('') # if user clicked quit button OR closed the form using the X, then break out of loop - if event == 'Quit' or values is None: + if event in (None, 'Quit'): break - window.CloseNonBlocking() - - - + window.Close() # ------------------------------------- main ------------------------------------- def main(): diff --git a/Demo_Ping_Line_Graph.py b/Demo_Ping_Line_Graph.py index 264cca2c..d7e8f182 100644 --- a/Demo_Ping_Line_Graph.py +++ b/Demo_Ping_Line_Graph.py @@ -638,8 +638,8 @@ prev_x, prev_y = canvas_left, canvas_bottom while True: time.sleep(.2) - event, values = window.ReadNonBlocking() - if event == 'Quit' or values is None: + event, values = window.Read(timeout=0) + if event == 'Quit' or event is None: break if g_response_time is None or prev_response_time == g_response_time: diff --git a/Demo_Pong.py b/Demo_Pong.py index ec8d1855..df4111e4 100644 --- a/Demo_Pong.py +++ b/Demo_Pong.py @@ -133,7 +133,7 @@ class pongbat2(): def pong(): # ------------- Define GUI layout ------------- layout = [[sg.Canvas(size=(700, 400), background_color='black', key='canvas')], - [sg.T(''), sg.ReadButton('Quit')]] + [sg.T(''), sg.Button('Quit')]] # ------------- Create window ------------- window = sg.Window('The Classic Game of Pong', return_keyboard_events=True).Layout(layout).Finalize() # window.Finalize() # TODO Replace with call to window.Finalize once code released @@ -155,9 +155,9 @@ def pong(): bat2.draw() # ------------- Read the form, get keypresses ------------- - event, values = window.ReadNonBlocking() + event, values = window.Read(timeout=0) # ------------- If quit ------------- - if event is None and values is None or event == 'Quit': + if event is None or event == 'Quit': exit(69) # ------------- Keypresses ------------- if event is not None: diff --git a/Demo_Popup_Custom.py b/Demo_Popup_Custom.py index c1b8c93d..718adb8f 100644 --- a/Demo_Popup_Custom.py +++ b/Demo_Popup_Custom.py @@ -20,10 +20,10 @@ else: def PopupDropDown(title, text, values): window = sg.Window(title).Layout([[sg.Text(text)], - [sg.DropDown(values, key='_drop')], + [sg.DropDown(values, key='_DROP_')], [sg.OK(), sg.Cancel()]]) event, values = window.Read() - return None if event != 'OK' else values['_drop'] + return None if event != 'OK' else values['_DROP_'] # ----------------------- Calling your PopupDropDown function ----------------------- diff --git a/Demo_Popups.py b/Demo_Popups.py index 90a370ad..a2435a82 100644 --- a/Demo_Popups.py +++ b/Demo_Popups.py @@ -9,7 +9,7 @@ from PySimpleGUI import Print as print print('test') -sg.PopupGetFile('Get file', save_as=True,file_types=(("ALL Files", "*.jpg"))) +sg.PopupGetFile('Get file', save_as=True,file_types=(("ALL Files", "*.jpg"),)) # Here, have some windows on me.... [sg.PopupNoWait('No-wait Popup', location=(500+100*x,500)) for x in range(10)] diff --git a/Demo_Progress_Meters.py b/Demo_Progress_Meters.py index a94bff45..f2b390a0 100644 --- a/Demo_Progress_Meters.py +++ b/Demo_Progress_Meters.py @@ -37,7 +37,7 @@ def DemoOneLineProgressMeter(): sg.T('Delay'), sg.In(default_text='10', key='TimeOuter', size=(5,1), do_not_clear=True), sg.T('ms')], [sg.T('Inner Loop Count', size=(15,1), justification='r'), sg.In(default_text='100', size=(5,1), key='CountInner', do_not_clear=True) , sg.T('Delay'), sg.In(default_text='10', key='TimeInner', size=(5,1), do_not_clear=True), sg.T('ms')], - [sg.RButton('Show', pad=((0,0), 3), bind_return_key=True), sg.T('me the meters!')] + [sg.Button('Show', pad=((0,0), 3), bind_return_key=True), sg.T('me the meters!')] ] window = sg.Window('One-Line Progress Meter Demo').Layout(layout) @@ -68,22 +68,22 @@ def DemoOneLineProgressMeter(): def CustomMeter(): # layout the form layout = [[sg.Text('A custom progress meter')], - [sg.ProgressBar(10000, orientation='h', size=(20,20), key='progress')], + [sg.ProgressBar(1000, orientation='h', size=(20,20), key='progress')], [sg.Cancel()]] # create the form` window = sg.Window('Custom Progress Meter').Layout(layout) progress_bar = window.FindElement('progress') # loop that would normally do something useful - for i in range(10000): + for i in range(1000): # check to see if the cancel button was clicked and exit loop if clicked - event, values = window.ReadNonBlocking() - if event == 'Cancel' or values == None: + event, values = window.Read(timeout=0) + if event == 'Cancel' or event == None: break # update bar with loop value +1 so that bar eventually reaches the maximum progress_bar.UpdateBar(i+1) # done with loop... need to destroy the window as it's still open - window.CloseNonBlocking() + window.Close() CustomMeter() DemoOneLineProgressMeter() \ No newline at end of file diff --git a/Demo_Script_Launcher.py b/Demo_Script_Launcher.py index f54edb37..bba71a51 100644 --- a/Demo_Script_Launcher.py +++ b/Demo_Script_Launcher.py @@ -48,7 +48,7 @@ def Launcher2(): layout = [ [sg.Listbox(values=namesonly, size=(30, 19), select_mode=sg.SELECT_MODE_EXTENDED, key='demolist'), sg.Output(size=(88, 20), font='Courier 10')], [sg.Checkbox('Wait for program to complete', default=False, key='wait')], - [sg.ReadButton('Run'), sg.ReadButton('Shortcut 1'), sg.ReadButton('Fav Program'), sg.Button('EXIT')], + [sg.Button('Run'), sg.Button('Shortcut 1'), sg.Button('Fav Program'), sg.Button('EXIT')], ] window.Layout(layout) diff --git a/Demo_Script_Parameters.py b/Demo_Script_Parameters.py index 81e321d0..82fdda64 100644 --- a/Demo_Script_Parameters.py +++ b/Demo_Script_Parameters.py @@ -19,9 +19,9 @@ stores the result in the variable fname, just like the command line parsing did. ''' if len(sys.argv) == 1: - event, (fname,) = sg.Window('My Script').LayoutAndRead([[sg.T('Document to open')], + event, (fname,) = sg.Window('My Script').Layout([[sg.T('Document to open')], [sg.In(), sg.FileBrowse()], - [sg.Open(), sg.Cancel()]]) + [sg.CloseButton('Open'), sg.CloseButton('Cancel')]]).Read() else: fname = sys.argv[1] diff --git a/Demo_Spinner_Compound_Element.py b/Demo_Spinner_Compound_Element.py index d93121c3..dc9bb246 100644 --- a/Demo_Spinner_Compound_Element.py +++ b/Demo_Spinner_Compound_Element.py @@ -11,9 +11,9 @@ else: sg.SetOptions(element_padding=(0,0)) # sg.ChangeLookAndFeel('Dark') # --- Define our "Big-Button-Spinner" compound element. Has 2 buttons and an input field --- # -NewSpinner = [sg.ReadButton('-', size=(2,1), font='Any 12'), +NewSpinner = [sg.Button('-', size=(2,1), font='Any 12'), sg.In('0', size=(2,1), font='Any 14', justification='r', key='spin'), - sg.ReadButton('+', size=(2,1), font='Any 12')] + sg.Button('+', size=(2,1), font='Any 12')] # --- Define Window --- # layout = [ [sg.Text('Spinner simulation')], diff --git a/Demo_Super_Simple_Form.py b/Demo_Super_Simple_Form.py index 43e3d506..d4178baa 100644 --- a/Demo_Super_Simple_Form.py +++ b/Demo_Super_Simple_Form.py @@ -11,10 +11,10 @@ else: layout = [ [sg.Text('Please enter your Name, Address, Phone')], - [sg.Text('Name', size=(15, 1)), sg.InputText('1', key='name')], + [sg.Text('Name', size=(15, 1)), sg.InputText('1', key='name'), sg.FileBrowse()], [sg.Text('Address', size=(15, 1)), sg.InputText('2', key='address')], [sg.Text('Phone', size=(15, 1)), sg.InputText('3', key='phone')], - [sg.Submit(), sg.Cancel()] + [sg.CloseButton('Submit'), sg.CloseButton('Cancel')] ] window = sg.Window('Simple Data Entry Window').Layout(layout) diff --git a/Demo_Table_CSV.py b/Demo_Table_CSV.py index 986c9d4b..0662e83d 100644 --- a/Demo_Table_CSV.py +++ b/Demo_Table_CSV.py @@ -28,16 +28,14 @@ def table_example(): sys.exit(69) sg.SetOptions(element_padding=(0, 0)) - col_layout = [[sg.Table(values=data, + layout = [[sg.Table(values=data, headings=header_list, max_col_width=25, auto_size_columns=True, justification='right', alternating_row_color='lightblue', - num_rows=len(data))]] + num_rows=min(len(data), 20))]] - canvas_size = (13*10*len(header_list), 600) # estimate canvas size - 13 pixels per char * 10 char per column * num columns - layout = [[sg.Column(col_layout, size=canvas_size, scrollable=True)],] window = sg.Window('Table', grab_anywhere=False).Layout(layout) event, values = window.Read() diff --git a/Demo_Table_Element.py b/Demo_Table_Element.py index 25640c2f..190be703 100644 --- a/Demo_Table_Element.py +++ b/Demo_Table_Element.py @@ -19,19 +19,28 @@ if filename is not None: except: sg.PopupError('Error reading file') sys.exit(69) +else: + sys.exit() -sg.SetOptions(element_padding=(0, 0)) - +# sg.SetOptions(element_padding=(0,0)) headings = [data[0][x] for x in range(len(data[0]))] -col_layout = [[sg.Table(values=data[1:][:], headings=headings, max_col_width=25, - auto_size_columns=True, display_row_numbers=True, justification='right', num_rows=len(data), alternating_row_color='lightblue')]] +layout = [[sg.Table(values=data[1:][:], headings=headings, max_col_width=25, + auto_size_columns=True, display_row_numbers=True, justification='right', num_rows=20, alternating_row_color='lightblue', key='_table_')], + [sg.Button('Read'), sg.Button('Double')], + [sg.T('Read = read which rows are selected')],[sg.T('Double = double the amount of data in the table')]] -canvas_size = (13 * 10 * len(headings), 600) # estimate canvas size - 13 pixels per char * 10 per column * num columns +window = sg.Window('Table', grab_anywhere=False, resizable=True).Layout(layout) -layout = [[sg.Column(col_layout, size=canvas_size, scrollable=True)],] -window = sg.Window('Table', grab_anywhere=False).Layout(layout) - -event, values = window.Read() +while True: + event, values = window.Read() + if event is None: + break + if event == 'Double': + for i in range(len(data)): + data.append(data[i]) + window.FindElement('_table_').Update(values = data) + sg.Popup(event, values) + # print(event, values) sys.exit(69) diff --git a/Demo_Table_Pandas.py b/Demo_Table_Pandas.py index 591ec52b..bc192046 100644 --- a/Demo_Table_Pandas.py +++ b/Demo_Table_Pandas.py @@ -28,16 +28,12 @@ def table_example(): except: sg.PopupError('Error reading file') sys.exit(69) - # sg.SetOptions(element_padding=(0, 0)) - col_layout = [[sg.Table(values=data, headings=header_list, display_row_numbers=True, - auto_size_columns=False, num_rows=len(data))]] - - canvas_size = (13*10*len(header_list), 600) # estimate canvas size - 13 pixels per char * 10 per column * num columns - layout = [[sg.Column(col_layout, size=canvas_size, scrollable=True)]] + layout = [[sg.Table(values=data, headings=header_list, display_row_numbers=True, + auto_size_columns=False, num_rows=min(25,len(data)))]] window = sg.Window('Table', grab_anywhere=False) - event, values = window.LayoutAndRead(layout) + event, values = window.Layout(layout).Read() sys.exit(69) diff --git a/Demo_Table_Simulation.py b/Demo_Table_Simulation.py index 829fa3f0..32018c5a 100644 --- a/Demo_Table_Simulation.py +++ b/Demo_Table_Simulation.py @@ -12,7 +12,6 @@ def TableSimulation(): Display data in a table format """ sg.SetOptions(element_padding=(0,0)) - sg.PopupNoWait('Give it a few seconds to load please...', auto_close=True) menu_def = [['File', ['Open', 'Save', 'Exit']], ['Edit', ['Paste', ['Special', 'Normal',], 'Undo'],], @@ -20,7 +19,7 @@ def TableSimulation(): columm_layout = [[]] - MAX_ROWS = 60 + MAX_ROWS = 20 MAX_COL = 10 for i in range(MAX_ROWS): inputs = [sg.T('{}'.format(i), size=(4,1), justification='right')] + [sg.In(size=(10, 1), pad=(1, 1), justification='right', key=(i,j), do_not_clear=True) for j in range(MAX_COL)] @@ -34,7 +33,7 @@ def TableSimulation(): sg.In(key='value', size=(8,1), pad=(1,1), justification='right', do_not_clear=True)], [sg.Column(columm_layout, size=(800,600), scrollable=True)] ] - window = sg.Window('Table', return_keyboard_events=True, grab_anywhere=False).Layout(layout) + window = sg.Window('Table', return_keyboard_events=True).Layout(layout) while True: event, values = window.Read() diff --git a/Demo_Tabs.py b/Demo_Tabs.py index 296a71cf..733cb334 100644 --- a/Demo_Tabs.py +++ b/Demo_Tabs.py @@ -38,7 +38,7 @@ layout = [[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout, background_color='darkslat selected_title_color='green', tab_location='left'), sg.TabGroup([[sg.Tab('Tab 4', tab4_layout,background_color='darkseagreen', key='_mykey_'), sg.Tab('Tab 5', tab5_layout)]], key='_group4_', tab_location='bottom', selected_title_color='purple')], - [sg.RButton('Read')]] + [sg.Button('Read')]] window = sg.Window('My window with tabs', default_element_size=(12,1)).Layout(layout) diff --git a/Demo_Tabs_Nested.py b/Demo_Tabs_Nested.py index 2812ba4f..8868cb53 100644 --- a/Demo_Tabs_Nested.py +++ b/Demo_Tabs_Nested.py @@ -26,7 +26,7 @@ layout = [[sg.T('My Window!')], [sg.Frame('A Frame', layout= [[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout), sg.Tab('Tab 2', tab2_layout)]]), sg.TabGroup([[sg.Tab('Tab3', tab3_layout), sg.Tab('Tab 4', tab4_layout)]])]])], [sg.T('This text is on a row with a column'),sg.Column(layout=[[sg.T('In a column')], [sg.TabGroup([[sg.Tab('Tab 5', tab5_layout), sg.Tab('Tab 6', tab6_layout)]])], - [sg.RButton('Click me')]])],] + [sg.Button('Click me')]])],] window = sg.Window('My window with tabs', default_element_size=(12,1)).Layout(layout).Finalize() diff --git a/Demo_Tabs_Simple.py b/Demo_Tabs_Simple.py index ad0af921..d666c326 100644 --- a/Demo_Tabs_Simple.py +++ b/Demo_Tabs_Simple.py @@ -7,13 +7,13 @@ else: tab1_layout = [[sg.T('Tab 1')], [sg.T('Put your layout in here')], - [sg.T('Input something'),sg.In(key='_in0_')]] + [sg.T('Input something'),sg.In(key='_IN0_')]] tab2_layout = [[sg.T('Tab2')]] -layout = [[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout), sg.Tab('Tab 2', tab2_layout)]])], - [sg.RButton('Read')]] +layout = [[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout), sg.Tab('Tab 2', tab2_layout)]], key='_TABGROUP_')], + [sg.Button('Read')]] window = sg.Window('My window with tabs', default_element_size=(12,1)).Layout(layout) diff --git a/Demo_Template.py b/Demo_Template.py index b2d90243..581bf934 100644 --- a/Demo_Template.py +++ b/Demo_Template.py @@ -11,15 +11,15 @@ else: import PySimpleGUI27 as sg layout = [[ sg.Text('My layout') ], - [ sg.Button('Next Window')]] + [ sg.CloseButton('Next Window')]] window = sg.Window('My window').Layout(layout) event, values = window.Read() -# -------------------------------------# -# DESIGN PATTERN 2 - Persistent Window # -# -------------------------------------# +# --------------------------------------------------# +# DESIGN PATTERN 2 - Persistent Window (stays open) # +# --------------------------------------------------# #!/usr/bin/env python import sys if sys.version_info[0] >= 3: @@ -27,13 +27,13 @@ if sys.version_info[0] >= 3: else: import PySimpleGUI27 as sg -layout = [[ sg.Text('My layout') ], - [ sg.RButton('Read The Window')]] +layout = [[ sg.Text('My Window') ], + [ sg.Button('Read The Window')]] -window = sg.Window('My new window').Layout(layout) +window = sg.Window('My Window Title').Layout(layout) -while True: # Event Loop +while True: # Event Loop event, values = window.Read() - if event is None: + if event is None: # if window closed with X break print(event, values) \ No newline at end of file diff --git a/Demo_Touch_Keyboard.py b/Demo_Touch_Keyboard.py index ac8eebed..8de2f2d1 100644 --- a/Demo_Touch_Keyboard.py +++ b/Demo_Touch_Keyboard.py @@ -1,84 +1,99 @@ import PySimpleGUI as sg + class keyboard(): - def __init__(self,font=('Arial',16)): - self.font=font - numberRow='1234567890' - topRow='QWERTYUIOP' - midRow='ASDFGHJKL' - bottomRow='ZXCVBNM' - keyboard_layout=[[sg.ReadButton(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in numberRow]+[sg.ReadButton('⌫', key='back', pad=(0, 0), size=(4, 2), font=self.font),sg.ReadButton('Esc', key='close', pad=(0, 0), size=(4, 2), font=self.font)], - [sg.T(' '*4)]+[sg.ReadButton(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in topRow], - [sg.T(' '*11)]+[sg.ReadButton(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in midRow], - [sg.T(' '*18)]+[sg.ReadButton(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in bottomRow]] + def __init__(self, font=('Arial', 16)): + self.font = font + numberRow = '1234567890' + topRow = 'QWERTYUIOP' + midRow = 'ASDFGHJKL' + bottomRow = 'ZXCVBNM' + keyboard_layout = [[sg.Button(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in numberRow] + [ + sg.Button('⌫', key='back', pad=(0, 0), size=(4, 2), font=self.font), + sg.Button('Esc', key='close', pad=(0, 0), size=(4, 2), font=self.font)], + [sg.T(' ' * 4)] + [sg.Button(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in + topRow], + [sg.T(' ' * 11)] + [sg.Button(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in + midRow], + [sg.T(' ' * 18)] + [sg.Button(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in + bottomRow]] - self.window=sg.Window('keyboard', grab_anywhere=True, keep_on_top=True, no_titlebar=True).Layout( - keyboard_layout).Finalize() - self.hide() - - def _keyboardhandler(self): - if self.event is not None: - if self.event=='close': + self.window = sg.Window('keyboard', + grab_anywhere=True, + keep_on_top=True, + alpha_channel=0, + location=(850,350), + no_titlebar=True, + ).Layout(keyboard_layout).Finalize() self.hide() - elif len(self.event)==1: - self.focus.Update(self.focus.Get()+self.event) - elif self.event=='back': - Text=self.focus.Get() - if len(Text)>0: - Text=Text[:-1] - self.focus.Update(Text) - def hide(self): - self.visible=False - self.window.Disappear() + def _keyboardhandler(self): + if self.event is not None: + if self.event == 'close': + self.hide() + elif len(self.event) == 1: + self.focus.Update(self.focus.Get() + self.event) + elif self.event == 'back': + Text = self.focus.Get() + if len(Text) > 0: + Text = Text[:-1] + self.focus.Update(Text) - def show(self): - self.visible=True - self.window.Reappear() + def hide(self): + self.visible = False + self.window.Disappear() - def togglevis(self): - if self.visible: - self.hide() - else: - self.show() + def show(self): + self.visible = True + self.window.Reappear() - def update(self,focus): - self.event,_=self.window.ReadNonBlocking() - self.focus=focus - self._keyboardhandler() + def togglevis(self): + if self.visible: + self.hide() + else: + self.show() - def close(self): - self.window.CloseNonBlocking() + def update(self, focus): + self.event, _ = self.window.Read(timeout=0) + if focus is not None: + self.focus = focus + self._keyboardhandler() + + def close(self): + self.window.Close() class GUI(): - def __init__(self): - layout = [[sg.Text('Enter Text')], - [sg.Input(size=(17, 1), key='input1',)], - [sg.InputText(size=(17, 1), key='input2')], - [sg.ReadButton('on-screen keyboard',key='keyboard')], - [sg.ReadButton('close',key='close')]] + def __init__(self): + layout = [[sg.Text('Enter Text')], + [sg.Input(size=(17, 1), key='input1', do_not_clear=True)], + [sg.InputText(size=(17, 1), key='input2', do_not_clear=True)], + [sg.Button('on-screen keyboard', key='keyboard')], + [sg.Button('close', key='close')]] - self.mainWindow = sg.Window('On-screen test', grab_anywhere=False,no_titlebar=True).Layout(layout) - self.keyboard=keyboard() - self.focus=None + self.mainWindow = sg.Window('On-screen test', + grab_anywhere=True, + no_titlebar=False, + ).Layout(layout).Finalize() + self.keyboard = keyboard() + self.focus = None + def run(self): + while True: + cur_focus = self.mainWindow.FindElementWithFocus() + if cur_focus is not None: + self.focus = cur_focus + event, values = self.mainWindow.Read(timeout=200, timeout_key='timeout') + if self.focus is not None: + self.keyboard.update(self.focus) + if event == 'keyboard': + self.keyboard.togglevis() + elif event == 'close' or event is None: + break + self.keyboard.close() + self.mainWindow.Close() - def run(self): - while True: - for row in self.mainWindow.Rows: - for element in row: - if element.Type=='input' and element.TKEntry is not None and element.TKEntry is element.TKEntry.focus_get(): - self.focus=element - event, values=self.mainWindow.ReadNonBlocking() - self.keyboard.update(self.focus) - if event=='keyboard': - self.keyboard.togglevis() - elif event=='close': - break - self.keyboard.close() - self.mainWindow.CloseNonBlocking() if __name__ == '__main__': - app=GUI() - app.run() + app = GUI() + app.run() diff --git a/Demo_Touch_Keyboard2.py b/Demo_Touch_Keyboard2.py deleted file mode 100644 index ece6107f..00000000 --- a/Demo_Touch_Keyboard2.py +++ /dev/null @@ -1,91 +0,0 @@ -import PySimpleGUI as sg - - -class keyboard(): - def __init__(self, font=('Arial', 16)): - self.font = font - numberRow = '1234567890' - topRow = 'QWERTYUIOP' - midRow = 'ASDFGHJKL' - bottomRow = 'ZXCVBNM' - keyboard_layout = [[sg.ReadButton(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in numberRow] + [ - sg.ReadButton('⌫', key='back', pad=(0, 0), size=(4, 2), font=self.font), - sg.ReadButton('Esc', key='close', pad=(0, 0), size=(4, 2), font=self.font)], - [sg.T(' ' * 4)] + [sg.ReadButton(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in - topRow], - [sg.T(' ' * 11)] + [sg.ReadButton(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in - midRow], - [sg.T(' ' * 18)] + [sg.ReadButton(c, key=c, pad=(0, 0), size=(4, 2), font=self.font) for c in - bottomRow]] - - self.window = sg.Window('keyboard', grab_anywhere=True, keep_on_top=True, no_titlebar=True).Layout( - keyboard_layout).Finalize() - self.hide() - - def _keyboardhandler(self): - if self.event is not None: - if self.event == 'close': - self.hide() - elif len(self.event) == 1: - self.focus.Update(self.focus.Get() + self.event) - elif self.event == 'back': - Text = self.focus.Get() - if len(Text) > 0: - Text = Text[:-1] - self.focus.Update(Text) - - def hide(self): - self.visible = False - self.window.Disappear() - - def show(self): - self.visible = True - self.window.Reappear() - - def togglevis(self): - if self.visible: - self.hide() - else: - self.show() - - def update(self, focus): - self.event, _ = self.window.ReadNonBlocking() - if focus is not None: - self.focus = focus - self._keyboardhandler() - - def close(self): - self.window.CloseNonBlocking() - - -class GUI(): - def __init__(self): - layout = [[sg.Text('Enter Text')], - [sg.Input(size=(17, 1), key='input1', do_not_clear=True )], - [sg.InputText(size=(17, 1), key='input2', do_not_clear=True)], - [sg.ReadButton('on-screen keyboard', key='keyboard')], - [sg.ReadButton('close', key='close')]] - - self.mainWindow = sg.Window('On-screen test', grab_anywhere=False, no_titlebar=True).Layout(layout) - self.keyboard = keyboard() - self.focus = None - - def run(self): - while True: - cur_focus = self.mainWindow.FindElementWithFocus() - if cur_focus is not None: - self.focus = cur_focus - event, values = self.mainWindow.Read(timeout=200, timeout_key='timeout') - if self.focus is not None: - self.keyboard.update(self.focus) - if event == 'keyboard': - self.keyboard.togglevis() - elif event == 'close': - break - self.keyboard.close() - self.mainWindow.CloseNonBlocking() - - -if __name__ == '__main__': - app = GUI() - app.run() diff --git a/Demo_Tree_Element.py b/Demo_Tree_Element.py index acc5dedf..2d2b5d1d 100644 --- a/Demo_Tree_Element.py +++ b/Demo_Tree_Element.py @@ -17,8 +17,8 @@ treedata.Insert("_A1_", '_A3_', 'A30', ['getting deep']) treedata.Insert("_C_", '_C2_', 'C2', ['nothing', 'at', 'all']) layout = [[ sg.Text('Tree Test') ], - [ sg.Tree(data=treedata, headings=['col1', 'col2', 'col3'], auto_size_columns=True, num_rows=10, col0_width=10)], - [ sg.RButton('Read')]] + [ sg.Tree(data=treedata, headings=['col1', 'col2', 'col3'], auto_size_columns=True, num_rows=10, col0_width=10, key='_tree_', show_expanded=True)], + [ sg.Button('Read')]] window = sg.Window('Tree Element Test').Layout(layout) diff --git a/Demo_Turtle.py b/Demo_Turtle.py index 4de9b3a8..dcc2b0b2 100644 --- a/Demo_Turtle.py +++ b/Demo_Turtle.py @@ -19,8 +19,8 @@ import turtle layout = [[ sg.Text('My layout') ], [sg.Canvas(size=(800,800), key='_canvas_')], - [ sg.RButton('F'), sg.RButton('B'), sg.RButton('L'), sg.RButton('R')], - [sg.RButton('Spiral'), sg.RButton('Inside Out'), sg.RButton('Circles')]] + [ sg.Button('F'), sg.Button('B'), sg.Button('L'), sg.Button('R')], + [sg.Button('Spiral'), sg.Button('Inside Out'), sg.Button('Circles')]] window = sg.Window('My new window').Layout(layout).Finalize() diff --git a/Demo_Window_Disappear.py b/Demo_Window_Disappear.py index 085cab76..603fdb0d 100644 --- a/Demo_Window_Disappear.py +++ b/Demo_Window_Disappear.py @@ -6,7 +6,7 @@ else: import PySimpleGUI27 as sg layout = [[ sg.Text('My Window') ], - [ sg.RButton('Disappear')]] + [ sg.Button('Disappear')]] window = sg.Window('My window').Layout(layout) diff --git a/Demo_Youtube-dl_Frontend.py b/Demo_Youtube-dl_Frontend.py index 1764c51c..adddc682 100644 --- a/Demo_Youtube-dl_Frontend.py +++ b/Demo_Youtube-dl_Frontend.py @@ -31,10 +31,10 @@ def DownloadSubtitlesGUI(): # ---===--- Loop taking in user input and using it to query HowDoI --- # while True: event, values = window.Read() - if event in ('EXIT', None): + if event in ('Exit', None): break # exit button clicked link = values['link'] - if event is 'Get List': + if event == 'Get List': print('Getting list of subtitles....') window.Refresh() command = [f'C:/Python/PycharmProjects/GooeyGUI/youtube-dl --list-subs {link}',] diff --git a/Demo_psutil_Kill_Processes.py b/Demo_psutil_Kill_Processes.py index 42ea9957..612259c2 100644 --- a/Demo_psutil_Kill_Processes.py +++ b/Demo_psutil_Kill_Processes.py @@ -46,9 +46,9 @@ def main(): [sg.Listbox(values=[' '], size=(50, 30), select_mode=sg.SELECT_MODE_EXTENDED, font=('Courier', 12), key='_processes_')], [sg.Text('Click refresh once or twice.. once for list, second to get CPU usage')], [sg.T('Filter by typing name', font='ANY 14'), sg.In(size=(15,1), font='any 14', key='_filter_')], - [sg.RButton('Sort by Name', ), - sg.RButton('Sort by % CPU', button_color=('white', 'DarkOrange2')), - sg.RButton('Kill', button_color=('white','red'), bind_return_key=True), + [sg.Button('Sort by Name', ), + sg.Button('Sort by % CPU', button_color=('white', 'DarkOrange2')), + sg.Button('Kill', button_color=('white','red'), bind_return_key=True), sg.Exit(button_color=('white', 'sea green'))]] window = sg.Window('Process Killer', @@ -63,12 +63,14 @@ def main(): while (True): # --------- Read and update window -------- event, values = window.Read() - if 'Mouse' in event or 'Control' in event or 'Shift' in event: - continue - # --------- Do Button Operations -------- - if values is None or event == 'Exit': + if event is None or event == 'Exit': break + # skip mouse, control key and shift key events entirely + if 'Mouse' in event or 'Control' in event or 'Shift' in event: + continue + + # --------- Do Button Operations -------- if event == 'Sort by Name': psutil.cpu_percent(interval=1) procs = psutil.process_iter() @@ -96,7 +98,7 @@ def main(): for process in sorted_by_cpu_procs: display_list.append('{:5d} {:5.2f} {}\n'.format(process[2], process[0]/10, process[1])) window.FindElement('_processes_').Update(display_list) - else: + else: # was a typed character if display_list is not None: new_output = [] for line in display_list: diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 16b930e7..f4b8bf94 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -2457,6 +2457,7 @@ class Table(Element): if self.AlternatingRowColor is not None: self.TKTreeview.tag_configure(1, background=self.AlternatingRowColor) self.Values = values + self.SelectedRows = [] def treeview_selected(self, event): selections = self.TKTreeview.selection() @@ -5991,7 +5992,7 @@ def PopupGetFolder(message, default_path='', no_window=False, size=(None, None), layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)], [InputText(default_text=default_path, size=size), FolderBrowse()], - [CloseButton('Ok', size=(5, 1)), CloseButton('Cancel', size=(5, 1))]] + [CloseButton('Ok', size=(5, 1), bind_return_key=True), CloseButton('Cancel', size=(5, 1))]] window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, background_color=background_color, @@ -6052,7 +6053,7 @@ def PopupGetFile(message, default_path='', default_extension='', save_as=False, layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)], [InputText(default_text=default_path, size=size), browse_button], - [CloseButton('Ok', size=(6, 1)), CloseButton('Cancel', size=(6, 1))]] + [CloseButton('Ok', size=(6, 1), bind_return_key=True), CloseButton('Cancel', size=(6, 1))]] window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, font=font, background_color=background_color, @@ -6091,7 +6092,7 @@ def PopupGetText(message, default_text='', password_char='', size=(None, None), layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color, font=font)], [InputText(default_text=default_text, size=size, password_char=password_char)], - [CloseButton('Ok', size=(5, 1)), CloseButton('Cancel', size=(5, 1))]] + [CloseButton('Ok', size=(5, 1), bind_return_key=True), CloseButton('Cancel', size=(5, 1))]] window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, no_titlebar=no_titlebar, background_color=background_color, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, diff --git a/PySimpleGUI27.py b/PySimpleGUI27.py index fe928f46..3451bd80 100644 --- a/PySimpleGUI27.py +++ b/PySimpleGUI27.py @@ -1183,7 +1183,7 @@ class Output(Element): # Button Class # # ---------------------------------------------------------------------- # class Button(Element): - def __init__(self, button_text='', button_type=BUTTON_TYPE_CLOSES_WIN, target=(None, None), tooltip=None, + def __init__(self, button_text='', button_type=BUTTON_TYPE_READ_FORM, target=(None, None), tooltip=None, file_types=(("ALL Files", "*.*"),), initial_folder=None, disabled=False, image_filename=None, image_data=None, image_size=(None, None), image_subsample=None, border_width=None, size=(None, None), auto_size_button=None, button_color=None, font=None, bind_return_key=False, @@ -2469,6 +2469,7 @@ class Table(Element): if self.AlternatingRowColor is not None: self.TKTreeview.tag_configure(1, background=self.AlternatingRowColor) self.Values = values + self.SelectedRows = [] def treeview_selected(self, event): selections = self.TKTreeview.selection() @@ -4706,7 +4707,7 @@ def _ProgressMeter(title, max_value, *args, **_3to2kwargs): bar_text = Text(single_line_message, size=(width, height + 3), auto_size_text=True) form.AddRow(bar_text) form.AddRow((bar2)) - form.AddRow((Cancel(button_color=button_color))) + form.AddRow((CloseButton('Cancel', button_color=button_color))) else: single_line_message, width, height = ConvertArgsToSingleString(*args) bar2.TextToDisplay = single_line_message @@ -4714,7 +4715,7 @@ def _ProgressMeter(title, max_value, *args, **_3to2kwargs): bar2.CurrentValue = 0 bar_text = Text(single_line_message, size=(width, height + 3), auto_size_text=True) form.AddRow(bar2, bar_text) - form.AddRow((Cancel(button_color=button_color))) + form.AddRow((CloseButton('Cancel', button_color=button_color))) form.NonBlocking = True form.Show(non_blocking=True) @@ -5121,165 +5122,6 @@ def PopupScrolled(*args, **_3to2kwargs): ScrolledTextBox = PopupScrolled -# ---------------------------------------------------------------------- # -# GetPathBox # -# Pre-made dialog that looks like this roughly # -# MESSAGE # -# __________________________ # -# |__________________________| (BROWSE) # -# (SUBMIT) (CANCEL) # -# RETURNS two values: # -# True/False, path # -# (True if Submit was pressed, false otherwise) # -# ---------------------------------------------------------------------- # - -def PopupGetFolder(message, default_path='', no_window=False, size=(None, None), button_color=None, - background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, - grab_anywhere=False, keep_on_top=False, location=(None, None)): - """ - Display popup with text entry field and browse button. Browse for folder - :param message: - :param default_path: - :param no_window: - :param size: - :param button_color: - :param background_color: - :param text_color: - :param icon: - :param font: - :param no_titlebar: - :param grab_anywhere: - :param keep_on_top: - :param location: - :return: Contents of text field. None if closed using X or cancelled - """ - if no_window: - root = tk.Tk() - try: - root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint' - except: - pass - folder_name = tk.filedialog.askdirectory() # show the 'get folder' dialog box - root.destroy() - return folder_name - - layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)], - [InputText(default_text=default_path, size=size), FolderBrowse()], - [Ok(size=(5, 1)), Cancel(size=(5, 1))]] - - window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, - background_color=background_color, - font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, - location=location) - - (button, input_values) = window.LayoutAndRead(layout) - - if button != 'Ok': - return None - else: - path = input_values[0] - return path - - -##################################### -# PopupGetFile # -##################################### -def PopupGetFile(message, default_path='', default_extension='', save_as=False, file_types=(("ALL Files", "*.*"),), - no_window=False, size=(None, None), button_color=None, background_color=None, text_color=None, - icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, grab_anywhere=False, keep_on_top=False, - location=(None, None)): - """ - Display popup with text entry field and browse button. Browse for file - :param message: - :param default_path: - :param default_extension: - :param save_as: - :param file_types: - :param no_window: - :param size: - :param button_color: - :param background_color: - :param text_color: - :param icon: - :param font: - :param no_titlebar: - :param grab_anywhere: - :param keep_on_top: - :param location: - :return: string representing the path chosen, None if cancelled or window closed with X - """ - if no_window: - root = tk.Tk() - try: - root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint' - except: - pass - if save_as: - filename = tk.filedialog.asksaveasfilename(filetypes=file_types, - defaultextension=default_extension) # show the 'get file' dialog box - else: - filename = tk.filedialog.askopenfilename(filetypes=file_types, - defaultextension=default_extension) # show the 'get file' dialog box - root.destroy() - return filename - - browse_button = SaveAs(file_types=file_types) if save_as else FileBrowse(file_types=file_types) - - layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)], - [InputText(default_text=default_path, size=size), browse_button], - [Ok(size=(6, 1)), Cancel(size=(6, 1))]] - - window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, font=font, - background_color=background_color, - no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location) - - (button, input_values) = window.Layout(layout).Read() - if button != 'Ok': - return None - else: - path = input_values[0] - return path - - -##################################### -# PopupGetText # -##################################### -def PopupGetText(message, default_text='', password_char='', size=(None, None), button_color=None, - background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, - grab_anywhere=False, keep_on_top=False, location=(None, None)): - """ - Display Popup with text entry field - :param message: - :param default_text: - :param password_char: - :param size: - :param button_color: - :param background_color: - :param text_color: - :param icon: - :param font: - :param no_titlebar: - :param grab_anywhere: - :param keep_on_top: - :param location: - :return: Text entered or None if window was closed - """ - - layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color, font=font)], - [InputText(default_text=default_text, size=size, password_char=password_char)], - [Ok(size=(5, 1)), Cancel(size=(5, 1))]] - - window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, no_titlebar=no_titlebar, - background_color=background_color, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, - location=location) - - (button, input_values) = window.Layout(layout).Read() - - if button != 'Ok': - return None - else: - return input_values[0] - # ============================== SetGlobalIcon ======# # Sets the icon to be used by default # @@ -5879,7 +5721,7 @@ def Popup(*args, **_3to2kwargs): if non_blocking: PopupButton = DummyButton # important to use or else button will close other windows too! else: - PopupButton = Button + PopupButton = CloseButton # show either an OK or Yes/No depending on paramater if button_type is POPUP_BUTTONS_YES_NO: window.AddRow(PopupButton('Yes', button_color=button_color, focus=True, bind_return_key=True, pad=((20, 5), 3), @@ -6461,6 +6303,160 @@ def PopupYesNo(*args, **_3to2kwargs): grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location) +############################################################################## +# The PopupGet_____ functions - Will return user input # +############################################################################## + +# --------------------------- PopupGetFolder --------------------------- + + +def PopupGetFolder(message, default_path='', no_window=False, size=(None, None), button_color=None, + background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, + grab_anywhere=False, keep_on_top=False, location=(None, None)): + """ + Display popup with text entry field and browse button. Browse for folder + :param message: + :param default_path: + :param no_window: + :param size: + :param button_color: + :param background_color: + :param text_color: + :param icon: + :param font: + :param no_titlebar: + :param grab_anywhere: + :param keep_on_top: + :param location: + :return: Contents of text field. None if closed using X or cancelled + """ + if no_window: + root = tk.Tk() + try: + root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint' + except: + pass + folder_name = tk.filedialog.askdirectory() # show the 'get folder' dialog box + root.destroy() + return folder_name + + layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)], + [InputText(default_text=default_path, size=size), FolderBrowse()], + [CloseButton('Ok', size=(5, 1), bind_return_key=True), CloseButton('Cancel', size=(5, 1))]] + + window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, + background_color=background_color, + font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, + location=location) + + (button, input_values) = window.LayoutAndRead(layout) + + if button != 'Ok': + return None + else: + path = input_values[0] + return path + + +# --------------------------- PopupGetFile --------------------------- + +def PopupGetFile(message, default_path='', default_extension='', save_as=False, file_types=(("ALL Files", "*.*"),), + no_window=False, size=(None, None), button_color=None, background_color=None, text_color=None, + icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, grab_anywhere=False, keep_on_top=False, + location=(None, None)): + """ + Display popup with text entry field and browse button. Browse for file + :param message: + :param default_path: + :param default_extension: + :param save_as: + :param file_types: + :param no_window: + :param size: + :param button_color: + :param background_color: + :param text_color: + :param icon: + :param font: + :param no_titlebar: + :param grab_anywhere: + :param keep_on_top: + :param location: + :return: string representing the path chosen, None if cancelled or window closed with X + """ + if no_window: + root = tk.Tk() + try: + root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint' + except: + pass + if save_as: + filename = tk.filedialog.asksaveasfilename(filetypes=file_types, + defaultextension=default_extension) # show the 'get file' dialog box + else: + filename = tk.filedialog.askopenfilename(filetypes=file_types, + defaultextension=default_extension) # show the 'get file' dialog box + root.destroy() + return filename + + browse_button = SaveAs(file_types=file_types) if save_as else FileBrowse(file_types=file_types) + + layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)], + [InputText(default_text=default_path, size=size), browse_button], + [CloseButton('Ok', size=(6, 1), bind_return_key=True), CloseButton('Cancel', size=(6, 1))]] + + window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, font=font, + background_color=background_color, + no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location) + + (button, input_values) = window.Layout(layout).Read() + if button != 'Ok': + return None + else: + path = input_values[0] + return path + + +# --------------------------- PopupGetText --------------------------- + +def PopupGetText(message, default_text='', password_char='', size=(None, None), button_color=None, + background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, + grab_anywhere=False, keep_on_top=False, location=(None, None)): + """ + Display Popup with text entry field + :param message: + :param default_text: + :param password_char: + :param size: + :param button_color: + :param background_color: + :param text_color: + :param icon: + :param font: + :param no_titlebar: + :param grab_anywhere: + :param keep_on_top: + :param location: + :return: Text entered or None if window was closed + """ + + layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color, font=font)], + [InputText(default_text=default_text, size=size, password_char=password_char)], + [CloseButton('Ok', size=(5, 1), bind_return_key=True), CloseButton('Cancel', size=(5, 1))]] + + window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, no_titlebar=no_titlebar, + background_color=background_color, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, + location=location) + + (button, input_values) = window.Layout(layout).Read() + + if button != 'Ok': + return None + else: + return input_values[0] + + + def main(): layout = [[Text('You are running the PySimpleGUI.py file itself')], [Text('You should be importing it rather than running it', size=(50, 2))], diff --git a/docs/cookbook.md b/docs/cookbook.md index bc44c835..3756f28e 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -17,6 +17,7 @@ with There is a short section in the Readme with instruction on installing PySimpleGUI +If you like this Cookbook, then you'll LOVE the 100+ sample programs that are just like these. You'll find them in the GitHub at http://www.PySimpleGUI.com. These Recipes are simply several of those programs displayed in document format. ## Simple Data Entry - Return Values As List Same GUI screen except the return values are in a list instead of a dictionary and doesn't have initial values. @@ -96,7 +97,7 @@ Quickly add a GUI allowing the user to browse for a filename if a filename is no if len(sys.argv) == 1: event, (fname,) = sg.Window('My Script').Layout([[sg.Text('Document to open')], [sg.In(), sg.FileBrowse()], - [sg.Open(), sg.Cancel()]]).Read() + [sg.CButton('Open'), sg.CButton('Cancel')]]).Read() else: fname = sys.argv[1] @@ -191,37 +192,60 @@ Example of nearly all of the widgets in a single window. Uses a customized colo ------------- + ## Window that stays open reading inputs and button clicks + +This is the most basic form of a "Persistent Window", a window that remains open after button clicks and data entry. + +```python +import PySimpleGUI as sg + +layout = [[sg.Text('Persistent window')], + [sg.Input()], + [sg.Button('Read'), sg.Exit()]] + +window = sg.Window('Window that stays open').Layout(layout) + +while True: + event, values = window.Read() + if event is None or event == 'Exit': + break + print(event, values) + +window.Close() +``` + + ## Non-Blocking Window With Periodic Update -An async Window that has a event read loop. A Text Element is updated periodically with a running timer. Note that `value` is checked for None which indicates the window was closed using X. +An async Window that has a event read loop. A Text Element is updated periodically with a running timer. Note that `value` is checked for None which indicates the window was closed using X. +Use caution when using windows with a timeout. You should rarely need to use a timeout=0, non-blocking call, so try not to abuse this design pattern. + ![non-blocking](https://user-images.githubusercontent.com/13696193/43955295-70f6ac48-9c6d-11e8-8ea2-e6729ba9330c.jpg) - - import PySimpleGUI as sg - import time - - gui_rows = [[sg.Text('Stopwatch', size=(20, 2), justification='center')], - [sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center', key='output')], - [sg.T(' ' * 5), sg.ReadButton('Start/Stop', focus=True), sg.Quit()]] - - window = sg.Window('Running Timer').Layout(gui_rows) - - timer_running = True - i = 0 - # Event Loop - while True: - i += 1 * (timer_running is True) - event, values = window.ReadNonBlocking() - - if values is None or event == 'Quit': # if user closed the window using X or clicked Quit button - break - elif event == 'Start/Stop': - timer_running = not timer_running - - window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) - time.sleep(.01) +```python +import PySimpleGUI as sg + +gui_rows = [[sg.Text('Stopwatch', size=(20, 2), justification='center')], + [sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center', key='output')], + [sg.T(' ' * 5), sg.Button('Start/Stop', focus=True), sg.Quit()]] + +window = sg.Window('Running Timer').Layout(gui_rows) + +timer_running = True +i = 0 +# Event Loop +while True: + i += 1 * (timer_running is True) + event, values = window.Read(timeout=10) # Please try and use a timeout when possible + if event is None or event == 'Quit': # if user closed the window using X or clicked Quit button + break + elif event == 'Start/Stop': + timer_running = not timer_running + window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) +``` + -------- @@ -247,7 +271,7 @@ The architecture of some programs works better with button callbacks instead of # Layout the design of the GUI layout = [[sg.Text('Please click a button', auto_size_text=True)], - [sg.ReadButton('1'), sg.ReadButton('2'), sg.Quit()]] + [sg.Button('1'), sg.Button('2'), sg.Quit()]] # Show the Window to the user window = sg.Window('Button callback example').Layout(layout) @@ -261,7 +285,8 @@ The architecture of some programs works better with button callbacks instead of button1() elif event == '2': button2() - elif event =='Quit' or event is None: + elif event =='Quit' or event is None: + window.Close() break # All done! @@ -288,19 +313,19 @@ This recipe implements a remote control interface for a robot. There are 4 dire # # Some place later in your code... - # You need to perform a ReadNonBlocking on your window every now and then or - # else it won't refresh. + # You need to perform a Read or Refresh on your window every now and then or + # else it will appear your program has hung # # your program's main loop while (True): # This is the code that reads and updates your window - event, values = window.ReadNonBlocking() + event, values = window.Read(timeout=10) if event is not None: print(event) if event == 'Quit' or values is None: break - window.CloseNonBlocking() # Don't forget to close your window! + window.Close() # Don't forget to close your window! --------- @@ -324,63 +349,83 @@ Buttons can have PNG of GIF images on them. This Media Player recipe requires 4 ![media player](https://user-images.githubusercontent.com/13696193/43958418-5dd133f2-9c79-11e8-9432-0a67007e85ac.jpg) - import PySimpleGUI as sg - - background = '#F0F0F0' - # Set the backgrounds the same as the background on the buttons - sg.SetOptions(background_color=background, element_background_color=background) - # Images are located in a subfolder in the Demo Media Player.py folder - image_pause = './ButtonGraphics/Pause.png' - image_restart = './ButtonGraphics/Restart.png' - image_next = './ButtonGraphics/Next.png' - image_exit = './ButtonGraphics/Exit.png' - - # define layout of the rows - layout = [[sg.Text('Media File Player', size=(17, 1), font=("Helvetica", 25))], - [sg.Text('', size=(15, 2), font=("Helvetica", 14), key='output')], - [sg.ReadButton('Restart Song', button_color=(background, background), - image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0), - sg.Text(' ' * 2), - sg.ReadButton('Pause', button_color=(background, background), - image_filename=image_pause, image_size=(50, 50), image_subsample=2, border_width=0), - sg.Text(' ' * 2), - sg.ReadButton('Next', button_color=(background, background), - image_filename=image_next, image_size=(50, 50), image_subsample=2, border_width=0), - sg.Text(' ' * 2), - sg.Text(' ' * 2), sg.Button('Exit', button_color=(background, background), - image_filename=image_exit, image_size=(50, 50), image_subsample=2, - border_width=0)], - [sg.Text('_' * 30)], - [sg.Text(' ' * 30)], - [ - sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', - font=("Helvetica", 15)), - sg.Text(' ' * 2), - sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', - font=("Helvetica", 15)), - sg.Text(' ' * 8), - sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', - font=("Helvetica", 15))], - [sg.Text('Bass', font=("Helvetica", 15), size=(6, 1)), - sg.Text('Treble', font=("Helvetica", 15), size=(10, 1)), - sg.Text('Volume', font=("Helvetica", 15), size=(7, 1))] - ] - - window = sg.Window('Media File Player', auto_size_text=True, default_element_size=(20, 1), - font=("Helvetica", 25)).Layout(layout) - # Our event loop - while (True): - # Read the window (this call will not block) - event, values = window.ReadNonBlocking() - if event == 'Exit' or values is None: - break - # If a button was pressed, display it on the GUI by updating the text element - if event: - window.FindElement('output').Update(event) + +```python + +#!/usr/bin/env python +import sys +if sys.version_info[0] >= 3: + import PySimpleGUI as sg +else: + import PySimpleGUI27 as sg + +# +# An Async Demonstration of a media player +# Uses button images for a super snazzy look +# See how it looks here: +# https://user-images.githubusercontent.com/13696193/43159403-45c9726e-8f50-11e8-9da0-0d272e20c579.jpg +# +def MediaPlayerGUI(): + background = '#F0F0F0' + # Set the backgrounds the same as the background on the buttons + sg.SetOptions(background_color=background, element_background_color=background) + # Images are located in a subfolder in the Demo Media Player.py folder + image_pause = './ButtonGraphics/Pause.png' + image_restart = './ButtonGraphics/Restart.png' + image_next = './ButtonGraphics/Next.png' + image_exit = './ButtonGraphics/Exit.png' + + # A text element that will be changed to display messages in the GUI + + + # define layout of the rows + layout= [[sg.Text('Media File Player',size=(17,1), font=("Helvetica", 25))], + [sg.Text('', size=(15, 2), font=("Helvetica", 14), key='output')], + [sg.Button('', button_color=(background,background), + image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0, key='Restart Song'), + sg.Text(' ' * 2), + sg.Button('', button_color=(background,background), + image_filename=image_pause, image_size=(50, 50), image_subsample=2, border_width=0, key='Pause'), + sg.Text(' ' * 2), + sg.Button('', button_color=(background,background), image_filename=image_next, image_size=(50, 50), image_subsample=2, border_width=0, key='Next'), + sg.Text(' ' * 2), + sg.Text(' ' * 2), sg.Button('', button_color=(background,background), + image_filename=image_exit, image_size=(50, 50), image_subsample=2, border_width=0, key='Exit')], + [sg.Text('_'*20)], + [sg.Text(' '*30)], + [ + sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15)), + sg.Text(' ' * 2), + sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15)), + sg.Text(' ' * 2), + sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15))], + [sg.Text(' Bass', font=("Helvetica", 15), size=(9, 1)), + sg.Text('Treble', font=("Helvetica", 15), size=(7, 1)), + sg.Text('Volume', font=("Helvetica", 15), size=(7, 1))] + ] + + # Open a form, note that context manager can't be used generally speaking for async forms + window = sg.Window('Media File Player', auto_size_text=True, default_element_size=(20, 1), + font=("Helvetica", 25)).Layout(layout) + # Our event loop + while(True): + event, values = window.Read(timeout=100) # Poll every 100 ms + if event == 'Exit' or event is None: + break + # If a button was pressed, display it on the GUI by updating the text element + if event != sg.TIMEOUT_KEY: + window.FindElement('output').Update(event) + +MediaPlayerGUI() + + +``` + + ---- ## Script Launcher - Persistent Window -This Window doesn't close after button clicks. To achieve this the buttons are specified as `sg.ReadButton` instead of `sg.Button`. The exception to this is the EXIT button. Clicking it will close the window. This program will run commands and display the output in the scrollable window. +This Window doesn't close after button clicks. To achieve this the buttons are specified as `sg.Button` instead of `sg.Button`. The exception to this is the EXIT button. Clicking it will close the window. This program will run commands and display the output in the scrollable window. ![launcher 2](https://user-images.githubusercontent.com/13696193/43958519-b30af218-9c79-11e8-88da-fadc69da818c.jpg) @@ -403,8 +448,8 @@ This Window doesn't close after button clicks. To achieve this the buttons are layout = [ [sg.Text('Script output....', size=(40, 1))], [sg.Output(size=(88, 20))], - [sg.ReadButton('script1'), sg.ReadButton('script2'), sg.Button('EXIT')], - [sg.Text('Manual command', size=(15, 1)), sg.InputText(focus=True), sg.ReadButton('Run', bind_return_key=True)] + [sg.Button('script1'), sg.Button('script2'), sg.Button('EXIT')], + [sg.Text('Manual command', size=(15, 1)), sg.InputText(focus=True), sg.Button('Run', bind_return_key=True)] ] @@ -469,25 +514,27 @@ Perhaps you don't want all the statistics that the EasyProgressMeter provides an ![custom progress meter](https://user-images.githubusercontent.com/13696193/43982958-3393b23e-9cc6-11e8-8b49-e7f4890cbc4b.jpg) - import PySimpleGUI as sg - - # layout the Window - layout = [[sg.Text('A custom progress meter')], - [sg.ProgressBar(10000, orientation='h', size=(20, 20), key='progbar')], - [sg.Cancel()]] - - # create the Window - window = sg.Window('Custom Progress Meter').Layout(layout) - # loop that would normally do something useful - for i in range(10000): - # check to see if the cancel button was clicked and exit loop if clicked - event, values = window.ReadNonBlocking() - if event == 'Cancel' or values == None: - break - # update bar with loop value +1 so that bar eventually reaches the maximum - window.FindElement('progbar').UpdateBar(i + 1) - # done with loop... need to destroy the window as it's still open - window.CloseNonBlocking() +```python +import PySimpleGUI as sg + +# layout the Window +layout = [[sg.Text('A custom progress meter')], + [sg.ProgressBar(1000, orientation='h', size=(20, 20), key='progbar')], + [sg.Cancel()]] + +# create the Window +window = sg.Window('Custom Progress Meter').Layout(layout) +# loop that would normally do something useful +for i in range(1000): + # check to see if the cancel button was clicked and exit loop if clicked + event, values = window.Read(timeout=0) + if event == 'Cancel' or event is None: + break + # update bar with loop value +1 so that bar eventually reaches the maximum + window.FindElement('progbar').UpdateBar(i + 1) +# done with loop... need to destroy the window as it's still open +window.Close() +``` ---- @@ -570,7 +617,7 @@ This simple program keep a window open, taking input values until the user termi [sg.Txt('_' * 10)], [sg.In(size=(8,1), key='denominator')], [sg.Txt('', size=(8,1), key='output') ], - [sg.ReadButton('Calculate', bind_return_key=True)]] + [sg.Button('Calculate', bind_return_key=True)]] window = sg.Window('Math').Layout(layout) @@ -609,7 +656,7 @@ While it's fun to scribble on a Canvas Widget, try Graph Element makes it a down layout = [ [sg.Canvas(size=(100, 100), background_color='red', key= 'canvas')], - [sg.T('Change circle color to:'), sg.ReadButton('Red'), sg.ReadButton('Blue')] + [sg.T('Change circle color to:'), sg.Button('Red'), sg.Button('Blue')] ] window = sg.Window('Canvas test') @@ -639,7 +686,7 @@ Just like you can draw on a tkinter widget, you can also draw on a Graph Element layout = [ [sg.Graph(canvas_size=(400, 400), graph_bottom_left=(0,0), graph_top_right=(400, 400), background_color='red', key='graph')], - [sg.T('Change circle color to:'), sg.ReadButton('Red'), sg.ReadButton('Blue'), sg.ReadButton('Move')] + [sg.T('Change circle color to:'), sg.Button('Red'), sg.Button('Blue'), sg.Button('Move')] ] window = sg.Window('Graph test') @@ -674,7 +721,7 @@ This Recipe implements a Raspberry Pi touchscreen based keypad entry. As the di There are a number of features used in this Recipe including: * Default Element Size * auto_size_buttons -* ReadButton +* Button * Dictionary Return values * Update of Elements in window (Input, Text) * do_not_clear of Input Elements @@ -689,17 +736,17 @@ There are a number of features used in this Recipe including: # Demonstrates a number of PySimpleGUI features including: # Default element size # auto_size_buttons - # ReadButton + # Button # Dictionary return values # Update of elements in window (Text, Input) # do_not_clear of Input elements layout = [[sg.Text('Enter Your Passcode')], [sg.Input(size=(10, 1), do_not_clear=True, justification='right', key='input')], - [sg.ReadButton('1'), sg.ReadButton('2'), sg.ReadButton('3')], - [sg.ReadButton('4'), sg.ReadButton('5'), sg.ReadButton('6')], - [sg.ReadButton('7'), sg.ReadButton('8'), sg.ReadButton('9')], - [sg.ReadButton('Submit'), sg.ReadButton('0'), sg.ReadButton('Clear')], + [sg.Button('1'), sg.Button('2'), sg.Button('3')], + [sg.Button('4'), sg.Button('5'), sg.Button('6')], + [sg.Button('7'), sg.Button('8'), sg.Button('9')], + [sg.Button('Submit'), sg.Button('0'), sg.Button('Clear')], [sg.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='out')], ] @@ -729,60 +776,61 @@ Use the Canvas Element to create an animated graph. The code is a bit tricky to ![animated matplotlib](https://user-images.githubusercontent.com/13696193/44640937-91b9ea80-a992-11e8-9c1c-85ae74013679.jpg) - from tkinter import * - from random import randint - import PySimpleGUI as g - from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg - from matplotlib.figure import Figure - import matplotlib.backends.tkagg as tkagg - import tkinter as Tk - - - fig = Figure() - - ax = fig.add_subplot(111) - ax.set_xlabel("X axis") - ax.set_ylabel("Y axis") - ax.grid() - - layout = [[g.Text('Animated Matplotlib', size=(40, 1), justification='center', font='Helvetica 20')], - [g.Canvas(size=(640, 480), key='canvas')], - [g.ReadButton('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]] - - # create the window and show it without the plot - - - window = g.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout) - window.Finalize() # needed to access the canvas element prior to reading the window - - canvas_elem = window.FindElement('canvas') - - graph = FigureCanvasTkAgg(fig, master=canvas_elem.TKCanvas) - canvas = canvas_elem.TKCanvas - - dpts = [randint(0, 10) for x in range(10000)] - # Our event loop - for i in range(len(dpts)): - event, values = window.ReadNonBlocking() - if event == 'Exit' or values is None: - exit(69) - - ax.cla() - ax.grid() - - ax.plot(range(20), dpts[i:i + 20], color='purple') - graph.draw() - figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds - figure_w, figure_h = int(figure_w), int(figure_h) - photo = Tk.PhotoImage(master=canvas, width=figure_w, height=figure_h) - - canvas.create_image(640 / 2, 480 / 2, image=photo) - - figure_canvas_agg = FigureCanvasAgg(fig) - figure_canvas_agg.draw() - - tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2) - +```python +from tkinter import * +from random import randint +import PySimpleGUI as sg +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg +from matplotlib.figure import Figure +import matplotlib.backends.tkagg as tkagg +import tkinter as Tk + +fig = Figure() + +ax = fig.add_subplot(111) +ax.set_xlabel("X axis") +ax.set_ylabel("Y axis") +ax.grid() + +layout = [[sg.Text('Animated Matplotlib', size=(40, 1), justification='center', font='Helvetica 20')], + [sg.Canvas(size=(640, 480), key='canvas')], + [sg.Button('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]] + +# create the window and show it without the plot + + +window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout) +window.Finalize() # needed to access the canvas element prior to reading the window + +canvas_elem = window.FindElement('canvas') + +graph = FigureCanvasTkAgg(fig, master=canvas_elem.TKCanvas) +canvas = canvas_elem.TKCanvas + +dpts = [randint(0, 10) for x in range(10000)] +# Our event loop +for i in range(len(dpts)): + event, values = window.Read(timeout=20) + if event == 'Exit' or event is None: + exit(69) + + ax.cla() + ax.grid() + + ax.plot(range(20), dpts[i:i + 20], color='purple') + graph.draw() + figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds + figure_w, figure_h = int(figure_w), int(figure_h) + photo = Tk.PhotoImage(master=canvas, width=figure_w, height=figure_h) + + canvas.create_image(640 / 2, 480 / 2, image=photo) + + figure_canvas_agg = FigureCanvasAgg(fig) + figure_canvas_agg.draw() + + tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2) + +``` ## Tight Layout with Button States @@ -809,10 +857,10 @@ In other GUI frameworks this program would be most likely "event driven" with ca layout = [[sg.T('User:', pad=((3,0),0)), sg.OptionMenu(values = ('User 1', 'User 2'), size=(20,1)), sg.T('0', size=(8,1))], [sg.T('Customer:', pad=((3,0),0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20,1)), sg.T('1', size=(8,1))], [sg.T('Notes:', pad=((3,0),0)), sg.In(size=(44,1), background_color='white', text_color='black')], - [sg.ReadButton('Start', button_color=('white', 'black'), key='Start'), - sg.ReadButton('Stop', button_color=('white', 'black'), key='Stop'), - sg.ReadButton('Reset', button_color=('white', 'firebrick3'), key='Reset'), - sg.ReadButton('Submit', button_color=('white', 'springgreen4'), key='Submit')] + [sg.Button('Start', button_color=('white', 'black'), key='Start'), + sg.Button('Stop', button_color=('white', 'black'), key='Stop'), + sg.Button('Reset', button_color=('white', 'firebrick3'), key='Reset'), + sg.Button('Submit', button_color=('white', 'springgreen4'), key='Submit')] ] window = sg.Window("Time Tracker", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False, @@ -973,10 +1021,10 @@ You can easily change colors to match your background by changing a couple of pa sg.SetOptions(element_padding=(0,0), button_element_size=(12,1), auto_size_buttons=False) layout = [[sg.Combo(values=namesonly, size=(35,30), key='demofile'), - sg.ReadButton('Run', button_color=('white', '#00168B')), - sg.ReadButton('Program 1'), - sg.ReadButton('Program 2'), - sg.ReadButton('Program 3', button_color=('white', '#35008B')), + sg.Button('Run', button_color=('white', '#00168B')), + sg.Button('Program 1'), + sg.Button('Program 2'), + sg.Button('Program 3', button_color=('white', '#35008B')), sg.Button('EXIT', button_color=('white','firebrick3'))], [sg.T('', text_color='white', size=(50,1), key='output')]] @@ -1032,72 +1080,82 @@ Much of the code is handling the button states in a fancy way. It could be much ![timer](https://user-images.githubusercontent.com/13696193/45336349-26a31300-b551-11e8-8b06-d1232ff8ca10.jpg) - import PySimpleGUI as sg - import time - - """ - Timer Desktop Widget Creates a floating timer that is always on top of other windows You move it by grabbing anywhere on the window Good example of how to do a non-blocking, polling program using PySimpleGUI Can be used to poll hardware when running on a Pi NOTE - you will get a warning message printed when you exit using exit button. It will look something like: invalid command name \"1616802625480StopMove\" - """ - - - # ---------------- Create window ---------------- - sg.ChangeLookAndFeel('Black') - sg.SetOptions(element_padding=(0, 0)) - - layout = [[sg.Text('')], - [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], - [sg.ReadButton('Pause', key='button', button_color=('white', '#001480')), - sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'), - sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]] - - window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout) - - - # ---------------- main loop ---------------- - current_time = 0 - paused = False - start_time = int(round(time.time() * 100)) - while (True): - # --------- Read and update window -------- - if not paused: - event, values = window.ReadNonBlocking() - current_time = int(round(time.time() * 100)) - start_time - else: - event, values = window.Read() - if event == 'button': - button = window.FindElement(button).GetText() - # --------- Do Button Operations -------- - if values is None or event == 'Exit': - break - if event is 'Reset': - start_time = int(round(time.time() * 100)) - current_time = 0 - paused_time = start_time - elif event == 'Pause': - paused = True - paused_time = int(round(time.time() * 100)) - element = window.FindElement('button') - element.Update(text='Run') - elif event == 'Run': - paused = False - start_time = start_time + int(round(time.time() * 100)) - paused_time - element = window.FindElement('button') - element.Update(text='Pause') - - # --------- Display timer in window -------- - window.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60, - (current_time // 100) % 60, - current_time % 100)) - time.sleep(.01) - - # --------- After loop -------- - - # Broke out of main loop. Close the window. - window.CloseNonBlocking() +```python +import sys +if sys.version_info[0] >= 3: + import PySimpleGUI as sg +else: + import PySimpleGUI27 as sg +import time + +""" + Timer Desktop Widget Creates a floating timer that is always on top of other windows You move it by grabbing anywhere on the window Good example of how to do a non-blocking, polling program using SimpleGUI Can be used to poll hardware when running on a Pi + + While the timer ticks are being generated by PySimpleGUI's "timeout" mechanism, the actual value + of the timer that is displayed comes from the system timer, time.time(). This guarantees an + accurate time value is displayed regardless of the accuracy of the PySimpleGUI timer tick. If + this design were not used, then the time value displayed would slowly drift by the amount of time + it takes to execute the PySimpleGUI read and update calls (not good!) + + NOTE - you will get a warning message printed when you exit using exit button. + It will look something like: invalid command name \"1616802625480StopMove\" +""" + + +# ---------------- Create Form ---------------- +sg.ChangeLookAndFeel('Black') +sg.SetOptions(element_padding=(0, 0)) + +layout = [[sg.Text('')], + [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], + [sg.Button('Pause', key='button', button_color=('white', '#001480')), + sg.Button('Reset', button_color=('white', '#007339'), key='Reset'), + sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]] + +window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout) + +# ---------------- main loop ---------------- +current_time = 0 +paused = False +start_time = int(round(time.time() * 100)) +while (True): + # --------- Read and update window -------- + if not paused: + event, values = window.Read(timeout=10) + current_time = int(round(time.time() * 100)) - start_time + else: + event, values = window.Read() + if event == 'button': + event = window.FindElement(event).GetText() + # --------- Do Button Operations -------- + if event is None or event == 'Exit': # ALWAYS give a way out of program + break + if event is 'Reset': + start_time = int(round(time.time() * 100)) + current_time = 0 + paused_time = start_time + elif event == 'Pause': + paused = True + paused_time = int(round(time.time() * 100)) + element = window.FindElement('button') + element.Update(text='Run') + elif event == 'Run': + paused = False + start_time = start_time + int(round(time.time() * 100)) - paused_time + element = window.FindElement('button') + element.Update(text='Pause') + + # --------- Display timer in window -------- + window.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60, + (current_time // 100) % 60, + current_time % 100)) +``` + ## Desktop Floating Widget - CPU Utilization -Like the Timer widget above, this script can be kept running. You will need the package psutil installed in order to run this Recipe. +Like the Timer widget above, this script can be kept running. You will need the package psutil installed in order to run this Recipe. + The spinner changes the number of seconds between reads. Note that you will get an error message printed when exiting because the window does not have have a titlebar. It's a known problem. @@ -1105,39 +1163,42 @@ The spinner changes the number of seconds between reads. Note that you will get - import PySimpleGUI as sg - import psutil - - # ---------------- Create Window ---------------- - sg.ChangeLookAndFeel('Black') - layout = [[sg.Text('')], - [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], - [sg.Exit(button_color=('white', 'firebrick4'), pad=((15,0), 0)), sg.Spin([x+1 for x in range(10)], 1, key='spin')]] - - window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout) - - - # ---------------- main loop ---------------- - while (True): - # --------- Read and update window -------- - event, values = window.ReadNonBlocking() - - # --------- Do Button Operations -------- - if values is None or event == 'Exit': - break - try: - interval = int(values['spin']) - except: - interval = 1 - - cpu_percent = psutil.cpu_percent(interval=interval) - - # --------- Display timer in window -------- - - window.FindElement('text').Update(f'CPU {cpu_percent:02.0f}%') - - # Broke out of main loop. Close the window. - window.CloseNonBlocking() +```python +import PySimpleGUI as sg +import psutil + +# ---------------- Create Window ---------------- +sg.ChangeLookAndFeel('Black') +layout = [[sg.Text('')], + [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], + [sg.Exit(button_color=('white', 'firebrick4'), pad=((15, 0), 0)), + sg.Spin([x + 1 for x in range(10)], 1, key='spin')]] + +window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, + grab_anywhere=True).Layout(layout) + +# ---------------- main loop ---------------- +while (True): + # --------- Read and update window -------- + event, values = window.Read(timeout=0) + + # --------- Do Button Operations -------- + if event is None or event == 'Exit': + break + try: + interval = int(values['spin']) + except: + interval = 1 + + cpu_percent = psutil.cpu_percent(interval=interval) + + # --------- Display timer in window -------- + + window.FindElement('text').Update(f'CPU {cpu_percent:02.0f}%') + +# Broke out of main loop. Close the window. +window.Close() +``` ## Menus @@ -1145,7 +1206,7 @@ Menus are nothing more than buttons that live in a menu-bar. When you click on Menu's are defined separately from the GUI window. To add one to your window, simply insert sg.Menu(menu_layout). The menu definition is a list of menu choices and submenus. They are a list of lists. Copy the Recipe and play with it. You'll eventually get when you're looking for. -If you double click the dashed line at the top of the list of choices, that menu will tear off and become a floating toolbar. How cool! +If you double click the dashed line at the top of the list of choices, that menu will tear off and become a floating toolbar. How cool! To enable this feature, set the parameter `tearoff=True` in your call to `sg.Menu()` ![tear off](https://user-images.githubusercontent.com/13696193/45307668-9aabcf80-b4ed-11e8-9b2b-8564d4bf82a8.jpg) @@ -1164,7 +1225,7 @@ If you double click the dashed line at the top of the list of choices, that menu # ------ GUI Defintion ------ # layout = [ - [sg.Menu(menu_def)], + [sg.Menu(menu_def, )], [sg.Output(size=(60, 20))] ] @@ -1247,7 +1308,7 @@ tab2_layout = [[sg.T('This is inside tab 2')], [sg.In(key='in')]] layout = [[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout, tooltip='tip'), sg.Tab('Tab 2', tab2_layout)]], tooltip='TIP2')], - [sg.RButton('Read')]] + [sg.Button('Read')]] window = sg.Window('My window with tabs', default_element_size=(12,1)).Layout(layout) diff --git a/docs/index.md b/docs/index.md index 61251053..e581cf1b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,5 @@ + @@ -23,9 +24,9 @@ ## Now supports both Python 2.7 & 3 -![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.11.0-red.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.12.0-red.svg?longCache=true&style=for-the-badge) - ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.11.0-blue.svg?longCache=true&style=for-the-badge) + ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.12.0-blue.svg?longCache=true&style=for-the-badge) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) @@ -934,14 +935,10 @@ Isn't this what a Python programmer looking for a GUI wants? Something easy to w ### Two Return Values -All Window Read and ReadNonBlocking calls return 2 values. By convention a read statement is written: +All Window Read calls return 2 values. By convention a read statement is written: ```python event, values = window.Read() ``` -All of the demo programs and the Cookbook recipes have this line of code for windows that are normal reads (not non-blocking). A similar line of code is used for non-blocking window reads: -```python -event, values = window.ReadNonBlocking() -``` You don't HAVE to write your reads in this way. You can name your variables however you want. But if you want to code them in a way that other programmers using PySimpleGUI are used to, then use these statements. @@ -970,6 +967,15 @@ To check for a closed window use this line of code: if event is None: +Putting it all together we end up with an "event loop" that looks something like this: +```python +while True: + event, values = window.Read() + if event is None: + break + +``` + ### The 'values' Variable - Return values as a list @@ -1181,8 +1187,12 @@ You will find it ***much easier*** to write code using PySimpleGUI if you use an ## Synchronous windows The most common use of PySimpleGUI is to display and collect information from the user. The most straightforward way to do this is using a "blocking" GUI call. Execution is "blocked" while waiting for the user to close the GUI window/dialog box. -You've already seen a number of examples above that use blocking windows. The call to look for that will show you non-blocking windows are calls to `ReadNonBlocking()`. You can read more about Async windows at the end of this document. +You've already seen a number of examples above that use blocking windows. A truly non-blocking Read call looks like this: +```python +event, values = window.Read(timeout=0) +``` +You can learn more about these async / non-blocking windows toward the end of this document. # Window Object - Beginning a window @@ -1287,12 +1297,12 @@ There are a few methods (functions) that you will see in this document that act window.Layout(layout) - Turns your definition of the Window into Window window.Finalize() - creates the tkinter objects for the Window. Normally you do not call this window.Read() - Read the Windows values and get the button / key that caused the Read to return. Can have an optional timeout - window.ReadNonBlocking() - Same as Read but will return right away + window.ReadNonBlocking() - NO LONGER USED! window.Refresh() - Use if updating elements and want to show the updates prior to the nex Read window.Fill(values_dict) - Fill each Element with entry from the dictionary passed in window.SaveToDisk(filename) - Save the Window's values to disk window.LoadFromDisk(filename) - Load the Window's values from disk - window.Close() - To close your window, if a button hasn't already closed iit + window.Close() - To close your window, if a button hasn't already closed it window.Disable() - Use to disable the window inpurt when opening another window on top of the primnary Window window.Enable() - Re-enable a Disabled window window.FindElement(key) - Returns the element that has a matching key value @@ -2056,7 +2066,7 @@ The Types of buttons include: * Color Chooser - Close window - Normal buttons like Submit, Cancel, Yes, No, etc, are "Close window" buttons. They cause the input values to be read and then the window is ***closed***, returning the values to the caller. +Close window - Normal buttons like Submit, Cancel, Yes, No, do NOT close the window... they used to. Now to close a window you need to use a CloseButton / CButton. Folder Browse - When clicked a folder browse dialog box is opened. The results of the Folder Browse dialog box are written into one of the input fields of the window. @@ -2070,17 +2080,20 @@ Read window - This is a window button that will read a snapshot of all of the in Realtime - This is another async window button. Normal button clicks occur after a button's click is released. Realtime buttons report a click the entire time the button is held down. -Most programs will use a combination of shortcut button calls (Submit, Cancel, etc), plain buttons that close the window, and ReadButton buttons that keep the window open but returns control back to the caller. +Most programs will use a combination of shortcut button calls (Submit, Cancel, etc), normal Buttons which leave the windows open and CloseButtons that close the window when clicked. -Sometimes there are multiple names for the same function. This is simply to make the job of the programmer quicker and easier. +Sometimes there are multiple names for the same function. This is simply to make the job of the programmer quicker and easier. Or they are old names that are no longer used but kept around so that existing programs don't break. -The 3 primary windows of PySimpleGUI buttons and their names are: +The 4 primary windows of PySimpleGUI buttons and their names are: - 1. `Button` = `SimpleButton` - 2. `ReadButton` = `RButton` = `ReadFormButton` (old style... use ReadButton instead) + 1. `Button`= `ReadButton` = `RButton` = `ReadFormButton` (old style... use Button instead) + 2. `CloseButton` = `CButton` 3. `RealtimeButton` + 4. `DummyButton` -You will find the long-form in the older programs. +You will find the long-form names in the older programs. ReadButton for example. + +In Oct 2018, the definition of Button changed. Previously Button would CLOSE the window when clicked. It has been changed so the Button calls will leave the window open in exactly the same way as a ReadButton. They are the same calls now. To enables windows to be closed using buttons, a new button was added... `CloseButton` or `CButton`. The most basic Button element call to use is `Button` @@ -2152,9 +2165,10 @@ These Pre-made buttons are some of the most important elements of all because th **IMPORT NOTE ABOUT SHORTCUT BUTTONS** Prior to release 3.11.0, these buttons closed the window. Starting with 3.11 they will not close the window. They act like RButtons (return the button text and do not close the window) -If you are having trouble with these buttons closing your window, please check your installed version of PySimpleGUI by typing `pip list` at a command promt. Prior to 3.11 these buttons close your window. +If you are having trouble with these buttons closing your window, please check your installed version of PySimpleGUI by typing `pip list` at a command prompt. Prior to 3.11 these buttons close your window. + +Using older versions, if you want a Submit() button that does not close the window, then you would instead use RButton('Submit'). Using the new version, if you want a Submit button that closes the window like the sold Submit() call did, you would write that as `CloseButton('Submit')` or `CButton('Submit')` -Using older versions, if you want a Submit() button that does not close the window, then you would instead use RButton('Submit') layout = [[sg.OK(), sg.Cancel()]] @@ -2299,22 +2313,22 @@ window = sg.Window('Robotics Remote Control', auto_size_text=True).Layout(gui_ro # # Some place later in your code... -# You need to perform a ReadNonBlocking on your window every now and then or -# else it won't refresh. +# You need to perform a Read or Refresh call on your window every now and then or +# else it will apprear as if the program has locked up. # # your program's main loop while (True): # This is the code that reads and updates your window - event, values = window.Read(timeout=0) + event, values = window.Read(timeout=0) if event is not None: print(event) if event == 'Quit' or values is None: - break + break window.Close() # Don't forget to close your window! ``` -This loop will read button values and print them. When one of the Realtime buttons is clicked, the call to `window.ReadNonBlocking` will return a button name matching the name on the button that was depressed or the key if there was a key assigned to the button. It will continue to return values as long as the button remains depressed. Once released, the ReadNonBlocking will return None for buttons until a button is again clicked. +This loop will read button values and print them. When one of the Realtime buttons is clicked, the call to `window.Read` will return a button name matching the name on the button that was depressed or the key if there was a key assigned to the button. It will continue to return values as long as the button remains depressed. Once released, the Read will return timeout events until a button is again clicked. **File Types** The `FileBrowse` & `SaveAs` buttons have an additional setting named `file_types`. This variable is used to filter the files shown in the file dialog box. The default value for this setting is @@ -2679,7 +2693,7 @@ Let me say up front that the Table Element has Beta status. The reason is that s ### Read return values from Table Element -The values returned from a `Window.Read` or `Window.ReadNonBlocking` call for the Tree Element are a list of row numbers that are currently highlighted. +The values returned from a `Window.Read` call for the Tree Element are a list of row numbers that are currently highlighted. ### Update Call @@ -2947,7 +2961,7 @@ There are 2 ways to keep a window open after the user has clicked a button. One The `RButton` Element creates a button that when clicked will return control to the user, but will leave the window open and visible. This button is also used in Non-Blocking windows. The difference is in which call is made to read the window. The normal `Read` call with no parameters will block, a call with a `timeout` value of zero will not block. -Note that `InputText` and `MultiLine` Elements will be **cleared** when performing a `ReadNonBlocking`. If you do not want your input field to be cleared after a `ReadNonBlocking` then you can set the `do_not_clear` parameter to True when creating those elements. The clear is turned on and off on an element by element basis. +Note that `InputText` and `MultiLine` Elements will be **cleared** when performing a `Read`. If you do not want your input field to be cleared after a `Read` then you can set the `do_not_clear` parameter to True when creating those elements. The clear is turned on and off on an element by element basis. The reasoning behind this is that Persistent Windows are often "forms". When "submitting" a form you want to have all of the fields left blank so the next entry of data will start with a fresh window. Also, when implementing a "Chat Window" type of interface, after each read / send of the chat data, you want the input field cleared. Think of it as a Texting application. Would you want to have to clear your previous text if you want to send a second text? @@ -2986,7 +3000,7 @@ Let's say you had a device that you want to "poll" every 100ms. The "easy way ```python # YOU SHOULD NOT DO THIS.... while True: # Event Loop - event, values = window.ReadNonBlocking() + event, values = window.ReadNonBlocking() # DO NOT USE THIS CALL ANYMORE read_my_hardware() # process my device here time.sleep(.1) # sleep 1/10 second ``` @@ -3518,6 +3532,7 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it | 3.10.1 & 1.2.1 | Oct 20, 2018 | 3.10.3 & 1.2.3 | Oct 23, 2018 | 3.11.0 & 1.11.0 | Oct 28, 2018 +| 3.12.0 & 1.12.0 | Oct 28, 2018 ## Release Notes @@ -3740,6 +3755,13 @@ Emergency patch release... going out same day as previous release * Shortcut buttons no longer close windows! * Added CloseButton, CButton that closes the windows +### 3.12.0 & 1.12.0 +* Changed Button to be the same as ReadButton which means it will no longer close the window +* All shortcut buttons no longer close the window +* Updating a table clears selected rows information in return values +* Progress meter uses new CloseButton +* Popups use new CloseButton + ### Upcoming Make suggestions people! Future release features diff --git a/docs/pysimplegui_readthedocs_io.pdf b/docs/pysimplegui_readthedocs_io.pdf deleted file mode 100644 index 3ffb0cce..00000000 Binary files a/docs/pysimplegui_readthedocs_io.pdf and /dev/null differ diff --git a/docs/pysimplegui_readthedocs_io_cookbook.pdf b/docs/pysimplegui_readthedocs_io_cookbook.pdf deleted file mode 100644 index 0164ce59..00000000 Binary files a/docs/pysimplegui_readthedocs_io_cookbook.pdf and /dev/null differ diff --git a/readme.md b/readme.md index 61251053..e581cf1b 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,5 @@ + @@ -23,9 +24,9 @@ ## Now supports both Python 2.7 & 3 -![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.11.0-red.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.12.0-red.svg?longCache=true&style=for-the-badge) - ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.11.0-blue.svg?longCache=true&style=for-the-badge) + ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.12.0-blue.svg?longCache=true&style=for-the-badge) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) @@ -934,14 +935,10 @@ Isn't this what a Python programmer looking for a GUI wants? Something easy to w ### Two Return Values -All Window Read and ReadNonBlocking calls return 2 values. By convention a read statement is written: +All Window Read calls return 2 values. By convention a read statement is written: ```python event, values = window.Read() ``` -All of the demo programs and the Cookbook recipes have this line of code for windows that are normal reads (not non-blocking). A similar line of code is used for non-blocking window reads: -```python -event, values = window.ReadNonBlocking() -``` You don't HAVE to write your reads in this way. You can name your variables however you want. But if you want to code them in a way that other programmers using PySimpleGUI are used to, then use these statements. @@ -970,6 +967,15 @@ To check for a closed window use this line of code: if event is None: +Putting it all together we end up with an "event loop" that looks something like this: +```python +while True: + event, values = window.Read() + if event is None: + break + +``` + ### The 'values' Variable - Return values as a list @@ -1181,8 +1187,12 @@ You will find it ***much easier*** to write code using PySimpleGUI if you use an ## Synchronous windows The most common use of PySimpleGUI is to display and collect information from the user. The most straightforward way to do this is using a "blocking" GUI call. Execution is "blocked" while waiting for the user to close the GUI window/dialog box. -You've already seen a number of examples above that use blocking windows. The call to look for that will show you non-blocking windows are calls to `ReadNonBlocking()`. You can read more about Async windows at the end of this document. +You've already seen a number of examples above that use blocking windows. A truly non-blocking Read call looks like this: +```python +event, values = window.Read(timeout=0) +``` +You can learn more about these async / non-blocking windows toward the end of this document. # Window Object - Beginning a window @@ -1287,12 +1297,12 @@ There are a few methods (functions) that you will see in this document that act window.Layout(layout) - Turns your definition of the Window into Window window.Finalize() - creates the tkinter objects for the Window. Normally you do not call this window.Read() - Read the Windows values and get the button / key that caused the Read to return. Can have an optional timeout - window.ReadNonBlocking() - Same as Read but will return right away + window.ReadNonBlocking() - NO LONGER USED! window.Refresh() - Use if updating elements and want to show the updates prior to the nex Read window.Fill(values_dict) - Fill each Element with entry from the dictionary passed in window.SaveToDisk(filename) - Save the Window's values to disk window.LoadFromDisk(filename) - Load the Window's values from disk - window.Close() - To close your window, if a button hasn't already closed iit + window.Close() - To close your window, if a button hasn't already closed it window.Disable() - Use to disable the window inpurt when opening another window on top of the primnary Window window.Enable() - Re-enable a Disabled window window.FindElement(key) - Returns the element that has a matching key value @@ -2056,7 +2066,7 @@ The Types of buttons include: * Color Chooser - Close window - Normal buttons like Submit, Cancel, Yes, No, etc, are "Close window" buttons. They cause the input values to be read and then the window is ***closed***, returning the values to the caller. +Close window - Normal buttons like Submit, Cancel, Yes, No, do NOT close the window... they used to. Now to close a window you need to use a CloseButton / CButton. Folder Browse - When clicked a folder browse dialog box is opened. The results of the Folder Browse dialog box are written into one of the input fields of the window. @@ -2070,17 +2080,20 @@ Read window - This is a window button that will read a snapshot of all of the in Realtime - This is another async window button. Normal button clicks occur after a button's click is released. Realtime buttons report a click the entire time the button is held down. -Most programs will use a combination of shortcut button calls (Submit, Cancel, etc), plain buttons that close the window, and ReadButton buttons that keep the window open but returns control back to the caller. +Most programs will use a combination of shortcut button calls (Submit, Cancel, etc), normal Buttons which leave the windows open and CloseButtons that close the window when clicked. -Sometimes there are multiple names for the same function. This is simply to make the job of the programmer quicker and easier. +Sometimes there are multiple names for the same function. This is simply to make the job of the programmer quicker and easier. Or they are old names that are no longer used but kept around so that existing programs don't break. -The 3 primary windows of PySimpleGUI buttons and their names are: +The 4 primary windows of PySimpleGUI buttons and their names are: - 1. `Button` = `SimpleButton` - 2. `ReadButton` = `RButton` = `ReadFormButton` (old style... use ReadButton instead) + 1. `Button`= `ReadButton` = `RButton` = `ReadFormButton` (old style... use Button instead) + 2. `CloseButton` = `CButton` 3. `RealtimeButton` + 4. `DummyButton` -You will find the long-form in the older programs. +You will find the long-form names in the older programs. ReadButton for example. + +In Oct 2018, the definition of Button changed. Previously Button would CLOSE the window when clicked. It has been changed so the Button calls will leave the window open in exactly the same way as a ReadButton. They are the same calls now. To enables windows to be closed using buttons, a new button was added... `CloseButton` or `CButton`. The most basic Button element call to use is `Button` @@ -2152,9 +2165,10 @@ These Pre-made buttons are some of the most important elements of all because th **IMPORT NOTE ABOUT SHORTCUT BUTTONS** Prior to release 3.11.0, these buttons closed the window. Starting with 3.11 they will not close the window. They act like RButtons (return the button text and do not close the window) -If you are having trouble with these buttons closing your window, please check your installed version of PySimpleGUI by typing `pip list` at a command promt. Prior to 3.11 these buttons close your window. +If you are having trouble with these buttons closing your window, please check your installed version of PySimpleGUI by typing `pip list` at a command prompt. Prior to 3.11 these buttons close your window. + +Using older versions, if you want a Submit() button that does not close the window, then you would instead use RButton('Submit'). Using the new version, if you want a Submit button that closes the window like the sold Submit() call did, you would write that as `CloseButton('Submit')` or `CButton('Submit')` -Using older versions, if you want a Submit() button that does not close the window, then you would instead use RButton('Submit') layout = [[sg.OK(), sg.Cancel()]] @@ -2299,22 +2313,22 @@ window = sg.Window('Robotics Remote Control', auto_size_text=True).Layout(gui_ro # # Some place later in your code... -# You need to perform a ReadNonBlocking on your window every now and then or -# else it won't refresh. +# You need to perform a Read or Refresh call on your window every now and then or +# else it will apprear as if the program has locked up. # # your program's main loop while (True): # This is the code that reads and updates your window - event, values = window.Read(timeout=0) + event, values = window.Read(timeout=0) if event is not None: print(event) if event == 'Quit' or values is None: - break + break window.Close() # Don't forget to close your window! ``` -This loop will read button values and print them. When one of the Realtime buttons is clicked, the call to `window.ReadNonBlocking` will return a button name matching the name on the button that was depressed or the key if there was a key assigned to the button. It will continue to return values as long as the button remains depressed. Once released, the ReadNonBlocking will return None for buttons until a button is again clicked. +This loop will read button values and print them. When one of the Realtime buttons is clicked, the call to `window.Read` will return a button name matching the name on the button that was depressed or the key if there was a key assigned to the button. It will continue to return values as long as the button remains depressed. Once released, the Read will return timeout events until a button is again clicked. **File Types** The `FileBrowse` & `SaveAs` buttons have an additional setting named `file_types`. This variable is used to filter the files shown in the file dialog box. The default value for this setting is @@ -2679,7 +2693,7 @@ Let me say up front that the Table Element has Beta status. The reason is that s ### Read return values from Table Element -The values returned from a `Window.Read` or `Window.ReadNonBlocking` call for the Tree Element are a list of row numbers that are currently highlighted. +The values returned from a `Window.Read` call for the Tree Element are a list of row numbers that are currently highlighted. ### Update Call @@ -2947,7 +2961,7 @@ There are 2 ways to keep a window open after the user has clicked a button. One The `RButton` Element creates a button that when clicked will return control to the user, but will leave the window open and visible. This button is also used in Non-Blocking windows. The difference is in which call is made to read the window. The normal `Read` call with no parameters will block, a call with a `timeout` value of zero will not block. -Note that `InputText` and `MultiLine` Elements will be **cleared** when performing a `ReadNonBlocking`. If you do not want your input field to be cleared after a `ReadNonBlocking` then you can set the `do_not_clear` parameter to True when creating those elements. The clear is turned on and off on an element by element basis. +Note that `InputText` and `MultiLine` Elements will be **cleared** when performing a `Read`. If you do not want your input field to be cleared after a `Read` then you can set the `do_not_clear` parameter to True when creating those elements. The clear is turned on and off on an element by element basis. The reasoning behind this is that Persistent Windows are often "forms". When "submitting" a form you want to have all of the fields left blank so the next entry of data will start with a fresh window. Also, when implementing a "Chat Window" type of interface, after each read / send of the chat data, you want the input field cleared. Think of it as a Texting application. Would you want to have to clear your previous text if you want to send a second text? @@ -2986,7 +3000,7 @@ Let's say you had a device that you want to "poll" every 100ms. The "easy way ```python # YOU SHOULD NOT DO THIS.... while True: # Event Loop - event, values = window.ReadNonBlocking() + event, values = window.ReadNonBlocking() # DO NOT USE THIS CALL ANYMORE read_my_hardware() # process my device here time.sleep(.1) # sleep 1/10 second ``` @@ -3518,6 +3532,7 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it | 3.10.1 & 1.2.1 | Oct 20, 2018 | 3.10.3 & 1.2.3 | Oct 23, 2018 | 3.11.0 & 1.11.0 | Oct 28, 2018 +| 3.12.0 & 1.12.0 | Oct 28, 2018 ## Release Notes @@ -3740,6 +3755,13 @@ Emergency patch release... going out same day as previous release * Shortcut buttons no longer close windows! * Added CloseButton, CButton that closes the windows +### 3.12.0 & 1.12.0 +* Changed Button to be the same as ReadButton which means it will no longer close the window +* All shortcut buttons no longer close the window +* Updating a table clears selected rows information in return values +* Progress meter uses new CloseButton +* Popups use new CloseButton + ### Upcoming Make suggestions people! Future release features