From c93898fd8118554cf16c2bad8243fb1eda2851d2 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 16 Mar 2024 14:39:47 -0400 Subject: [PATCH 001/125] Changed Sign Up to Register. Added Paste button to help paste keys. --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index d0abf7c4c..bb021af75 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -6,3 +6,4 @@ Changelog since last major release 5.0.3.2 execute_restart - changed how the restart is executed. Now uses the execute python call and works corectly. execute_restart(__file__) is a handy pattern 5.0.3.3 changed "development build" to "maintanence release"... documenation changes underway to match. 5.0.3.4 fixed key error message +5.0.3.5 Changed Sign Up to Register. Added Paste button to help paste keys. From f5bd44657835fdc279ff6752038fd676c8a7fd11 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 21 Mar 2024 05:29:48 -0400 Subject: [PATCH 002/125] Added link to FAQ to the list of documenation links --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe70c88dc..a37e1893d 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,9 @@ You can try PySimpleGUI for 30 days, after which you will need to Sign Up. Hobby PySimpleGUI provides extensive documentation. Here are some starting points, depending on your needs and expertise: -* [Documentation](https://docs.pysimplegui.com/) - Extensive PySimpleGUI documentation + +* [FAQ](https://faq.pysimplegui.com/) - Frequently Asked Questions +* [Documentation](https://docs.pysimplegui.com/) - Extensive PySimpleGUI documentation* * [Examples](https://examples.pysimplegui.com/) - Hundreds of sample PySimpleGUI applications. * [SDK Reference](https://sdk.pysimplegui.com/) - details for each PySimpleGUI element * [Home Website](https://PySimpleGUI.com) - New PySimpleGUI home page From c57902e0b1976ab080d15ad2c890c9f4abfdf3ec Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sat, 23 Mar 2024 04:59:57 -0400 Subject: [PATCH 003/125] Initial release of Demo Program showing how to use the setting parameter new to PSG5 --- ...User_Settings_Element_setting_Parameter.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 DemoPrograms/Demo_User_Settings_Element_setting_Parameter.py diff --git a/DemoPrograms/Demo_User_Settings_Element_setting_Parameter.py b/DemoPrograms/Demo_User_Settings_Element_setting_Parameter.py new file mode 100644 index 000000000..a9a17538c --- /dev/null +++ b/DemoPrograms/Demo_User_Settings_Element_setting_Parameter.py @@ -0,0 +1,48 @@ +import PySimpleGUI as sg + +""" + Demo - User Settings + + Using the PySimpleGUI 5 setting parameter. + + New in the PSG5 release is a capability to automatically save and restore values in elements. Each element that has the capability has a parameter called "setting" + + Copyright 2020-2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + + Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. + + You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. +""" + + +def make_window(): + layout = [[sg.Text('Window with values saved between runs')], + [sg.Input(key='-IN-', setting='My initial value')], + [sg.Checkbox('Checkbox', key='-CB-', setting=True)], + [sg.Button('Re-create Window'), sg.Button('Exit')]] + + window = sg.Window('Setting Example', layout, enable_close_attempted_event=True, print_event_values=True, auto_save_location=True) + + return window + + +def main(): + + window = make_window() + + while True: + event, values = window.read() + if event == sg.WIN_CLOSED or event == 'Exit' or event == sg.WIN_CLOSE_ATTEMPTED_EVENT: + window.settings_save(values) + break + + if event == 'Re-create Window': # You can also close and re-open a window using the values previously entered + window.settings_save(values) + window.close() + window = make_window() + + window.close() + + +if __name__ == '__main__': + main() \ No newline at end of file From dc78b9d1f39351a79b9345e0098a69e9f66ccd66 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 30 Mar 2024 08:28:54 -0400 Subject: [PATCH 004/125] Switched away from subscription license to a perpetual license --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index bb021af75..565826561 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -7,3 +7,4 @@ Changelog since last major release 5.0.3.3 changed "development build" to "maintanence release"... documenation changes underway to match. 5.0.3.4 fixed key error message 5.0.3.5 Changed Sign Up to Register. Added Paste button to help paste keys. +5.0.3.6 Switched away from subscription service From c5154a143919797c1ed4c6e220676f0d1149498e Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sat, 30 Mar 2024 13:31:25 -0400 Subject: [PATCH 005/125] New license with subscription removed. --- LICENSE.txt | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index d688c9b82..6a6cce6db 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ PySimpleGUI License Agreement -Version 1.0, Last updated: January 17, 2024 +Version 1.1, Last updated: March 26, 2024 This PySimpleGUI License Agreement (the "Agreement") governs the use, reproduction, distribution, modification and all other exploitation of @@ -58,9 +58,8 @@ follows. more of Licensee's own applications which make use of the Library as a dependency in accordance with Section 1.5 (collectively, "Licensee Applications") and is either (1) a Hobbyist Developer; or (2) a Commercial - Developer who has purchased an active PySimpleGUI paid license hereunder, in - effect at the time of development, which is fully paid up pursuant to Section - 3. + Developer who has purchased an active PySimpleGUI paid license hereunder + which is fully paid up pursuant to Section 3. * "Hobbyist Developer" means any individual who uses PySimpleGUI for development purposes solely for either or both of the following: (1) personal @@ -132,10 +131,11 @@ install, use, execute, reproduce and modify the Utilities, but not to distribute or publish the Utilities or any modified version. 1.2.5. Developer Key Required. The licenses granted in this Section 1.2 may -only be exercised by Authorized Developers within the period of time during -which each such Authorized Developer has a then-active Developer Key pursuant -to Section 3. Licensor may in its discretion permit recipients of PySimpleGUI -to make limited use of it for a limited trial period without a Developer Key. +only be exercised by Authorized Developers. For Hobbyist Developers, these +licenses may only be exercised within the period of time during which each such +Hobbyist Developer has a then-active Developer Key pursuant to Section 3. +Licensor may in its discretion permit recipients of PySimpleGUI to make limited +use of it for a limited trial period without a Developer Key. 1.2.6. Limitations for Hobbyist Developers. For Hobbyist Developers, the licenses granted in this Section 1.2 may only be exercised for the Permitted @@ -310,20 +310,23 @@ Licensor Marks shall inure to the benefit of Licensor. 3.1. Developer Keys. In order to develop Licensee Applications pursuant to Section 1.2 (and subject to any limited trial period usage as may be permitted by Licensor from time to time), each Authorized Developer shall obtain a -PySimpleGUI developer license key ("Developer Key") by registering on the Site -as set forth therein. Each Developer Key is personal to the specific Authorized -Developer, and Licensee shall not permit Authorized Developers to disclose, -share or reuse Developer Keys. For the avoidance of doubt, any disclosure, -sharing or reuse of a Developer Key by Licensee's Authorized Developers, -whether or not authorized by Licensee, shall be a material breach permitting -termination of this Agreement pursuant to Section 8.3. Developer Keys are -Licensor's Confidential Information pursuant to Section 5. Developer Keys are -limited to a specified time period (which shall be annual from the start date -of the Developer Key, unless otherwise explicitly stated by Licensor). Upon the -expiration of a Developer Key, the corresponding Authorized Developer may no -longer use the Developer Key and must obtain a new Developer Key from the Site -in order to continue using PySimpleGUI for development purposes pursuant to -Section 1.2. +PySimpleGUI developer license key ("Developer Key") by registering on the +Site as set forth therein. Each Developer Key is personal to the specific +Authorized Developer, and Licensee shall not permit Authorized Developers to +disclose, share or reuse Developer Keys. For the avoidance of doubt, any +disclosure, sharing or reuse of a Developer Key by Licensee's Authorized +Developers, whether or not authorized by Licensee, shall be a material breach +permitting termination of this Agreement pursuant to Section 8.3. Developer +Keys are Licensor's Confidential Information pursuant to Section 5. Developer +Keys are limited to a specified time period (which shall be annual from the +start date of the Developer Key, unless otherwise explicitly stated by +Licensor). Upon the expiration of a Developer Key for a Hobbyist Developer, +they may no longer use the Developer Key and must obtain a new Developer Key +from the Site in order to continue using PySimpleGUI for development purposes +pursuant to Section 1.2. Upon the expiration of a Developer Key for a +Commercial Developer, they may continue to use their Developer Key for versions +of PySimpleGUI released during that period, but may not obtain subsequent +updated versions under Section 4.2 unless they purchase a new Developer Key. 3.2. Fees for Commercial Developer Keys; Taxes. Before obtaining each Developer Key for a Commercial Developer, Licensee shall pay to Licensor the From dc528da5f36a137bf00dfea0fa2dde32541138be Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:36:04 -0400 Subject: [PATCH 006/125] Added new license (ver 1.2) to the release package so that it's installed with the PySimpleGUI module --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 565826561..ccd74a29d 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -8,3 +8,4 @@ Changelog since last major release 5.0.3.4 fixed key error message 5.0.3.5 Changed Sign Up to Register. Added Paste button to help paste keys. 5.0.3.6 Switched away from subscription service +5.0.3.7 Added new license (ver 1.2) to the release package so that it's installed with the PySimpleGUI module From 1083042e810f663ccedb0f4af434007d5f16d9e3 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:39:29 -0400 Subject: [PATCH 007/125] Changed license ver to correct number (1.1) --- development_build_changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index ccd74a29d..de8fbeb18 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -8,4 +8,4 @@ Changelog since last major release 5.0.3.4 fixed key error message 5.0.3.5 Changed Sign Up to Register. Added Paste button to help paste keys. 5.0.3.6 Switched away from subscription service -5.0.3.7 Added new license (ver 1.2) to the release package so that it's installed with the PySimpleGUI module +5.0.3.7 Added new license (ver 1.1) to the release package so that it's installed with the PySimpleGUI module From baefa6b16255585f7d693c399b1c08e8466a8d78 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sat, 30 Mar 2024 17:05:06 -0400 Subject: [PATCH 008/125] New Demo Program - Downloading images using the PySimpleGUI Net API calls --- DemoPrograms/Demo_Net_API_Download_Image.py | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 DemoPrograms/Demo_Net_API_Download_Image.py diff --git a/DemoPrograms/Demo_Net_API_Download_Image.py b/DemoPrograms/Demo_Net_API_Download_Image.py new file mode 100644 index 000000000..908fda718 --- /dev/null +++ b/DemoPrograms/Demo_Net_API_Download_Image.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import PySimpleGUI as sg + +# Only need these imports if using JPGs +from PIL import Image +from io import BytesIO +import base64 + + +""" + Demo of Net API - Download Images using net_download_file_binary + + This Demo Program shows how to download a binary file (an image) and display in a window. + The PNG image display is quite easy. Displaying a JPG requires converting using PIL. + + Copyright 2018-2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + + Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. + + You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. +""" + + +sg.theme('black') + +# First the easy case.... display a PNG image + +sg.Window('BIG News', [[sg.Image(sg.net_download_file_binary(r'https://pysimplegui.net/images/big_news_emoji.png'))]], + use_custom_titlebar=True, titlebar_background_color='black', titlebar_text_color='white').read(close=True) + + +# Now display a JPG image. It requires converting the image to PNG before using with PySimpleGUI +jpg_data = sg.net_download_file_binary(r'https://pysimplegui.net/images/tests/powered-by-pysimplegui-5.jpg') + +jpg_image = Image.open(BytesIO(jpg_data)) + +with BytesIO() as bio: + jpg_image.save(bio, format='PNG') + contents = bio.getvalue() + encoded = base64.b64encode(contents) + +sg.Window('Powered By', [[sg.Image(data=encoded)]], + use_custom_titlebar=True, titlebar_background_color='black', titlebar_text_color='white').read(close=True) From 7ec3b16695fe3a04e7457fce2d9cada9f23a5c72 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sun, 31 Mar 2024 14:22:33 -0400 Subject: [PATCH 009/125] Removed custom titlebar as it just made things messy, complicated. Added second demo program just for JPG --- DemoPrograms/Demo_Net_API_Download_Image.py | 27 +++------------ .../Demo_Net_API_Download_Image_jpg.py | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 DemoPrograms/Demo_Net_API_Download_Image_jpg.py diff --git a/DemoPrograms/Demo_Net_API_Download_Image.py b/DemoPrograms/Demo_Net_API_Download_Image.py index 908fda718..055e84984 100644 --- a/DemoPrograms/Demo_Net_API_Download_Image.py +++ b/DemoPrograms/Demo_Net_API_Download_Image.py @@ -1,17 +1,12 @@ #!/usr/bin/env python import PySimpleGUI as sg -# Only need these imports if using JPGs -from PIL import Image -from io import BytesIO -import base64 - """ - Demo of Net API - Download Images using net_download_file_binary + Demo of Net API - Download a PNG Image using net_download_file_binary & Display in a Window + + This Demo Program shows how to download a binary file (an image) and display it in a window. - This Demo Program shows how to download a binary file (an image) and display in a window. - The PNG image display is quite easy. Displaying a JPG requires converting using PIL. Copyright 2018-2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. @@ -25,19 +20,5 @@ # First the easy case.... display a PNG image -sg.Window('BIG News', [[sg.Image(sg.net_download_file_binary(r'https://pysimplegui.net/images/big_news_emoji.png'))]], - use_custom_titlebar=True, titlebar_background_color='black', titlebar_text_color='white').read(close=True) - - -# Now display a JPG image. It requires converting the image to PNG before using with PySimpleGUI -jpg_data = sg.net_download_file_binary(r'https://pysimplegui.net/images/tests/powered-by-pysimplegui-5.jpg') - -jpg_image = Image.open(BytesIO(jpg_data)) - -with BytesIO() as bio: - jpg_image.save(bio, format='PNG') - contents = bio.getvalue() - encoded = base64.b64encode(contents) +sg.Window('PNG Download', [[sg.Image(sg.net_download_file_binary(r'https://pysimplegui.net/images/tests/powered-by-pysimplegui-5.png'))]]).read(close=True) -sg.Window('Powered By', [[sg.Image(data=encoded)]], - use_custom_titlebar=True, titlebar_background_color='black', titlebar_text_color='white').read(close=True) diff --git a/DemoPrograms/Demo_Net_API_Download_Image_jpg.py b/DemoPrograms/Demo_Net_API_Download_Image_jpg.py new file mode 100644 index 000000000..a209f1875 --- /dev/null +++ b/DemoPrograms/Demo_Net_API_Download_Image_jpg.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +import PySimpleGUI as sg + +# Only need these imports if using JPGs +from PIL import Image +from io import BytesIO + + +""" + Demo of Net API - Download JPG Image using net_download_file_binary and display in a window + + The download is the easiest part. It's the displaying of a JPG that's tricky. + + + Copyright 2018-2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + + Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. + + You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. +""" + + +# Download the binary file (into a variable, not a local file) +jpg_data = sg.net_download_file_binary(r'https://pysimplegui.net/images/tests/powered-by-pysimplegui-5.jpg') + +# Now display a JPG image. It requires converting the image to PNG before using with PySimpleGUI +jpg_image = Image.open(BytesIO(jpg_data)) + +with BytesIO() as bio: + jpg_image.save(bio, format='PNG') + data = bio.getvalue() + +sg.Window('Download JPG', [[sg.Image(data=data)]]).read(close=True) From a9531367ce149e9160df9202e5bc08ab81e1b75a Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:59:23 -0400 Subject: [PATCH 010/125] Fixed bug in license display in Home Window --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index de8fbeb18..cc1f3668d 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -9,3 +9,4 @@ Changelog since last major release 5.0.3.5 Changed Sign Up to Register. Added Paste button to help paste keys. 5.0.3.6 Switched away from subscription service 5.0.3.7 Added new license (ver 1.1) to the release package so that it's installed with the PySimpleGUI module +5.0.3.8 Fixed bug in license display in Home Window From 9654ead9c605b54ec913bfd97911b2f8c607884b Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Mon, 1 Apr 2024 09:56:56 -0400 Subject: [PATCH 011/125] Fixed gallery images --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e82a10131..b096a4cc4 100644 --- a/README.md +++ b/README.md @@ -55,11 +55,9 @@ For most users (Hobbyist Users), the license is at NO COST. If you are a Commerc PySimpleGUI users have created thousands of amazing desktop applications. Here are a few screen shots. For more examples, see the [PySimpleGUI gallery](https://gallery.PySimpleGUI.com/).

- -   - -   - + + +

## Get Started at No Cost From 4f56e77ab3e12641576cf331b36903ed8b1fd9c6 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Mon, 1 Apr 2024 10:35:02 -0400 Subject: [PATCH 012/125] New graphic --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b096a4cc4..90c74b589 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ period, all PySimpleGUI 5 users must register at PySimpleGUI.com to obtain a Dev For most users (Hobbyist Users), the license is at NO COST. If you are a Commercial User, you must buy a license.

- +

[Register Now](https://pricing.PySimpleGUI.com) and help support the PySimpleGUI community. From 66763140ad769dc9d9f9eaf85f47351108fd3a2e Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 1 Apr 2024 13:44:49 -0400 Subject: [PATCH 013/125] Release 5.0.4 --- development_build_changelog.txt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index cc1f3668d..be4144d59 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,12 +1,5 @@ Changelog since last major release -5.0.3 Released 3-Mar-2024 +5.0.4 Released 1-Apr-2024 + -5.0.3.1 popup_get_text - setting focus so that the window focuses immediate and the cursor goes to the input. Something about modal wasn't doing this. -5.0.3.2 execute_restart - changed how the restart is executed. Now uses the execute python call and works corectly. execute_restart(__file__) is a handy pattern -5.0.3.3 changed "development build" to "maintanence release"... documenation changes underway to match. -5.0.3.4 fixed key error message -5.0.3.5 Changed Sign Up to Register. Added Paste button to help paste keys. -5.0.3.6 Switched away from subscription service -5.0.3.7 Added new license (ver 1.1) to the release package so that it's installed with the PySimpleGUI module -5.0.3.8 Fixed bug in license display in Home Window From c86b3a2674463adea2a398e6c92ce58806bb9160 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 2 Apr 2024 08:58:52 -0400 Subject: [PATCH 014/125] Added code without a context manager for simplicity (ALSO kept the context manager code as it's important) --- .../Demo_Net_API_Download_Image_jpg.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/DemoPrograms/Demo_Net_API_Download_Image_jpg.py b/DemoPrograms/Demo_Net_API_Download_Image_jpg.py index a209f1875..0736f884d 100644 --- a/DemoPrograms/Demo_Net_API_Download_Image_jpg.py +++ b/DemoPrograms/Demo_Net_API_Download_Image_jpg.py @@ -20,10 +20,27 @@ """ + # Download the binary file (into a variable, not a local file) jpg_data = sg.net_download_file_binary(r'https://pysimplegui.net/images/tests/powered-by-pysimplegui-5.jpg') # Now display a JPG image. It requires converting the image to PNG before using with PySimpleGUI +# Note a commented out version below of this same code. Here the context manager was removed for simplicity. +jpg_image = Image.open(BytesIO(jpg_data)) +bio = BytesIO() +jpg_image.save(bio, format='PNG') + +sg.Window('Download JPG', [[sg.Image(data=bio.getvalue())]]).read(close=True) + +del bio # delete the BytesIO object since done with it + + + + +# This is another implementation using the io module with a context manager +# The "with" statement makes understanding what's going on a little more difficult +# but is perhaps the "preferred" method to use. +""" jpg_image = Image.open(BytesIO(jpg_data)) with BytesIO() as bio: @@ -31,3 +48,4 @@ data = bio.getvalue() sg.Window('Download JPG', [[sg.Image(data=data)]]).read(close=True) +""" \ No newline at end of file From f8fcf33fec4b0db832a28e12abe52dadc683cb1a Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Wed, 3 Apr 2024 11:09:54 -0400 Subject: [PATCH 015/125] Fixed open GitHub issue window so that the checklist matches the newsest on GitHub --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index be4144d59..eff9ca5b0 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -2,4 +2,5 @@ Changelog since last major release 5.0.4 Released 1-Apr-2024 +5.0.4.1 Fixed open GitHub issue window so that the checklist matches the newsest on GitHub From 91b5e4ed58c33555a78a35619d613da58088a475 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Wed, 3 Apr 2024 11:42:41 -0400 Subject: [PATCH 016/125] Fix for ButtonMenu not clearing the value dictionary entry after the event is returned. Used to work this way but broke along the way. --- development_build_changelog.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index eff9ca5b0..143ab8695 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -2,5 +2,6 @@ Changelog since last major release 5.0.4 Released 1-Apr-2024 -5.0.4.1 Fixed open GitHub issue window so that the checklist matches the newsest on GitHub +5.0.4.1 Fixed open GitHub issue window so that the checklist matches the newest on GitHub +5.0.4.2 Fix for ButtonMenu not clearing the value dictionary entry after the event is returned. Used to work this way but broke along the way. From c9b73e1dc88289d7f58f12f889b1393a1877fe49 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Fri, 5 Apr 2024 05:37:53 -0400 Subject: [PATCH 017/125] Changed to using the PSG supplied thread start. Added more/clearer events from thread --- .../Demo_Multithreaded_Write_Event_Value.py | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/DemoPrograms/Demo_Multithreaded_Write_Event_Value.py b/DemoPrograms/Demo_Multithreaded_Write_Event_Value.py index 0ebdb7f2c..e3629282e 100644 --- a/DemoPrograms/Demo_Multithreaded_Write_Event_Value.py +++ b/DemoPrograms/Demo_Multithreaded_Write_Event_Value.py @@ -1,4 +1,3 @@ -import threading import time import PySimpleGUI as sg @@ -6,24 +5,17 @@ """ Threaded Demo - Uses Window.write_event_value communications - Requires PySimpleGUI.py version 4.25.0 and later - - This is a really important demo to understand if you're going to be using multithreading in PySimpleGUI. - - Older mechanisms for multi-threading in PySimpleGUI relied on polling of a queue. The management of a communications - queue is now performed internally to PySimpleGUI. + The only PySimpleGUI call allowed from a thread is write_event_value. + Using a tuple for thread events makes processing them easier to see and understand. - The importance of using the new window.write_event_value call cannot be emphasized enough. It will hav a HUGE impact, in - a positive way, on your code to move to this mechanism as your code will simply "pend" waiting for an event rather than polling. - Copyright 2020-2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + Copyright 2020-2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. """ -THREAD_EVENT = '-THREAD-' def the_thread(window): """ @@ -31,12 +23,13 @@ def the_thread(window): Once a second wakes and sends a new event and associated value to the window """ - i = 0 - while True: - time.sleep(1) - window.write_event_value('-THREAD-', (threading.current_thread().name, i)) # Data sent is a tuple of thread name and counter - i += 1 + window.write_event_value(('-THREAD-', '-STARTED-'), None) # Tell the GUI the thread started + + for i in range(5): + time.sleep(1) + window.write_event_value(('-THREAD-', '-PRINT-'), i) # Data sent is a tuple of thread name and counter + # Note that the thread ended event is sent automatically by PySimpleGUI if you started the thread using window.start_thread def main(): """ @@ -50,20 +43,26 @@ def main(): [sg.Multiline(size=(65,20), key='-ML-', autoscroll=True, reroute_stdout=True, write_only=True, reroute_cprint=True)], [sg.T('Input so you can see data in your dictionary')], [sg.Input(key='-IN-', size=(30,1))], - [sg.B('Start A Thread'), sg.B('Dummy'), sg.Button('Exit')] ] + [sg.B('Start A Thread', key='-START-'), sg.B('Dummy'), sg.Button('Exit')] ] - window = sg.Window('Window Title', layout) + window = sg.Window('Multithreading + Tuple Events', layout) while True: # Event Loop event, values = window.read() sg.cprint(event, values) if event == sg.WIN_CLOSED or event == 'Exit': break - if event.startswith('Start'): - threading.Thread(target=the_thread, args=(window,), daemon=True).start() - if event == THREAD_EVENT: - sg.cprint(f'Data from the thread ', colors='white on purple', end='') - sg.cprint(f'{values[THREAD_EVENT]}', colors='white on red') + if event == '-START-': + window.start_thread(lambda : the_thread(window), ('-THREAD-', '-ENDED-')) + if event[0] == '-THREAD-': + if event[1] == '-PRINT-': + sg.cprint(f'Data from the thread ', colors='white on purple', end='') + sg.cprint(f'{values[event]}', colors='white on red') + elif event[1] == '-ENDED-': + sg.cprint('Thread has ended', colors='white on blue') + elif event[1] == '-STARTED-': + sg.cprint('Thread has started', colors='white on green') + window.close() From 1da4503d49fedb4f34b05d19d6c6df2600178bcd Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:23:56 -0400 Subject: [PATCH 018/125] Added set_hscroll_position for elements --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 143ab8695..daf6fbccc 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -4,4 +4,5 @@ Changelog since last major release 5.0.4.1 Fixed open GitHub issue window so that the checklist matches the newest on GitHub 5.0.4.2 Fix for ButtonMenu not clearing the value dictionary entry after the event is returned. Used to work this way but broke along the way. +5.0.4.3 Added set_hscroll_position for elements From 67c1489f322f21e0f8e4f504bd07da74d0a1fc69 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:28:07 -0400 Subject: [PATCH 019/125] Added set_hscroll_position for elements From e410c4d91f7a64902a88586ae419ef25c92314e6 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Mon, 8 Apr 2024 06:45:28 -0400 Subject: [PATCH 020/125] New version of the ping Demo Program --- .../Demo_Matplotlib_Ping_Graph_Large.py | 73 ++++++++++++------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/DemoPrograms/Demo_Matplotlib_Ping_Graph_Large.py b/DemoPrograms/Demo_Matplotlib_Ping_Graph_Large.py index e15b973d2..9794db4ad 100644 --- a/DemoPrograms/Demo_Matplotlib_Ping_Graph_Large.py +++ b/DemoPrograms/Demo_Matplotlib_Ping_Graph_Large.py @@ -1,13 +1,18 @@ #!/usr/bin/env python -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg -import matplotlib.backends.tkagg as tkagg +from matplotlib.backends.backend_tkagg import FigureCanvasAgg import matplotlib.pyplot as plt import PySimpleGUI as sg -import tkinter as tk -import ping +import io +import random +import time +import ping3 """ - Copyright 2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + Shows ping time to google.com using Matplotlib and ping3 module. + + Note that you will need to pip install ping3 for this demo program. + + Copyright 2023-2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. @@ -34,16 +39,13 @@ class MyGlobals: # ================================================================================ -def run_a_ping_and_graph(): +def graph_a_ping(ping_time): # graphs are global so that can be retained across multiple calls to this callback global g_my_globals #===================== Do the ping =====================# - response = ping.quiet_ping('google.com', timeout=1000) - if response[0] == 0: - ping_time = 1000 - else: - ping_time = response[0] + # Insert your code to run a ping + # ping_time = random.randint(0, 100) #===================== Store current ping in historical array =====================# g_my_globals.ping_x_array.append(len(g_my_globals.ping_x_array)) g_my_globals.ping_y_array.append(ping_time) @@ -69,6 +71,14 @@ def run_a_ping_and_graph(): # ================================================================================ +def ping_thread(window: sg.Window): + while True: + # time.sleep(.1) + # ping_time = random.randint(0, 100) + ping_time = ping3.ping('google.com') + window.write_event_value('-THREAD-', ping_time) + + def set_chart_labels(): global g_my_globals @@ -77,16 +87,25 @@ def set_chart_labels(): g_my_globals.axis_ping.set_title('Current Ping Duration', fontsize=12) -def draw(fig, canvas): - # Magic code that draws the figure onto the Canvas Element's canvas - 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) - return photo +def draw(element, figure): + """ + Draws the previously created "figure" in the supplied Image Element + + :param element: an Image Element + :param figure: a Matplotlib figure + :return: The figure canvas + """ + + # plt.close('all') # erases previously drawn plots + canv = FigureCanvasAgg(figure) + buf = io.BytesIO() + canv.print_figure(buf, format='png') + if buf is not None: + buf.seek(0) + element.update(data=buf.read()) + return canv + else: + return None # ================================================================================ # Function: MAIN @@ -99,28 +118,28 @@ 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.Image(size=(640, 480), key='-IMAGE-')], [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, finalize=True) - canvas_elem = window['canvas'] - canvas = canvas_elem.TKCanvas + image_elem = window['-IMAGE-'] fig = plt.figure() g_my_globals.axis_ping = fig.add_subplot(1, 1, 1) set_chart_labels() plt.tight_layout() - + window.start_thread(lambda: ping_thread(window)) while True: event, values = window.read(timeout=0) if event in ('Exit', None): break + if event == '-THREAD-': + graph_a_ping(values[event]) + draw(image_elem, fig) - run_a_ping_and_graph() - photo = draw(fig, canvas) if __name__ == '__main__': From d48ccdf3e2fde16047b6e3c02babfec324505b73 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:49:55 -0400 Subject: [PATCH 021/125] NEW FEATURE added - parameter repeating_timer_ms added to the Window object. Causes a repeating timer to be started. --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index daf6fbccc..ad08d1a66 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -5,4 +5,5 @@ Changelog since last major release 5.0.4.1 Fixed open GitHub issue window so that the checklist matches the newest on GitHub 5.0.4.2 Fix for ButtonMenu not clearing the value dictionary entry after the event is returned. Used to work this way but broke along the way. 5.0.4.3 Added set_hscroll_position for elements +5.0.4.4 NEW FEATURE added - parameter repeating_timer_ms added to the Window object. Causes a repeating timer to be started. From fd99d02b3a883a70f944997b59691ac53956b41b Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Fri, 12 Apr 2024 10:53:37 -0400 Subject: [PATCH 022/125] new version of the LED Clock Weather Demo Program - uses openweathermap.org --- ...rogram_Desktop_Widget_LED_Clock_Weather.py | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py diff --git a/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py b/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py new file mode 100644 index 000000000..1c7bf9470 --- /dev/null +++ b/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python +import PySimpleGUI as sg +import datetime +import calendar +import requests + +''' + Example of a weather App, using: + - openweathermap.org + + + + Copyright 2023-2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + + Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. + + You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. +''' + + + +NUM_COLS = 5 # Changes number of days in forecast (don't change this) + + +def settings_window(location=(None, None)): + tsize = 15 + layout = [[sg.Text('Settings')], + [sg.T('API Key', s=tsize, justification='r'), sg.I(s=35, setting='', k='-api key-')], + [sg.T('Latitude', s=tsize, justification='r'), sg.I(s=12, setting='', k='-lat-')], + [sg.T('Longitude', s=tsize, justification='r'), sg.I(s=12, setting='', k='-lon-')], + [sg.CB('Use F Degrees', s=tsize, setting=True, k='-faren-')], + [sg.OK(), sg.Cancel()], + ] + + window = sg.Window('Settings', layout, location=location) + + while True: + event, values = window.read() + if event == sg.WIN_CLOSED or event == 'Cancel': + break + if event == 'OK': + window.settings_save(values) + break + window.close() + + return event == 'OK' + + +class GUI(): + lat = 0 + lon = 0 + api_key = '' + faren = True + def __init__(self): + self.blink_count = 0 + sg.theme('black') + sg.set_options(border_width=0) + + # Create clock layout + clock = [ + [ + sg.Image(data=ledblank, key='-HOUR1-'), + sg.Image(data=ledblank, key='-HOUR2-'), + sg.Image(data=ledblank, key='-COLON-'), + sg.Image(data=ledblank, key='-MIN1-'), + sg.Image(data=ledblank, key='-MIN2-')]] + + # Create the weather columns layout + weather_cols = [] + for i in range(NUM_COLS): + weather_cols.append( + [[sg.T('', size=(4, 1), font='Any 20', justification='center', key='_DAY_' + str(i)), ], + [sg.Image(data=w1, background_color='black', key='-ICON-' + str(i), pad=((4, 0), 3)), ], + [sg.T('--', size=(3, 1), justification='center', font='Any 20', key='_high_' + str(i), pad=((10, 0), 3))], + # [sg.T('--', size=(3, 1), justification='center', font='Any 20', key='_low_' + str(i), pad=((10, 0), 3))], + ]) + + # Create the overall layout + layout = [[sg.Column(clock, background_color='black', justification='c')], + [sg.Column(weather_cols[x], background_color='black') for x in range(NUM_COLS)] + [sg.T('×', enable_events=True, key='Exit')] + [ + sg.T('C' if GUI.faren else 'F', enable_events=True, key='-CELCIUS-')], + ] + + right_click = sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT + right_click[1].append('Settings') + # Create the window + self.window = sg.Window('DarkSky Weather Forecast Widget', layout, + background_color='black', + grab_anywhere=True, + use_default_focus=False, + no_titlebar=True, + alpha_channel=.8, # set an alpha channel if want transparent + right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, + enable_close_attempted_event=True, + auto_save_location=True, + finalize=True) + + self.colon_elem = self.window.find_element('-COLON-') + self.hour1 = self.window.find_element('-HOUR1-') + self.hour2 = self.window.find_element('-HOUR2-') + self.min1 = self.window.find_element('-MIN1-') + self.min2 = self.window.find_element('-MIN2-') + + self.window['Exit'].set_cursor('hand2') + self.window['-CELCIUS-'].set_cursor('hand2') + + def update_clock(self): + # update the clock + now = datetime.datetime.now() + real_hour = now.hour - 12 if now.hour > 12 else now.hour + hour1_digit = led_digits[real_hour // 10] + self.hour1.Update(data=hour1_digit) + self.hour2.Update(data=led_digits[real_hour % 10]) + self.min2.Update(data=led_digits[int(now.minute) % 10]) + self.min1.Update(data=led_digits[int(now.minute) // 10]) + # Blink the : + self.colon_elem.Update(data=ledcolon if self.blink_count % 2 else ledblank) + self.blink_count += 1 + + def update_weather(self): + today_weekday = datetime.datetime.today().weekday() + + try: + # Make the API + units = 'imperial' if GUI.faren else 'metric' + api_url = f'http://api.openweathermap.org/data/2.5/forecast?lat={GUI.lat}&lon={GUI.lon}&units={units}&appid={GUI.api_key}' + + response = requests.get(api_url) + data = response.json() + # print(data) + # Extract relevant information (e.g., temperature, weather conditions) + i = 0 + for forecast in data["list"]: + date_time = forecast["dt_txt"] + if not '12:00:00' in date_time: + continue + # print(forecast) + temp = forecast["main"]["temp"] + weather_desc = forecast["weather"][0]["description"] + + print(f"{date_time}: {temp}°F, {weather_desc}") + day_element = self.window.find_element('_DAY_' + str(i)) + max_element = self.window.find_element('_high_' + str(i)) + icon_element = self.window.find_element('-ICON-' + str(i)) + day_element.Update(calendar.day_abbr[(today_weekday + i) % 7]) + # sg.Print(icon_data, wait=True) + try: + icon_data = weather_icon_dict[weather_desc] + icon_element.update(icon_data) + except: + sg.Print(f'Missing icon data for description: "{weather_desc}"') + max_element.update(int(temp)) + i += 1 + except requests.RequestException as e: + print(f"Error fetching data: {e}") + + +def main(): + if not sg.user_settings_get_entry('-api key-', None): + if not settings_window(): + sg.popup_error('Cancelling setup and exiting') + exit() + GUI.lat = sg.user_settings_get_entry('-lat-', 0) + GUI.lon = sg.user_settings_get_entry('-lon-', 0) + GUI.api_key = sg.user_settings_get_entry('-api key-', '') + GUI.faren = sg.user_settings_get_entry('-faren-', True) + + # Get the GUI object that is used to update the window + # location = sg.user_settings_get_entry('-location-', (None, None)) + + gui = GUI() + + # ---------- EVENT LOOP ---------- + last_update_time = 0 + while True: + # Wake up once a second to update the clock and weather + event, values = gui.window.read(timeout=1000) + if event in (None, 'Exit', sg.WIN_CLOSE_ATTEMPTED_EVENT): + break + elif event == '-CELCIUS-': + GUI.faren = not GUI.faren + sg.user_settings_set_entry('-faren-', GUI.faren) + gui.window['-CELCIUS-'].update('C' if GUI.faren else 'F') + elif event == 'Edit Me': + sg.execute_editor(__file__) + elif event == 'Version': + sg.main_get_debug_data() + elif event == 'Settings': + if settings_window(gui.window.current_location()): # if settings changed + GUI.lat = sg.user_settings_get_entry('-lat-', 0) + GUI.lon = sg.user_settings_get_entry('-lon-', 0) + GUI.api_key = sg.user_settings_get_entry('-api key-', '') + GUI.faren = sg.user_settings_get_entry('-faren-', True) + gui.window['-CELCIUS-'].update('C' if GUI.faren else 'F') + gui.update_weather() + + gui.update_clock() + # update weather once ever 6 hours + now = datetime.datetime.now() + if last_update_time == 0 or (now - last_update_time).seconds >= 60 * 60 * 6 or event == '-CELCIUS-': + print('*** Updating Weather ***') + last_update_time = now + gui.update_weather() + + +led0 = b'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==' + +led1 = b'iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAACD0lEQVRoge2Yv27UQBCHv9kkRCAhXgAkSh6AmoKCFFBENLwBFQ0PQIPES0BJky4NDR0lJT28A4ICQc7nH4V3FccZ+9zcrAv/pJVP3jntp525+XMGiIUp1QbwtELN1Qo1V1WhLK+hqkEVIC8fVYFKdDB3gLvW3ZUN9qsAmRlnKfEoQ6WBTShQCxybcW7Gk7blt2N3GAVkGegmcA6ctC0AB45tyE2VgL5txifgROLfhH0YFMBH4DGwYdpFIVBtft5XlwA8l/UVGujtbhMgGMrL3p7W2rcrlor2DtV32dHM7+wdSiOfp7TG1FytUHO1Qnllpko7bL3n3N4pJE+VFvhLftf09n/Kz16KXO/NJJBAr8wEKF23i4GxvDDTh5T0OqUxII2NXntRiavGDJM4pOtChwqfZhrgDfDCjA3jnUOY+zDT2xxTf0Gnea9KTJVD36UkgS5ALWgDeugEe+jg8DTPegnY0k009/JetbF92wMoEFu73rmHB/oVOUCuXbS2zrvqUF6RrgIlLmPKy9zrTXlaJNQi3bc8KGmBUCwgphYzOPRVeqdd/1OFQJVDvuVa1y8tcgaHEKhy7EvgsxlHXE401dxXysqFxHO6UevGhH1YTCkf9kfiFPiaXdk4tuG/vgT8Ap4B31PilmMXOmIVHdAF+wMzGuCHdAWkClQ5mJHDq2X0/q0MVe2mplS99nlaJNR/DW4BrSasyZkAAAAASUVORK5CYII=' + +led2 = b'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=' + +led3 = b'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=' + +led4 = b'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=' + +led5 = b'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' + +led6 = b'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' + +led7 = b'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=' + +led8 = b'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=' + +led9 = b'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' + +ledcolon = b'iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAABdklEQVRoge2YQU7DMBBFn9MEVVBYcADEmiVH4BScD3EL7gIcAYEEFSQdFrZRCJGYeBa1qnlSZSuVJz8/k/EkARAqo9m3gDlclBYXpcVFaXFRWlyUFhelpUpRrWVxAFaTYwIMlqApbnX9VJFT+UpOgRugA3ZE195C4EHE7JYs/TVpvAJ5BxGQPo1PTSObEASQUBA7xS/nE/hI8+yMEJ20YBK1G81zwnfWoKXr85MxAF9p3o/+24tT45N2k/EYOLIoovDpy069AvfAOdGxDngR+cmzUg6nTv0XoJ85toQqnapyQ3ZRWlyUFhelpUpR5oo+1xFYq3GVFd3Uo18Cd8CGuN+tgcem4VaErUjxFZtErYFr4ITY8K2AM2ILsy0JnDD36Pnk49a4mh49B2qtQUvX5zzZ8TdnVqVBR5jWD/x2C6Ko6av8UkwloQUuiImd32J64Bnb94TDqVNj5u7/9JYupUqnqtyQXZQWF6XFRWlxUVpclBYXpeUbS+1qOYf8HRgAAAAASUVORK5CYII=' + +ledblank = b'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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 = b'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' + +w2 = b'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' + +w3 = b'iVBORw0KGgoAAAANSUhEUgAAAEYAAABGCAYAAABxLuKEAAAACXBIWXMAAAsSAAALEgHS3X78AAAUBklEQVR4nO1beZBcxXn/fd3vmnv23tUB0molWIjQgZCLCJEEfCAji0oRTAQ4TrBTVCAWMf4j5QQcEhKTlJNAhRSmbMoYMBaJXRAwhyEmICSEJZAMYgWy0O5KWkmr2WN2555573V3/pj3RrOrGUl7CPsPfVVTu/X69fH9+ru7HymlcI5OJvabXsBvK50Dpg6dA6YOnQOmDp0Dpg6dA6YOnQOmDmmf1ERKKcKJjZBEpCa1MwDk/RQRiU9qbbWIzkKAR1X/K6UUEREDMJlR5s9NRAAga7QTEUkAn3gUOqvAKKX4pJ3m5cdKjo2NteXz+YUAyDTNgYaGhiOaVhZYx3FYMplcWCwWFwJguq4PxuPxj4PBYNGTJHgA1Vz/ZOmbDZpNYAiAKhaLgXw+H9M0zYlGo6NjY2MdPT09/wBgnaZpYaUUSSmzruv2LFy48B4icvv6+v5F07TlmqaFAJAQIi+EOBSLxR7t7u5+1DAMWymloayC0tsAQlnKFE5Il8AsSdeMgFFKkbejjIicPXv23DQwMPB1TdO6pJRZy7KeKxaLl0Sj0bXhcFgopbjf1XVdSiQSI0Rktba2hjVNg1IKjDEQEUqlkkwkEgzAU1dcccUdsVgsxRjz1VQBQLFYZFJKMgxD+P2VUhoRKSJS3tpkHWk7O8D4Io6yoVR79+5dt3v37hfmzp3LPJuBfD4PzjlM0xSu6/qGtdKHc85QlhAhpWScc5RKJZRKJQQCARmJRGDbNtc07YCmabva2toemT9//hsHDx78fDKZvEFK2QXAAHA8FAq9vGTJkh9YlmV7AIExVuFxquo2XWAIgMrn85EDBw78fiKRuOTw4cN/FIlEljPGHCmlBgCcc1cpxaSU3AermpQ3ORERYwyFQgGGYWDBggUIBALw2pRSimzbRiqVypRKpe2WZV0diUQ0TdNARJBSqkwmQ67r/nTevHkPjYyMbHAcp9U0zY+7urp+1Nzc3C+l1BhjZ6xqUwbG02WVSqVan3766ceSyeTnTdNUlmUR51yhyitN8jonJiXyGYJSCkIISClhmia6u7vBOYfjONXvSyJSjDHmOA4ZhqGEEMLHFYDSdZ1ls1nKZDIqEokwTdOkbdtsfHz84JVXXrm+o6Njr6fKRETurAMjpeSMMfHss89+a/v27X/f0dFhO47DpJSsyoNMAGYyKEIIuK4LXdehaRosy0JTUxMCgQBM05zQrxpUz/fLWhKolALnXHHOZRkzRbquu5lMxmSMPXHZZZfdH4/Hh8Ph8KgHzikZn3KA5w945MiRRdlsFolEgtm2rflSAABCCDDGUGUQIaUEYwy2bSMYDOLiiy/Geeedh3A4DMYYSqUShoeHMTQ0BE3TfPsAIUQFKMYYEREnopqg+0EklfUPADhjTOZyuS89++yzt3DOf718+fLvrFmz5jGlFDuVUZ4yMFJK4pxj3rx5uzZv3vwnnZ2dLhExIQTzGUmn05BSIhAIgHMOzjksy4LjOGhra8O6deswZ86ciioBQDAYRDweRywWQ09PT8VwBwIBWJYFpRTy+Tzy+TyIqAJ6rb3zDa+UEmXzxRTnHK7rdr/00kvfDwaDIytWrPiZL/2zAgxjTALANddc8/jWrVs//corr3whGo1K0zRBRCgUCmhqakJXVxcaGhoghMDg4CB6e3uh6zpuvvlmtLS0YHx8HNVS5jPZ1NSERYsW4cMPP8QFF1yAlpYWcF728o7jIJlMYv/+/RgaGgLnHJ7HOWmdnmr50kpEBMMwSq7rmvv3779yxYoVP/NUf3aAISIlpWSWZaXvu+++P12xYsWfP//881/q7++/0HEcrFmzhl977bVoaWmBpmmQUsJxHOzYsQPHjx9Ha2srxsbGajIDALZtIxqNYvXq1TBNE67rVgwxYwwtLS2IxWLYvn07+vv7KzbJ/xERcrkcUqkUotEo/A3zqbe3F1dcccUhH7+6fE7DKzHPzlQ6SilZX19f56uvvvrPCxYsuL6hoUHk83k/mAPnHLquo1AogIgq9uN05KnCBKmSUsIwDOTzeTz55JNIJpMVlfVdfqFQ8CVGaprmu3saGRmh1atX73jggQfWhUKhMd/D1pp7ShLj62QqlWrs7e39XDqdvkQIYTQ3N+9qaGjY3dHRsRQARkdHqZr5KuNZzy5MiUqlEnRdR2dnZ8WT9ff3Y3h4GLqug3OuGGNydHSUO46jdF2nlpaW4VtuuWXzrbfeev/pQAGmIDF+gtjT0/O5HTt2PMw579Q0TQEg13VlsVjMRyKRcDAYrOx0jTHqqtB0Sdd1AEAqlcLrr7+Od955BwBUJpOh9evXv7hq1ao3FixY8OulS5fubGxsHMKJjP+UjJ8RML5r279//5oXXnjh5ba2tgjnXCqlfHfHiIhJKZVv6M42+SBLWV6C5+LV5s2bae/evdnbb7/9wTvuuOOfDMMo+kB48Y86k9zptMB42bDOGLN/+MMfvlooFD5jWZbjuq5eI/g6+4jUX6fSNI0SicT4unXrrl2+fPl2IYQGLy/zf2c63iltjJc9E+fcTqVS0WQy2anrOrLZLK/x+lkBpR7WtaJj27YBwOro6Bjxnqt6ccrp6IyM77Zt22557733/jibzc61LAuYQq24Xr50qneBE4ZaCDE5+q0Ejf6z6kAxm80WALjenMqLhv35z1hi6qqSlJIxxuSTTz754FtvvXVnQ0ODNAyDTdWr+AyeST/flVcDEg6H0djYWMm2c7kckskkcrlc5V3LsmCaJjjncnh4mHV2dn73tttuu92rzVQSRs+BnFGptKbE+G65t7f30rfffvu2xsZGIYRQuVyO1YpW6zHNOUcul6ukBL6hrKUGfh6VzWYRCAQQDAaxbNkyLF68GMFgsBL9uq6LTCaD999/H8eOHcOyZcvQ3t4O0zQBgGzbxkcffXTbBx988POlS5c+n06no7ZtR0Oh0HAgEChVxWGnpJoS4wPT19d36V133bUtFouZsVhMoOyameM4lM1mEQwGEQgEajLq5zajo6MgIsTjcQQCgUqFrjoRlFLCtm0kEgkQESKRCK655hosX74cxWJxgsQREXRdh+M4KBQKiMVikFJWQGeMSc45O3bs2CHO+RYp5VoAjUKIwZaWlu9eeumlD3POfV8h6oFUV5V8F/3oo4/e9/jjj9/tGTIKhUKSiGDbNmOMIRAIwDAMcM4rzDqOU6nE+c/83MULwCrRr+u6EEIgn8+jq6sLa9euRTgcRigUqvStVWLw1c513Xr1HmXbNvkpARHJwcFBdv755399zZo1D3q8+/bnJPd9OndNAKinp+fynTt3XjUyMnL+li1b1ieTybZ4PO46jsOFENXG7ZTFqWojWc0gAJimiS9+8Ytob2+HbdsQ4vTO5DQBo1+7IQDEGBOMMS2ZTB7t6Oh4qr29fWd3d/fPA4FAzuNzAhBnAoy3hrIE7dmzZ+0999zzeF9f38JYLKZ0XZdVoj7hTKlq0cpz/az6veogTdM0bNiwAc3NzSiVSmecT02RFAAqFouqWCxSIBDYccMNN2xsbW3tn1yfOaMAz/8BIM65m0gk5j/xxBPfePHFF7+cGBqKM0Bpmkbcq8H6XsVxnIru67oO0zR9G+CfRFakzXEcNXfuXHbZZZdx0zQr/arLoPVUqjoCnhR0VmxalbQqzrk0DEMMDQ0Z3d3dD23cuHGTmnQmNp3SJgPKRm7w+PHz33zjjWvf3fXu7x3o7b8wkxoPF0sli3PuRqPRTGdn54GVK1e+deTIka7du3d/6ujRox3j4+Mttm3TZLUCERgRVq1apS688ELygfBtVjVY1TbGd+uGYVSY98HxTx3y+Tyi0eiE0EHXdSeZTOqLFi3avGnTpptQTmvOXGJqkZKSVLls6AKgN8bkpk6UXg+76ePpfLFJ49yJx+Mj4XB43O9j23bg8OHDXYlEYs7Q0NDckZGR1nw+H3NdaZiGlm1taRrQraCx+cc/vlsp1R4KhZRSihWLRZRKJcRiMViWNUHFfNdt27aKRCLkt/sS4p0swLZtFQwGXa+dpJQsn89TIpGge++998tXXXXVE0IIjXNeiXlmdHxiC2m+OWQ/sCXhfmnThcGLWyx2WAGMvHNoKSX31BCc85NcowKIAJWWqj2t0DqP05677777p88999z1bW1trlfjnVym9CVJKaWU67okhCBd1yURMT9fk1JCCKE0TZNSSjY+Pl7h1TAM0djYOHrTTTf968aNG79TK9uezm0HAkAlCf5uGg8fdI1bGwL6ngCnAqSChCIqi2UlTykDoKCUZFKWDbACiBMJWyrtmYPFx3Ul9928JHrnpjvv/IYrhNr65pvr8/m8pZRSUkpijEHX9eoiOZVKJViWJWKx2Pjg4GBjKBSCVwpRAEhKSZlMhkcikcyNN964uampaciyrPH29vZDF1100S9bW1uPTDa6FSanXMHzJCLloPOVEfwi5WBhZwA/uLoFX6mWlsr7CowIsuCqMCNIk1Pef2/cUW3vZeSTWxPyM7/bwDZd3c4fkgoalFT9/f0XHTlyZIGUUhsYGFi4ffv2zw0MDCzJ5XIRIlLRaHRswYIFH2zYsOGpxYsXf/i9733vm7947bXr0qlUyFeLWCyWW7Vq1davfvWr9y9btmz7ZMnw056auz8tG+OpwFEbN+ZcXDLfxCMBjgGUk8vqiQiAStuy6X8OFf7903PN++cEtX1SwWSE0i/H1Lf35embYwXgC+1Y3xXGi0IqnaBErQWXSqVAOp2OMsZUNBpN6bpeqm4fPHZs/kf79q3MZDLRcDiSvuCCJXvmzZvXDwBeCaJyYMgYqxv1wntxNn40+Zn0YpZxW573zFFn230f5PtGi2KOUgpCKk0phSMFte7VEfXxrrR62JYqUD2WEIL7P9d1uSuEdtIcUjJXCF1JydIlJ551ZHzyO0IIJoTgU+VpZrcdyvdfGAGuF5SoaonKuYhuHZMvHcrTGumqrV9ZyNYZDDm/3XtXo/IRxqkWwgBIP4QHJsQrCgB+1Jv/wdI437ysUf9fVyiDCPK0UnGaCadNBAgCHM9mTGoCJBDPu2xhgAOrm9kbHijaJADd6j4nTwFypdL2p5xPAUTw8iCPW5VzVeyNUfHU/hz7s5CujYIYuMZdzrk7XVCAWbiDpwBOgOhJlq5uDWi9rQF+EN5lnoiGw1e14A+Lks5vMfCsx+mJWOGElCgPpMlEAOT2EfcbH47ZVyyJ6dfKspRKAkgqqJ3j+O5AiW0MmUa2yWKjJ5Y1M5ppQsIIEH1Z8Qc/O+p8vygQ9Jbl33ZCXMPOdgM/4VWATCLyVBK2UFb1c1dCvZeW976bpm8HDWPMH7v69pAExQiEy5vZczEDA6hS05nQtCXGd7kf59RN76TxmGBmLqKzEQAATVgYkwAjBUl00gVEX+Lc144Vv9Zsso+XNRk/Fwo6Jzi9eXyhJ0t/R9AwP6x6J3Yrd1zTiFuzAivjGt5i5c2YldrzdCWGCJB5gfCv0vi3nMuN88Jaf9ykYUzaMVWeRNQCBQApBfeDlPzLraPqfky6txI3MNik48CyRuxY2UCPexP7iZ4CAIsh0azjZU7IVD+fKU1XYhQAsjhyXSF6ZszFZy8I0r1UthWsyrgyAuRwwT0v46iWzqi+yw/4/LZfpdXf7s/jHzk3nSaLJQCAUVnt2gy8++lmupIIRQ0YAybYJX8h5asfJ1+HnRHNxPgqBmBFFF9D2dPYfnnFay9LlYvYMwPOjxaF6JnOqL6rKjomBeB4ia7LO4TuOPZ2BLAvY4uGgMZyGiNbAaQTBoETIcCkNfj2ZtYvS8/U+JJnI+wdQ6UbjxfcLgCQqny/d9RWC7eNy/8bdLS1DZZ+0OvjV6oUAbi8EXesbcb969tx3bG8WPLfBwsP2xKW/44qr7EmKKpG5W22aMZlMlJw9qbETa8cdx9wFfnjEQDsStH9R4u0Mm5q6Tkh3gd42WSZJAA0aNi1JIi/SdlyzqvD8tWs0NsMRgV/DDpxl7dCHlgqa8t4xpENM+WhFs0EGKYA1ZOVf7U7S09pmqlHjXIc4TM/L4AdbSaGrmqjR9ot7IGnXv4AvloNFrHs7RQ9kxHanI6w0auxctBYZ14iQAoFPHO4+PChrLsaqEjprNG0bIw6YT+iH2bYX+ddYFGU7YvpGPEWLgCgO4yHFofovzTCEFW6TqBy7ZcQtyXFWy2oVQ14pbqtxrwq56rmHWPykY8L7Po1Gn9wOjycjqYFjKfvFNSQXhbDf6RcdfWiAH0LOMlICp1wrN7uEyAUQHNMbLm6RW0gAI0a/QITUwUAZYlgBOVK4Jfj+M8jNr8+brLBmEHjwAQVnRWarW8JfCNY/ReTnk11rAp5Bl4CgKugbRvDT44VcN35ATx2eSNuZV6aMMV5Tr2ImQLjh/MoexnpxylSgbHyLp7RBP44VOPzHaEUH8yLRXNC2gEGuCWJ1pxEd5Rjl0bI4ix4p5l7pXKGLQmQEtCIIH897qzZMVS8ESfc7ZmOMxkUDYB8L+ne/PKR0rdZubzBTYahRg1bNELNw7LZoNk61VIAiAFuoqiWvpAQz4866AQANX1vwRTgfpSVf7FtDI/pmu5445EqxzCVjzxmYf0nTz4bg/hxxUBerd2ZVq/npN7YFjQOnWie4nhlMGVvFp99P0MPC2iYE9b7/XYvODyrX77N1jeRBAAHC7QhUaKmOUGMzg9hHzBNb+H1sTTYJkN6SZTGVsTxtDferOZEdZcwS16JAZDjLn7nUAF3NRl4bZ6Jp2qdGkyVchKLOZC3GI7iLKYAk+lsfCw6m1QBok4SedZotj8vZp7bVZMDtGmSV86B+iRBAX77JeY3Rue+1K9D54CpQ+eAqUPngKlD54CpQ/8PuEuH8eJdKe0AAAAASUVORK5CYII=' + +w4 = b'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' + +w5 = b'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' +orangeround = b'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' + +weather_icon_dict = {'clear sky': w1, 'rain': w3, 'light rain':w3, 'shower rain':w3, 'thunderstorm':w3, 'snow': w3, 'sleet': w3, 'wind': w3, 'mist': w3, + 'cloudy': w4, 'scattered clouds': w5, 'few clouds': w5,'broken clouds': w5, 'overcast clouds': w5, 'partly-cloudy-night': w5} + +led_digits = [led0, led1, led2, led3, led4, led5, led6, led7, led8, led9] + +if __name__ == '__main__': + main() \ No newline at end of file From c2e790a277f327de84908cf9cc7870b4cefc16d7 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Fri, 12 Apr 2024 10:54:33 -0400 Subject: [PATCH 023/125] Removed old LED Weather Demo Program --- DemoPrograms/Demo_LED_Clock_Weather.py | 214 ------------------------- 1 file changed, 214 deletions(-) delete mode 100644 DemoPrograms/Demo_LED_Clock_Weather.py diff --git a/DemoPrograms/Demo_LED_Clock_Weather.py b/DemoPrograms/Demo_LED_Clock_Weather.py deleted file mode 100644 index 00fc8df76..000000000 --- a/DemoPrograms/Demo_LED_Clock_Weather.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/env python -import PySimpleGUI as sg -import datetime -import calendar -import forecastio - -''' - Example of a weather App, using: - - DARKSKY - - google maps coordinates - - Copyright 2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. - - Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. - - You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. -''' - - - -##### CHANGE these settings to match your location... check Google Maps ##### -MY_LOCATION_LAT = 35.000000 -MY_LOCATION_LON = -79.000000 -# MY_LOCATION_LAT = 37.7568 -# MY_LOCATION_LON = -87.1191 -# MY_LOCATION_LAT = 41.5726 - -# MY_LOCATION_LON = -93.6102 - - -##### You need a free dark-sky key. You get 1000 calls a month for free ##### -# *** INSERT YOUR DARKSKY KEY HERE ** -DARKSKY_KEY = "INSERT YOUR DARKSKY KEY HERE!" - - -NUM_COLS = 8 # Changes number of days in forecast -USE_CELCIUS = False - -class GUI(): - def __init__(self, location): - self.api_key = DARKSKY_KEY - self.lat = MY_LOCATION_LAT - self.lng = MY_LOCATION_LON - self.blink_count = 0 - - sg.set_options(border_width=0, text_color='white', - background_color='black', text_element_background_color='black') - - # Create clock layout - clock = [ - [sg.T('', pad=((220,0),0)), - sg.Image(data=ledblank, key='-HOUR1-'), - sg.Image(data=ledblank, key='-HOUR2-'), - sg.Image(data=ledblank, key='-COLON-'), - sg.Image(data=ledblank, key='-MIN1-'), - sg.Image(data=ledblank, key='-MIN2-')], ] - - # Create the weather columns layout - weather_cols = [] - for i in range(NUM_COLS): - weather_cols.append( - [[sg.T('', size=(4, 1), font='Any 20', justification='center', key='_DAY_' + str(i)), ], - [sg.Image(data=w1, background_color='black', key='-ICON-'+str(i), pad=((4, 0), 3)), ], - [sg.T('--', size=(3, 1), justification='center', font='Any 20', key='_high_' + str(i), pad=((10, 0), 3))], - [sg.T('--', size=(3, 1), justification='center', font='Any 20', key='_low_' + str(i), pad=((10, 0), 3))]]) - - # Create the overall layout - layout = [[sg.Column(clock, background_color='black')], - [sg.Column(weather_cols[x], background_color='black') for x in range(NUM_COLS)] + [sg.T('×',enable_events=True, key='Exit')] + [sg.T('C',enable_events=True, key='-CELCIUS-')], - # [sg.RButton('Exit', button_color=('black', 'black'), - # image_data=orangeround, tooltip='close window', pad=((450,0),(10,0)))] - ] - - # Create the window - self.window = sg.Window('DarkSky Weather Forecast Widget', layout, - background_color='black', - grab_anywhere=True, - use_default_focus=False, - no_titlebar=True, - alpha_channel=.8, # set an alpha channel if want transparent - location=location, - right_click_menu=[[''], ['Edit Me', 'Exit',]], - enable_close_attempted_event=True, - finalize=True) - - self.colon_elem = self.window.find_element('-COLON-') - self.hour1 = self.window.find_element('-HOUR1-') - self.hour2 = self.window.find_element('-HOUR2-') - self.min1 = self.window.find_element('-MIN1-') - self.min2 = self.window.find_element('-MIN2-') - - self.window['Exit'].set_cursor('hand2') - self.window['-CELCIUS-'].set_cursor('hand2') - - def update_clock(self): - # update the clock - now = datetime.datetime.now() - real_hour = now.hour - 12 if now.hour > 12 else now.hour - hour1_digit = led_digits[real_hour // 10] - self.hour1.Update(data=hour1_digit) - self.hour2.Update(data=led_digits[real_hour % 10]) - self.min2.Update(data=led_digits[int(now.minute) % 10]) - self.min1.Update(data=led_digits[int(now.minute) // 10]) - # Blink the : - self.colon_elem.Update(data=ledcolon if self.blink_count %2 else ledblank) - self.blink_count += 1 - - def update_weather(self): - forecast = forecastio.load_forecast(self.api_key, self.lat, self.lng) - daily = forecast.daily() - today_weekday = datetime.datetime.today().weekday() - - max_temps = [] - min_temps = [] - daily_icons = [] - for daily_data in daily.data: - daily_icons.append(daily_data.d['icon']) - max_temps.append(int(daily_data.d['temperatureMax'])) - min_temps.append(int(daily_data.d['temperatureMin'])) - - for i in range(NUM_COLS): - day_element = self.window.find_element('_DAY_' + str(i)) - max_element = self.window.find_element('_high_' + str(i)) - min_element = self.window.find_element('_low_' + str(i)) - icon_element = self.window.find_element('-ICON-' + str(i)) - day_element.Update(calendar.day_abbr[(today_weekday + i) % 7]) - max_temp = max_temps[i] - min_temp = min_temps[i] - if USE_CELCIUS: - max_temp = int((max_temp-32)/1.8) - min_temp = int((min_temp-32)/1.8) - max_element.Update(max_temp) - min_element.Update(min_temp) - icon_data = weather_icon_dict[daily_icons[i]] - # sg.Print(icon_data, wait=True) - try: - icon_element.update(icon_data) - except: - pass - - -def main(): - global USE_CELCIUS - # Get the GUI object that is used to update the window - location = sg.user_settings_get_entry('-location-', (None, None)) - - gui = GUI(location) - - # ---------- EVENT LOOP ---------- - last_update_time = 0 - while True: - # Wake up once a second to update the clock and weather - event, values = gui.window.read(timeout=1000) - if event in (None, 'Exit', sg.WIN_CLOSE_ATTEMPTED_EVENT): - sg.user_settings_set_entry('-location-', gui.window.current_location()) # The line of code to save the position before exiting - break - elif event == '-CELCIUS-': - USE_CELCIUS = not USE_CELCIUS - elif event == 'Edit Me': - sg.execute_editor(__file__) - elif event == 'Last': - sg.popup(gui.window.last_right_click_widget) - # update clock - gui.update_clock() - # update weather once ever 6 hours - now = datetime.datetime.now() - if last_update_time == 0 or (now-last_update_time).seconds >= 60*60*6 or event == '-CELCIUS-': - print('*** Updating Weather ***') - last_update_time = now - gui.update_weather() - - -led0 = b'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==' - -led1 = b'iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAACD0lEQVRoge2Yv27UQBCHv9kkRCAhXgAkSh6AmoKCFFBENLwBFQ0PQIPES0BJky4NDR0lJT28A4ICQc7nH4V3FccZ+9zcrAv/pJVP3jntp525+XMGiIUp1QbwtELN1Qo1V1WhLK+hqkEVIC8fVYFKdDB3gLvW3ZUN9qsAmRlnKfEoQ6WBTShQCxybcW7Gk7blt2N3GAVkGegmcA6ctC0AB45tyE2VgL5txifgROLfhH0YFMBH4DGwYdpFIVBtft5XlwA8l/UVGujtbhMgGMrL3p7W2rcrlor2DtV32dHM7+wdSiOfp7TG1FytUHO1Qnllpko7bL3n3N4pJE+VFvhLftf09n/Kz16KXO/NJJBAr8wEKF23i4GxvDDTh5T0OqUxII2NXntRiavGDJM4pOtChwqfZhrgDfDCjA3jnUOY+zDT2xxTf0Gnea9KTJVD36UkgS5ALWgDeugEe+jg8DTPegnY0k009/JetbF92wMoEFu73rmHB/oVOUCuXbS2zrvqUF6RrgIlLmPKy9zrTXlaJNQi3bc8KGmBUCwgphYzOPRVeqdd/1OFQJVDvuVa1y8tcgaHEKhy7EvgsxlHXE401dxXysqFxHO6UevGhH1YTCkf9kfiFPiaXdk4tuG/vgT8Ap4B31PilmMXOmIVHdAF+wMzGuCHdAWkClQ5mJHDq2X0/q0MVe2mplS99nlaJNR/DW4BrSasyZkAAAAASUVORK5CYII=' - -led2 = b'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=' - -led3 = b'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=' - -led4 = b'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=' - -led5 = b'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' - -led6 = b'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' - -led7 = b'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=' - -led8 = b'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=' - -led9 = b'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' - -ledcolon = b'iVBORw0KGgoAAAANSUhEUgAAACUAAAA8CAYAAADhR3NQAAAACXBIWXMAADBKAAAwSgHCONjjAAABdklEQVRoge2YQU7DMBBFn9MEVVBYcADEmiVH4BScD3EL7gIcAYEEFSQdFrZRCJGYeBa1qnlSZSuVJz8/k/EkARAqo9m3gDlclBYXpcVFaXFRWlyUFhelpUpRrWVxAFaTYwIMlqApbnX9VJFT+UpOgRugA3ZE195C4EHE7JYs/TVpvAJ5BxGQPo1PTSObEASQUBA7xS/nE/hI8+yMEJ20YBK1G81zwnfWoKXr85MxAF9p3o/+24tT45N2k/EYOLIoovDpy069AvfAOdGxDngR+cmzUg6nTv0XoJ85toQqnapyQ3ZRWlyUFhelpUpR5oo+1xFYq3GVFd3Uo18Cd8CGuN+tgcem4VaErUjxFZtErYFr4ITY8K2AM2ILsy0JnDD36Pnk49a4mh49B2qtQUvX5zzZ8TdnVqVBR5jWD/x2C6Ko6av8UkwloQUuiImd32J64Bnb94TDqVNj5u7/9JYupUqnqtyQXZQWF6XFRWlxUVpclBYXpeUbS+1qOYf8HRgAAAAASUVORK5CYII=' - -ledblank = b'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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 = b'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' - -w2 = b'iVBORw0KGgoAAAANSUhEUgAAAEYAAABGCAYAAABxLuKEAAAACXBIWXMAAAsSAAALEgHS3X78AAAUwElEQVR4nO1ceZQV1Zn/3Xur3toLvTe0dAPKIgk0YZuMOhk1JKiZxATUicswyclINCPq6MzJiZPVWYxbMpnEUWMyw9FoopkomkRxXCBAAwICBqHZl17pbrr7db+tXlXd75s/XtXjdUMj3bSG5PCdU+edrqp3l9/9fd/3+27Va8HMOGcnmvxDD+BstXPADGHngBnCzgEzhJ1NwAjvOCvMeL87YGYFQAghkJcBSQhBAIR3HXl/C2ZmKaV+v8d2KhOjla6ZWeL4ijMACCE4e4lBRBBCQErp3688MJiZYds2pJQwTTO/PR+wD9xGhTHMLAdNQADgRCIROHLkyCLLsi4mooiU0jIMY3d1dfWKqqqq5r6+vuKmpqarM5nMx7TWlUIIxzCMfZFIZFVtbe2bkUjEISJDCEH57Xug+WGAALC3CKNmo8EYqbWmI0eOzOru7l7AzMExY8asj0aju3bu3Pk/kUjkykgkwkII4TMjHo83h0Kh+zKZzA3hcPiSSCTCUkoBZOmTTCZFIpF4bcaMGV8YN25cG7JAS2ZmIQSQBWPwPIQPnrdQDI+5I7ERAUNEEoCUUmoi4nXr1t3R1dX1/eLiYgEA6XQ6bVlWR2VlZW0oFBKu6+bihQeAisViKCwshGEYpLXW8NzQsiwIIWQ6nZaWZb01Y8aMh4qKit6urKw8JKWE4ziiq6vrfMuypjCzaZpmS2lp6Y6CggLbc89cX178opGwadjAeCsDrbXQWosDBw7M3rBhw5u1tbVRx3E0soFWIMskZuYTMg1nZ0Su6wpmloZhwHEcWJaFkpISFBcXIxgMklJKpFIpTqfTvaFQ6AcVFRW/am5uvldKealpmoXMrJg5aVnW3pqamjsmT568PpFIjLEsqzgUCvUWFBT0ewvx/gLjgcLt7e3jGxoavnLs2LHLXde9oKioqFRKSX7A5GyjLISQg7JRttOsO8C/Zts2iAhTp05FYWEhtNZ+sNZewFbxeFz39/ejoqJCBQIBsGdCCEFEorOz84hS6tdE9HEhxAQiaolGoz+ZN2/e9yKRCHvtnTZ7ThsYH5Rjx46VPfHEEy8S0cWFhYUkhJBExDgNDSKEgBACruuCiKC1hmmaCAaDGDt2LEpLS5HJZHKZK69vUkpBKSVd19VE5LPSZwNJKaVlWSIcDvv9cGdnpygvL7/nsssuu89zM2bm08p0pw2M1tpQSrmrV6/+mxUrVjxZUVFh2bZtMrP03cs/mPmkLCEiOI6D4uJilJSUIBgMorCwEEqpXLpWSsEb/GBwTno+/xYppdZa+7JBK6WMWCx2pKKi4vHKysq906ZNe6OoqCjmL/Kp5jvsdJ3JZERLSwuYWTiOo3wwAMC2bWitEQ6Hc2D4QLmui0AggPnz52PatGkoKCiAUgpaa6RSKXR1daG5uRlEBD/mEFEOYB80KeUJoHsmmNnw+xNC+HOrPXTo0L/t2bNHNDQ0rF20aNGNdXV1zSeRGCc0dlqAEJGUUlJra+t5S5cu/V0ymZxUUVFhE5EiIqm1FslkEo7jIBKJIBgM5iaglEIoFMLnP/95TJ8+PRdTfAb4k+7p6cGWLVuQSCRQUlKCsrIyhEIhaK2RTCbR3d2NVCqVE4FDTmpgXGMppTYMg2OxmFldXf2TpUuX3vxewJw2Y6SURESypqam5Z577vnyvffe+0RjY+OEYDDIgUBAaK1RU1ODuXPnYty4cVBKIRaLobGxEVu2bMHVV1+NyZMno6enB/ksywMeBQUFuOCCC9DT04OpU6fCNM0BrplKpbBnzx7s27cvX0EPAAQAMpkMgsGg/10BwJBSatu20draOs2795SMGHZWIiJDKeUcPXr0vJdeeun6hoaGvzp48OCUKVOmVFx33XWqrKxswEAdx8H69etRW1uL2tpaWJZ1QnDNax+GYUBKmQvQ+aaUgmma2Lx5M7Zu3YpgMDjgu8yMRCKBWCyGcDiMcDjsux8BcPft2xe45ppr/nXZsmXfICJ1qnpsOFkppya11n6QNIQQ7qZNmz7d1NT0QnV1tUylUkBehvIn4zjOULFhSJBOFoD9uLR8+XK0tbUhEonkAPW1kJQSQgjtJ4ZMJsOJRELMmTNnzUMPPfS50tLSnlGJMX4U7+rqKtu1a9f1fX19FxFRhVLqYE1Nzc9bWlq+xMw3KaVcDHJPv/1TZJNhmQ/Cjh07EI/HoZTC/v37sXfvXgBAMBgkZqZ4PG4IITgajVrjx49/d+HChc8sXrx4+elmpfcExpfZ27dv/9jGjRuXh8PhiYFAwBdWSCaTHAwGRUFBwQnUf7/MB8c0zVzGe/vtt/Hyyy8jmUzCsiwsWbLkiSuuuOKZqqqq1rq6uoOmaWpPgJ5WwXlKYHy6HTx4cMqKFStWVVVVjZNSutnSJqv9ZTYqiw8KlLyxDWBjJBLBypUredu2bQeWLVv28JIlS37sxRYAEEQkh6N8T5mViEgppWjz5s1LDcMYl8lkHNu2zfzgOdquMhJjZrZtW0yZMqVn2bJl19fX12/RWiuttRJCsJSShrvxNSQwzCyUUi4RoaOjY7bjOIjH48q7lrvPB+RkzDtZnXQqy7//ZCnd72fwYgghfPUcEkJE/ea8mDciGxIYIuI1a9Ysfuedd26IxWKzQ6EQbNv+QPaI89XyYGC9mglAFiRfKEopEY/H04ZhdHtt+Hsz7xloTzqGwR37+f3pp5/+2tq1a/+9tLSUTdPM3Tcc15FSQmv9nvf6adgHxHEcCCFQUlKCkpIShEKhnEbp6upCKpWClBJEhEgkglAoBKUU9fT0yMLCwidvu+22myORiGbmXEwZ7t7MAMZ4sl8fPnz4/A0bNtxSUlICrbWbyWTMk1S8A0DKB1gpBdd1kUqlUFhYmLv/ZOaXBIlEAo7jIBwOo7S0FHPnzsV5552XU7AAoLVGIpHA9u3b0d7ejvr6eowdOxaBQAAAJBHpxsbGJdu3b//dRRdd9N/pdDqQSqUioVDIikaj1qCK/JQ2gDF+PdTS0jLxrrvuWhUIBOpKSkocr1Phuq6Mx+MwDCM34ZOZ1ho9PT1IJpOIRqMoKiqCaZq+8MrvD0SEeDyO7u5uBINBVFRUYNGiRaipqUEmkzlBApimCdd1kU6nUVxcnL93AwAcDAZFe3t7HMCPmfkSIpoEoDccDv98zpw5DxUVFaW8DKVPxZ4hXemZZ5657bHHHvs+AMMrAtkwDLJtWwkhEAwGfQrnaK21huM4SKfTIKIBruTrDh8cZobWGrZtw3EczJ8/H/X19QiFQohGo/mTHRDg/Xjilw0nMVZKCb9e8lVwT0+PUkr98Kqrrro9LzTI/K3QUwKTbzt37py5bt26z/T29o7dvHnz5S0tLdPKysocx3GU1lr6sj3flfLPDRitFyjzTUoJ27YxYcIELF68OCf3TzcuneoeKaVLRNIrIikQCMhjx471lJeXP15TU7O+rq5uQ3l5ec9QwXlIYHy38j8PHTo0+Z577nny3Xff/WhRURF70pv9eiR/zIN36JlZedljwEyEELBtG3V1dbjyyisHZBh/8v59+YDkn/MBGiwhBp/3J59Op2HbtiCiXQsWLFhaX1/fcLK66T2VL2efDEqllBOLxUqWL19+54oVK77c0dFRJYTgYDAoDMMYMFDbtmHbNpgZpmkiFArBMAwtpeRcv+RIIRRDKAIYs2fPlpMmTZL5rDpd1gzFHh/kvDZZSkmGYVA6nTa11m/feuutHxszZkxqMHOGtVEFZPdl2traal57/fVrtm7e9NG9+/ZP643FxluWFWJmhEKhVGlpafucOXPeKiws7N+0adNFTU1NU2KxWFlWlxCkABAsY6EtQW4KrkuIRMJYsGABV1ZW5soLPzgbhnGCG/qADHXd3+lLJpO57Ye83UACIPr6+prvuOOOyydMmHDA94xhA+OtgGBmASEhBWhXP83q7+8prM7EOhJWJiqyWcEuKytrLykp6QWAZDIZaWtrq2tpaak9fPjQlKNHO8bGUtq09jx9C0frDhaOm/9mdeWYo4lUpqqhoeH28vJyYRiG9L4L13VRVlZ2wq4dEaG/vx/pdBqlpaUn7M1ordHf3494PI5oNErRaJR8Yai15qamJrO+vv7XDzzwwGeQfapJ+awbyQM3CWZqSrj1Tx22X7h8bPDOPy83XmJAirwnhFprg5mFYRguAAZYAB5Vd977z2hd8Vlc/spVfaIqHlQQia6u6PU33rg7Y1llBQUFLjEJwSQBwcQCpmkIf2JeBuS87VFWSinDMPy+fdVMhmEgHo/LTCaTA00ppadPn77u61//+s0TJ07cd7IYM6zNcH/yB9P4i9+n1dO2CIwvMcVRMIEIygfcq11cbyCCyTVZKA22gtzwpZ/SwRc/G7h649yErIot39X384vKzcfmVVe8fvfdd//dA/ff/3hrW1ulIIfZKIaSkEGTIKUBKQV7rIVt28J1XZim6StgFkK43qoLANK2bZFIJMSsWbM219bW7lRKUXV1ddOFF164ef78+asjkUhqsAv5NjxXAgww3N908hsdNi5nLVquPw+zCwx0cZaOLAarSnYNCMNF5mglrb/pZ3TkjU8YV7x0ZU/Vp1et77BWbTomZn2hzpg1qVDuBQSOHm0fu3bN6qvauu1idL36qQMHmqe+21qU7OtpHWu7ZAgA4XA4VVFR0XnFFVc8/ZGPfGTDI4888q1t27Zd7DiOyg4TQimlq6urO6+55prHvvjFL36voKAgkR9g+T3ephguMFIA1GrhxpYMbh0fxKPjQnjaOy8ADBRLPih922fSW0ue4qM7Zso/++7X3Klf/e6rR53VXa75l70pbrt5kriw0EA/ESspGBBSo2/VRdj/1Qcw8xc3dPaW9jc3778wkUhGpJRcVlZ2rK6ubm80GrUAwLKs0MYNGy5t3L17Zm9v75iCgoJ0XV3dvnnz5q0ZN25cGx9/QpoDREp5SuWbU5PDPAQzB5gZ5FE7aevwwT67zssUgrVtMjO4881L9W9rmvWzYHpn2Q+zVXPGXH1Mv/ZcG/PWPr7Pa0cya0nMcDtXLnRWlne4Xesu1sxgyorJwWPQWitXa4OZhhyr1loRkRjuHEf0fgxn2eEAUAJgYvArbfY3wEQTi8yvE9hU0rS55Wc38va/fwQcL0ZhQUqMmfEOdCqsVCR9SSkWZ5hmRCRtBgwI1gJCafS+epX4/VW/VeHSLqRf/zgShf0UnrwXIuD65PaUNQkpIQF3Q4f1ybDi9KyywFrb5YAUYGRLAz3iN7NGyBifNUi5HFjXo//jX3ZleGVr5s7sKtlharzrQf0iWK8M2vq1qK1XGqR/A9ZrZrxD+779TUrun5jPOGZvVdMHzueWB+/i7fXbeB2YGpTDXU/dyMxgcpXfPzFLIsbufnfBd3akurcesz/DzNDExhnM6Xg9NiI0s6yRALC1j5cdSss7TGXa46LGfgAApYMwIklRNn2nALNwk6ZQgAiFHZF6dwb2ffs72DBrB+246Slx7JWFcLpLs6mcBUITD6LmH7+HD6++FNEJh0XVtf+L0sUvMENAZJUzZ5lKjQm+7p2kfAUyoIqDRuvxoZ25jfiNKm9wek8Ct+xM8KMVAfF/88bg6pCEBX/Pw+0rRPerC7n9Zzeh97WF7FohYZgEaTLYlpxxhQgU94v5ay9BdMYOgCTIVZABhw9/5VHhHivGBb+44fgjrePB0iYEX+nil7oy4pNhie2fHYtLowp9DIgTMuOIJnjmtDMyxNM0c9GA8+SqnHuwK7l/20zad+fDen11J60JMK0Jkl5bFOPulZ/gXID1XKXrsVv4wKJfMtlKM0tmQlvCKe1Ou6XHAzWj3eLPbe/nX/c4/HE/44yGGzHzmQPjxwgaEC/8gwSTYzB7WcXuLOctUxt5nWRaF7So69lrKQeid09y43xu+sp/sttbnG2P0JHW4x7eGX9+b8yp9+LIqAEw1HHGb216tJXe5yAKC4ZQGkwSAsChWx5DZs80loCY9KM7uPy6XwpoCSEJEAy3sxLprTMx9jvfZjWmX4C5I8MXrO3lFxNkFhYGZA+Q9RXPpJchWZz4wuKZ2fuNPJOnZ44+eiuvB/MGwdT64N3MjJTjhHf2WB8iYsFMgtPbZ7LTUZn9rkbc4ejz7bTpx03E/7WfNlqaTWYWJzJz9I/RaETooXzbT6+JLbN5c0mM14O5+VvfzF5z8FKz9c0n9iR+yswgcg2mjOm5oGRmWJqjr3fxW79qZ26M8z94LqtGYczvLzCUXb0h/J2yLNCJCO26ZC01gKnproeZGWnHVRt73Qfv32Pzs4edb2WBYZVVsOTHLMXMcInLLeJZHwQYowKMD0i/rYs3dVoXO5oGCitysn83/9P93ADmw19+XJMTYiZs6dW3PNnK/MBe4k3dfMNgJnhtS6JsUPcX4Y8CGGYWjiY835z5waO7k88OmJzvQn2/+RRtBPP+v36OyVZEboCZ0JrmhS93cPvaHl6Vcrncb4+ZoTmrXA/2OR9qaE8vHAza2QyMYGYkHAqu7XV/cP9eh39x2Hno+KS8tOscreJtlV28+9JVTOnAAMCY4RDXauLi/Db9z640Vf5ob3rL84fT9zIzNH3wwAy7JGBAAcD+pLh2f0LebgoD5xcZe72LApzdG8bhv30SgYpOXPDCIoiQDWgFobTXhjAEmoRAHF669fdzWlI8dXOC3+h2jDnlYbNxlJLvsG0kOoYAYHwY7/ZrvB0t4oPTC8SvAEDCFRCGg477vgZr93RM2zgXakwvQBJQuSr3ZNrHr38OpHFbuyU/XBYWzvlFYg8ACDE69c+wbKTxxfP9Ivb2ZXJuEn/zUt41eR9bjdOy553TrXYlM6Pb5ovfitHvDqX5dhroZmd9jMmfiCBmSayzoDjt1bT/sjUcf+Oy7D3uSGODGPT5gR9n+nulvKdcDLTf/TAiF29E8eLnctuaw2Xw8R9oCU/mf/BuhFH5IZf3WKTvuWsBIVF87bMjBeVssjMsIj1Q7P3nQ43pRsEn3wRI/rGDAowGY9iVcNoqYVb3QATs0RnWH95G7Ve0AInsLsCfho3Sy4b8JwUKMGrAjO5Pe88GO5v+hcFZZeeAGcLOATOEnQNmCDsHzBB2Dpgh7P8B+QrwZW3eDSIAAAAASUVORK5CYII=' - -w3 = b'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' - -w4 = b'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' - -w5 = b'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' -orangeround = b'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' - -weather_icon_dict = {'clear-day': w1, 'clear-night': w1, 'rain': w3, 'snow': w3, 'sleet': w3, 'wind': w3, 'fog': w3, - 'cloudy': w4, 'partly-cloudy-day': w5, 'partly-cloudy-night': w5} - -led_digits = [led0, led1, led2, led3, led4, led5, led6, led7, led8, led9] - -if __name__ == '__main__': - main() \ No newline at end of file From bae8708299d7be0c9050ec2cb3a3b5d6fc978571 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Fri, 12 Apr 2024 11:03:04 -0400 Subject: [PATCH 024/125] Added button in settings window to register for an openweathermap API key --- .../Demo_Program_Desktop_Widget_LED_Clock_Weather.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py b/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py index 1c7bf9470..7b78ef408 100644 --- a/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py +++ b/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py @@ -3,6 +3,7 @@ import datetime import calendar import requests +import webbrowser ''' Example of a weather App, using: @@ -29,7 +30,7 @@ def settings_window(location=(None, None)): [sg.T('Latitude', s=tsize, justification='r'), sg.I(s=12, setting='', k='-lat-')], [sg.T('Longitude', s=tsize, justification='r'), sg.I(s=12, setting='', k='-lon-')], [sg.CB('Use F Degrees', s=tsize, setting=True, k='-faren-')], - [sg.OK(), sg.Cancel()], + [sg.OK(), sg.B('Register with openweathermap.org for API key', k='-register-'), sg.Cancel()], ] window = sg.Window('Settings', layout, location=location) @@ -41,6 +42,9 @@ def settings_window(location=(None, None)): if event == 'OK': window.settings_save(values) break + if event == '-register-': + webbrowser.open(r'https://home.openweathermap.org/users/sign_up') + window.close() return event == 'OK' From 1bb2b51177b9371211233f0059987befc203c152 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Fri, 12 Apr 2024 11:11:23 -0400 Subject: [PATCH 025/125] Fix for hand cursor when over the C/F change and the X for exit. --- DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py b/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py index 7b78ef408..246cbac5c 100644 --- a/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py +++ b/DemoPrograms/Demo_Program_Desktop_Widget_LED_Clock_Weather.py @@ -105,8 +105,8 @@ def __init__(self): self.min1 = self.window.find_element('-MIN1-') self.min2 = self.window.find_element('-MIN2-') - self.window['Exit'].set_cursor('hand2') - self.window['-CELCIUS-'].set_cursor('hand2') + self.window['Exit'].set_cursor('hand1') + self.window['-CELCIUS-'].set_cursor('hand1') def update_clock(self): # update the clock From c25187c14687274ea6d19092b63bb8be26684c95 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 12 Apr 2024 16:15:06 -0400 Subject: [PATCH 026/125] Uncommented line of code that imports askcolor from tkinter. Not sure why it was commented out --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index ad08d1a66..790f5ef8d 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -6,4 +6,5 @@ Changelog since last major release 5.0.4.2 Fix for ButtonMenu not clearing the value dictionary entry after the event is returned. Used to work this way but broke along the way. 5.0.4.3 Added set_hscroll_position for elements 5.0.4.4 NEW FEATURE added - parameter repeating_timer_ms added to the Window object. Causes a repeating timer to be started. +5.0.4.5 Uncommented line of code that imports askcolor from tkinter. Not sure why it was commented out From 6e7eaf8b31319cc11d15ced49071c5b66df63a2c Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:22:28 -0400 Subject: [PATCH 027/125] Fix for bug that was showing trial expired in Home Window. Added new function dict_to_string to format dictionaries nicely --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 790f5ef8d..dff937cae 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -7,4 +7,5 @@ Changelog since last major release 5.0.4.3 Added set_hscroll_position for elements 5.0.4.4 NEW FEATURE added - parameter repeating_timer_ms added to the Window object. Causes a repeating timer to be started. 5.0.4.5 Uncommented line of code that imports askcolor from tkinter. Not sure why it was commented out +5.0.4.6 Fix for bug that was showing trial expired in Home Window. Added new function dict_to_string to format dictionaries nicely From 5b3a0796323015830164967415b00e9c4a2ca126 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 26 Apr 2024 11:43:28 -0400 Subject: [PATCH 028/125] Changed function dict_to_string to use the pprint.pformat function rather the reinvent the wheel this time... but at least my version did right justify keys in a field the width of the max key length. :-) New option in global settings to control if formatted dictionary is printed when the automatic "print event values" feature is enabled --- development_build_changelog.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index dff937cae..d907e3929 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -8,4 +8,7 @@ Changelog since last major release 5.0.4.4 NEW FEATURE added - parameter repeating_timer_ms added to the Window object. Causes a repeating timer to be started. 5.0.4.5 Uncommented line of code that imports askcolor from tkinter. Not sure why it was commented out 5.0.4.6 Fix for bug that was showing trial expired in Home Window. Added new function dict_to_string to format dictionaries nicely - +5.0.4.7 Changed function dict_to_string to use the pprint.pformat function rather the reinvent the wheel this time... + but at least my version did right justify keys in a field the width of the max key length. :-) + New option in global settings to control if formatted dictionary is printed when the automatic "print event values" feature is enabled + From 149ff929314120660ded21fb7bb707e19ce2bcf5 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 27 Apr 2024 13:52:47 -0400 Subject: [PATCH 029/125] Changed how Input.update select parm works: If set to True, then the input is selected (highlighted). The new addition is that if False, it clears the selection --- development_build_changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index d907e3929..4c2d29ac6 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -11,4 +11,6 @@ Changelog since last major release 5.0.4.7 Changed function dict_to_string to use the pprint.pformat function rather the reinvent the wheel this time... but at least my version did right justify keys in a field the width of the max key length. :-) New option in global settings to control if formatted dictionary is printed when the automatic "print event values" feature is enabled +5.0.4.8 Changed how Input.update select parm works: + If set to True, then the input is selected (highlighted). The new addition is that if False, it clears the selection From 96f10faa8a126ea34160a0bcec85a3db215d85c3 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 2 May 2024 07:57:17 -0400 Subject: [PATCH 030/125] Added arrow and arrow_type to Graph.draw_line method --- development_build_changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 4c2d29ac6..06f9ae1d8 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -13,4 +13,6 @@ Changelog since last major release New option in global settings to control if formatted dictionary is printed when the automatic "print event values" feature is enabled 5.0.4.8 Changed how Input.update select parm works: If set to True, then the input is selected (highlighted). The new addition is that if False, it clears the selection +5.0.4.9 Internal test +5.0.4.10 Added arrow and arrow_type to Graph.draw_line method From 29a4e10eeb0750aeff2555b3455da2a274e4737f Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 3 May 2024 08:30:03 -0400 Subject: [PATCH 031/125] Made aliases for HorizontalSeparator using word "Line" (HorizontalLine, HLine, Line) --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 06f9ae1d8..af7424a83 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -15,4 +15,5 @@ Changelog since last major release If set to True, then the input is selected (highlighted). The new addition is that if False, it clears the selection 5.0.4.9 Internal test 5.0.4.10 Added arrow and arrow_type to Graph.draw_line method +5.0.4.11 Made aliases for HorizontalSeparator using word "Line" (HorizontalLine, HLine, Line) From 9fb899ea13f3656e8bb54b0dd2d0efbdf6ec241a Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 3 May 2024 11:46:41 -0400 Subject: [PATCH 032/125] Quick test with no changes to main code.... From 643a884efc30f7abbbb255a70333f55557bc803e Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 3 May 2024 11:50:12 -0400 Subject: [PATCH 033/125] Internal test --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index af7424a83..269774ad1 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -16,4 +16,5 @@ Changelog since last major release 5.0.4.9 Internal test 5.0.4.10 Added arrow and arrow_type to Graph.draw_line method 5.0.4.11 Made aliases for HorizontalSeparator using word "Line" (HorizontalLine, HLine, Line) +5.0.4.12 Internal test From 15b0c7f4cf79b2941b0cbacfb0dd6e3e0790db76 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 10 May 2024 12:41:45 -0400 Subject: [PATCH 034/125] Updated the checklist in the builtin GitHub Issue logger to use the newest checklist. Was using an old one. --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 269774ad1..2f94835f1 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -17,4 +17,5 @@ Changelog since last major release 5.0.4.10 Added arrow and arrow_type to Graph.draw_line method 5.0.4.11 Made aliases for HorizontalSeparator using word "Line" (HorizontalLine, HLine, Line) 5.0.4.12 Internal test +5.0.4.13 Updated the checklist in the builtin GitHub Issue logger to use the newest checklist. Was using an old one. From 38c9453a348ba14e91946109c56a682546f15c69 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 13 May 2024 14:23:25 -0400 Subject: [PATCH 035/125] Changed docstring for Table element and others that have "Select Mode" parameter to str --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 2f94835f1..cee772dde 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -18,4 +18,5 @@ Changelog since last major release 5.0.4.11 Made aliases for HorizontalSeparator using word "Line" (HorizontalLine, HLine, Line) 5.0.4.12 Internal test 5.0.4.13 Updated the checklist in the builtin GitHub Issue logger to use the newest checklist. Was using an old one. +5.0.4.14 Changed docstring for Table element and others that have "Select Mode" parameter to str From 89f27c3fe318d9f1055b48a001ec556173b29b48 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 14 May 2024 02:55:54 -0400 Subject: [PATCH 036/125] added select_mode=sg.TABLE_SELECT_MODE_NONE so that the rows no longer flash when selected --- DemoPrograms/Demo_Table_Checkmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DemoPrograms/Demo_Table_Checkmark.py b/DemoPrograms/Demo_Table_Checkmark.py index da554f4d9..f2e580d82 100644 --- a/DemoPrograms/Demo_Table_Checkmark.py +++ b/DemoPrograms/Demo_Table_Checkmark.py @@ -61,7 +61,7 @@ def make_table(num_rows, num_cols): # ------ Window Layout ------ layout = [[sg.Table(values=data[1:][:], headings=headings, max_col_width=25, auto_size_columns=False, col_widths=[10, 10, 20, 20 ,30, 5], display_row_numbers=True, justification='center', num_rows=20, key='-TABLE-', selected_row_colors='red on yellow', - expand_x=False, expand_y=True, vertical_scroll_only=False, enable_click_events=True, font='_ 14'), + expand_x=False, expand_y=True, vertical_scroll_only=False, enable_click_events=True, select_mode=sg.TABLE_SELECT_MODE_NONE, font='_ 14'), sg.Sizegrip()]] # ------ Create Window ------ From b6f9502025ba3a874dce3649a041c9cc48bd0db6 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 16 May 2024 07:02:18 -0400 Subject: [PATCH 037/125] Added right_click_menu paramter to Combo element --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index cee772dde..5f40d7582 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -19,4 +19,5 @@ Changelog since last major release 5.0.4.12 Internal test 5.0.4.13 Updated the checklist in the builtin GitHub Issue logger to use the newest checklist. Was using an old one. 5.0.4.14 Changed docstring for Table element and others that have "Select Mode" parameter to str +5.0.4.15 Added right_click_menu paramter to Combo element From 84455bf576d75d3c81846c499aa46b815874c863 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 17 May 2024 07:46:07 -0400 Subject: [PATCH 038/125] Added window.right_click_menu_element. Will either be None or the element that was right clicked if right click menu used --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 5f40d7582..b2a327917 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -20,4 +20,5 @@ Changelog since last major release 5.0.4.13 Updated the checklist in the builtin GitHub Issue logger to use the newest checklist. Was using an old one. 5.0.4.14 Changed docstring for Table element and others that have "Select Mode" parameter to str 5.0.4.15 Added right_click_menu paramter to Combo element +5.0.4.16 Added window.right_click_menu_element. Will either be None or the element that was right clicked if right click menu used From df44fb7bbc5576bf4d2390b35ce653dc45aeb722 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 18 May 2024 09:36:02 -0400 Subject: [PATCH 039/125] Addition of Priority Support Code to the GitHub-Based form The priority support code is already part of the built-in GitHub Issue GUI in PySimpleGUI but missed getting it into the manually entered one --- ...must-fill-in-this-form-with-every-new-issue-submitted.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md index b7c36c524..99f849691 100644 --- a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md +++ b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md @@ -39,6 +39,12 @@ Or you can print each version shown in () #### GUI Version (tkinter (`sg.tclversion_detailed`), PySide2, WxPython, Remi) +#### GUI Version (tkinter (`sg.tclversion_detailed`), PySide2, WxPython, Remi) + + +### Priority Support Code (Commercial Users) + + --------------------- From dc7fcc4b03aca2619d2508ef9bf40a77b9714b08 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 18 May 2024 11:38:24 -0400 Subject: [PATCH 040/125] Fix in def dict_to_string. The pformat call didn't add the sort_dict parm until Python 3.8 so need to check python ver Made priority suport code field in markdown for GitHub issue be the same level as the manual form on GitHub Added new function execute_pip_get_local_package_version so that local version of pip installed package can be easily obtained --- development_build_changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index b2a327917..cc609f836 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -21,4 +21,7 @@ Changelog since last major release 5.0.4.14 Changed docstring for Table element and others that have "Select Mode" parameter to str 5.0.4.15 Added right_click_menu paramter to Combo element 5.0.4.16 Added window.right_click_menu_element. Will either be None or the element that was right clicked if right click menu used +5.0.4.17 Fix in def dict_to_string. The pformat call didn't add the sort_dict parm until Python 3.8 so need to check python ver +5.0.4.18 Made priority suport code field in markdown for GitHub issue be the same level as the manual form on GitHub +5.0.4.19 Added new function execute_pip_get_local_package_version so that local version of pip installed package can be easily obtained From a650a70ac276ac43d0ec6dcfad7b1c02bfec01f4 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 19 May 2024 10:50:30 -0400 Subject: [PATCH 041/125] Changed execute_command_subprocess so that the interpreter can have spaces for things like parameters (i.e. py -3.11) --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index cc609f836..c872f54e5 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -24,4 +24,5 @@ Changelog since last major release 5.0.4.17 Fix in def dict_to_string. The pformat call didn't add the sort_dict parm until Python 3.8 so need to check python ver 5.0.4.18 Made priority suport code field in markdown for GitHub issue be the same level as the manual form on GitHub 5.0.4.19 Added new function execute_pip_get_local_package_version so that local version of pip installed package can be easily obtained +5.0.4.20 Changed execute_command_subprocess so that the interpreter can have spaces for things like parameters (i.e. py -3.11) From d6c54cbc4f81f487fc94184b5428e796ccae44e5 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 23 May 2024 11:43:49 -0400 Subject: [PATCH 042/125] Changed the function execute_pip_get_local_package_version to use the temofile package so that there can be multiple requests outstanding at a time --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index c872f54e5..c7b357bd0 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -25,4 +25,5 @@ Changelog since last major release 5.0.4.18 Made priority suport code field in markdown for GitHub issue be the same level as the manual form on GitHub 5.0.4.19 Added new function execute_pip_get_local_package_version so that local version of pip installed package can be easily obtained 5.0.4.20 Changed execute_command_subprocess so that the interpreter can have spaces for things like parameters (i.e. py -3.11) +5.0.4.21 Changed the function execute_pip_get_local_package_version to use the temofile package so that there can be multiple requests outstanding at a time From 579f444713d1cc77d9c11da6a8a853c21acce1b4 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 23 May 2024 11:48:00 -0400 Subject: [PATCH 043/125] New demo program to show how to use the new pip install API calls... in particular the threaded and not-threaded get installed versions number. --- DemoPrograms/Demo_Pip_Installs.py | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 DemoPrograms/Demo_Pip_Installs.py diff --git a/DemoPrograms/Demo_Pip_Installs.py b/DemoPrograms/Demo_Pip_Installs.py new file mode 100644 index 000000000..dddcc3fb8 --- /dev/null +++ b/DemoPrograms/Demo_Pip_Installs.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +""" + Obtain the version number for a package installed on any versions of Python on your system by uysing + the sg.execute_pip_get_local_package_version() call. + + Copyright 2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + + Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. + + You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. +""" + +import PySimpleGUI as sg + + +layout = [ [sg.T('Package Name:'), sg.In(s=15, setting='', k='-PACKAGE-')], + [sg.T('Full path to interpreter'), sg.In(setting='', k='-INT-')], + [sg.CB('Use threading', setting=False, k='-THREADED-')], + [sg.MLine(s=(80,20), reroute_cprint=True, k='-ML-')], + [sg.Button('Show', bind_return_key=True), sg.Button('Exit')] ] + +window = sg.Window('Get PIP Installed Versions', layout, print_event_values=False, enable_close_attempted_event=True, auto_save_location=True) + +while True: # Event Loop + event, values = window.read() + if event == sg.WINDOW_CLOSE_ATTEMPTED_EVENT: + window.settings_save(values) + if event in (None, 'Exit'): + break + if event == 'Show': + package = values['-PACKAGE-'] + if not package: + continue + + win = window if values['-THREADED-'] else None + + out = sg.execute_pip_get_local_package_version(package, interpreter=values['-INT-'] if values['-INT-'] else None, window=win, key='-PIP VER-') + if win is None: + if not out: + out = 'Not Installed' + sg.cprint(f'Not threaded {package} ', end='', c='white on green') + sg.cprint(f'version = {out}', end='', c='white on red') + sg.cprint('') + elif event == '-PIP VER-': + out = values[event] if values[event] else 'Not Installed' + sg.cprint(f'{package} ', end='', c='white on blue') + sg.cprint(f'version = {out}', end='', c='white on red') + sg.cprint('') + +window.close() From fcfd7324d31a3acac49b0e517df729694514eac2 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 23 May 2024 12:41:23 -0400 Subject: [PATCH 044/125] Oops... forgot to break out of program if "X" clicked.... --- DemoPrograms/Demo_Pip_Installs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/DemoPrograms/Demo_Pip_Installs.py b/DemoPrograms/Demo_Pip_Installs.py index dddcc3fb8..c5a58d17b 100644 --- a/DemoPrograms/Demo_Pip_Installs.py +++ b/DemoPrograms/Demo_Pip_Installs.py @@ -25,6 +25,7 @@ event, values = window.read() if event == sg.WINDOW_CLOSE_ATTEMPTED_EVENT: window.settings_save(values) + break if event in (None, 'Exit'): break if event == 'Show': From acff2776269fc0a14424d3bba8df7dd80f9d3948 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 24 May 2024 12:40:13 -0400 Subject: [PATCH 045/125] Fixed all links to the online documenation in the built-in SDK Call Reference Window (You know about this window, right?) Type psghelp from the command line. Or from the Home Window, click the help tab and the button "SDK Reference Window" --- development_build_changelog.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index c7b357bd0..433386dc8 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -25,5 +25,8 @@ Changelog since last major release 5.0.4.18 Made priority suport code field in markdown for GitHub issue be the same level as the manual form on GitHub 5.0.4.19 Added new function execute_pip_get_local_package_version so that local version of pip installed package can be easily obtained 5.0.4.20 Changed execute_command_subprocess so that the interpreter can have spaces for things like parameters (i.e. py -3.11) -5.0.4.21 Changed the function execute_pip_get_local_package_version to use the temofile package so that there can be multiple requests outstanding at a time +5.0.4.21 Changed the function execute_pip_get_local_package_version to use + the tempfile package so that there can be multiple requests outstanding at a time +5.0.4.22 Fixed all links to the online documenation in the built-in SDK Call Reference Window (You know about this window, right?) + Type psghelp from the command line. Or from the Home Window, click the help tab and the button "SDK Reference Window" From 70629f3bb6c9d081ced3835f6842cb92b077c05d Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sat, 25 May 2024 11:37:24 -0400 Subject: [PATCH 046/125] Updated Graph Element Demo Program to use the ping3 module or simulated data --- DemoPrograms/Demo_Graph_Element.py | 93 ++++++++++++++++-------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/DemoPrograms/Demo_Graph_Element.py b/DemoPrograms/Demo_Graph_Element.py index 879e7a463..7008bf12f 100644 --- a/DemoPrograms/Demo_Graph_Element.py +++ b/DemoPrograms/Demo_Graph_Element.py @@ -1,10 +1,15 @@ #!/usr/bin/env python import PySimpleGUI as sg -import ping -from threading import Thread -import time +import random +try: + import ping3 +except: + ping3 = None + sg.popup_quick_message('WARNING! You do not have ping3 installed so data will be simulated', font='_ 18', text_color='white', background_color='red', auto_close_duration=6) """ + Use a Graph element to show ping times to a URL using a line graph + Copyright 2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. @@ -12,68 +17,72 @@ You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. """ +if ping3: + ping_url = 'google.com' +else: + ping_url = 'simulated data' -STEP_SIZE = 1 -SAMPLES = 1000 -CANVAS_SIZE = (1000, 500) - -# globale used to communicate with thread.. yea yea... it's working fine -g_exit = False -g_response_time = None - -def ping_thread(args): - global g_exit, g_response_time - while not g_exit: - g_response_time = ping.quiet_ping('google.com', timeout=1000) +def ping_thread(window: sg.Window): + while True: + if ping3: + ping_time = int(ping3.ping(ping_url) * 1000) + else: + ping_time = random.randint(0, 100) + if ping_time: + window.write_event_value('-THREAD-', ping_time) def main(): - global g_exit, g_response_time + global ping_url - # start ping measurement thread - thread = Thread(target=ping_thread, args=(None,)) - thread.start() + STEP_SIZE = 1 + SAMPLES = 100 + CANVAS_SIZE = (1000, 500) + Y_MAX = 500 + X_MAX = 500 sg.theme('Black') - sg.set_options(element_padding=(0, 0)) layout = [ - [sg.Text('Ping times to Google.com', font='Any 12'), - sg.Quit(pad=((100, 0), 0), button_color=('white', 'black'))], - [sg.Graph(CANVAS_SIZE, (0, 0), (SAMPLES, 500), - background_color='black', key='graph')] + sg.vbottom( + [sg.Column([[sg.T('Ping in MS'), sg.T(k='-TIME-', s=4)],[sg.Slider((50, Y_MAX), default_value=Y_MAX, orientation='v', size=(20, 20), k='-Y SLIDER-', expand_y=True, enable_events=True)]], expand_y=True, element_justification='r'), + sg.Column([ + [sg.Graph(CANVAS_SIZE, (0, 0), (SAMPLES, 200), background_color='black', key='-GRAPH-')], + [sg.Text('# Samples:'), sg.Slider((50, X_MAX), default_value=SAMPLES, orientation='h', size=(50, 20), k='-X SLIDER-', expand_x=True, enable_events=True)], + [sg.Text('Ping times to:'), sg.Input(ping_url, size=15, key='-URL-', readonly=not ping3, use_readonly_for_disable=True, disabled_readonly_text_color='black', disabled=not ping3), sg.B('Set', disabled=not ping3)],])], + expand_x=True, expand_y=True) ] - window = sg.Window('Canvas test', layout, - grab_anywhere=True, background_color='black', - no_titlebar=False, use_default_focus=False) + window = sg.Window('Ping Graph', layout, background_color='black', finalize=True, font='_ 16') - graph = window['graph'] - prev_response_time = None - i = 0 - prev_x, prev_y = 0, 0 + graph = window['-GRAPH-'] + + i = prev_x = prev_y = 0 + + window.start_thread(lambda : ping_thread(window)) while True: - event, values = window.read(timeout=200) + event, values = window.read() if event == 'Quit' or event == sg.WIN_CLOSED: break - if g_response_time is None or prev_response_time == g_response_time: - continue - new_x, new_y = i, g_response_time[0] - prev_response_time = g_response_time + if event == '-THREAD-': + new_x, new_y = i, values[event] + window['-TIME-'].update(values[event]) if i >= SAMPLES: graph.move(-STEP_SIZE, 0) prev_x = prev_x - STEP_SIZE graph.draw_line((prev_x, prev_y), (new_x, new_y), color='white') - # window['graph'].draw_point((new_x, new_y), color='red') prev_x, prev_y = new_x, new_y i += STEP_SIZE if i < SAMPLES else 0 - - # tell thread we're done. wait for thread to exit - g_exit = True - thread.join() - + if event == '-X SLIDER-' or event == '-Y SLIDER-': + graph.change_coordinates((0,0), (values['-X SLIDER-'], values['-Y SLIDER-'])) + graph.erase() + i = 0 + prev_x, prev_y = 0, 0 + SAMPLES = values['-X SLIDER-'] + if event == 'Set': # set a new URL to ping + ping_url = values['-URL-'] window.close() From 88c461198b9017651c6d16c546c0cd6b6c106b79 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sat, 25 May 2024 11:42:47 -0400 Subject: [PATCH 047/125] Added tiny delay when simulating --- DemoPrograms/Demo_Graph_Element.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DemoPrograms/Demo_Graph_Element.py b/DemoPrograms/Demo_Graph_Element.py index 7008bf12f..a359d4bc1 100644 --- a/DemoPrograms/Demo_Graph_Element.py +++ b/DemoPrograms/Demo_Graph_Element.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import PySimpleGUI as sg import random +import time try: import ping3 except: @@ -28,6 +29,7 @@ def ping_thread(window: sg.Window): if ping3: ping_time = int(ping3.ping(ping_url) * 1000) else: + time.sleep(.001) ping_time = random.randint(0, 100) if ping_time: window.write_event_value('-THREAD-', ping_time) From 323fa91ab75d97b97cf310f6cf5052dc7bb332bb Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sat, 25 May 2024 12:37:13 -0400 Subject: [PATCH 048/125] Added code to ask user if they want ping3 installed for them. If so, then PySimpleGUI will pip install it --- DemoPrograms/Demo_Graph_Element.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DemoPrograms/Demo_Graph_Element.py b/DemoPrograms/Demo_Graph_Element.py index a359d4bc1..bdece0705 100644 --- a/DemoPrograms/Demo_Graph_Element.py +++ b/DemoPrograms/Demo_Graph_Element.py @@ -6,7 +6,11 @@ import ping3 except: ping3 = None - sg.popup_quick_message('WARNING! You do not have ping3 installed so data will be simulated', font='_ 18', text_color='white', background_color='red', auto_close_duration=6) + if sg.popup_yes_no('This version of Python does not have the ping3 module installed. Would you like it to be installed?') == 'Yes': + sg.execute_pip_install_package('ping3') # pip install the ping3 package + sg.execute_restart(__file__) # restart this program so that it'll pick up the new ping3 installation + else: + sg.popup_quick_message('OK... Ping3 not installed so data will be simulated', font='_ 18', text_color='white', background_color='red', auto_close_duration=6) """ Use a Graph element to show ping times to a URL using a line graph From fd528d9861787513e130561383afa945611e143b Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 2 Jun 2024 12:09:56 -0400 Subject: [PATCH 049/125] Release 5.0.5 --- development_build_changelog.txt | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 433386dc8..5c0128689 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,32 +1,5 @@ Changelog since last major release -5.0.4 Released 1-Apr-2024 +5.0.5 Released 2-Jun-2024 + -5.0.4.1 Fixed open GitHub issue window so that the checklist matches the newest on GitHub -5.0.4.2 Fix for ButtonMenu not clearing the value dictionary entry after the event is returned. Used to work this way but broke along the way. -5.0.4.3 Added set_hscroll_position for elements -5.0.4.4 NEW FEATURE added - parameter repeating_timer_ms added to the Window object. Causes a repeating timer to be started. -5.0.4.5 Uncommented line of code that imports askcolor from tkinter. Not sure why it was commented out -5.0.4.6 Fix for bug that was showing trial expired in Home Window. Added new function dict_to_string to format dictionaries nicely -5.0.4.7 Changed function dict_to_string to use the pprint.pformat function rather the reinvent the wheel this time... - but at least my version did right justify keys in a field the width of the max key length. :-) - New option in global settings to control if formatted dictionary is printed when the automatic "print event values" feature is enabled -5.0.4.8 Changed how Input.update select parm works: - If set to True, then the input is selected (highlighted). The new addition is that if False, it clears the selection -5.0.4.9 Internal test -5.0.4.10 Added arrow and arrow_type to Graph.draw_line method -5.0.4.11 Made aliases for HorizontalSeparator using word "Line" (HorizontalLine, HLine, Line) -5.0.4.12 Internal test -5.0.4.13 Updated the checklist in the builtin GitHub Issue logger to use the newest checklist. Was using an old one. -5.0.4.14 Changed docstring for Table element and others that have "Select Mode" parameter to str -5.0.4.15 Added right_click_menu paramter to Combo element -5.0.4.16 Added window.right_click_menu_element. Will either be None or the element that was right clicked if right click menu used -5.0.4.17 Fix in def dict_to_string. The pformat call didn't add the sort_dict parm until Python 3.8 so need to check python ver -5.0.4.18 Made priority suport code field in markdown for GitHub issue be the same level as the manual form on GitHub -5.0.4.19 Added new function execute_pip_get_local_package_version so that local version of pip installed package can be easily obtained -5.0.4.20 Changed execute_command_subprocess so that the interpreter can have spaces for things like parameters (i.e. py -3.11) -5.0.4.21 Changed the function execute_pip_get_local_package_version to use - the tempfile package so that there can be multiple requests outstanding at a time -5.0.4.22 Fixed all links to the online documenation in the built-in SDK Call Reference Window (You know about this window, right?) - Type psghelp from the command line. Or from the Home Window, click the help tab and the button "SDK Reference Window" - From 95694a6c1d1ebae579bb099a6fa1d9b583708f7c Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:45:12 -0400 Subject: [PATCH 050/125] Made the Window member variable right_click_menu_element a property so that it'll show up in the call reference documenation --- development_build_changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 5c0128689..d4997d378 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,5 +1,5 @@ Changelog since last major release -5.0.5 Released 2-Jun-2024 +5.0.5.1 Made the Window member variable right_click_menu_element a property so that it'll show up in the call reference documenation From 3b7035d696398937bee32c3ca2415cff5ac9fd0b Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:09:34 -0400 Subject: [PATCH 051/125] Changed several docstrings that had type "enum" for parms like SELECT_MODE. They were "enum" and were changed to "str" The reason for the change - PyCharm is highlighting those parms with a warning --- development_build_changelog.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index d4997d378..be9e655f9 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,5 +1,7 @@ Changelog since last major release -5.0.5.1 Made the Window member variable right_click_menu_element a property so that it'll show up in the call reference documenation +5.0.5.1 Made the Window member variable right_click_menu_element a property so that it'll show up in the call reference documentation +5.0.5.2 Changed several docstrings that had type "enum" for parms like SELECT_MODE. They were "enum" and were changed to "str" + The reason for the change - PyCharm is highlighting those parms with a warning From da7d9dcd818e471b472963cccf9cab2d6d05fe2d Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Mon, 3 Jun 2024 10:06:34 -0400 Subject: [PATCH 052/125] Changed demo to use the PySimpleGUI supplied threading API --- .../Demo_Multithreaded_Different_Threads.py | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/DemoPrograms/Demo_Multithreaded_Different_Threads.py b/DemoPrograms/Demo_Multithreaded_Different_Threads.py index b83c68f90..b74dc5e7a 100644 --- a/DemoPrograms/Demo_Multithreaded_Different_Threads.py +++ b/DemoPrograms/Demo_Multithreaded_Different_Threads.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -import threading import time import itertools import PySimpleGUI as sg @@ -15,7 +14,7 @@ The PySimpleGUI code is structured just like a typical PySimpleGUI program. A layout defined, a Window is created, and an event loop is executed. - Copyright 2020-2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + Copyright 2020-2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. @@ -96,7 +95,7 @@ def worker_thread3(thread_name, run_freq, window): # ###### ####### #### -def the_gui(): +def main(): """ Starts and executes the GUI Reads data from a Queue and displays the data to the window @@ -108,16 +107,16 @@ def the_gui(): layout = [[sg.Text('Multithreaded Window Example')], [sg.Text('', size=(15, 1), key='-OUTPUT-')], [sg.Multiline(size=(40, 26), key='-ML-', autoscroll=True)], - [sg.Button('Exit')], ] + [sg.Push(), sg.Button('Exit')], ] window = sg.Window('Multithreaded Window', layout, finalize=True) # -- Create a Queue to communicate with GUI -- # queue used to communicate between the gui and the threads # -- Start worker threads, each taking a different amount of time - threading.Thread(target=worker_thread1, args=('Thread 1', 500, window,), daemon=True).start() - threading.Thread(target=worker_thread2, args=('Thread 2', 200, window,), daemon=True).start() - threading.Thread(target=worker_thread3, args=('Thread 3', 1000, window,), daemon=True).start() + window.start_thread(lambda: worker_thread1('Thread 1', 500, window)) + window.start_thread(lambda: worker_thread2('Thread 2', 200, window)) + window.start_thread(lambda: worker_thread3('Thread 3', 1000, window)) # -- Start the GUI passing in the Queue -- sg.cprint_set_output_destination(window, '-ML-') @@ -135,14 +134,6 @@ def the_gui(): window.close() -## ## ### #### ## ## -### ### ## ## ## ### ## -#### #### ## ## ## #### ## -## ### ## ## ## ## ## ## ## -## ## ######### ## ## #### -## ## ## ## ## ## ### -## ## ## ## #### ## ## - if __name__ == '__main__': - the_gui() + main() From 362a55512b0d9ecd3f3cb1047e735c31448e6b47 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Mon, 3 Jun 2024 10:07:12 -0400 Subject: [PATCH 053/125] New demo program showing how to communicate with thread to cause it to end --- ...Demo_Multithreaded_Signal_Thread_To_End.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 DemoPrograms/Demo_Multithreaded_Signal_Thread_To_End.py diff --git a/DemoPrograms/Demo_Multithreaded_Signal_Thread_To_End.py b/DemoPrograms/Demo_Multithreaded_Signal_Thread_To_End.py new file mode 100644 index 000000000..9b7989599 --- /dev/null +++ b/DemoPrograms/Demo_Multithreaded_Signal_Thread_To_End.py @@ -0,0 +1,86 @@ +import time +import datetime +import PySimpleGUI as sg + +""" + Multithreading with signaling to thread when to stop. + If exiting the program, waits for the thread to finish. + + In this example, the thread runs at a rate of twice a second. It sends the time as a string + The main GUI checks the value sent by the thread to see if it differs from what is displayed. + If the display is different, then the GUI is updated with the new time. + + Copyright 2020-2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + + Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. + + You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. + +""" + + +# dP dP dP +# 88 88 88 +# d8888P 88d888b. 88d888b. .d8888b. .d8888b. .d888b88 +# 88 88' `88 88' `88 88ooood8 88' `88 88' `88 +# 88 88 88 88 88. ... 88. .88 88. .88 +# dP dP dP dP `88888P' `88888P8 `88888P8 +# + +def the_thread(window): + + while window.job_running: + window.write_event_value('-THREAD-', datetime.datetime.now().strftime('%H:%M:%S')) + time.sleep(0.5) + + +# oo +# +# 88d8b.d8b. .d8888b. dP 88d888b. +# 88'`88'`88 88' `88 88 88' `88 +# 88 88 88 88. .88 88 88 88 +# dP dP dP `88888P8 dP dP dP + +def main(): + + layout = [ + [sg.Text('00:00:00', font=('Courier New', 20, 'bold'), justification='center', expand_x=True, key='-TIME-')], + [sg.Push(), sg.Button('Start'), sg.Button('Stop')]] + + window = sg.Window('Threading', layout, enable_close_attempted_event=True, + print_event_values=True, # enable to watch the events and values print out + ) + + window.job_running = False # Create a member variable to signal to the thread when to stop + exiting = False # Used when X is clicked + + while True: + + event, values = window.read() + + if event == sg.WINDOW_CLOSE_ATTEMPTED_EVENT: + if window.job_running: # if thread running, tell it to exit + window.job_running = False + exiting = True + else: + break # if thread not running then OK to exit + + if exiting and event == '-THREAD ENDED-': # If exiting and thread is finished then OK to exit + break + + elif event == 'Start': + window['Start'].update(disabled=True) + window.job_running = True + window.start_thread(lambda: the_thread(window), '-THREAD ENDED-') + + elif event == 'Stop': + window.job_running = False + window['Start'].update(disabled=False) + + elif event == '-THREAD-' and values[event] != window['-TIME-'].get(): + window['-TIME-'].update(values[event]) + + window.close() + +if __name__ == '__main__': + main() \ No newline at end of file From dbfae0ea4a560185b9589a7665b1d0e452597ebd Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 6 Jun 2024 12:11:09 -0400 Subject: [PATCH 054/125] Added better memory management by deleting drawn figures as they are moved off the screen --- DemoPrograms/Demo_Graph_Element.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/DemoPrograms/Demo_Graph_Element.py b/DemoPrograms/Demo_Graph_Element.py index bdece0705..1c1d10f0f 100644 --- a/DemoPrograms/Demo_Graph_Element.py +++ b/DemoPrograms/Demo_Graph_Element.py @@ -2,6 +2,8 @@ import PySimpleGUI as sg import random import time +import gc + try: import ping3 except: @@ -65,7 +67,7 @@ def main(): graph = window['-GRAPH-'] i = prev_x = prev_y = 0 - + fig_list = [] window.start_thread(lambda : ping_thread(window)) while True: @@ -78,12 +80,19 @@ def main(): if i >= SAMPLES: graph.move(-STEP_SIZE, 0) prev_x = prev_x - STEP_SIZE - graph.draw_line((prev_x, prev_y), (new_x, new_y), color='white') + fig = fig_list[0] + fig_list.pop(0) + graph.delete_figure(fig) + # gc.collect() # Run garbage collect. Uncomment if you want the space freed immediately + fig = graph.draw_line((prev_x, prev_y), (new_x, new_y), color='white') + fig_list.append(fig) prev_x, prev_y = new_x, new_y i += STEP_SIZE if i < SAMPLES else 0 if event == '-X SLIDER-' or event == '-Y SLIDER-': + graph.delete_figure(fig_list) graph.change_coordinates((0,0), (values['-X SLIDER-'], values['-Y SLIDER-'])) graph.erase() + fig_list = [] i = 0 prev_x, prev_y = 0, 0 SAMPLES = values['-X SLIDER-'] From bfcd52e52d601b9396602afed1d9d2147fdc77c9 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 6 Jun 2024 12:23:21 -0400 Subject: [PATCH 055/125] New Demo Program - Using a Walrus Operator in layouts --- .../Demo_Layouts_Using_Walrus_Operator.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 DemoPrograms/Demo_Layouts_Using_Walrus_Operator.py diff --git a/DemoPrograms/Demo_Layouts_Using_Walrus_Operator.py b/DemoPrograms/Demo_Layouts_Using_Walrus_Operator.py new file mode 100644 index 000000000..2d7883b9e --- /dev/null +++ b/DemoPrograms/Demo_Layouts_Using_Walrus_Operator.py @@ -0,0 +1,34 @@ +import PySimpleGUI as sg +import random + +""" + Using Python's Walrus Operator in Layouts + + Some elements you call many different memeber functions for. Rather than looking up the element by the key and storing + into a variable, you can use the walrus operator to store the element, right from the layout itself. + + Copyright 2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + + Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. + + You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. +""" + +layout = [[sg.Text('Using Walrus Operator In Layouts', font='_ 16')], + [graph_elem := sg.Graph((500, 500), (0, 0), (100, 100), key='-GRAPH-')], + [sg.Button('Draw'), sg.Button('Exit')]] + +window = sg.Window('Walrus Operator In Layouts', layout, auto_save_location=True) + +# graph_elem = window['-GRAPH-'] # This is the way elements are normally looked up and stored in a variable + +while True: + event, values = window.read() + if event == sg.WIN_CLOSED or event == 'Exit': + break + if event == 'Draw': + emoji = random.choice(sg.EMOJI_BASE64_HAPPY_LIST) + location = random.randint(0, 100), random.randint(0, 100) + graph_elem.draw_image(data=emoji, location=location) + +window.close() From 3d0642c2d7caa8b9f7cf95b14a37cf448c2ae2d8 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:39:41 -0400 Subject: [PATCH 056/125] Change to handle cx_freeze UnicodeDecodeError --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index be9e655f9..bdaaaeb81 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -3,5 +3,6 @@ Changelog since last major release 5.0.5.1 Made the Window member variable right_click_menu_element a property so that it'll show up in the call reference documentation 5.0.5.2 Changed several docstrings that had type "enum" for parms like SELECT_MODE. They were "enum" and were changed to "str" The reason for the change - PyCharm is highlighting those parms with a warning +5.0.5.3 Change to handle cx_freeze UnicodeDecodeError From c2bee2dcd329c5688d7b2f3895418a6c50bbedf9 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 27 Jun 2024 07:59:04 -0400 Subject: [PATCH 057/125] A better change to handle cx_freeze UnicodeDecodeError. As a test, will print "Frozen app detected" if successfully detected --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index bdaaaeb81..90d26f6e2 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -4,5 +4,6 @@ Changelog since last major release 5.0.5.2 Changed several docstrings that had type "enum" for parms like SELECT_MODE. They were "enum" and were changed to "str" The reason for the change - PyCharm is highlighting those parms with a warning 5.0.5.3 Change to handle cx_freeze UnicodeDecodeError +5.0.5.4 A better change to handle cx_freeze UnicodeDecodeError. As a test, will print "Frozen app detected" if successfully detected From ab5a0ba7844fc0adf80ab359ffbad97ba02c1701 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 27 Jun 2024 08:57:21 -0400 Subject: [PATCH 058/125] Removed the test print "Frozen app detected" if cx_freeze detected --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 90d26f6e2..6b9bd62fb 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -5,5 +5,6 @@ Changelog since last major release The reason for the change - PyCharm is highlighting those parms with a warning 5.0.5.3 Change to handle cx_freeze UnicodeDecodeError 5.0.5.4 A better change to handle cx_freeze UnicodeDecodeError. As a test, will print "Frozen app detected" if successfully detected +5.0.5.5 Removed the test print "Frozen app detected" if cx_freeze detected From 924588c87a4884b14f760bccf4edb713bd4fe1ff Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:14:24 -0400 Subject: [PATCH 059/125] Fix for PyInstaller uuid not found error --- development_build_changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 6b9bd62fb..77e4ab71b 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -6,5 +6,5 @@ Changelog since last major release 5.0.5.3 Change to handle cx_freeze UnicodeDecodeError 5.0.5.4 A better change to handle cx_freeze UnicodeDecodeError. As a test, will print "Frozen app detected" if successfully detected 5.0.5.5 Removed the test print "Frozen app detected" if cx_freeze detected - +5.0.5.6 Fix for PyInstaller uuid not found error From 2687c7a2f5ad84e61d8070e4dcdadde3b53d2a60 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 28 Jun 2024 14:50:06 -0400 Subject: [PATCH 060/125] Changed how hashlib is imported. Improved a couple of error messsages. --- development_build_changelog.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 77e4ab71b..f7c9b9619 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -6,5 +6,6 @@ Changelog since last major release 5.0.5.3 Change to handle cx_freeze UnicodeDecodeError 5.0.5.4 A better change to handle cx_freeze UnicodeDecodeError. As a test, will print "Frozen app detected" if successfully detected 5.0.5.5 Removed the test print "Frozen app detected" if cx_freeze detected -5.0.5.6 Fix for PyInstaller uuid not found error +5.0.5.6 Fix for PyInstaller module not found error +5.0.5.7 Changed how hashlib is imported. Improved a couple of error messsages. From 2e3d0289102a5d3f0e7211911a84607ae96f8015 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:54:04 -0400 Subject: [PATCH 061/125] 5.0.6 Released 28-Jun-2024 --- development_build_changelog.txt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index f7c9b9619..a16484813 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,11 +1,5 @@ Changelog since last major release -5.0.5.1 Made the Window member variable right_click_menu_element a property so that it'll show up in the call reference documentation -5.0.5.2 Changed several docstrings that had type "enum" for parms like SELECT_MODE. They were "enum" and were changed to "str" - The reason for the change - PyCharm is highlighting those parms with a warning -5.0.5.3 Change to handle cx_freeze UnicodeDecodeError -5.0.5.4 A better change to handle cx_freeze UnicodeDecodeError. As a test, will print "Frozen app detected" if successfully detected -5.0.5.5 Removed the test print "Frozen app detected" if cx_freeze detected -5.0.5.6 Fix for PyInstaller module not found error -5.0.5.7 Changed how hashlib is imported. Improved a couple of error messsages. +5.0.6 Released 28-Jun-2024 + From 10c15dbb8833a77403d40756ba80f9473c6487dc Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 29 Jun 2024 15:49:13 -0400 Subject: [PATCH 062/125] New Table feature - Editing individual table values using double-click/return key New Table feature - values property returns the table's current values --- development_build_changelog.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index a16484813..98b011c33 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,5 +1,6 @@ Changelog since last major release 5.0.6 Released 28-Jun-2024 - +5.0.6.1 New Table feature - Editing individual table values using double-click/return key + New Table feature - values property returns the table's current values From 231720943fc9ab5c49d7d1d6932dd4875a8c649f Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sat, 29 Jun 2024 16:04:50 -0400 Subject: [PATCH 063/125] Added support for the new Table Element parameter enable_cell_editing --- ...emo_Table_Element_Header_or_Cell_Clicks.py | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/DemoPrograms/Demo_Table_Element_Header_or_Cell_Clicks.py b/DemoPrograms/Demo_Table_Element_Header_or_Cell_Clicks.py index cac64fd3f..ae9fb439c 100644 --- a/DemoPrograms/Demo_Table_Element_Header_or_Cell_Clicks.py +++ b/DemoPrograms/Demo_Table_Element_Header_or_Cell_Clicks.py @@ -5,30 +5,33 @@ import operator """ - Table Element Demo With Sorting + Table Element Demo With Sorting and Cell Editing The data for the table is assumed to have HEADERS across the first row. This is often the case for CSV files or spreadsheets - In release 4.48.0 a new enable_click_events parameter was added to the Table Element - This enables you to click on Column Headers and individual cells as well as the standard Row selection - This demo shows how you can use these click events to sort your table by columns + Additionally, in release 5.0.6.1 individual cell editing was added to the Table element using the parameter enable_cell_editing + + Copyright 2022-2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. - Copyright 2022-2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved. - Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. - + You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. """ sg.theme('Light green 6') + + # ------ Some functions to help generate data for the table ------ def word(): return ''.join(random.choice(string.ascii_lowercase) for i in range(10)) + + def number(max_val=1000): return random.randint(0, max_val) + def make_table(num_rows, num_cols): data = [[j for j in range(num_cols)] for i in range(num_rows)] data[0] = [word() for _ in range(num_cols)] @@ -36,10 +39,12 @@ def make_table(num_rows, num_cols): data[i] = [i, word(), *[number() for i in range(num_cols - 2)]] return data + # ------ Make the Table Data ------ data = make_table(num_rows=15, num_cols=6) # headings = [str(data[0][x])+' ..' for x in range(len(data[0]))] -headings = [f'Col {col}' for col in range(1,len(data[0])+1)] +headings = [f'Col {col}' for col in range(1, len(data[0]) + 1)] + def sort_table(table, cols): """ sort a table by multiple columns @@ -55,6 +60,7 @@ def sort_table(table, cols): sg.popup_error('Error in sort_table', 'Exception in sort_table', e) return table + # ------ Window Layout ------ layout = [[sg.Table(values=data[1:][:], headings=headings + ['Extra'], max_col_width=25, auto_size_columns=True, @@ -68,7 +74,8 @@ def sort_table(table, cols): enable_events=True, expand_x=True, expand_y=True, - enable_click_events=True, # Comment out to not enable header and other clicks + enable_click_events=True, # Comment out to not enable header and other clicks + enable_cell_editing=True, tooltip='This is a table')], [sg.Button('Read'), sg.Button('Double'), sg.Button('Change Colors')], [sg.Text('Cell clicked:'), sg.T(k='-CLICKED-')], @@ -81,8 +88,6 @@ def sort_table(table, cols): # ttk_theme='clam', resizable=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, finalize=True) -# Add the ability to double-click a cell -window["-TABLE-"].bind('' , "+-double click-") # ------ Event Loop ------ while True: @@ -94,22 +99,19 @@ def sort_table(table, cols): sg.execute_editor(__file__) elif event == 'Version': sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True) + if event == 'Read': + [print(row) for row in window['-TABLE-'].values] if event == 'Double': for i in range(1, len(data)): data.append(data[i]) window['-TABLE-'].update(values=data[1:][:]) elif event == 'Change Colors': window['-TABLE-'].update(row_colors=((8, 'white', 'red'), (9, 'green'))) - elif event == '-TABLE-+-double click-': - print('Last cell clicked was', window['-TABLE-'].get_last_clicked_position()) - if isinstance(event, tuple): - # TABLE CLICKED Event has value in format ('-TABLE=', '+CLICKED+', (row,col)) - # You can also call Table.get_last_clicked_position to get the cell clicked - if event[0] == '-TABLE-': - if event[2][0] == -1 and event[2][1] != -1: # Header was clicked and wasn't the "row" column - col_num_clicked = event[2][1] - new_table = sort_table(data[1:][:],(col_num_clicked, 0)) - window['-TABLE-'].update(new_table) - data = [data[0]] + new_table - window['-CLICKED-'].update(f'{event[2][0]},{event[2][1]}') + if event[0] == '-TABLE-': + if event[2][0] == -1 and event[2][1] != -1: # Header was clicked and wasn't the "row" column + col_num_clicked = event[2][1] + new_table = sort_table(data[1:][:], (col_num_clicked, 0)) + window['-TABLE-'].update(new_table) + data = [data[0]] + new_table + window['-CLICKED-'].update(f'{event[2][0]},{event[2][1]}') window.close() From 832973c3165b4b6d492dc118ad5200c99d7b738c Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 29 Jun 2024 17:25:08 -0400 Subject: [PATCH 064/125] New Table feature - Pressing Escape Key during cell edit will abort the edit and restore value --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 98b011c33..c8b56dbe4 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -3,4 +3,5 @@ Changelog since last major release 5.0.6 Released 28-Jun-2024 5.0.6.1 New Table feature - Editing individual table values using double-click/return key New Table feature - values property returns the table's current values +5.0.6.2 New Table feature - Pressing Escape Key during cell edit will abort the edit and restore value From d056a81aea04d7bf79267f9222ff18a0e5206aac Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 30 Jun 2024 05:17:56 -0400 Subject: [PATCH 065/125] New Vertical Separator alias added to match the Horizontal ones added earlier. VLine, VerticalLine --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index c8b56dbe4..ec768c5af 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -4,4 +4,5 @@ Changelog since last major release 5.0.6.1 New Table feature - Editing individual table values using double-click/return key New Table feature - values property returns the table's current values 5.0.6.2 New Table feature - Pressing Escape Key during cell edit will abort the edit and restore value +5.0.6.3 New Vertical Separator alias added to match the Horizontal ones added earlier. VLine, VerticalLine From d1282eb3eacc1e529e77b39d6ece238d7a1c50f4 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 30 Jun 2024 05:37:53 -0400 Subject: [PATCH 066/125] New Table feature - If user double-clicks and modifies a cell in a table, then an event with a tuple is generated that is similar to the Clicked tuple. The constant TABLE_EDITED_INDICATOR will be part of tuple --- development_build_changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index ec768c5af..96e596577 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -5,4 +5,6 @@ Changelog since last major release New Table feature - values property returns the table's current values 5.0.6.2 New Table feature - Pressing Escape Key during cell edit will abort the edit and restore value 5.0.6.3 New Vertical Separator alias added to match the Horizontal ones added earlier. VLine, VerticalLine +5.0.6.4 New Table feature - If user double-clicks and modifies a cell in a table, then an event with a tuple + is generated that is similar to the Clicked tuple. The constant TABLE_EDITED_INDICATOR will be part of tuple From 7749c1be4f6d5d685ce83bd9cd9115af2306540c Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 30 Jun 2024 14:40:49 -0400 Subject: [PATCH 067/125] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 90c74b589..84409c58d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ + ##

From ad36d776098511bf9c4a861d5f84833db94c75fa Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 30 Jun 2024 14:42:56 -0400 Subject: [PATCH 068/125] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 84409c58d..42c869e60 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@

- +
For more information visit PySimpleGUI.com

- - ##

From c9ecdcfb1af9addacf0795380df5dff342ee24c1 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Tue, 2 Jul 2024 04:45:01 -0400 Subject: [PATCH 069/125] Table Element - Finishing up the cell editing by adding more parms to the Table element for edit colors and select color cell_edit_colors controls colors of cells during editin cell_edit_select_colors controls colors of the sections during editing --- development_build_changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 96e596577..1a52c3f08 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -7,4 +7,7 @@ Changelog since last major release 5.0.6.3 New Vertical Separator alias added to match the Horizontal ones added earlier. VLine, VerticalLine 5.0.6.4 New Table feature - If user double-clicks and modifies a cell in a table, then an event with a tuple is generated that is similar to the Clicked tuple. The constant TABLE_EDITED_INDICATOR will be part of tuple +5.0.6.5 Table Element - Finishing up the cell editing by adding more parms to the Table element for edit colors and select color + cell_edit_colors controls colors of cells during editing + cell_edit_select_colors controls colors of the sections during editing From 979d4082ec39dd009d4902b0be8b0a55e74c331e Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 2 Jul 2024 08:43:37 -0400 Subject: [PATCH 070/125] Updated Table Element Demo Program to include new Editing features --- ...emo_Table_Element_Header_or_Cell_Clicks.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/DemoPrograms/Demo_Table_Element_Header_or_Cell_Clicks.py b/DemoPrograms/Demo_Table_Element_Header_or_Cell_Clicks.py index ae9fb439c..ef5061c57 100644 --- a/DemoPrograms/Demo_Table_Element_Header_or_Cell_Clicks.py +++ b/DemoPrograms/Demo_Table_Element_Header_or_Cell_Clicks.py @@ -6,12 +6,12 @@ """ Table Element Demo With Sorting and Cell Editing - + NOTE: release 5.0.6.5 needed in order to use the Cell Editing features. Comment out the parameters that contain cell_edit to remove from demo + The data for the table is assumed to have HEADERS across the first row. This is often the case for CSV files or spreadsheets This demo shows how you can use these click events to sort your table by columns - Additionally, in release 5.0.6.1 individual cell editing was added to the Table element using the parameter enable_cell_editing Copyright 2022-2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. @@ -75,7 +75,9 @@ def sort_table(table, cols): expand_x=True, expand_y=True, enable_click_events=True, # Comment out to not enable header and other clicks - enable_cell_editing=True, + enable_cell_editing=True, # Comment out to if your PSG version does not support cell edint + cell_edit_colors='white on blue', # Comment out to if your PSG version does not support cell edint + cell_edit_select_colors='yellow on red', # Comment out to if your PSG version does not support cell edint tooltip='This is a table')], [sg.Button('Read'), sg.Button('Double'), sg.Button('Change Colors')], [sg.Text('Cell clicked:'), sg.T(k='-CLICKED-')], @@ -84,15 +86,12 @@ def sort_table(table, cols): [sg.Text('Change Colors = Changes the colors of rows 8 and 9'), sg.Sizegrip()]] # ------ Create Window ------ -window = sg.Window('The Table Element', layout, - # ttk_theme='clam', - resizable=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, finalize=True) +window = sg.Window('The Table Element', layout, resizable=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, finalize=True, print_event_values=True) # ------ Event Loop ------ while True: event, values = window.read() - print(event, values) if event == sg.WIN_CLOSED or event == 'Exit': break if event == 'Edit Me': @@ -103,11 +102,13 @@ def sort_table(table, cols): [print(row) for row in window['-TABLE-'].values] if event == 'Double': for i in range(1, len(data)): - data.append(data[i]) + data.append(data[i].copy()) window['-TABLE-'].update(values=data[1:][:]) elif event == 'Change Colors': window['-TABLE-'].update(row_colors=((8, 'white', 'red'), (9, 'green'))) - if event[0] == '-TABLE-': + + # See if was a table clicked or edited event by checking event[0] for table's key + if event[0] == '-TABLE-': # TABLE CELL Event has value in format ('-TABLE=', '+type of event+', (row,col)) if event[2][0] == -1 and event[2][1] != -1: # Header was clicked and wasn't the "row" column col_num_clicked = event[2][1] new_table = sort_table(data[1:][:], (col_num_clicked, 0)) From 28437e7b9c2882fca55a666bcd911de22fc1919a Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Wed, 3 Jul 2024 11:08:50 -0400 Subject: [PATCH 071/125] Menu Element - Added generating an event when a top level menu item has no submenu. NOTE this is not a normal Windows behavior. --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 1a52c3f08..562efe921 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -10,4 +10,5 @@ Changelog since last major release 5.0.6.5 Table Element - Finishing up the cell editing by adding more parms to the Table element for edit colors and select color cell_edit_colors controls colors of cells during editing cell_edit_select_colors controls colors of the sections during editing +5.0.6.6 Menu Element - Added generating an event when a top level menu item has no submenu. NOTE this is not a normal Windows behavior. From dc744ec26c5d96be85fc0d80219304460d914aae Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 4 Jul 2024 08:20:05 -0400 Subject: [PATCH 072/125] dict_to_string changed so width used with pformat is defined by constant DEFAULT_DICT_TO_STRING_WIDTH which is 80. Made retroactive by changing the key in the global settings. Old default was 1. This function is used when print_event_values=True when making the window. --- development_build_changelog.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 562efe921..d9825028e 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -11,4 +11,6 @@ Changelog since last major release cell_edit_colors controls colors of cells during editing cell_edit_select_colors controls colors of the sections during editing 5.0.6.6 Menu Element - Added generating an event when a top level menu item has no submenu. NOTE this is not a normal Windows behavior. - +5.0.6.7 dict_to_string changed so width used with pformat is defined by constant DEFAULT_DICT_TO_STRING_WIDTH which is 80. + Made retroactive by changing the key in the global settings. Old default was 1. + This function is used when print_event_values=True when making the window. From 57664214103e458197dae60c1514e64a524deef2 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 4 Jul 2024 09:30:24 -0400 Subject: [PATCH 073/125] Menu Element - Added support for using a key with a top-level menu item since these can now be events --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index d9825028e..e2b0b1e2a 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -14,3 +14,4 @@ Changelog since last major release 5.0.6.7 dict_to_string changed so width used with pformat is defined by constant DEFAULT_DICT_TO_STRING_WIDTH which is 80. Made retroactive by changing the key in the global settings. Old default was 1. This function is used when print_event_values=True when making the window. +5.0.6.8 Menu Element - Added support for using a key with a top-level menu item since these can now be events From 0b91c70312c8027d09cc10f0e770b0f140c086c5 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 4 Jul 2024 15:14:26 -0400 Subject: [PATCH 074/125] Fix for incorrectly calculating row when checking for type when Table editing --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index e2b0b1e2a..d51369c19 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -15,3 +15,4 @@ Changelog since last major release Made retroactive by changing the key in the global settings. Old default was 1. This function is used when print_event_values=True when making the window. 5.0.6.8 Menu Element - Added support for using a key with a top-level menu item since these can now be events +5.0.6.9 Fix for incorrectly calculating row when checking for type when Table editing From 69dc95da54917a8ebd714ad49541032c615f6ce3 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sat, 6 Jul 2024 09:35:00 -0400 Subject: [PATCH 075/125] Set the header for the folder and filenames --- DemoPrograms/Demo_Tree_Element.py | 1 + 1 file changed, 1 insertion(+) diff --git a/DemoPrograms/Demo_Tree_Element.py b/DemoPrograms/Demo_Tree_Element.py index 27473c604..85532cf6f 100644 --- a/DemoPrograms/Demo_Tree_Element.py +++ b/DemoPrograms/Demo_Tree_Element.py @@ -50,6 +50,7 @@ def add_files_in_folder(parent, dirname): select_mode=sg.TABLE_SELECT_MODE_EXTENDED, num_rows=20, col0_width=40, + col0_heading='Files & Folders', key='-TREE-', show_expanded=False, enable_events=True, From 817dfbfdcd1cd7f185c552e8319c89dff8032a10 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 16 Jul 2024 05:32:22 -0400 Subject: [PATCH 076/125] Added note that PSG4 no longer supported --- ...-must-fill-in-this-form-with-every-new-issue-submitted.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md index 99f849691..fa838ba93 100644 --- a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md +++ b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md @@ -22,7 +22,7 @@ assignees: '' ---------------------------------------- -## Versions +## Versions (NOTE - PSG4 is no longer supported) Version information can be obtained by calling `sg.main_get_debug_data()` Or you can print each version shown in () @@ -35,14 +35,13 @@ Or you can print each version shown in () #### PySimpleGUI Version (`sg.__version__`) - #### GUI Version (tkinter (`sg.tclversion_detailed`), PySide2, WxPython, Remi) #### GUI Version (tkinter (`sg.tclversion_detailed`), PySide2, WxPython, Remi) -### Priority Support Code (Commercial Users) +### Priority Support Code (Commercial License Users) From 3a7128e4e861a937dc82e52089ce43164f7913ac Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Tue, 16 Jul 2024 05:34:55 -0400 Subject: [PATCH 077/125] Update issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md --- ...m---must-fill-in-this-form-with-every-new-issue-submitted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md index fa838ba93..d227e544a 100644 --- a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md +++ b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md @@ -1,5 +1,5 @@ --- -name: Issue Form - **Must fill in this form** with every new issue submitted. +name: Issue Form - **Must fill in this form** with every new issue submitted. PSG4 is no longer supported on this GitHub. about: This form contains the information needed to help you solve your problem title: "[ Enhancement/Bug/Question] NOTE - you can also call sg.main() or sg.main_open_github_issue() to post an issue" labels: '' From 5d9736007b7743762ee747e705abc6befc3e15f2 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 25 Jul 2024 12:38:51 -0400 Subject: [PATCH 078/125] New Demo Program - Smart desktop icon --- DemoPrograms/Demo_Smart_Desktop_Icon.pyw | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 DemoPrograms/Demo_Smart_Desktop_Icon.pyw diff --git a/DemoPrograms/Demo_Smart_Desktop_Icon.pyw b/DemoPrograms/Demo_Smart_Desktop_Icon.pyw new file mode 100644 index 000000000..5e3b9c49f --- /dev/null +++ b/DemoPrograms/Demo_Smart_Desktop_Icon.pyw @@ -0,0 +1,49 @@ +""" + +Creates what appears to be an icon on your desktop, but is in reality a PySimpleGUI program. + +Copyright 2024 PySimpleSoft, Inc. and/or its licensors. All rights reserved. + +Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com. + +You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant to the PySimpleGUI License Agreement. +""" + + + +import PySimpleGUI as sg + +def main(): + icon=sg.EMOJI_BASE64_COOL + + #------- GUI definition & setup --------# + + sg.theme('black') + + + layout = [[sg.Image(source=icon, key='-IMAGE-', p=0, enable_events=True)]] + + window = sg.Window('Desktop Icon Demo', layout, element_justification='center', finalize=True, resizable=True, no_titlebar=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, margins=(0,0), grab_anywhere=True, auto_save_location=True) + + window['-IMAGE-'].bind('', '+DOUBLE_CLICK+') + + #------------ The Event Loop ------------# + while True: + event, values = window.read() + if event == sg.WIN_CLOSED or event == 'Exit': + break + # Add your double-click action here... such as launching another program + if event == '-IMAGE-+DOUBLE_CLICK+': + sg.popup_quick_message('Double Clicked', location=window.current_location(), font='_ 20', background_color='red', text_color='white') + # Example of launch explorer when the icon is double-clicked. Add change to your own program. + sg.execute_command_subprocess('explorer', wait=False) + elif event == 'Version': + sg.popup_scrolled(sg.get_versions(), f'This Program: {__file__}' ,keep_on_top=True, non_blocking=True, location=window.current_location()) + elif event == 'Edit Me': + sg.execute_editor(__file__) + + window.close() + +if __name__ == '__main__': + + main() \ No newline at end of file From 313bfd0f5dc39666594fa836a0150c76f21dcfc3 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 25 Jul 2024 14:37:35 -0400 Subject: [PATCH 079/125] Cleaned up a bit with constants, added rotating icon --- DemoPrograms/Demo_Smart_Desktop_Icon.pyw | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/DemoPrograms/Demo_Smart_Desktop_Icon.pyw b/DemoPrograms/Demo_Smart_Desktop_Icon.pyw index 5e3b9c49f..380b6890f 100644 --- a/DemoPrograms/Demo_Smart_Desktop_Icon.pyw +++ b/DemoPrograms/Demo_Smart_Desktop_Icon.pyw @@ -12,8 +12,14 @@ You may not redistribute, modify or otherwise use PySimpleGUI or its contents ex import PySimpleGUI as sg +import random def main(): + + PROGRAM_TO_LAUNCH_WHEN_DOUBLE_CLICKED = 'explorer' # This will be run when icon is double-clicked. Change to any program you want. + + # Set to your own custom icon. This is dislayed on the desktop + # For fun, the icon is changed every 5 minutes to a random PSG Emoji icon=sg.EMOJI_BASE64_COOL #------- GUI definition & setup --------# @@ -27,6 +33,8 @@ def main(): window['-IMAGE-'].bind('', '+DOUBLE_CLICK+') + window.timer_start(5*60*1000) # every 5 minutes, change the icon (totally optional... just for fun) + #------------ The Event Loop ------------# while True: event, values = window.read() @@ -35,8 +43,10 @@ def main(): # Add your double-click action here... such as launching another program if event == '-IMAGE-+DOUBLE_CLICK+': sg.popup_quick_message('Double Clicked', location=window.current_location(), font='_ 20', background_color='red', text_color='white') - # Example of launch explorer when the icon is double-clicked. Add change to your own program. - sg.execute_command_subprocess('explorer', wait=False) + # Example of launch when the icon is double-clicked. Of course you can do some other action than launching a program + sg.execute_command_subprocess(PROGRAM_TO_LAUNCH_WHEN_DOUBLE_CLICKED, wait=False) + elif event == sg.TIMER_KEY: # Change the icon shown every TIMER event + window['-IMAGE-'].update(random.choice(sg.EMOJI_BASE64_HAPPY_LIST)) elif event == 'Version': sg.popup_scrolled(sg.get_versions(), f'This Program: {__file__}' ,keep_on_top=True, non_blocking=True, location=window.current_location()) elif event == 'Edit Me': From dc8715965a7759c3d5f3397d89258f422f202868 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 27 Jul 2024 02:13:23 -0400 Subject: [PATCH 080/125] Test fix for Y-scrolling of columns that have other scrollable elements in them --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index d51369c19..12d0cd0dc 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -16,3 +16,4 @@ Changelog since last major release This function is used when print_event_values=True when making the window. 5.0.6.8 Menu Element - Added support for using a key with a top-level menu item since these can now be events 5.0.6.9 Fix for incorrectly calculating row when checking for type when Table editing +5.0.6.10 Test fix for Y-scrolling of columns that have other scrollable elements in them From 5194be0695317161327937cf480cf9a779245a88 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 27 Jul 2024 12:50:07 -0400 Subject: [PATCH 081/125] More changes to the scrollable Column to try and fix the Y-scrolling issue --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 12d0cd0dc..3a3943830 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -17,3 +17,4 @@ Changelog since last major release 5.0.6.8 Menu Element - Added support for using a key with a top-level menu item since these can now be events 5.0.6.9 Fix for incorrectly calculating row when checking for type when Table editing 5.0.6.10 Test fix for Y-scrolling of columns that have other scrollable elements in them +5.0.6.11 More changes to the scrollable Column to try and fix the Y-scrolling issue From d6ade196efd3528d11871821a46a7296be63ab13 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 27 Jul 2024 14:20:24 -0400 Subject: [PATCH 082/125] Applied above changes to the X-scrollling of columns (both using mouse wheel and scrollbars) --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 3a3943830..5e32567d7 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -18,3 +18,4 @@ Changelog since last major release 5.0.6.9 Fix for incorrectly calculating row when checking for type when Table editing 5.0.6.10 Test fix for Y-scrolling of columns that have other scrollable elements in them 5.0.6.11 More changes to the scrollable Column to try and fix the Y-scrolling issue +5.0.6.12 Applied above changes to the X-scrollling of columns (both using mouse wheel and scrollbars) From 7940672d9676dc2e4966159245ada7e376051946 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sun, 28 Jul 2024 08:35:53 -0400 Subject: [PATCH 083/125] Changed Udemy link to include the latest coupon --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42c869e60..57a968f20 100644 --- a/README.md +++ b/README.md @@ -95,4 +95,4 @@ PySimpleGUI provides extensive documentation. Here are some starting points, dep * [Updated Documentation](https://docs.PySimpleGUI.com) - Everything you need to know about the latest and best PySimpleGUI * [Cookbook](https://cookbook.PySimpleGUI.com) - Hundreds of basic PySimpleGUI examples. Find a starting point that is close to what you need. * [Call Reference](https://cookbook.PySimpleGUI.com) - Just the facts, Ma'am -* [Udemy Course](https://udemy.PySimpleGUI.com) - Become a PySimpleGUI expert in no time. Bundled with Commercial Developer License. +* [Udemy Course](https://www.udemy.com/course/pysimplegui/?couponCode=2B7B3757CA48ACB49EBF) - Become a PySimpleGUI expert in no time. Bundled with Commercial Developer License. From 0c69245a63aa869fc1018a58fc29a1cde288d912 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Wed, 14 Aug 2024 08:51:19 -0400 Subject: [PATCH 084/125] Fixed erronous import error (when import line started with "from") Added a new "Path" input so that an arbitrary file can be executed easily (or edited) --- ...rowser_START_HERE_Demo_Programs_Browser.py | 80 +++++++++++++++---- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/DemoPrograms/Browser_START_HERE_Demo_Programs_Browser.py b/DemoPrograms/Browser_START_HERE_Demo_Programs_Browser.py index 329a6ec12..76bd93ff7 100644 --- a/DemoPrograms/Browser_START_HERE_Demo_Programs_Browser.py +++ b/DemoPrograms/Browser_START_HERE_Demo_Programs_Browser.py @@ -14,7 +14,7 @@ import warnings import PySimpleGUI as sg -version = '5.0.0' +version = '5.2.0' __version__ = version.split()[0] @@ -52,7 +52,9 @@ Versions: 5.0.0 11-Feb-2024 The NEW Demo Browser for use with PySimpleGUI 5! - + 5.1.0 08-Apr-2024 Several new Demo Programs, updated Matplotlib ping demo, license ver 1.1 + 5.2.0 14-Aug-2024 Fixed erronous import error (when import line started with "from") + Added a new "Path" input so that an arbitrary file can be executed easily (or edited) Copyright 2021, 2022, 2023, 2024 PySimpleSoft Inc. """ @@ -114,6 +116,18 @@ def get_file_list(): return sorted(list(get_file_list_dict().keys())) +def get_file_list_full_filename(): + """ + Returns list of filenames of files to display + No path is shown, only the short filename + + :return: List of filenames + :rtype: List[str] + """ + return sorted(list(get_file_list_dict().values())) + + + def get_demo_path(): """ Get the top-level folder path @@ -310,7 +324,7 @@ def check_imports_in_file(filename): # Check if the module exists if not check_modules_on_import_line(sline): all_passed = False - elif sline.startswith('from'): + elif sline.startswith('from') and 'import' in sline: module = re.search(r'from (\w+)', sline).group(1) if not check_module(module): all_passed = False @@ -336,6 +350,25 @@ def check_imports_in_file(filename): MMMMMMMMMMM ''' + + +# def search_files(file_list, search_string): +# found_list = [] +# for file in file_list: +# with open(file, 'r') as f: +# # Memory-map the file +# try: +# mmapped_file = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) +# except: +# continue +# # Search for the string in the file +# if mmapped_file.find(bytes(search_string, 'utf-8')) != -1: +# # print(f"String found in file: {file}") +# found_list.append(os.path.basename(file)) +# # else: +# # print(f"String not found in file: {file}") +# return found_list + def find_in_file(string, demo_files_dict, regex=False, verbose=False, window=None, ignore_case=True, show_first_match=True): """ Search through the demo files for a string. @@ -645,19 +678,21 @@ def make_window(): find_tooltip = "Find in file\nEnter a string in box to search for string inside of the files.\nFile list will update with list of files string found inside." filter_tooltip = "Filter files\nEnter a string in box to narrow down the list of files.\nFile list will update with list of files with string in filename." find_re_tooltip = "Find in file using Regular Expression\nEnter a string in box to search for string inside of the files.\nSearch is performed after clicking the FindRE button." + run_tooltip = "Run any python file\nEnter full absolute path and then click RUN button." left_col = sg.Column([ [sg.Listbox(values=get_file_list(), select_mode=sg.SELECT_MODE_EXTENDED, size=(50,20), bind_return_key=True, key='-DEMO LIST-', expand_x=True, expand_y=True)], - [sg.Text('Filter (F1):', tooltip=filter_tooltip), sg.Input(size=(25, 1), focus=True, enable_events=True, key='-FILTER-', tooltip=filter_tooltip), + [sg.Text('Filter (F1):', tooltip=filter_tooltip, s=8), sg.Input(size=(25, 1), focus=True, enable_events=True, key='-FILTER-', tooltip=filter_tooltip), sg.T(size=(15,1), k='-FILTER NUMBER-')], [sg.Button('Run'), sg.B('Edit'), sg.B('Clear'), sg.B('Open Folder'), sg.B('Copy Path')], - [sg.Text('Find (F2):', tooltip=find_tooltip), sg.Input(size=(25, 1), enable_events=True, key='-FIND-', tooltip=find_tooltip), + [sg.Text('Find (F2):', tooltip=find_tooltip, s=8), sg.Input(size=(25, 1), enable_events=True, key='-FIND-', tooltip=find_tooltip), sg.T(size=(15,1), k='-FIND NUMBER-')], + [sg.Text('Path (F3):', tooltip=run_tooltip, s=8), sg.Input(size=(25, 1), enable_events=True, key='-RUN PATH-', tooltip=run_tooltip)], ], element_justification='l', expand_x=True, expand_y=True) lef_col_find_re = sg.pin(sg.Col([ - [sg.Text('Find (F3):', tooltip=find_re_tooltip), sg.Input(size=(25, 1),key='-FIND RE-', tooltip=find_re_tooltip),sg.B('Find RE')]], k='-RE COL-')) + [sg.Text('Find (F4):', tooltip=find_re_tooltip, s=8), sg.Input(size=(25, 1),key='-FIND RE-', tooltip=find_re_tooltip),sg.B('Find RE')]], k='-RE COL-')) right_col = [ [sg.Multiline(size=(70, 21), write_only=True, expand_x=True, expand_y=True, key=ML_KEY, reroute_stdout=True, echo_stdout_stderr=True, reroute_cprint=True)], @@ -687,10 +722,7 @@ def make_window(): [options_at_bottom, sg.Sizegrip()]] # --------------------------------- Create Window --------------------------------- - # TODO Uncomment when deploy PSG5 - # window = sg.Window('PSG Demo & Project Browser', layout, finalize=True, resizable=True, use_default_focus=False, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, auto_save_location=True) - # TODO Remove when deploy PSG5 - window = sg.Window('PSG Demo & Project Browser', layout, finalize=True, resizable=True, use_default_focus=False, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT) + window = sg.Window('PSG Demo & Project Browser', layout, finalize=True, resizable=True, use_default_focus=False, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, auto_save_location=True) window.set_min_size(window.size) @@ -698,7 +730,8 @@ def make_window(): window.bind('', '-FOCUS FILTER-') window.bind('', '-FOCUS FIND-') - window.bind('', '-FOCUS RE FIND-') + window.bind('', '-FOCUS RUN PATH-') + window.bind('', '-FOCUS RE FIND-') if not advanced_mode(): window['-FOLDER CHOOSE-'].update(visible=False) window['-RE COL-'].update(visible=False) @@ -906,9 +939,16 @@ def main(): elif event == 'Run': sg.cprint('Running....', c='white on green', end='') sg.cprint('') - - for file in values['-DEMO LIST-']: - file_to_run = str(file_list_dict[file]) + if values['-RUN PATH-']: # if a manual file was entered: + files_to_run = (values['-RUN PATH-'],) + else: + files_to_run = values['-DEMO LIST-'] + # for file in values['-DEMO LIST-']: + for file in files_to_run: + try: + file_to_run = str(file_list_dict[file]) + except: + file_to_run = file # sg.cprint('Checking Imports....', c='white on green') if sg.user_settings_get_entry('-check imports-', False) and not check_imports_in_file(file_to_run): sg.cprint(f'The demo program {os.path.basename(file_to_run)} depends on modules that are not installed.') @@ -949,7 +989,13 @@ def main(): window['-FILTER-'].set_focus() elif event == '-FOCUS RE FIND-': window['-FIND RE-'].set_focus() + elif event == '-FOCUS RUN PATH-': + window['-RUN PATH-'].set_focus() elif event == '-FIND-' or event == '-FIRST MATCH ONLY-' or event == '-VERBOSE-' or event == '-FIND RE-': + # file_list = (search_files(get_file_list_full_filename(), values['-FIND-'])) + # window['-DEMO LIST-'].update(file_list) + # continue + is_ignore_case = values['-IGNORE CASE-'] old_ignore_case = False current_typed_value = str(values['-FIND-']) @@ -975,6 +1021,7 @@ def main(): window['-FILTER NUMBER-'].update('') window['-FIND RE-'].update('') window['-FILTER-'].update('') + window['-RUN PATH-'].update('') elif values['-FIND RE-']: window['-ML-'].update('') file_list = find_in_file(values['-FIND RE-'], get_file_list_dict(), regex=True, verbose=values['-VERBOSE-'],window=window) @@ -983,6 +1030,7 @@ def main(): window['-FILTER NUMBER-'].update('') window['-FIND-'].update('') window['-FILTER-'].update('') + window['-RUN PATH-'].update('') elif event == 'Find RE': window['-ML-'].update('') file_list = find_in_file(values['-FIND RE-'], get_file_list_dict(), regex=True, verbose=values['-VERBOSE-'],window=window) @@ -991,6 +1039,7 @@ def main(): window['-FILTER NUMBER-'].update('') window['-FIND-'].update('') window['-FILTER-'].update('') + window['-RUN PATH-'].update('') sg.cprint('Regular expression find completed') elif event == 'Settings': if settings_window() is True: @@ -1007,6 +1056,7 @@ def main(): window['-DEMO LIST-'].update(file_list) window['-FIND NUMBER-'].update('') window['-FIND RE-'].update('') + window['-RUN PATH-'].update('') window['-ML-'].update('') elif event == '-FOLDERNAME-': sg.user_settings_set_entry('-demos folder-', values['-FOLDERNAME-']) @@ -1019,6 +1069,7 @@ def main(): window['-FIND-'].update('') window['-FIND RE-'].update('') window['-FILTER-'].update('') + window['-RUN PATH-'].update('') elif event == 'Open Folder': explorer_program = get_explorer() if explorer_program: @@ -1054,6 +1105,7 @@ def main(): window['-FIND-'].update('') window['-FIND RE-'].update('') window['-FILTER-'].update('') + window['-RUN PATH-'].update('') window.close() From 665cd7c8a35722d9f3d18b17d8dd5a79ea73695b Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Thu, 15 Aug 2024 05:02:43 -0400 Subject: [PATCH 085/125] Demo Browser - One last change for the new path input... clear other fields if chars are entered --- .../Browser_START_HERE_Demo_Programs_Browser.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/DemoPrograms/Browser_START_HERE_Demo_Programs_Browser.py b/DemoPrograms/Browser_START_HERE_Demo_Programs_Browser.py index 76bd93ff7..00b41d515 100644 --- a/DemoPrograms/Browser_START_HERE_Demo_Programs_Browser.py +++ b/DemoPrograms/Browser_START_HERE_Demo_Programs_Browser.py @@ -14,7 +14,7 @@ import warnings import PySimpleGUI as sg -version = '5.2.0' +version = '5.3.0' __version__ = version.split()[0] @@ -55,6 +55,7 @@ 5.1.0 08-Apr-2024 Several new Demo Programs, updated Matplotlib ping demo, license ver 1.1 5.2.0 14-Aug-2024 Fixed erronous import error (when import line started with "from") Added a new "Path" input so that an arbitrary file can be executed easily (or edited) + 5.3.0 15-Aug-2024 One last change for the new path input... clear other fields if chars are entered Copyright 2021, 2022, 2023, 2024 PySimpleSoft Inc. """ @@ -982,7 +983,17 @@ def main(): window['-FILTER NUMBER-'].update(f'{len(new_list)} files') window['-FIND NUMBER-'].update('') window['-FIND-'].update('') + window['-RUN PATH-'].update('') window['-FIND RE-'].update('') + elif event == '-RUN PATH-': + file_list = get_file_list() + window['-FILTER-'].update('') + window['-FILTER NUMBER-'].update(f'{len(file_list)} files') + window['-FIND-'].update('') + window['-DEMO LIST-'].update(file_list) + window['-FIND NUMBER-'].update('') + window['-FIND RE-'].update('') + window['-ML-'].update('') elif event == '-FOCUS FIND-': window['-FIND-'].set_focus() elif event == '-FOCUS FILTER-': From 029af3f6be2a95537b6a2561ba1a0fcff5a5f663 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 17 Aug 2024 06:50:55 -0400 Subject: [PATCH 086/125] Fix for Table cell editing. Checked to make sure not already editing when get an edit request --- development_build_changelog.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 5e32567d7..b104ad417 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -6,7 +6,7 @@ Changelog since last major release 5.0.6.2 New Table feature - Pressing Escape Key during cell edit will abort the edit and restore value 5.0.6.3 New Vertical Separator alias added to match the Horizontal ones added earlier. VLine, VerticalLine 5.0.6.4 New Table feature - If user double-clicks and modifies a cell in a table, then an event with a tuple - is generated that is similar to the Clicked tuple. The constant TABLE_EDITED_INDICATOR will be part of tuple + is generated tha+t is similar to the Clicked tuple. The constant TABLE_EDITED_INDICATOR will be part of tuple 5.0.6.5 Table Element - Finishing up the cell editing by adding more parms to the Table element for edit colors and select color cell_edit_colors controls colors of cells during editing cell_edit_select_colors controls colors of the sections during editing @@ -19,3 +19,4 @@ Changelog since last major release 5.0.6.10 Test fix for Y-scrolling of columns that have other scrollable elements in them 5.0.6.11 More changes to the scrollable Column to try and fix the Y-scrolling issue 5.0.6.12 Applied above changes to the X-scrollling of columns (both using mouse wheel and scrollbars) +5.0.6.13 Fix for Table cell editing. Checked to make sure not already editing when get an edit request From 5d509ca92af19569b1a42aeceed9e0ff23b83ff7 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:24:36 -0400 Subject: [PATCH 087/125] Fix for mouse scrollwheel crash when leaving a scrollable area and returning --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index b104ad417..d1b814e44 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -20,3 +20,4 @@ Changelog since last major release 5.0.6.11 More changes to the scrollable Column to try and fix the Y-scrolling issue 5.0.6.12 Applied above changes to the X-scrollling of columns (both using mouse wheel and scrollbars) 5.0.6.13 Fix for Table cell editing. Checked to make sure not already editing when get an edit request +5.0.6.14 Fix for mouse scrollwheel crash when leaving a scrollable area and returning From 08215b97a9bd5348548e000cc73d335779179e06 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Fri, 13 Sep 2024 05:29:29 -0400 Subject: [PATCH 088/125] Fixed maintanence spelling error (ugh... sorry everyone...) --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index d1b814e44..417086c5f 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -21,3 +21,4 @@ Changelog since last major release 5.0.6.12 Applied above changes to the X-scrollling of columns (both using mouse wheel and scrollbars) 5.0.6.13 Fix for Table cell editing. Checked to make sure not already editing when get an edit request 5.0.6.14 Fix for mouse scrollwheel crash when leaving a scrollable area and returning +5.0.6.15 Fixed maintanence spelling error (ugh... sorry everyone...) From 75fa1934a8c5f711d31147ad719f597f857674a4 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:45:34 -0400 Subject: [PATCH 089/125] Upgraded Python version of Python 3.13.0 used to build PySimpleGUI. From 22d63778ed208ec6b64fd5ae23d2d91d45ae540a Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:47:21 -0400 Subject: [PATCH 090/125] Upgraded version of Python 3.13.0 used to build PySimpleGUI From 25bbfae2e15ab239a6d824e2572f5d3daca18871 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:51:19 -0400 Subject: [PATCH 091/125] Upgraded version of Python 3.13.0 used to build PySimpleGUI --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 417086c5f..60732468a 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -22,3 +22,4 @@ Changelog since last major release 5.0.6.13 Fix for Table cell editing. Checked to make sure not already editing when get an edit request 5.0.6.14 Fix for mouse scrollwheel crash when leaving a scrollable area and returning 5.0.6.15 Fixed maintanence spelling error (ugh... sorry everyone...) +5.0.6.16 Upgraded version of Python 3.13.0 used to build PySimpleGUI From 37b0d228790a1ae9ed8758948f4a25347ade119c Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:39:18 -0400 Subject: [PATCH 092/125] 5.0.7 Released 20-Oct-2024 --- development_build_changelog.txt | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 60732468a..31e97b3ac 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,25 +1,4 @@ Changelog since last major release -5.0.6 Released 28-Jun-2024 -5.0.6.1 New Table feature - Editing individual table values using double-click/return key - New Table feature - values property returns the table's current values -5.0.6.2 New Table feature - Pressing Escape Key during cell edit will abort the edit and restore value -5.0.6.3 New Vertical Separator alias added to match the Horizontal ones added earlier. VLine, VerticalLine -5.0.6.4 New Table feature - If user double-clicks and modifies a cell in a table, then an event with a tuple - is generated tha+t is similar to the Clicked tuple. The constant TABLE_EDITED_INDICATOR will be part of tuple -5.0.6.5 Table Element - Finishing up the cell editing by adding more parms to the Table element for edit colors and select color - cell_edit_colors controls colors of cells during editing - cell_edit_select_colors controls colors of the sections during editing -5.0.6.6 Menu Element - Added generating an event when a top level menu item has no submenu. NOTE this is not a normal Windows behavior. -5.0.6.7 dict_to_string changed so width used with pformat is defined by constant DEFAULT_DICT_TO_STRING_WIDTH which is 80. - Made retroactive by changing the key in the global settings. Old default was 1. - This function is used when print_event_values=True when making the window. -5.0.6.8 Menu Element - Added support for using a key with a top-level menu item since these can now be events -5.0.6.9 Fix for incorrectly calculating row when checking for type when Table editing -5.0.6.10 Test fix for Y-scrolling of columns that have other scrollable elements in them -5.0.6.11 More changes to the scrollable Column to try and fix the Y-scrolling issue -5.0.6.12 Applied above changes to the X-scrollling of columns (both using mouse wheel and scrollbars) -5.0.6.13 Fix for Table cell editing. Checked to make sure not already editing when get an edit request -5.0.6.14 Fix for mouse scrollwheel crash when leaving a scrollable area and returning -5.0.6.15 Fixed maintanence spelling error (ugh... sorry everyone...) -5.0.6.16 Upgraded version of Python 3.13.0 used to build PySimpleGUI +5.0.7 Released 20-Oct-2024 + From acd422c093f3915f9ddf270bf4ac692330d478c1 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:55:35 -0500 Subject: [PATCH 093/125] Added ability to indicate no emoji should be used in error popup. Needed so that recursive error calls aren't made from image element error --- development_build_changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 31e97b3ac..d9f230244 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -2,3 +2,5 @@ Changelog since last major release 5.0.7 Released 20-Oct-2024 +5.0.7.1 Added ability to indicate no emoji should be used in error popup. Needed so that recursive error calls aren't made from image element error + From e909a01067dccb00616cb1c5eb4b1cfefe8d66b4 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 29 Dec 2024 08:27:49 -0500 Subject: [PATCH 094/125] Replaced call to StringVar.trace with StringVar.trace_add --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index d9f230244..cbfaf1641 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -3,4 +3,5 @@ Changelog since last major release 5.0.7 Released 20-Oct-2024 5.0.7.1 Added ability to indicate no emoji should be used in error popup. Needed so that recursive error calls aren't made from image element error +5.0.7.2 Replaced call to StringVar.trace with StringVar.trace_add From b6624b364d4dad5e8f54ac4b2b7391287edbf020 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 4 Jan 2025 06:11:47 -0500 Subject: [PATCH 095/125] Support for Python 3.14 Alpha --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index cbfaf1641..567a9e2a7 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -4,4 +4,5 @@ Changelog since last major release 5.0.7.1 Added ability to indicate no emoji should be used in error popup. Needed so that recursive error calls aren't made from image element error 5.0.7.2 Replaced call to StringVar.trace with StringVar.trace_add +5.0.7.3 Support for Python 3.14 Alpha From ac2741eecdc6ddcfd1aa8d56c662a0ca9f4f2188 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sat, 4 Jan 2025 09:47:24 -0500 Subject: [PATCH 096/125] 5.0.7.4 Swapped Window method in start_thread as the main definition and perform_long_operation as the alias for easier understanding 5.0.7.5 New command line options: versionnogui - prints versions numbers on the command line without creating any windows upgrade nogui - prints the maint releases available without making nay windows upgrade nogui x.x.x.x - upgrades to maint release specified by x.x.x.x without making nay windows --- development_build_changelog.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 567a9e2a7..a9a975432 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -5,4 +5,9 @@ Changelog since last major release 5.0.7.1 Added ability to indicate no emoji should be used in error popup. Needed so that recursive error calls aren't made from image element error 5.0.7.2 Replaced call to StringVar.trace with StringVar.trace_add 5.0.7.3 Support for Python 3.14 Alpha +5.0.7.4 Swapped Window method in start_thread as the main definition and perform_long_operation as the alias for easier understanding +5.0.7.5 New command line options: + versionnogui - prints versions numbers on the command line without creating any windows + upgrade nogui - prints the maint releases available without making nay windows + upgrade nogui x.x.x.x - upgrades to maint release specified by x.x.x.x without making nay windows From e2a8599931674af3be301eab2bb586e9ac718559 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 5 Jan 2025 07:38:08 -0500 Subject: [PATCH 097/125] Added new command line function to the __main__ when building a release... hopefully this will work... --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index a9a975432..b14717eb8 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -10,4 +10,5 @@ Changelog since last major release versionnogui - prints versions numbers on the command line without creating any windows upgrade nogui - prints the maint releases available without making nay windows upgrade nogui x.x.x.x - upgrades to maint release specified by x.x.x.x without making nay windows +5.0.7.6 Added new command line function to the __main__ when building a release... hopefully this will work... From a30a6df9aad57d74b4d9cd287666e4e4ebad7506 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 5 Jan 2025 08:07:44 -0500 Subject: [PATCH 098/125] Added specific version number optional parm to the psgupgrade command to make specific maint release installs easier --- development_build_changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index b14717eb8..60bb865c2 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -11,4 +11,4 @@ Changelog since last major release upgrade nogui - prints the maint releases available without making nay windows upgrade nogui x.x.x.x - upgrades to maint release specified by x.x.x.x without making nay windows 5.0.7.6 Added new command line function to the __main__ when building a release... hopefully this will work... - +5.0.7.7 Added specific version number optional parm to the psgupgrade command to make specific maint release installs easier From b3af1b57e68adb964c80a4841f31dc8af1403e84 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 5 Jan 2025 08:15:50 -0500 Subject: [PATCH 099/125] Fixed typo in specifying "versionnogui" on the command line. Changed to using just "version": python -m PySimpleGUI version --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 60bb865c2..c2e6341f6 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -12,3 +12,4 @@ Changelog since last major release upgrade nogui x.x.x.x - upgrades to maint release specified by x.x.x.x without making nay windows 5.0.7.6 Added new command line function to the __main__ when building a release... hopefully this will work... 5.0.7.7 Added specific version number optional parm to the psgupgrade command to make specific maint release installs easier +5.0.7.8 Fixed typo in specifying "versionnogui" on the command line. Changed to using just "version": python -m PySimpleGUI version From ff5dda6586f41ba0be8ff690ad8c29dd05bb5423 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:12:26 -0500 Subject: [PATCH 100/125] Added printing of command line options to the "help" command line option. Will print the options and then open the SDK Help Window --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index c2e6341f6..0f8c8b6ac 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -13,3 +13,4 @@ Changelog since last major release 5.0.7.6 Added new command line function to the __main__ when building a release... hopefully this will work... 5.0.7.7 Added specific version number optional parm to the psgupgrade command to make specific maint release installs easier 5.0.7.8 Fixed typo in specifying "versionnogui" on the command line. Changed to using just "version": python -m PySimpleGUI version +5.0.7.9 Added printing of command line options to the "help" command line option. Will print the options and then open the SDK Help Window From e2d67c5294a978b934b768420f674214b49660cd Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:30:32 -0500 Subject: [PATCH 101/125] Fixed problem when using upgrade command line option --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 0f8c8b6ac..e5270650e 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -14,3 +14,4 @@ Changelog since last major release 5.0.7.7 Added specific version number optional parm to the psgupgrade command to make specific maint release installs easier 5.0.7.8 Fixed typo in specifying "versionnogui" on the command line. Changed to using just "version": python -m PySimpleGUI version 5.0.7.9 Added printing of command line options to the "help" command line option. Will print the options and then open the SDK Help Window +5.0.7.10 Fixed problem when using upgrade command line option From 7a3c616d928dbcaffe49e5d10d4e0de87e08b624 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:51:03 -0500 Subject: [PATCH 102/125] Added "version" command line option to the help that's printed --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index e5270650e..53825fd33 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -15,3 +15,4 @@ Changelog since last major release 5.0.7.8 Fixed typo in specifying "versionnogui" on the command line. Changed to using just "version": python -m PySimpleGUI version 5.0.7.9 Added printing of command line options to the "help" command line option. Will print the options and then open the SDK Help Window 5.0.7.10 Fixed problem when using upgrade command line option +5.0.7.11 Added "version" command line option to the help that's printed From 521cc41bf70b03ce5c43c4e0c8d457e8df51acb1 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:03:03 -0500 Subject: [PATCH 103/125] A bit more upgrade command line problems.. --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 53825fd33..ecdacd93f 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -16,3 +16,4 @@ Changelog since last major release 5.0.7.9 Added printing of command line options to the "help" command line option. Will print the options and then open the SDK Help Window 5.0.7.10 Fixed problem when using upgrade command line option 5.0.7.11 Added "version" command line option to the help that's printed +5.0.7.12 A bit more upgrade command line problems.. From 08ecbdff54018f32c6471b07eaa1bedb9e419534 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 6 Jan 2025 07:37:53 -0500 Subject: [PATCH 104/125] Release 5.0.8 - candidate for PyPI --- development_build_changelog.txt | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index ecdacd93f..16dc264e6 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,19 +1,4 @@ Changelog since last major release -5.0.7 Released 20-Oct-2024 +5.0.8 Released 06-Jan-2025 -5.0.7.1 Added ability to indicate no emoji should be used in error popup. Needed so that recursive error calls aren't made from image element error -5.0.7.2 Replaced call to StringVar.trace with StringVar.trace_add -5.0.7.3 Support for Python 3.14 Alpha -5.0.7.4 Swapped Window method in start_thread as the main definition and perform_long_operation as the alias for easier understanding -5.0.7.5 New command line options: - versionnogui - prints versions numbers on the command line without creating any windows - upgrade nogui - prints the maint releases available without making nay windows - upgrade nogui x.x.x.x - upgrades to maint release specified by x.x.x.x without making nay windows -5.0.7.6 Added new command line function to the __main__ when building a release... hopefully this will work... -5.0.7.7 Added specific version number optional parm to the psgupgrade command to make specific maint release installs easier -5.0.7.8 Fixed typo in specifying "versionnogui" on the command line. Changed to using just "version": python -m PySimpleGUI version -5.0.7.9 Added printing of command line options to the "help" command line option. Will print the options and then open the SDK Help Window -5.0.7.10 Fixed problem when using upgrade command line option -5.0.7.11 Added "version" command line option to the help that's printed -5.0.7.12 A bit more upgrade command line problems.. From ee2e765c3a7fbc56eeb0c5cf0b7626a8647aba8f Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Wed, 8 Jan 2025 08:19:07 -0500 Subject: [PATCH 105/125] User Setting API - if attempting to read the global PSG settings file and it's empty, print an error rather than attempting to display an error window --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 16dc264e6..96876c8f3 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -2,3 +2,4 @@ Changelog since last major release 5.0.8 Released 06-Jan-2025 +5.0.8.1 User Setting API - if attempting to read the global PSG settings file and it's empty, print an error rather than attempting to display an error window From abb1bfb8f93997248ff1777f9ae8e0f8e51bfb6f Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:38:56 -0500 Subject: [PATCH 106/125] Fix in Input.update for crash when using one of the "system default" themes. Was trying to set foreground color to magic number COLOR_SYSTEM_DEFAULT Added new functions to set a widget's foreground and background colors... first step in a major refactor Added protection against recursive errors to the popup error with traceback function which caused problems if error creating a window Maybe fixed (or maybe created) a bug in Input.update. Was setting foreground color when switching to enabled but not the background. Added setting background color --- development_build_changelog.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 96876c8f3..993dcc8e5 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -3,3 +3,7 @@ Changelog since last major release 5.0.8 Released 06-Jan-2025 5.0.8.1 User Setting API - if attempting to read the global PSG settings file and it's empty, print an error rather than attempting to display an error window +5.0.8.2 Fix in Input.update for crash when using one of the "system default" themes. Was trying to set foreground color to magic number COLOR_SYSTEM_DEFAULT + Added new functions to set a widget's foreground and background colors... first step in a major refactor + Added protection against recursive errors to the popup error with traceback function which caused problems if error creating a window + Maybe fixed (or maybe created) a bug in Input.update. Was setting foreground color when switching to enabled but not the background. Added setting background color From 7012522fac65ee68e208b0cc86f6142120cb4c0a Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Tue, 14 Jan 2025 16:27:01 -0500 Subject: [PATCH 107/125] Added import of ssl and a line of code to fix error we're seeing on the Mac --- development_build_changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 993dcc8e5..c25325fb5 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -7,3 +7,5 @@ Changelog since last major release Added new functions to set a widget's foreground and background colors... first step in a major refactor Added protection against recursive errors to the popup error with traceback function which caused problems if error creating a window Maybe fixed (or maybe created) a bug in Input.update. Was setting foreground color when switching to enabled but not the background. Added setting background color +5.0.8.3 Added import of ssl and a line of code to fix error we're seeing on the Mac + From cde2b94f712ac551cde3d500ba20ca81ea1b0c16 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 25 Feb 2025 16:49:21 -0500 Subject: [PATCH 108/125] New readme --- README.md | 109 +++++++++++++++++++++--------------------------------- 1 file changed, 42 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 57a968f20..6a0fec91b 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,73 @@ -

- -
- For more information visit PySimpleGUI.com -

- - -##

-

User Interfaces for HumansTM

+

-# Welcome to PySimpleGUI 5 !! + ![](https://PySimpleGUI.net/images/emojis/news_112.png) + +# Two Important updates about PySimpleGUI + + ![](https://PySimpleGUI.net/images/emojis/search_56.png) + + ## 1. New Package Location + +We were recently informed by PyPI that PySimpleGUI does not meet updated PyPI Terms of Service, that it needs to be removed, and hosted on a private server. As a result, you’ll need to add a parameter to your pip install commands in order to access the PySimpleGUI private PyPI server. +The parameter to add is: -Do you use PySimpleGUI 4? [Here is what you need to know.](https://docs.pysimplegui.com/en/latest/readme/sunset/) +`-i https://PySimpleGUI.net/install` -**PySimpleGUI creates desktop applications easily**, enhancing the tkinter, Qt, WxPython, and Remi frameworks with a much simpler programming interface: +The basic install/upgrade command was: -1. PySimpleGUI user interfaces are defined using core Python data types (lists and dictionaries) that are easily understood by beginners. -2. PySimpleGUI event handling changes from a complex callback-based model to a simple message passing one. -3. PySimpleGUI uses simple Python code and has no requirement for object oriented architecture. +`python -m pip install –-upgrade PySimpleGUI` -PySimpleGUI is more than a GUI library: PySimpleGUI simplifies much of your Python development process. Sure, it makes developing user interfaces much easier, but PySimpleGUI also tames advanced Python functionality (such as threading) and makes it easy for all users to take their Python applications to the next level. PySimpleGUI is a robust toolkit. +or for Linux/Mac -## Introducing PySimpleGUI 5 +`python3 -m pip install –-upgrade PySimpleGUI` -For the last 5 years, PySimpleGUI offered free software with the hope of sustaining the -company by donations. We appreciate the support we received, but the amount has been too -small to support the PySimpleGUI project. For this reason, PySimpleGUI is switching to a -commercial model, where commercial users are expected to pay a nominal license fee. +The **new command** with the new parameter is: +`python -m pip install --upgrade -i https://PySimpleGUI.net/install PySimpleGUI` -PySimpleGUI is now part of PySimpleSoft, Inc., whose mission is to make the best Python -application development environment much, much better. Since launching in 2018, PySimpleGUI -has helped hobbyists and professionals alike create Python GUIs in a fraction of the time. -PySimpleGUI 5 takes PySimpleGUI to the next level, providing hundreds of improvements, -including new features, enhanced security, and priority support. + ![](https://PySimpleGUI.net/images/emojis/wave_56.png) + +## 2. PySimpleGUI Shutdown +We gave it our best shot…. After 7 years of attempting to make the PySimpleGUI project sustainable, we are stopping the PySimpleGUI project. -PySimpleGUI 5 is licensed software. As the [License Agreement](LICENSE.txt) explains, after a trial -period, all PySimpleGUI 5 users must register at PySimpleGUI.com to obtain a Developer Key. -For most users (Hobbyist Users), the license is at NO COST. If you are a Commercial User, you must buy a license. +If you've followed the project over the years, you'll have read about the difficulties that all open-source projects face in generating enough income to pay for the project, seen the requests for sponsorships, and attempts to generate income via a Udemy course. There was not enough income to cover the costs of running a business and, of course, wasn’t able to generate any income for our small team. This isn’t a sustainable situation. -

- -

+## One Year Update PySimpleGUI 5 -[Register Now](https://pricing.PySimpleGUI.com) and help support the PySimpleGUI community. +It's been a little over a year since the release of PySimpleGUI 5. Of the 100,000’s of users, 10,000's of which were large corporate users, only 600 people paid the $99 for a commercial license. -## Examples +## End of PySimpleGUI Project -PySimpleGUI users have created thousands of amazing desktop applications. Here are a few screen shots. For more examples, see the [PySimpleGUI gallery](https://gallery.PySimpleGUI.com/). +The revenue generated was not enough to cover the basic costs, so we've made the difficult decision to end the PySimpleGUI project. -

- - - -

+## Support for Commercial Users + +Unlike traditional software companies, where stopping business means support ends immediately, we thought it would be classier to go the extra mile by continuing to provide support to Commercial License users this year as a way of saying "thank you" for your help in trying to give the project a viable future. Please provide your Priority Support code or your submission will be automatically blocked. We'll do the best we can to help with the limited resources we've got. -## Get Started at No Cost +Your license doesn’t expire so you’ll be able to continue to run your applications at the end of the year we’re providing maintenance and beyond. We’ll be distributing an offline version of the documentation so that you’ll have it for the future. -Whether you are a Hobbyist User or Commercial User, you can start using PySimpleGUI at no cost. -To get started with a 30-day trial period, first install Python and then +## Hobbyists - python -m pip install pysimplegui +Hobbyists can continue to use PySimpleGUI 5 until their keys expire. After that you'll need to switch to version 4, which you'll find 1,000s of copies on GitHub with at least 1 being community supported. -and run some code, like +If you wish to use PySimpleGUI without the key expiring or want support, then you can buy a Commercial License which is good perpetually. - import PySimpleGUI as sg - layout = [ [sg.Text('Hello, world!')] ] - window = sg.Window('Hello Example', layout) - while True: - event, values = window.read() - if event == sg.WIN_CLOSED: - break - window.close() +## Websites Availability -(You might need to use `python3` instead of `python`.) +The PySimpleGUI registration and documentation websites will continue to operate for a couple of months to give commercial customers an opportunity to create distribution keys. No new Hobbyist keys will be available. + +## Thank you to everyone -You can try PySimpleGUI for 30 days, after which you will need to register. Hobbyist users register at no cost, and Commercial Users must buy a license. For more details, see [PySimpleGUI.com/pricing](https://pricing.PySimpleGUI.com). +PySimpleGUI has been an experience of a lifetime, and we’ve +enjoyed watching & helping people create incredible applications. -## Documentation +## Business Partnership Inquires -PySimpleGUI provides extensive documentation. Here are some starting points, depending on your needs and expertise: +If you're a business with a serious partnership that you wish to discuss, email mike@PySimpleGUI.com. -* [FAQ](https://faq.pysimplegui.com/) - Frequently Asked Questions -* [Documentation](https://docs.pysimplegui.com/) - Extensive PySimpleGUI documentation* -* [Examples](https://examples.pysimplegui.com/) - Hundreds of sample PySimpleGUI applications. -* [SDK Reference](https://sdk.pysimplegui.com/) - details for each PySimpleGUI element -* [Home Website](https://PySimpleGUI.com) - New PySimpleGUI home page -* [GitHub Repo](https://github.PySimpleGUI.com) - Informational only. Download from PyPi with pip. -* [Updated Documentation](https://docs.PySimpleGUI.com) - Everything you need to know about the latest and best PySimpleGUI - * [Cookbook](https://cookbook.PySimpleGUI.com) - Hundreds of basic PySimpleGUI examples. Find a starting point that is close to what you need. - * [Call Reference](https://cookbook.PySimpleGUI.com) - Just the facts, Ma'am -* [Udemy Course](https://www.udemy.com/course/pysimplegui/?couponCode=2B7B3757CA48ACB49EBF) - Become a PySimpleGUI expert in no time. Bundled with Commercial Developer License. From 50e95bc187dc984770aea58561eebc064b6ceeab Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 25 Feb 2025 16:59:08 -0500 Subject: [PATCH 109/125] Added missing emoji --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6a0fec91b..a7cde1d65 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ If you wish to use PySimpleGUI without the key expiring or want support, then yo The PySimpleGUI registration and documentation websites will continue to operate for a couple of months to give commercial customers an opportunity to create distribution keys. No new Hobbyist keys will be available. +![](https://PySimpleGUI.net/images/emojis/pray_56.png) + ## Thank you to everyone PySimpleGUI has been an experience of a lifetime, and we’ve From e6fdc5f000a572a56154e373009adbcd81a6ed3a Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Wed, 26 Feb 2025 06:17:54 -0500 Subject: [PATCH 110/125] Added version number for clairification --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a7cde1d65..514d6874b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ![](https://PySimpleGUI.net/images/emojis/search_56.png) - ## 1. New Package Location +## 1. New Package Location We were recently informed by PyPI that PySimpleGUI does not meet updated PyPI Terms of Service, that it needs to be removed, and hosted on a private server. As a result, you’ll need to add a parameter to your pip install commands in order to access the PySimpleGUI private PyPI server. The parameter to add is: @@ -39,7 +39,7 @@ If you've followed the project over the years, you'll have read about the diffic ## One Year Update PySimpleGUI 5 -It's been a little over a year since the release of PySimpleGUI 5. Of the 100,000’s of users, 10,000's of which were large corporate users, only 600 people paid the $99 for a commercial license. +It's been a little over a year since the release of PySimpleGUI 5. Of the 100,000’s of Version 5 users, 10,000's of which were large corporate users, only 600 people paid the $99 for a commercial license. ## End of PySimpleGUI Project From 5ceaba5909260c4785ea691f15d8a863511eff26 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Sun, 2 Mar 2025 12:39:21 -0500 Subject: [PATCH 111/125] Released bug fixes and new features as 5.0.9 --- development_build_changelog.txt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index c25325fb5..41c66724f 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,11 +1,3 @@ Changelog since last major release -5.0.8 Released 06-Jan-2025 - -5.0.8.1 User Setting API - if attempting to read the global PSG settings file and it's empty, print an error rather than attempting to display an error window -5.0.8.2 Fix in Input.update for crash when using one of the "system default" themes. Was trying to set foreground color to magic number COLOR_SYSTEM_DEFAULT - Added new functions to set a widget's foreground and background colors... first step in a major refactor - Added protection against recursive errors to the popup error with traceback function which caused problems if error creating a window - Maybe fixed (or maybe created) a bug in Input.update. Was setting foreground color when switching to enabled but not the background. Added setting background color -5.0.8.3 Added import of ssl and a line of code to fix error we're seeing on the Mac - +5.0.9 Released 02-Mar-2025 From 366b4502e4a220b5004d443e6452cc1255d3a45e Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Mon, 3 Mar 2025 04:02:52 -0500 Subject: [PATCH 112/125] Uninstall instructions added --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 514d6874b..da72f2d59 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ # Two Important updates about PySimpleGUI ![](https://PySimpleGUI.net/images/emojis/search_56.png) - + ## 1. New Package Location We were recently informed by PyPI that PySimpleGUI does not meet updated PyPI Terms of Service, that it needs to be removed, and hosted on a private server. As a result, you’ll need to add a parameter to your pip install commands in order to access the PySimpleGUI private PyPI server. @@ -31,6 +31,15 @@ The **new command** with the new parameter is: ![](https://PySimpleGUI.net/images/emojis/wave_56.png) + ### Uninstall Needed If Error + + If you installed the stub version 5.0.99. from PyPI, the only way we're able to show a message from PyPI, then you will need to uninstall PySimpleGUI and reinstall using the private PyPI server + + `python -m pip uninstall PySimpleGUI` + + `python -m pip install --upgrade -i https://PySimpleGUI.net/install PySimpleGUI` + + ## 2. PySimpleGUI Shutdown We gave it our best shot…. After 7 years of attempting to make the PySimpleGUI project sustainable, we are stopping the PySimpleGUI project. From 08c634e9f95e0ac5214ca20319142bfb8af7e88a Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Mon, 3 Mar 2025 04:09:23 -0500 Subject: [PATCH 113/125] Moved emoji --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index da72f2d59..da23ef0c7 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,6 @@ The **new command** with the new parameter is: `python -m pip install --upgrade -i https://PySimpleGUI.net/install PySimpleGUI` - ![](https://PySimpleGUI.net/images/emojis/wave_56.png) - ### Uninstall Needed If Error If you installed the stub version 5.0.99. from PyPI, the only way we're able to show a message from PyPI, then you will need to uninstall PySimpleGUI and reinstall using the private PyPI server @@ -40,6 +38,9 @@ The **new command** with the new parameter is: `python -m pip install --upgrade -i https://PySimpleGUI.net/install PySimpleGUI` + ![](https://PySimpleGUI.net/images/emojis/wave_56.png) + + ## 2. PySimpleGUI Shutdown We gave it our best shot…. After 7 years of attempting to make the PySimpleGUI project sustainable, we are stopping the PySimpleGUI project. From f9828344394b075b813727c87364097ea0a1dea8 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:35:54 -0400 Subject: [PATCH 114/125] Update issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md Updated info about PSG 5 needing priority support --- ...--must-fill-in-this-form-with-every-new-issue-submitted.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md index d227e544a..27339a902 100644 --- a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md +++ b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md @@ -1,5 +1,5 @@ --- -name: Issue Form - **Must fill in this form** with every new issue submitted. PSG4 is no longer supported on this GitHub. +name: Issue Form - **Must fill in this form** PSG4 is no longer supported. You MUST supply your Priority Support Code to log an issue. about: This form contains the information needed to help you solve your problem title: "[ Enhancement/Bug/Question] NOTE - you can also call sg.main() or sg.main_open_github_issue() to post an issue" labels: '' @@ -42,7 +42,7 @@ Or you can print each version shown in () ### Priority Support Code (Commercial License Users) - +Replace this text with your Priority Support Code --------------------- From e4ab886222d4ed1055bf1d3098528458d727000b Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:56:28 -0400 Subject: [PATCH 115/125] Priority field --- ...-must-fill-in-this-form-with-every-new-issue-submitted.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md index 27339a902..99c1733a3 100644 --- a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md +++ b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md @@ -1,7 +1,7 @@ --- name: Issue Form - **Must fill in this form** PSG4 is no longer supported. You MUST supply your Priority Support Code to log an issue. about: This form contains the information needed to help you solve your problem -title: "[ Enhancement/Bug/Question] NOTE - you can also call sg.main() or sg.main_open_github_issue() to post an issue" +title: "[ Enhancement/Bug/Question] NOTE You must supply your Priority Support Code in order to receive support- " labels: '' assignees: '' @@ -41,8 +41,7 @@ Or you can print each version shown in () #### GUI Version (tkinter (`sg.tclversion_detailed`), PySide2, WxPython, Remi) -### Priority Support Code (Commercial License Users) -Replace this text with your Priority Support Code +### Priority Support Code - Only Commercially Licensed Users Receive Support as of Feb 2025 --------------------- From 14c5632e780c78a1f8492f1f3118069bffaf60b6 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:59:46 -0400 Subject: [PATCH 116/125] Update issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md From f03903eac19a476b907bda19d9581ab6817892f1 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:12:44 -0400 Subject: [PATCH 117/125] Update issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md --- ...m---must-fill-in-this-form-with-every-new-issue-submitted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md index 99c1733a3..a1fed6f02 100644 --- a/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md +++ b/.github/ISSUE_TEMPLATE/issue-form---must-fill-in-this-form-with-every-new-issue-submitted.md @@ -42,7 +42,7 @@ Or you can print each version shown in () ### Priority Support Code - Only Commercially Licensed Users Receive Support as of Feb 2025 - +Replace this text with your Priority Support Code --------------------- From a780e520172cffe9b08893cda4798ea90b750c89 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 11 Mar 2025 16:08:43 -0400 Subject: [PATCH 118/125] Updated install instructions --- README.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index da23ef0c7..c0231479d 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,18 @@ We were recently informed by PyPI that PySimpleGUI does not meet updated PyPI Terms of Service, that it needs to be removed, and hosted on a private server. As a result, you’ll need to add a parameter to your pip install commands in order to access the PySimpleGUI private PyPI server. The parameter to add is: -`-i https://PySimpleGUI.net/install` +`--extra-index-url https://PySimpleGUI.net/install ` -The basic install/upgrade command was: +### To force a reinstall of PySimpleGUI from new server + +`python -m pip install --force-reinstall --extra-index-url https://PySimpleGUI.net/install PySimpleGUI` + + +### Performing an upgrade + +This command will also install needed modules like rsa from PyPI automatically + +The basic install/upgrade command **was**: `python -m pip install –-upgrade PySimpleGUI` @@ -27,20 +36,15 @@ or for Linux/Mac The **new command** with the new parameter is: -`python -m pip install --upgrade -i https://PySimpleGUI.net/install PySimpleGUI` +`pip install --upgrade --extra-index-url https://PySimpleGUI.net/install PySimpleGUI` + +### Uninstall Needed If Error + + + +![](https://PySimpleGUI.net/images/emojis/wave_56.png) + - ### Uninstall Needed If Error - - If you installed the stub version 5.0.99. from PyPI, the only way we're able to show a message from PyPI, then you will need to uninstall PySimpleGUI and reinstall using the private PyPI server - - `python -m pip uninstall PySimpleGUI` - - `python -m pip install --upgrade -i https://PySimpleGUI.net/install PySimpleGUI` - - - ![](https://PySimpleGUI.net/images/emojis/wave_56.png) - - ## 2. PySimpleGUI Shutdown We gave it our best shot…. After 7 years of attempting to make the PySimpleGUI project sustainable, we are stopping the PySimpleGUI project. From e3ad39833cabdd665e0ea823f62791e94d2ed478 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Tue, 11 Mar 2025 16:11:28 -0400 Subject: [PATCH 119/125] Updated python command --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c0231479d..281b4302f 100644 --- a/README.md +++ b/README.md @@ -36,11 +36,11 @@ or for Linux/Mac The **new command** with the new parameter is: -`pip install --upgrade --extra-index-url https://PySimpleGUI.net/install PySimpleGUI` +`python -m pip install --upgrade --extra-index-url https://PySimpleGUI.net/install PySimpleGUI` ### Uninstall Needed If Error - +If you're getting errors, please uninstall PySimpleGUI entirely and install again using the new parameter. ![](https://PySimpleGUI.net/images/emojis/wave_56.png) From d1dec2301ec91f9e213a37015159861a195aedfa Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 23 Mar 2025 11:44:51 -0400 Subject: [PATCH 120/125] Added Table.disable_edit_for_cells. Provide a list of (row, col) cells to not allowed to be edited --- development_build_changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 41c66724f..1b8009b69 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,3 +1,6 @@ Changelog since last major release 5.0.9 Released 02-Mar-2025 + +5.0.9.1 Added Table.disable_edit_for_cells. Provide a list of (row, col) cells to not allowed to be edited + From d09951f57a5ec5831ef3a496094fc75b673b2be9 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Wed, 2 Apr 2025 07:58:37 -0400 Subject: [PATCH 121/125] 5.0.9.2 Fixed bug that incorrectly flagged commercial keys as expired --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 1b8009b69..6daf45a6f 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -3,4 +3,5 @@ Changelog since last major release 5.0.9 Released 02-Mar-2025 5.0.9.1 Added Table.disable_edit_for_cells. Provide a list of (row, col) cells to not allowed to be edited +5.0.9.2 Fixed bug that incorrectly flagged commercial keys as expired From fb8f9dcae79252cb8b0f86b98cb57a7f9d14ee0a Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Wed, 2 Apr 2025 08:06:24 -0400 Subject: [PATCH 122/125] Release 5.0.10 Released 02-Apr-2025 --- development_build_changelog.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 6daf45a6f..8222f1117 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -1,7 +1,6 @@ Changelog since last major release -5.0.9 Released 02-Mar-2025 +5.0.10 Released 02-Apr-2025 + + -5.0.9.1 Added Table.disable_edit_for_cells. Provide a list of (row, col) cells to not allowed to be edited -5.0.9.2 Fixed bug that incorrectly flagged commercial keys as expired - From 2f1bb0509a8d2d06c0757bc8a080f818793b3c43 Mon Sep 17 00:00:00 2001 From: PySimpleGUI Date: Mon, 14 Apr 2025 13:52:05 -0400 Subject: [PATCH 123/125] Notice regarding 5.0.10 --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 281b4302f..bd233387c 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@

- ![](https://PySimpleGUI.net/images/emojis/news_112.png) - +![](https://PySimpleGUI.net/images/emojis/news_112.png) + # Two Important updates about PySimpleGUI - - ![](https://PySimpleGUI.net/images/emojis/search_56.png) + +![](https://PySimpleGUI.net/images/emojis/search_56.png) ## 1. New Package Location - + We were recently informed by PyPI that PySimpleGUI does not meet updated PyPI Terms of Service, that it needs to be removed, and hosted on a private server. As a result, you’ll need to add a parameter to your pip install commands in order to access the PySimpleGUI private PyPI server. The parameter to add is: @@ -38,10 +38,17 @@ The **new command** with the new parameter is: `python -m pip install --upgrade --extra-index-url https://PySimpleGUI.net/install PySimpleGUI` -### Uninstall Needed If Error +### Uninstall May Be Needed If Error If you're getting errors, please uninstall PySimpleGUI entirely and install again using the new parameter. + +### BUG - Commercial Key Expiration - Upgrade to 5.0.10 + +There is a bug in versions of PySimpleGUI older than 5.0.10 that causes an erroneous expired error when using a Commercial Developer key. These keys do not expire and shouldn't not be generating the error. + +A fix was released in version 5.0.10 on 2-Apr-2025. **Please upgrade to version 5.0.10** so that your key doesn't generate an expiration error. + ![](https://PySimpleGUI.net/images/emojis/wave_56.png) From c4401389d2c204ac37bc155d26c1fd4aecc4bfb2 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Tue, 27 May 2025 17:00:52 -0400 Subject: [PATCH 124/125] 5.0.10.3 Fix for a potential problem with trial periods --- development_build_changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index 8222f1117..e33148750 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -2,5 +2,5 @@ Changelog since last major release 5.0.10 Released 02-Apr-2025 - +5.0.10.3 Fix for a potential problem with trial periods From 2e3830b2e44308254613aa5fab163dc50ebbf4a5 Mon Sep 17 00:00:00 2001 From: PySimpleGUI <46163555+PySimpleGUI@users.noreply.github.com> Date: Sun, 1 Jun 2025 09:30:19 -0400 Subject: [PATCH 125/125] More fixes --- development_build_changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/development_build_changelog.txt b/development_build_changelog.txt index e33148750..78ce3e8b7 100644 --- a/development_build_changelog.txt +++ b/development_build_changelog.txt @@ -3,4 +3,5 @@ Changelog since last major release 5.0.10 Released 02-Apr-2025 5.0.10.3 Fix for a potential problem with trial periods +5.0.10.4 More fixes