diff --git a/PySimpleGUI.py b/PySimpleGUI.py index c89076cf..697b1ef2 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -1,10 +1,21 @@ #!/usr/bin/python3 -version = __version__ = "4.60.0 Released 8-May-2022" +version = __version__ = "4.60.5" _change_log = """ - Changelog since 4.60.0 released to PyPI on 8-May-2022 + Changelog since 4.60.0 released to PyPI on 8-May-2022. These are "Dot" releases.... + 4.60.1 + A "dot-release" / patch for crash that occurs if the horizontal_scroll parm is set in Listbox element + Was created when the ttk scrollbars were added + 4.60.2 + A "dot-release" for Mac 12.3+ "Invisible window" problem. Adds option in Mac control panel to set Alpha to 0.99 as default + 4.60.3 + Another shot at the 12.3+ Mac OS problem. Had bug in the version check code + 4.60.4 + Dot release to quickly fix the Trinket detection which stopped working recently + 4.60.5 + Fix for the Mac. If running MacOS Ventura 13.2.1 then windows with no titlebar had problems with Input elements. Added support for the Upgrade Service. """ __version__ = version.split()[0] # For PEP 396 and PEP 345 @@ -136,7 +147,6 @@ from uuid import uuid4 # get the tkinter detailed version tclversion_detailed = tkinter.Tcl().eval('info patchlevel') framework_version = tclversion_detailed - import time import pickle import calendar @@ -188,7 +198,7 @@ import re import tempfile import ctypes import platform - +import socket pil_import_attempted = pil_imported = False @@ -318,21 +328,22 @@ def running_windows(): return sys.platform.startswith('win') + def running_trinket(): """ - A special case for Trinket. Checks both the OS and the number of environment variables - Currently, Trinket only has ONE environment variable. This fact is used to figure out if Trinket is being used. + A special case for Trinket. Uses the hostname an platform together to determine if running on Trinket Returns True if "Trinket" (in theory) - :return: True if sys.platform indicates Linux and the number of environment variables is 1 + :return: True if sys.platform indicates Linux and hostname starts with 'pygame-' :rtype: (bool) """ - if len(os.environ) == 1 and sys.platform.startswith('linux'): + if sys.platform.startswith('linux') and socket.gethostname().startswith('pygame-'): return True return False + def running_replit(): """ A special case for REPLIT. Checks both the OS and for the existance of the number of environment variable REPL_OWNER @@ -419,6 +430,7 @@ DEFAULT_TOOLTIP_TIME = 400 DEFAULT_TOOLTIP_OFFSET = (0, -20) DEFAULT_KEEP_ON_TOP = None DEFAULT_SCALING = None +DEFAULT_ALPHA_CHANNEL = 1.0 TOOLTIP_BACKGROUND_COLOR = "#ffffe0" TOOLTIP_FONT = None #################### COLOR STUFF #################### @@ -612,6 +624,7 @@ ENABLE_TREEVIEW_869_PATCH = True ENABLE_MAC_NOTITLEBAR_PATCH = False ENABLE_MAC_MODAL_DISABLE_PATCH = False ENABLE_MAC_DISABLE_GRAB_ANYWHERE_WITH_TITLEBAR = True +ENABLE_MAC_ALPHA_99_PATCH= False OLD_TABLE_TREE_SELECTED_ROW_COLORS = ('#FFFFFF', '#4A6984') ALTERNATE_TABLE_AND_TREE_SELECTED_ROW_COLORS = ('SystemHighlightText', 'SystemHighlight') @@ -9325,7 +9338,7 @@ class Window: element_padding=None, margins=(None, None), button_color=None, font=None, progress_bar_color=(None, None), background_color=None, border_depth=None, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, icon=None, force_toplevel=False, - alpha_channel=1, return_keyboard_events=False, use_default_focus=True, text_justification=None, + alpha_channel=None, return_keyboard_events=False, use_default_focus=True, text_justification=None, no_titlebar=False, grab_anywhere=False, grab_anywhere_using_control=True, keep_on_top=None, resizable=False, disable_close=False, disable_minimize=False, right_click_menu=None, transparent_color=None, debugger_enabled=True, right_click_menu_background_color=None, right_click_menu_text_color=None, right_click_menu_disabled_text_color=None, @@ -9515,7 +9528,7 @@ class Window: self.KeepOnTop = keep_on_top self.ForceTopLevel = force_toplevel self.Resizable = resizable - self._AlphaChannel = alpha_channel + self._AlphaChannel = alpha_channel if alpha_channel is not None else DEFAULT_ALPHA_CHANNEL self.Timeout = None self.TimeoutKey = TIMEOUT_KEY self.TimerCancelled = False @@ -15543,7 +15556,6 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # Horizontal scrollbar if element.HorizontalScroll: - element.TKText.config(wrap='none') _make_ttk_scrollbar(element, 'h', toplevel_form) element.hsb.pack(side=tk.BOTTOM, fill='x') element.Widget.configure(xscrollcommand=element.hsb.set) @@ -16833,7 +16845,10 @@ def StartupTK(window): # for the Raspberry Pi. Need to set the attributes here, prior to the building of the window # so going ahead and doing it for all platforms, in addition to doing it after the window is packed - _no_titlebar_setup(window) + # 2023-April - this call seems to be causing problems on MacOS 13.2.1 Ventura. Input elements become non-responsive + # if this call is made here and at the end of building the window + if not running_mac(): + _no_titlebar_setup(window) if not window.Resizable: root.resizable(False, False) @@ -17738,7 +17753,7 @@ def set_options(icon=None, button_color=None, element_size=(None, None), button_ suppress_error_popups=None, suppress_raise_key_errors=None, suppress_key_guessing=None,warn_button_key_duplicates=False, enable_treeview_869_patch=None, enable_mac_notitlebar_patch=None, use_custom_titlebar=None, titlebar_background_color=None, titlebar_text_color=None, titlebar_font=None, titlebar_icon=None, user_settings_path=None, pysimplegui_settings_path=None, pysimplegui_settings_filename=None, keep_on_top=None, dpi_awareness=None, scaling=None, disable_modal_windows=None, force_modal_windows=None, tooltip_offset=(None, None), - sbar_trough_color=None, sbar_background_color=None, sbar_arrow_color=None, sbar_width=None, sbar_arrow_width=None, sbar_frame_color=None, sbar_relief=None): + sbar_trough_color=None, sbar_background_color=None, sbar_arrow_color=None, sbar_width=None, sbar_arrow_width=None, sbar_frame_color=None, sbar_relief=None, alpha_channel=None): """ :param icon: Can be either a filename or Base64 value. For Windows if filename, it MUST be ICO format. For Linux, must NOT be ICO. Most portable is to use a Base64 of a PNG file. This works universally across all OS's :type icon: bytes | str @@ -17865,6 +17880,8 @@ def set_options(icon=None, button_color=None, element_size=(None, None), button_ :param sbar_frame_color: Scrollbar Color of frame around scrollbar (available only on some ttk themes) :type sbar_frame_color: (str) :param sbar_relief: Scrollbar relief that will be used for the "thumb" of the scrollbar (the thing you grab that slides). Should be a constant that is defined at starting with "RELIEF_" - RELIEF_RAISED, RELIEF_SUNKEN, RELIEF_FLAT, RELIEF_RIDGE, RELIEF_GROOVE, RELIEF_SOLID + :param alpha_channel Default alpha channel to be used on all windows + :type alpha_channel (float) :type sbar_relief: (str) :return: None :rtype: None @@ -17924,6 +17941,8 @@ def set_options(icon=None, button_color=None, element_size=(None, None), button_ global DEFAULT_MODAL_WINDOWS_ENABLED global DEFAULT_MODAL_WINDOWS_FORCED global DEFAULT_TOOLTIP_OFFSET + global DEFAULT_ALPHA_CHANNEL + global _pysimplegui_user_settings global ttk_part_overrides_from_options # global _my_windows @@ -18109,6 +18128,8 @@ def set_options(icon=None, button_color=None, element_size=(None, None), button_ if tooltip_offset != (None, None): DEFAULT_TOOLTIP_OFFSET = tooltip_offset + if alpha_channel is not None: + DEFAULT_ALPHA_CHANNEL = alpha_channel # ---------------- ttk scrollbar section ---------------- if sbar_background_color is not None: @@ -22254,7 +22275,8 @@ available to make this process more atuomatic. # Dictionary of Mac Patches. Used to find the key in the global settings and the default value MAC_PATCH_DICT = {'Enable No Titlebar Patch' : ('-mac feature enable no titlebar patch-', False), 'Disable Modal Windows' : ('-mac feature disable modal windows-', True), - 'Disable Grab Anywhere with Titlebar' : ('-mac feature disable grab anywhere with titlebar-', True)} + 'Disable Grab Anywhere with Titlebar' : ('-mac feature disable grab anywhere with titlebar-', True), + 'Set Alpha Channel to 0.99 for MacOS >= 12.3' : ('-mac feature disable Alpha 0.99', True)} def _read_mac_global_settings(): """ @@ -22265,6 +22287,7 @@ def _read_mac_global_settings(): global ENABLE_MAC_MODAL_DISABLE_PATCH global ENABLE_MAC_NOTITLEBAR_PATCH global ENABLE_MAC_DISABLE_GRAB_ANYWHERE_WITH_TITLEBAR + global ENABLE_MAC_ALPHA_99_PATCH ENABLE_MAC_MODAL_DISABLE_PATCH = pysimplegui_user_settings.get(MAC_PATCH_DICT['Disable Modal Windows'][0], MAC_PATCH_DICT['Disable Modal Windows'][1]) @@ -22272,7 +22295,8 @@ def _read_mac_global_settings(): MAC_PATCH_DICT['Enable No Titlebar Patch'][1]) ENABLE_MAC_DISABLE_GRAB_ANYWHERE_WITH_TITLEBAR = pysimplegui_user_settings.get(MAC_PATCH_DICT['Disable Grab Anywhere with Titlebar'][0], MAC_PATCH_DICT['Disable Grab Anywhere with Titlebar'][1]) - + ENABLE_MAC_ALPHA_99_PATCH = pysimplegui_user_settings.get(MAC_PATCH_DICT['Set Alpha Channel to 0.99 for MacOS >= 12.3'][0], + MAC_PATCH_DICT['Set Alpha Channel to 0.99 for MacOS >= 12.3'][1]) def _mac_should_apply_notitlebar_patch(): """ @@ -22295,6 +22319,28 @@ def _mac_should_apply_notitlebar_patch(): return False +def _mac_should_set_alpha_to_99(): + + if not running_mac(): + return False + + if not ENABLE_MAC_ALPHA_99_PATCH: + return False + + # At this point, we're running a Mac and the alpha patch is enabled + # Final check is to see if Mac OS version is 12.3 or later + try: + platform_mac_ver = platform.mac_ver()[0] + mac_ver = platform_mac_ver.split('.') if '.' in platform_mac_ver else (platform_mac_ver, 0) + if (int(mac_ver[0]) >= 12 and int(mac_ver[1]) >= 3) or int(mac_ver[0]) >= 13 : + print("Mac OS Version is {} and patch enabled so applying the patch".format(platform_mac_ver)) + return True + except Exception as e: + warnings.warn('_mac_should_seet_alpha_to_99 Exception while trying check mac_ver. Error = {}'.format(e), UserWarning) + return False + + return False + def main_mac_feature_control(): """ @@ -23136,6 +23182,196 @@ RED_X_BASE64 = b'iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAAA4qEECAAAQ5ElEQVR4nO1ca3S GREEN_CHECK_BASE64 = b'iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAAA4qEECAAAJV0lEQVR4nO2cTWwc5RnHf8/M7Dq7ttdxIIIUcqGA1BQU6Ac9VSkp0NwoJE5PJJygKki9tIIEO7ND3ICEeqJUJYcqCYdKDoS0lWgpH21KuVShH/TjUolLkIpKguO1vWvvfDw9zOxH1l8zjnc3Xs/vFEXy7uzPz/7f93nnGUNKSkpKSkpKSkpKSkpKzyFMYDKC2e0L2TjYGN2+hN5DkXoVP1s4wdjgDwB4jEw3L6u30CguAJzCCV4YUp4bUuzC94BlZaclHx9hPwb78bELp8jJQaa1yrx65OQljhSe4DguLy8uOxUdhzAuDE5HkvvlEWbVRcgSYDKnHnn5CXbhSR5fXHYqemXCSj6Nj1M4Qb88wrR6EMkUpC47Jy8yFsm2sa58kZSlUYTTUVw4hRPkjIPMBC6ySDwoioHPJrEo65M8W3qJx8hwHBdS0UujTZVcLJwkLweY0cUlN35GEQJyYlLRJ3BKP2UEk9P4qejFWTyTibGFq1V2ViwqPMXRqRcYwUgzupXmha9YOJlIMoSZ7ROQEZBgJ6DsQNKKbmZBJsvBFeOilQCPQbGo6Ens0qNRdARpRddollwsnAwXPq0mkgwug2Ixq69glx7Fjr4ZoGlFhyzM5KSVrLgMSIZZfQWndKBWyYBCuo9erhlJIrnKgJGhrKdwSgeYwGSiIRnS7V1Dci2Tp9XDuLLZWJZaJdcyOTw6DZCGZNjIFR0eEDVJNsKFL4lkIsllPVVf+BaRDBu1olfTjCzEpX/pTG5lI1Z0Q7JdOEVeDqwik0PJtUweWZjJrWws0VfbjISv4TJghJlcLB2sL3yLxEUzGyc62tiMsEwl19gYFd2OZiRGXDSzESq67c1IHHq7ojvUjMShlyu6Y81IHHqzojvcjMSh9yq6C81IHHqtorvSjMShd0R3sRmJQ29ER5ebkTjEE21j8EWE/fhr8aZrTFhvgoaZbBxgJqgiZBO8xsJMXqNKblzkStgYOAQL/n2tUB9UKfy8W81IHJbPaBsLh4DRgS8wVvgWDkHrBE5Xscni4Bk69H2GjEeY1fluNCNxWLqid2FxDo9nCp8ny/v0yQ1U/L04M2d4mQyPhxM4XSOaAio4N391Wqbf0ECHUQzixuEaNiNxWLyi7Ujy6OBtZHkPU25gTj2yxgSjAw8vNlvWUWwsjuMOjt30tWlj5k019HoChPiL+5o2I3FYeGFhXHg8PXg7A/I2yHaq6gMGJoopwpz/MOMzZ5tnyzpGdH2FwzffM52f+Y1qsAUXH4n9iMOaNyNxuFJ0TfIPB29jSN5BZDvz6iFR9SoayTZw/YdwZs52NEai68uPfu7uSt/sO4oOJ5KsTZVcLB1sx+5iKRqiJzDZj8/TQ7eQ1z9iyk3M68IP0ZAtzLGP8akz0aJUbeuVRpKH7G1fKlmz7yoMJZdsZKgEHcnkVsKMtuuT7LeS1/eXlAy12TLBVyXHBIcH9uJQbeszHJHk3OEbvzJllkPJVYLYkgO8cOELGs3I/s5JBpDGE0XDOzD9NzBl+5KSm1ECTMACZoN9HJt5vS2ZXYuLseu/XO5z30T1uqvO5A7FRTMG1JoQ/2fkje1UtIoR40MIBj7gAXnjDKMD3+Y47ppWdiQ5Yw/dVelzf5tYsi6x8HVYMoSig7Cqze9SDi6QkyxBzFY7lB2OqW4yXmds6KHlHphJxGNkcPAyo1t3ehbvqOr1CSV3rBmJQ6Oldib/ic9ufP2EPjHR2LKlIZtXGRvYy+O49cfEVkO0T87bW+9ys/PnFN0SO5MVRZlnQLJUgsYpXAcXvsVIvutYilpmmyjzwXc4OnOmfmyZhFpcjA7d7fbxFnAdbszrCKfthYJAqfNbuOVodIb78bGxeH7qI6b1XlQvRJXtxXolwcADAkyxjBMjE3YmPIBPcObdLHkTb5JMsk8WEZVJqyRPUiwdBOhWJrdypQQHDxuLF6b/w4zeh+oFsmLFjhEDAx9fTcm99u8Xz47YI1mKaCzZtWZpdPhOt4+3UN2aSHIGUzAuDTK4xytefimKLqFLmdzK4mcD9Q89eBsZOYcl2xLFSEDAgBjGvPHruz++Ze8H2z4If1FLHbHWK3n4TjfrncOQYaoxF76G5MlBb2BPyfn4zx1poBKy8uldmNl/wkwoO9paSdX45b4P79t7esfpsLJaZdclb97pZv3fIxK/rQ4IyGJIwPRgMLS75Fw435Xzlxgs/ZU+F8XI81MfUeLrBPoxfSTZjWSYVVezwYOv3vm718SRULA2/XJr3xw7f5e7Sd9GjPiSw0w2BJnMycCuknPhfG23Euv6OkycOyxXnuaJbGdO/VhNTUhY2WX9lRZLD9ZFFzFx8Hgqv5NB6y2QrVQTZrLIpZybeaDsXPxL/TqvUeLeM2zIzsu7GHJTbCnQfGp2ln+V9rEDwcHjUP8d5M0/APE7vkgyyKWcl9tTcT45f61LhiR3weuyC7eS5z1MuXE1mY2rZxgt7cUevgPLfw9hc+yFL8pk4HK+2n9f+eh/P1gPkiHpuMHVNzUeebGoBOdAbiebYIGtVzKXM17fva7z6d/Wi2RYzVzHSjcHViIgICcGnoIbdXIr0ZTJltu323X+9+F6kgyrHaBZ7HbXfIJJzXDnIkiMRkbxyYiJcDE/n9lTPnpx3cRFM6ufVGptavpkG+UEMRKHmmT4LFPJ3O8eu/Z3F0txdSNhTU2N5PmFCvfgaxDd9r86wn2yic9UxjV2ueOX/75eJcNazN5F00uCYBS3OH7OO0I54XBhK7WFT+Qz5oxvMD75j/UsGdZqyDE8NDLEEc90ho94m3yHirooVuL3UHyyYgKfUuYBjk2tq93FUqztNKmNJQ6e6WwZ9Tb5R6moF8mOR9PCl5njAXd86q+9IBnaMbYbyRZ782iQ11B2gLXiO9UkazBJ1byXdZ7JrbRjPlqww3MMoyF7+RipLXyBTlK1dvVCJrfSvkH0aILJKBaeCXIyHi2QC2XXFz4uMufvZny25yRDOx+tiP6iYVAs/YiKHiYvGcLhhMYdj3omy6e43v29Khk68WhF7SD+SOEQ/XIsWiBNlCBqRi4xL9/stUxupf0PCx2PRnyfLT3HrH+YnFgoLhlMVC9T9nb3uuTOUptgOlI4xI+HlKOFixzqvwNoejwiZW2oCS0WnuBw4Z4r/i9ljWkePUj/ZHubsbFSySkpKSkpKSkpKSkpKSkpKW3g/3+PYisYNf7zAAAAAElFTkSuQmCC' + +''' +M""MMMMM""M dP +M MMMMM M 88 +M MMMMM M 88d888b. .d8888b. 88d888b. .d8888b. .d888b88 .d8888b. +M MMMMM M 88' `88 88' `88 88' `88 88' `88 88' `88 88ooood8 +M `MMM' M 88. .88 88. .88 88 88. .88 88. .88 88. ... +Mb dM 88Y888P' `8888P88 dP `88888P8 `88888P8 `88888P' +MMMMMMMMMMM 88 .88 + dP d8888P +MP""""""`MM oo +M mmmmm..M +M. `YM .d8888b. 88d888b. dP .dP dP .d8888b. .d8888b. +MMMMMMM. M 88ooood8 88' `88 88 d8' 88 88' `"" 88ooood8 +M. .MMM' M 88. ... 88 88 .88' 88 88. ... 88. ... +Mb. .dM `88888P' dP 8888P' dP `88888P' `88888P' +MMMMMMMMMMM +''' + +__upgrade_server_ip = 'upgradeapi.PySimpleGUI.com' +__upgrade_server_port = '5353' + + +def __send_dict(ip, port, dict_to_send): + """ + Send a dictionary to the upgrade server and get back a dictionary in response + :param ip: ip address of the upgrade server + :type ip: str + :param port: port number + :type port: int | str + :param dict_to_send: dictionary of items to send + :type dict_to_send: dict + :return: dictionary that is the reply + :rtype: dict + """ + + # print(f'sending dictionary to ip {ip} port {port}') + try: + # Create a socket object + s = socket.socket() + + s.settimeout(5.0) # set a 5 second timeout + + # connect to the server on local computer + s.connect((ip , int(port))) + # send a python dictionary + s.send(json.dumps(dict_to_send).encode()) + + # receive data from the server + reply_data = s.recv(1024).decode() + # close the connection + s.close() + except Exception as e: + # print(f'Error sending to server:', e) + # print(f'payload:\n', dict_to_send) + reply_data = e + try: + data_dict = json.loads(reply_data) + except Exception as e: + # print(f'UPGRADE THREAD - Error decoding reply {reply_data} as a dictionary. Error = {e}') + data_dict = {} + return data_dict + +def __show_previous_upgrade_information(): + """ + Shows information about upgrades if upgrade information is waiting to be shown + + :return: + """ + + # if nothing to show, then just return + if pysimplegui_user_settings.get('-upgrade info seen-', True) and not pysimplegui_user_settings.get('-upgrade info available-', False): + return + if pysimplegui_user_settings.get('-upgrade show only critical-', False) and pysimplegui_user_settings.get('-severity level-', '') != 'Critical': + return + + message1 = pysimplegui_user_settings.get('-upgrade message 1-', '') + message2 = pysimplegui_user_settings.get('-upgrade message 2-', '') + recommended_version = pysimplegui_user_settings.get('-upgrade recommendation-', '') + severity_level = pysimplegui_user_settings.get('-severity level-', '') + + message2 = r'https://www.PySimpleGUI.org' + + layout = [[Image(EMOJI_BASE64_HAPPY_THUMBS_UP), T('An upgrade is available & recommended', font='_ 14')], + [T('It is recommended you upgrade to version {}'.format(recommended_version))], + [T(message1, enable_events=True, k='-MESSAGE 1-')], + [T(message2, enable_events=True, k='-MESSAGE 2-')], + [CB('Do not show this message again in the future', default=True, k='-SKIP IN FUTURE-')], + [B('Close'), T('This window auto-closes in'), T('30', k='-CLOSE TXT-', text_color='white', background_color='red'), T('seconds')]] + + window = Window('PySimpleGUI Intelligent Upgrade', layout, finalize=True) + if 'http' in message1: + window['-MESSAGE 1-'].set_cursor('hand1') + if 'http' in message2: + window['-MESSAGE 2-'].set_cursor('hand1') + + seconds_left=30 + while True: + event, values = window.read(timeout=1000) + if event in ('Close', WIN_CLOSED) or seconds_left < 1: + break + if values['-SKIP IN FUTURE-']: + pysimplegui_user_settings['-upgrade info available-'] = False + pysimplegui_user_settings['-upgrade info seen-'] = True + if event == '-MESSAGE 1-' and 'http' in message1 and webbrowser_available: + webbrowser.open_new_tab(message1) + elif event == '-MESSAGE 2-' and 'http' in message2 and webbrowser_available: + webbrowser.open_new_tab(message2) + window['-CLOSE TXT-'].update(seconds_left) + seconds_left -= 1 + + window.close() + + +def __get_linux_distribution(): + with open('/etc/os-release') as f: + data = f.read() + lines = data.split('\n') + for line in lines: + if line.startswith('PRETTY_NAME'): + line_split = line.split('=')[1].strip('"') + line_tuple = tuple(line_split.split(' ')) + return line_tuple + return ('Linux Distro', 'Unknown','No lines Found in //etc//os-release') + + +def __perform_upgrade_check_thread(): + # print(f'Upgrade thread...seen = {pysimplegui_user_settings.get("-upgrade info seen-", False)}') + try: + if running_trinket(): + os_name = 'Trinket' + os_ver = __get_linux_distribution() + elif running_replit(): + os_name = 'REPL.IT' + os_ver = __get_linux_distribution() + elif running_windows(): + os_name = 'Windows' + os_ver = platform.win32_ver() + elif running_linux(): + os_name = 'Linux' + os_ver = __get_linux_distribution() + elif running_mac(): + os_name = 'Mac' + os_ver = platform.mac_ver() + else: + os_name = 'Other' + os_ver = '' + + psg_ver = version + framework_ver = framework_version + python_ver = sys.version + + upgrade_dict = { + 'OSName' : str(os_name), + 'OSVersion' : str(os_ver), + 'PythonVersion' : str(python_ver), + 'PSGVersion' : str(psg_ver), + 'FrameworkName' : 'tkinter', + 'FrameworkVersion' : str(framework_ver), + } + reply_data = __send_dict(__upgrade_server_ip, __upgrade_server_port, upgrade_dict) + + recommended_version = reply_data.get('SuggestedVersion', '') + message1 = reply_data.get('Message1', '') + message2 = reply_data.get('Message2', '') + severity_level = reply_data.get('SeverityLevel', '') + # If any part of the reply has changed from the last reply, overwrite the data and set flags so user will be informed + if (message1 or message2) and not running_trinket(): + if pysimplegui_user_settings.get('-upgrade message 1-', '') != message1 or \ + pysimplegui_user_settings.get('-upgrade message 2-', '') != message2 or \ + pysimplegui_user_settings.get('-upgrade recommendation-', '') != recommended_version or \ + pysimplegui_user_settings.get('-severity level-', '') != severity_level: + # Save the data to the settings file + pysimplegui_user_settings['-upgrade info seen-'] = False + pysimplegui_user_settings['-upgrade info available-'] = True + pysimplegui_user_settings['-upgrade message 1-'] = message1 + pysimplegui_user_settings['-upgrade message 2-'] = message2 + pysimplegui_user_settings['-upgrade recommendation-'] = recommended_version + pysimplegui_user_settings['-severity level-'] = severity_level + except Exception as e: + reply_data = {} + # print('Upgrade server error', e) + # print(f'Upgrade Reply = {reply_data}') + +def __perform_upgrade_check(): + # For now, do not show data returned. Still testing and do not want to "SPAM" users with any popups + # __show_previous_upgrade_information() + threading.Thread(target=lambda: __perform_upgrade_check_thread(), daemon=True).start() + + # =========================================================================# # MP""""""`MM dP dP # M mmmmm..M 88 88 @@ -23229,7 +23465,7 @@ GREEN_CHECK_BASE64 = b'iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAAA4qEECAAAJV0lEQVR4n def _github_issue_post_make_markdown(issue_type, operating_system, os_ver, psg_port, psg_ver, gui_ver, python_ver, python_exp, prog_exp, used_gui, gui_notes, - cb_docs, cb_demos, cb_demo_port, cb_readme_other, cb_command_line, cb_issues, cb_github, + cb_docs, cb_demos, cb_demo_port, cb_readme_other, cb_command_line, cb_issues, cb_latest_pypi, cb_github, detailed_desc, code, project_details, where_found): body = \ """ @@ -23287,11 +23523,12 @@ def _github_issue_post_make_markdown(issue_type, operating_system, os_ver, psg_p These items may solve your problem. Please check those you've done by changing - [ ] to - [X] - [{}] Searched main docs for your problem www.PySimpleGUI.org -- [{}] Looked for Demo Programs that are similar to your goal Demos.PySimpleGUI.org +- [{}] Looked for Demo Programs that are similar to your goal. It is recommend you use the Demo Browser! Demos.PySimpleGUI.org - [{}] If not tkinter - looked for Demo Programs for specific port - [{}] For non tkinter - Looked at readme for your specific port if not PySimpleGUI (Qt, WX, Remi) - [{}] Run your program outside of your debugger (from a command line) - [{}] Searched through Issues (open and closed) to see if already reported Issues.PySimpleGUI.org +- [{}] Upgraded to the latest official release of PySimpleGUI on PyPI - [{}] Tried using the PySimpleGUI.py file on GitHub. Your problem may have already been fixed but not released ## Detailed Description @@ -23312,7 +23549,7 @@ These items may solve your problem. Please check those you've done by changing - """.format(python_exp, prog_exp, used_gui, gui_notes, - cb_docs, cb_demos, cb_demo_port, cb_readme_other, cb_command_line, cb_issues, cb_github, + cb_docs, cb_demos, cb_demo_port, cb_readme_other, cb_command_line, cb_issues, cb_latest_pypi, cb_github, detailed_desc, code if len(code) > 10 else '# Paste your code here') @@ -23496,11 +23733,12 @@ def main_open_github_issue(): [In(size=(25, 1), k='-EXP NOTES-', expand_x=True)]] checklist = (('Searched main docs for your problem', 'www.PySimpleGUI.org'), - ('Looked for Demo Programs that are similar to your goal ', 'http://Demos.PySimpleGUI.org'), + ('Looked for Demo Programs that are similar to your goal.\nIt is recommend you use the Demo Browser!', 'https://Demos.PySimpleGUI.org'), ('If not tkinter - looked for Demo Programs for specific port', ''), ('For non tkinter - Looked at readme for your specific port if not PySimpleGUI (Qt, WX, Remi)', ''), ('Run your program outside of your debugger (from a command line)', ''), - ('Searched through Issues (open and closed) to see if already reported', 'http://Issues.PySimpleGUI.org'), + ('Searched through Issues (open and closed) to see if already reported', 'https://Issues.PySimpleGUI.org'), + ('Upgraded to the latest official release of PySimpleGUI on PyPI', 'https://Upgrading.PySimpleGUI.org'), ('Tried using the PySimpleGUI.py file on GitHub. Your problem may have already been fixed but not released.', '')) checklist_col1 = Col([[CB(c, k=('-CB-', i)), T(t, k='-T{}-'.format(i), enable_events=True)] for i, (c, t) in enumerate(checklist[:4])], k='-C FRAME CBs1-') @@ -23563,6 +23801,15 @@ def main_open_github_issue(): # window['-ML DETAILS-'].expand(True, True, True) # window['-ML MARKDOWN-'].expand(True, True, True) # window['-PANE-'].expand(True, True, True) + + if running_mac(): + window['-OS MAC VER-'].update(platform.mac_ver()) + elif running_windows(): + window['-OS WIN VER-'].update(platform.win32_ver()) + elif running_linux(): + window['-OS LINUX VER-'].update(platform.libc_ver()) + + window.bring_to_front() while True: # Event Loop event, values = window.read() @@ -23624,7 +23871,7 @@ def main_open_github_issue(): continue cb_dict = {'cb_docs': checkboxes[0], 'cb_demos': checkboxes[1], 'cb_demo_port': checkboxes[2], 'cb_readme_other': checkboxes[3], - 'cb_command_line': checkboxes[4], 'cb_issues': checkboxes[5], 'cb_github': checkboxes[6], 'detailed_desc': values['-ML DETAILS-'], + 'cb_command_line': checkboxes[4], 'cb_issues': checkboxes[5], 'cb_latest_pypi': checkboxes[6], 'cb_github': checkboxes[7], 'detailed_desc': values['-ML DETAILS-'], 'code': values['-ML CODE-'], 'project_details': values['-ML PROJECT DETAILS-'].rstrip(), 'where_found': values['-ML FOUND PSG-']} @@ -24199,37 +24446,37 @@ def main_sdk_help(): """ online_help_links = { - 'Button': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#button-element', - 'ButtonMenu': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#buttonmenu-element', - 'Canvas': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#canvas-element', - 'Checkbox': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#checkbox-element', - 'Column': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#column-element', - 'Combo': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#combo-element', - 'Frame': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#frame-element', - 'Graph': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#graph-element', - 'HorizontalSeparator': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#horizontalseparator-element', - 'Image': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#image-element', - 'Input': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#input-element', - 'Listbox': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#listbox-element', - 'Menu': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#menu-element', - 'MenubarCustom': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#menubarcustom-element', - 'Multiline': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#multiline-element', - 'OptionMenu': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#optionmenu-element', - 'Output': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#output-element', - 'Pane': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#pane-element', - 'ProgressBar': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#progressbar-element', - 'Radio': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#radio-element', - 'Slider': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#slider-element', - 'Spin': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#spin-element', - 'StatusBar': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#statusbar-element', - 'Tab': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#tab-element', - 'TabGroup': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#tabgroup-element', - 'Table': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#table-element', - 'Text': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#text-element', - 'Titlebar': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#titlebar-element', - 'Tree': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#tree-element', - 'VerticalSeparator': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#verticalseparator-element', - 'Window': r'https://pysimplegui.readthedocs.io/en/latest/call%20reference/#window', + 'Button': r'https://PySimpleGUI.org/en/latest/call%20reference/#button-element', + 'ButtonMenu': r'https://PySimpleGUI.org/en/latest/call%20reference/#buttonmenu-element', + 'Canvas': r'https://PySimpleGUI.org/en/latest/call%20reference/#canvas-element', + 'Checkbox': r'https://PySimpleGUI.org/en/latest/call%20reference/#checkbox-element', + 'Column': r'https://PySimpleGUI.org/en/latest/call%20reference/#column-element', + 'Combo': r'https://PySimpleGUI.org/en/latest/call%20reference/#combo-element', + 'Frame': r'https://PySimpleGUI.org/en/latest/call%20reference/#frame-element', + 'Graph': r'https://PySimpleGUI.org/en/latest/call%20reference/#graph-element', + 'HorizontalSeparator': r'https://PySimpleGUI.org/en/latest/call%20reference/#horizontalseparator-element', + 'Image': r'https://PySimpleGUI.org/en/latest/call%20reference/#image-element', + 'Input': r'https://PySimpleGUI.org/en/latest/call%20reference/#input-element', + 'Listbox': r'https://PySimpleGUI.org/en/latest/call%20reference/#listbox-element', + 'Menu': r'https://PySimpleGUI.org/en/latest/call%20reference/#menu-element', + 'MenubarCustom': r'https://PySimpleGUI.org/en/latest/call%20reference/#menubarcustom-element', + 'Multiline': r'https://PySimpleGUI.org/en/latest/call%20reference/#multiline-element', + 'OptionMenu': r'https://PySimpleGUI.org/en/latest/call%20reference/#optionmenu-element', + 'Output': r'https://PySimpleGUI.org/en/latest/call%20reference/#output-element', + 'Pane': r'https://PySimpleGUI.org/en/latest/call%20reference/#pane-element', + 'ProgressBar': r'https://PySimpleGUI.org/en/latest/call%20reference/#progressbar-element', + 'Radio': r'https://PySimpleGUI.org/en/latest/call%20reference/#radio-element', + 'Slider': r'https://PySimpleGUI.org/en/latest/call%20reference/#slider-element', + 'Spin': r'https://PySimpleGUI.org/en/latest/call%20reference/#spin-element', + 'StatusBar': r'https://PySimpleGUI.org/en/latest/call%20reference/#statusbar-element', + 'Tab': r'https://PySimpleGUI.org/en/latest/call%20reference/#tab-element', + 'TabGroup': r'https://PySimpleGUI.org/en/latest/call%20reference/#tabgroup-element', + 'Table': r'https://PySimpleGUI.org/en/latest/call%20reference/#table-element', + 'Text': r'https://PySimpleGUI.org/en/latest/call%20reference/#text-element', + 'Titlebar': r'https://PySimpleGUI.org/en/latest/call%20reference/#titlebar-element', + 'Tree': r'https://PySimpleGUI.org/en/latest/call%20reference/#tree-element', + 'VerticalSeparator': r'https://PySimpleGUI.org/en/latest/call%20reference/#verticalseparator-element', + 'Window': r'https://PySimpleGUI.org/en/latest/call%20reference/#window', } NOT_AN_ELEMENT = 'Not An Element' @@ -24516,14 +24763,23 @@ def _create_main_window(): frame6 = [[VPush()],[graph_elem]] - global_settings_tab_layout = [[T('Settings Filename:'), T(pysimplegui_user_settings.full_filename, s=(50,2))], - [T('Settings Dictionary:'), MLine(pysimplegui_user_settings, size=(50,8))], - ] - themes_tab_layout = [[T('You can see a preview of the themes, the color swatches, or switch themes for this window')], [T('If you want to change the default theme for PySimpleGUI, use the Global Settings')], [B('Themes'), B('Theme Swatches'), B('Switch Themes')]] + + upgrade_recommendation_tab_layout = [[T('Latest Recommendation and Announcements For You', font='_ 14')], + [T('Severity Level of Update:'), T(pysimplegui_user_settings.get('-severity level-',''))], + [T('Recommended Version To Upgrade To:'), T(pysimplegui_user_settings.get('-upgrade recommendation-',''))], + [T(pysimplegui_user_settings.get('-upgrade message 1-',''))], + [T(pysimplegui_user_settings.get('-upgrade message 2-',''))], + [Checkbox('Show Only Critical Messages', default=pysimplegui_user_settings.get('-upgrade show only critical-', False), key='-UPGRADE SHOW ONLY CRITICAL-', enable_events=True)], + [Button('Show Notification Again'), B('Upgrade from GitHub', button_color='white on red', key='-UPGRADE FROM GITHUB-'), +], + ] + tab_upgrade = Tab('Upgrade\n',upgrade_recommendation_tab_layout, expand_x=True) + + tab1 = Tab('Graph\n', frame6, tooltip='Graph is in here', title_color='red') tab2 = Tab('CB, Radio\nList, Combo', [[Frame('Multiple Choice Group', frame2, title_color='#FFFFFF', tooltip='Checkboxes, radio buttons, etc', vertical_alignment='t',), @@ -24535,25 +24791,25 @@ def _create_main_window(): tab6 = Tab('Course or\nSponsor', frame7, k='-TAB SPONSOR-') tab7 = Tab('Popups\n', pop_test_tab_layout, k='-TAB POPUP-') tab8 = Tab('Themes\n', themes_tab_layout, k='-TAB THEMES-') - tab9 = Tab('Global\nSettings', global_settings_tab_layout, k='-TAB GlOBAL SETTINGS-') def VerLine(version, description, justification='r', size=(40, 1)): - return [T(version, justification=justification, font='Any 12', text_color='yellow', size=size, pad=(0,0)), vtop(T(description, font='Any 12', pad=(0,0)))] + return [T(version, justification=justification, font='Any 12', text_color='yellow', size=size, pad=(0,0)), T(description, font='Any 12', pad=(0,0))] layout_top = Column([ [Image(EMOJI_BASE64_HAPPY_BIG_SMILE, enable_events=True, key='-LOGO-', tooltip='This is PySimpleGUI logo'), Image(data=DEFAULT_BASE64_LOADING_GIF, enable_events=True, key='-IMAGE-'), - Text('PySimpleGUI Test Harness\nYou are running PySimpleGUI.py file vs importing', font='ANY 14', + Text('PySimpleGUI Test Harness', font='ANY 14', tooltip='My tooltip', key='-TEXT1-')], VerLine(ver, 'PySimpleGUI Version') + [Image(HEART_3D_BASE64, subsample=4)], - VerLine('{}/{}'.format(tkversion, tclversion), 'TK/TCL Versions'), + # VerLine('{}/{}'.format(tkversion, tclversion), 'TK/TCL Versions'), VerLine(tclversion_detailed, 'detailed tkinter version'), - VerLine(os.path.dirname(os.path.abspath(__file__)), 'PySimpleGUI Location', size=(40, 2)), + VerLine(os.path.dirname(os.path.abspath(__file__)), 'PySimpleGUI Location', size=(40, None)), + VerLine(sys.executable, 'Python Executable'), VerLine(sys.version, 'Python Version', size=(40,2)) +[Image(PYTHON_COLORED_HEARTS_BASE64, subsample=3, k='-PYTHON HEARTS-', enable_events=True)]], pad=0) layout_bottom = [ [B(SYMBOL_DOWN, pad=(0, 0), k='-HIDE TABS-'), - pin(Col([[TabGroup([[tab1, tab2, tab3, tab6, tab4, tab5, tab7, tab8, tab9]], key='-TAB_GROUP-')]], k='-TAB GROUP COL-'))], + pin(Col([[TabGroup([[tab1, tab2, tab3, tab6, tab4, tab5, tab7, tab8, tab_upgrade]], key='-TAB_GROUP-')]], k='-TAB GROUP COL-'))], [B('Button', highlight_colors=('yellow', 'red'),pad=(1, 0)), B('ttk Button', use_ttk_buttons=True, tooltip='This is a TTK Button',pad=(1, 0)), B('See-through Mode', tooltip='Make the background transparent',pad=(1, 0)), @@ -24656,7 +24912,7 @@ def main(): elif event.startswith('See'): window._see_through = not window._see_through window.set_transparent_color(theme_background_color() if window._see_through else '') - elif event == '-INSTALL-': + elif event == '-INSTALL-' or event == '-UPGRADE FROM GITHUB-': _upgrade_gui() elif event == 'Popup': popup('This is your basic popup', keep_on_top=True) @@ -24726,6 +24982,13 @@ def main(): window.minimize() main_open_github_issue() window.normal() + elif event == 'Show Notification Again': + pysimplegui_user_settings.set('-upgrade info seen-', False) + __show_previous_upgrade_information() + elif event == '-UPGRADE SHOW ONLY CRITICAL-': + pysimplegui_user_settings.set('-upgrade show only critical-', values['-UPGRADE SHOW ONLY CRITICAL-']) + + i += 1 # _refresh_debugger() print('event = ', event) @@ -24813,6 +25076,10 @@ if running_windows(): _read_mac_global_settings() + +if _mac_should_set_alpha_to_99(): + # Applyting Mac OS 12.3+ Alpha Channel fix. Sets the default Alpha Channel to 0.99 + set_options(alpha_channel=0.99) # if running_mac(): # print('Your Mac patches are:') # print('Modal windows disabled:', ENABLE_MAC_MODAL_DISABLE_PATCH) @@ -24820,6 +25087,10 @@ _read_mac_global_settings() # print('No grab anywhere allowed with titlebar:', ENABLE_MAC_DISABLE_GRAB_ANYWHERE_WITH_TITLEBAR) # print('Currently the no titlebar patch ' + ('WILL' if _mac_should_apply_notitlebar_patch() else 'WILL NOT') + ' be applied') + +__perform_upgrade_check() + + # -------------------------------- ENTRY POINT IF RUN STANDALONE -------------------------------- # if __name__ == '__main__': # To execute the upgrade from command line, type: