diff --git a/FreeSimpleGUI/__init__.py b/FreeSimpleGUI/__init__.py index 3d9efbd5..f71754b7 100644 --- a/FreeSimpleGUI/__init__.py +++ b/FreeSimpleGUI/__init__.py @@ -31,10 +31,109 @@ from tkinter import filedialog # noqa from tkinter import ttk from tkinter.colorchooser import askcolor # noqa -from typing import Any # noqa -from typing import Dict # noqa -from typing import List # noqa -from typing import Tuple # noqa + +# noreorder +from typing import ( + Any, + Dict, + List, + Literal, + Optional, + Tuple, + Union, +) + +from FreeSimpleGUI._utils import _error_popup_with_traceback +from FreeSimpleGUI.elements.base import Element +from FreeSimpleGUI.elements.button import Button +from FreeSimpleGUI.elements.button import ButtonMenu +from FreeSimpleGUI.elements.calendar import TKCalendar +from FreeSimpleGUI.elements.canvas import Canvas +from FreeSimpleGUI.elements.checkbox import Checkbox +from FreeSimpleGUI.elements.column import Column +from FreeSimpleGUI.elements.column import TkFixedFrame +from FreeSimpleGUI.elements.column import TkScrollableFrame +from FreeSimpleGUI.elements.combo import Combo +from FreeSimpleGUI.elements.error import ErrorElement +from FreeSimpleGUI.elements.frame import Frame +from FreeSimpleGUI.elements.graph import Graph +from FreeSimpleGUI.elements.helpers import AddMenuItem +from FreeSimpleGUI.elements.helpers import button_color_to_tuple +from FreeSimpleGUI.elements.image import Image +from FreeSimpleGUI.elements.input import Input +from FreeSimpleGUI.elements.list_box import Listbox +from FreeSimpleGUI.elements.menu import Menu +from FreeSimpleGUI.elements.multiline import Multiline +from FreeSimpleGUI.elements.multiline import Output +from FreeSimpleGUI.elements.option_menu import OptionMenu +from FreeSimpleGUI.elements.pane import Pane +from FreeSimpleGUI.elements.progress_bar import ProgressBar +from FreeSimpleGUI.elements.progress_bar import TKProgressBar +from FreeSimpleGUI.elements.radio import Radio +from FreeSimpleGUI.elements.separator import HorizontalSeparator +from FreeSimpleGUI.elements.separator import VerticalSeparator +from FreeSimpleGUI.elements.sizegrip import Sizegrip +from FreeSimpleGUI.elements.slider import Slider +from FreeSimpleGUI.elements.spin import Spin +from FreeSimpleGUI.elements.status_bar import StatusBar +from FreeSimpleGUI.elements.stretch import Push +from FreeSimpleGUI.elements.stretch import VPush +from FreeSimpleGUI.elements.tab import Tab +from FreeSimpleGUI.elements.tab import TabGroup +from FreeSimpleGUI.elements.table import Table +from FreeSimpleGUI.elements.text import Text +from FreeSimpleGUI.elements.tree import Tree +from FreeSimpleGUI.elements.tree import TreeData +from FreeSimpleGUI.tray import SystemTray +from FreeSimpleGUI.window import Window + +# Element aliases +In = Input +InputText = Input +I = Input # noqa +InputCombo = Combo +DropDown = InputCombo +Drop = InputCombo +DD = Combo +InputOptionMenu = OptionMenu +LBox = Listbox +LB = Listbox +R = Radio +Rad = Radio +CB = Checkbox +CBox = Checkbox +Check = Checkbox +Sp = Spin +ML = Multiline +MLine = Multiline +Txt = Text # type: Text +T = Text # type: Text +SBar = StatusBar +B = Button +Btn = Button +BMenu = ButtonMenu +BM = ButtonMenu +Im = Image +PBar = ProgressBar +Prog = ProgressBar +Progress = ProgressBar +G = Graph +Fr = Frame +VSeperator = VerticalSeparator +VSeparator = VerticalSeparator +VSep = VerticalSeparator +MenuBar = Menu +HSeparator = HorizontalSeparator +HSep = HorizontalSeparator +SGrip = Sizegrip +Sl = Slider +Col = Column +MenuBar = Menu +P = Push +Stretch = Push +VStretch = VPush +VP = VPush +FlexForm = Window # get the tkinter detailed version @@ -782,7 +881,7 @@ def running_replit(): # ====================================================================== # # One-liner functions that are handy as f_ck # # ====================================================================== # -def rgb(red, green, blue): +def rgb(red: int, green: int, blue: int) -> str: """ Given integer values of Red, Green, Blue, return a color string "#RRGGBB" :param red: Red portion from 0 to 255 @@ -973,7 +1072,7 @@ class ToolTip: This is an INTERNALLY USED only class. Users should not refer to this class at all. """ - def __init__(self, widget, text, timeout=DEFAULT_TOOLTIP_TIME): + def __init__(self, widget, text: str, timeout: int = DEFAULT_TOOLTIP_TIME) -> None: """ :param widget: The tkinter widget :type widget: widget type varies @@ -993,7 +1092,7 @@ def __init__(self, widget, text, timeout=DEFAULT_TOOLTIP_TIME): self.widget.bind('', self.leave) self.widget.bind('', self.leave) - def enter(self, event=None): + def enter(self, event=None) -> None: """ Called by tkinter when mouse enters a widget :param event: from tkinter. Has x,y coordinates of mouse @@ -1004,7 +1103,7 @@ def enter(self, event=None): self.y = event.y self.schedule() - def leave(self, event=None): + def leave(self, event=None) -> None: """ Called by tktiner when mouse exits a widget :param event: from tkinter. Event info that's not used by function. @@ -1014,14 +1113,14 @@ def leave(self, event=None): self.unschedule() self.hidetip() - def schedule(self): + def schedule(self) -> None: """ Schedule a timer to time how long mouse is hovering """ self.unschedule() self.id = self.widget.after(self.timeout, self.showtip) - def unschedule(self): + def unschedule(self) -> None: """ Cancel timer used to time mouse hover """ @@ -1029,7 +1128,7 @@ def unschedule(self): self.widget.after_cancel(self.id) self.id = None - def showtip(self): + def showtip(self) -> None: """ Creates a topoltip window with the tooltip text inside of it """ @@ -1061,7 +1160,7 @@ def showtip(self): label.config(font=TOOLTIP_FONT) label.pack() - def hidetip(self): + def hidetip(self) -> None: """ Destroy the tooltip window """ @@ -1071,11 +1170,11 @@ def hidetip(self): class _TimerPeriodic: - id_counter = 1 + id_counter: int = 1 # Dictionary containing the active timers. Format is {id : _TimerPeriodic object} active_timers = {} # type: dict[int:_TimerPeriodic] - def __init__(self, window, frequency_ms, key=EVENT_TIMER, repeating=True): + def __init__(self, window: Window, frequency_ms: int, key=EVENT_TIMER, repeating: bool = True): """ :param window: The window to send events to :type window: FreeSimpleGUI.window.Window @@ -1084,16 +1183,16 @@ def __init__(self, window, frequency_ms, key=EVENT_TIMER, repeating=True): :param repeating: If True then the timer will run, repeatedly sending events, until stopped :type repeating: bool """ - self.window = window - self.frequency_ms = frequency_ms - self.repeating = repeating + self.window: Window = window + self.frequency_ms: int = frequency_ms + self.repeating: bool = repeating self.key = key - self.id = _TimerPeriodic.id_counter + self.id: int = _TimerPeriodic.id_counter _TimerPeriodic.id_counter += 1 self.start() @classmethod - def stop_timer_with_id(cls, timer_id): + def stop_timer_with_id(cls, timer_id: int): """ Not user callable! :return: A simple counter that makes each container element unique @@ -1104,7 +1203,7 @@ def stop_timer_with_id(cls, timer_id): timer.stop() @classmethod - def stop_all_timers_for_window(cls, window): + def stop_all_timers_for_window(cls, window: Window) -> None: """ Stops all timers for a given window :param window: The window to stop timers for @@ -1115,7 +1214,7 @@ def stop_all_timers_for_window(cls, window): timer.running = False @classmethod - def get_all_timers_for_window(cls, window): + def get_all_timers_for_window(cls, window: Window): """ Returns a list of timer IDs for a given window :param window: The window to find timers for @@ -1130,7 +1229,7 @@ def get_all_timers_for_window(cls, window): return timers - def timer_thread(self): + def timer_thread(self) -> None: """ The thread that sends events to the window. Runs either once or in a loop until timer is stopped """ @@ -1149,7 +1248,7 @@ def timer_thread(self): del _TimerPeriodic.active_timers[self.id] return - def start(self): + def start(self) -> None: """ Starts a timer by starting a timer thread Adds timer to the list of active timers @@ -1159,14 +1258,14 @@ def start(self): self.thread.start() _TimerPeriodic.active_timers[self.id] = self - def stop(self): + def stop(self) -> None: """ Stops a timer """ self.running = False -def _long_func_thread(window, end_key, original_func): +def _long_func_thread(window: Window, end_key, original_func) -> None: """ Used to run long operations on the user's behalf. Called by the window object @@ -1183,7 +1282,7 @@ def _long_func_thread(window, end_key, original_func): window.write_event_value(end_key, return_value) -def _timeout_alarm_callback_hidden(): +def _timeout_alarm_callback_hidden() -> None: """ Read Timeout Alarm callback. Will kick a mainloop call out of the tkinter event loop and cause it to return """ @@ -1199,7 +1298,7 @@ def _timeout_alarm_callback_hidden(): Window._window_that_exited = None -def read_all_windows(timeout=None, timeout_key=TIMEOUT_KEY): +def read_all_windows(timeout: Optional[int] = None, timeout_key=TIMEOUT_KEY): """ Reads all windows that are "active" when the call is made. "Active" means that it's been finalized or read. If a window has not been finalized then it will not be considered an "active window" @@ -1291,7 +1390,7 @@ def read_all_windows(timeout=None, timeout_key=TIMEOUT_KEY): # ------------------------- A fake Element... the Pad Element ------------------------- # -def Sizer(h_pixels=0, v_pixels=0): +def Sizer(h_pixels: int = 0, v_pixels: int = 0): """ "Pushes" out the size of whatever it is placed inside of. This includes Columns, Frames, Tabs and Windows @@ -1306,7 +1405,7 @@ def Sizer(h_pixels=0, v_pixels=0): return Canvas(size=(0, 0), pad=((h_pixels, 0), (v_pixels, 0))) -def pin(elem, vertical_alignment=None, shrink=True, expand_x=None, expand_y=None): +def pin(elem: Element, vertical_alignment=None, shrink: bool = True, expand_x: Optional[bool] = None, expand_y: Optional[bool] = None): """ Pin's an element provided into a layout so that when it's made invisible and visible again, it will be in the correct place. Otherwise it will be placed at the end of its containing window/column. @@ -1342,7 +1441,7 @@ def pin(elem, vertical_alignment=None, shrink=True, expand_x=None, expand_y=None return Column([[elem]], pad=(0, 0), vertical_alignment=vertical_alignment, expand_x=expand_x, expand_y=expand_y) -def vtop(elem_or_row, expand_x=None, expand_y=None, background_color=None): +def vtop(elem_or_row, expand_x: Optional[bool] = None, expand_y: Optional[bool] = None, background_color: Optional[str] = None): """ Align an element or a row of elements to the top of the row that contains it @@ -1381,7 +1480,7 @@ def vtop(elem_or_row, expand_x=None, expand_y=None, background_color=None): ) -def vcenter(elem_or_row, expand_x=None, expand_y=None, background_color=None): +def vcenter(elem_or_row, expand_x: Optional[bool] = None, expand_y: Optional[bool] = None, background_color: Optional[str] = None): """ Align an element or a row of elements to the center of the row that contains it @@ -1420,7 +1519,7 @@ def vcenter(elem_or_row, expand_x=None, expand_y=None, background_color=None): ) -def vbottom(elem_or_row, expand_x=None, expand_y=None, background_color=None): +def vbottom(elem_or_row, expand_x: Optional[bool] = None, expand_y: Optional[bool] = None, background_color: Optional[str] = None): """ Align an element or a row of elements to the bottom of the row that contains it @@ -1459,7 +1558,7 @@ def vbottom(elem_or_row, expand_x=None, expand_y=None, background_color=None): ) -def Titlebar(title='', icon=None, text_color=None, background_color=None, font=None, key=None, k=None): +def Titlebar(title: str = '', icon=None, text_color: Optional[str] = None, background_color: Optional[str] = None, font=None, key=None, k=None): """ A custom titlebar that replaces the OS provided titlebar, thus giving you control the is not possible using the OS provided titlebar such as the color. @@ -1567,16 +1666,16 @@ def Titlebar(title='', icon=None, text_color=None, background_color=None, font=N def MenubarCustom( menu_definition, - disabled_text_color=None, + disabled_text_color: Optional[str] = None, bar_font=None, font=None, - tearoff=False, + tearoff: bool = False, pad=0, p=None, - background_color=None, - text_color=None, - bar_background_color=None, - bar_text_color=None, + background_color: Optional[str] = None, + text_color: Optional[str] = None, + bar_background_color: Optional[str] = None, + bar_text_color: Optional[str] = None, key=None, k=None, ): @@ -1656,26 +1755,26 @@ def MenubarCustom( # ------------------------- FOLDER BROWSE Element lazy function ------------------------- # def FolderBrowse( - button_text='Browse', + button_text: str = 'Browse', target=(ThisRow, -1), - initial_folder=None, - tooltip=None, + initial_folder: Optional[str] = None, + tooltip: Optional[str] = None, size=(None, None), s=(None, None), - auto_size_button=None, + auto_size_button: Optional[bool] = None, button_color=None, - disabled=False, - change_submits=False, - enable_events=False, + disabled: bool = False, + change_submits: bool = False, + enable_events: bool = False, font=None, pad=None, p=None, key=None, k=None, - visible=True, + visible: bool = True, metadata=None, - expand_x=False, - expand_y=False, + expand_x: bool = False, + expand_y: bool = False, ): """ :param button_text: text in the button (Default value = 'Browse') @@ -4262,7 +4361,7 @@ def _BuildResultsForSubform(form, initialize_only, top_level_form): return form.ReturnValues -def fill_form_with_values(window, values_dict): +def fill_form_with_values(window: Window, values_dict): """ Fills a window with values provided in a values dictionary { element_key : new_value } @@ -4451,7 +4550,7 @@ def _make_ttk_style_name(base_style, element, primary_style=False): return style_name -def _make_ttk_scrollbar(element, orientation, window): +def _make_ttk_scrollbar(element, orientation, window: Window): """ Creates a ttk scrollbar for elements as they are being added to the layout @@ -4583,7 +4682,7 @@ def _make_ttk_scrollbar(element, orientation, window): # @_timeit -def PackFormIntoFrame(form, containing_frame, toplevel_form): +def PackFormIntoFrame(form: Window, containing_frame, toplevel_form: Window): """ :param form: a window class @@ -6865,7 +6964,7 @@ def _get_hidden_master_root(): return Window.hidden_master_root -def _no_titlebar_setup(window): +def _no_titlebar_setup(window: Window): """ Does the operations required to turn off the titlebar for the window. The Raspberry Pi required the settings to be make after the window's creation. @@ -6892,7 +6991,7 @@ def _no_titlebar_setup(window): warnings.warn(f'** Problem setting no titlebar {e} **', UserWarning) -def _convert_window_to_tk(window): +def _convert_window_to_tk(window: Window): """ :type window: (Window) @@ -6953,7 +7052,7 @@ def _convert_window_to_tk(window): # ----====----====----====----====----==== STARTUP TK ====----====----====----====----====----# -def StartupTK(window): +def StartupTK(window: Window): """ NOT user callable Creates the window (for real) lays out all the elements, etc. It's a HUGE set of things it does. It's the basic @@ -6963,7 +7062,7 @@ def StartupTK(window): :type window: (Window) """ - window = window # type: Window + window: Window = window # global _my_windows # ow = _my_windows.NumOpenWindows ow = Window.NumOpenWindows @@ -7225,16 +7324,16 @@ class _QuickMeter: def __init__( self, - title, - current_value, - max_value, + title: str, + current_value: int, + max_value: int, key, *args, orientation='v', bar_color=(None, None), button_color=(None, None), size=DEFAULT_PROGRESS_BAR_SIZE, - border_width=None, + border_width: Optional[int] = None, grab_anywhere=False, no_titlebar=False, keep_on_top=None, @@ -7280,7 +7379,7 @@ def __init__( self.button_color = button_color self.border_width = border_width self.no_titlebar = no_titlebar - self.title = title + self.title: str = title self.current_value = current_value self.max_value = max_value self.close_reason = None @@ -7872,7 +7971,7 @@ def easy_print_close(): CPRINT_DESTINATION_MULTILINE_ELMENT_KEY = None -def cprint_set_output_destination(window, multiline_key): +def cprint_set_output_destination(window: Window, multiline_key): """ Sets up the color print (cprint) output destination :param window: The window that the cprint call will route the output to @@ -11597,16 +11696,16 @@ def popup_get_text( def popup_get_date( - start_mon=None, - start_day=None, - start_year=None, - begin_at_sunday_plus=0, - no_titlebar=True, - title='Choose Date', - keep_on_top=True, + start_mon: Optional[int] = None, + start_day: Optional[int] = None, + start_year: Optional[int] = None, + begin_at_sunday_plus: int = 0, + no_titlebar: bool = True, + title: str = 'Choose Date', + keep_on_top: bool = True, location=(None, None), relative_location=(None, None), - close_when_chosen=False, + close_when_chosen: bool = False, icon=None, locale=None, month_names=None, @@ -11614,8 +11713,8 @@ def popup_get_date( day_font='TkFixedFont 9', mon_year_font='TkFixedFont 10', arrow_font='TkFixedFont 7', - modal=True, -): + modal: bool = True, +) -> Optional[int, int, int]: """ Display a calendar window, get the user's choice, return as a tuple (mon, day, year) @@ -11676,7 +11775,7 @@ def popup_get_date( cur_day = cur_day cur_year = start_year or cur_year - def update_days(window, month, year, begin_at_sunday_plus): + def update_days(window: Window, month: int, year: int, begin_at_sunday_plus): [window[(week, day)].update('') for day in range(7) for week in range(6)] weeks = calendar.monthcalendar(year, month) month_days = list(itertools.chain.from_iterable([[0 for _ in range(8 - begin_at_sunday_plus)]] + weeks)) @@ -11839,21 +11938,21 @@ def make_days_layout(): def popup_animated( image_source, - message=None, - background_color=None, - text_color=None, + message: Optional[str] = None, + background_color: Optional[str] = None, + text_color: Optional[str] = None, font=None, - no_titlebar=True, - grab_anywhere=True, - keep_on_top=True, + no_titlebar: bool = True, + grab_anywhere: bool = True, + keep_on_top: bool = True, location=(None, None), relative_location=(None, None), alpha_channel=None, - time_between_frames=0, + time_between_frames: int = 0, transparent_color=None, - title='', + title: str = '', icon=None, - no_buffering=False, + no_buffering: bool = False, ): """ Show animation one frame at a time. This function has its own internal clocking meaning you can call it at any frequency @@ -12029,7 +12128,7 @@ def popup_notify( ) -def popup_menu(window, element, menu_def, title=None, location=(None, None)): +def popup_menu(window: Window, element, menu_def, title=None, location=(None, None)): """ Makes a "popup menu" This type of menu is what you get when a normal menu or a right click menu is torn off @@ -16346,7 +16445,7 @@ def main(): set_options(force_modal_windows=forced_modal) -def _optional_window_data(window): +def _optional_window_data(window: Window): """ A function to help with testing PySimpleGUI releases. Makes it easier to add a watermarked line to the bottom of a window while testing release candidates. @@ -16387,104 +16486,12 @@ def _optional_window_data(window): set_options(alpha_channel=0.99) -from FreeSimpleGUI.elements.base import Element -from FreeSimpleGUI.elements.button import Button -from FreeSimpleGUI.elements.button import ButtonMenu -from FreeSimpleGUI.elements.calendar import TKCalendar -from FreeSimpleGUI.elements.canvas import Canvas -from FreeSimpleGUI.elements.checkbox import Checkbox -from FreeSimpleGUI.elements.column import Column -from FreeSimpleGUI.elements.column import TkFixedFrame -from FreeSimpleGUI.elements.column import TkScrollableFrame -from FreeSimpleGUI.elements.combo import Combo -from FreeSimpleGUI.elements.error import ErrorElement -from FreeSimpleGUI.elements.frame import Frame -from FreeSimpleGUI.elements.graph import Graph -from FreeSimpleGUI.elements.image import Image -from FreeSimpleGUI.elements.input import Input -from FreeSimpleGUI.elements.list_box import Listbox -from FreeSimpleGUI.elements.menu import Menu -from FreeSimpleGUI.elements.helpers import AddMenuItem, button_color_to_tuple -from FreeSimpleGUI.elements.multiline import Multiline -from FreeSimpleGUI.elements.multiline import Output -from FreeSimpleGUI.elements.option_menu import OptionMenu -from FreeSimpleGUI.elements.pane import Pane -from FreeSimpleGUI.elements.progress_bar import ProgressBar -from FreeSimpleGUI.elements.progress_bar import TKProgressBar -from FreeSimpleGUI.elements.radio import Radio -from FreeSimpleGUI.elements.separator import HorizontalSeparator -from FreeSimpleGUI.elements.separator import VerticalSeparator -from FreeSimpleGUI.elements.sizegrip import Sizegrip -from FreeSimpleGUI.elements.slider import Slider -from FreeSimpleGUI.elements.spin import Spin -from FreeSimpleGUI.elements.status_bar import StatusBar -from FreeSimpleGUI.elements.stretch import Push -from FreeSimpleGUI.elements.stretch import VPush -from FreeSimpleGUI.elements.tab import Tab -from FreeSimpleGUI.elements.tab import TabGroup -from FreeSimpleGUI.elements.table import Table -from FreeSimpleGUI.elements.text import Text -from FreeSimpleGUI.elements.tree import Tree -from FreeSimpleGUI.elements.tree import TreeData -from FreeSimpleGUI.tray import SystemTray -from FreeSimpleGUI.window import Window -from FreeSimpleGUI._utils import _error_popup_with_traceback - -# Element aliases -In = Input -InputText = Input -I = Input # noqa -InputCombo = Combo -DropDown = InputCombo -Drop = InputCombo -DD = Combo -InputOptionMenu = OptionMenu -LBox = Listbox -LB = Listbox -R = Radio -Rad = Radio -CB = Checkbox -CBox = Checkbox -Check = Checkbox -Sp = Spin -ML = Multiline -MLine = Multiline -Txt = Text # type: Text -T = Text # type: Text -SBar = StatusBar -B = Button -Btn = Button -BMenu = ButtonMenu -BM = ButtonMenu -Im = Image -PBar = ProgressBar -Prog = ProgressBar -Progress = ProgressBar -G = Graph -Fr = Frame -VSeperator = VerticalSeparator -VSeparator = VerticalSeparator -VSep = VerticalSeparator -MenuBar = Menu -HSeparator = HorizontalSeparator -HSep = HorizontalSeparator -SGrip = Sizegrip -Sl = Slider -Col = Column -MenuBar = Menu -P = Push -Stretch = Push -VStretch = VPush -VP = VPush -FlexForm = Window - # additional aliases popup_timed = popup_auto_close test = main sdk_help = main_sdk_help - # ------------------------ Set the "Official PySimpleGUI Theme Colors" ------------------------ diff --git a/FreeSimpleGUI/elements/column.py b/FreeSimpleGUI/elements/column.py index 605d74f6..0566ba4c 100644 --- a/FreeSimpleGUI/elements/column.py +++ b/FreeSimpleGUI/elements/column.py @@ -3,6 +3,11 @@ import tkinter as tk import warnings +# noreorder +from typing import ( + Optional, +) + import FreeSimpleGUI from FreeSimpleGUI import _make_ttk_scrollbar from FreeSimpleGUI import _random_error_emoji @@ -152,19 +157,19 @@ class Column(Element): def __init__( self, layout, - background_color=None, + background_color: Optional[str] = None, size=(None, None), s=(None, None), - size_subsample_width=1, - size_subsample_height=2, + size_subsample_width: float = 1, + size_subsample_height: float = 2, pad=None, p=None, - scrollable=False, - vertical_scroll_only=False, + scrollable: bool = False, + vertical_scroll_only: bool = False, right_click_menu=None, key=None, k=None, - visible=True, + visible: bool = True, justification=None, element_justification=None, vertical_alignment=None, diff --git a/FreeSimpleGUI/window.py b/FreeSimpleGUI/window.py index 167b8aee..41e35b85 100644 --- a/FreeSimpleGUI/window.py +++ b/FreeSimpleGUI/window.py @@ -11,21 +11,34 @@ import tkinter import tkinter as tk import warnings -from typing import Any -from typing import Dict -from typing import List -from typing import Tuple + +# noreorder +from typing import ( + Any, + Dict, + List, + Literal, + Optional, + Tuple, + Union, +) import FreeSimpleGUI -from FreeSimpleGUI import _BuildResults -from FreeSimpleGUI import _Debugger -from FreeSimpleGUI import _debugger_window_is_open -from FreeSimpleGUI import _FindElementWithFocusInSubForm -from FreeSimpleGUI import _get_hidden_master_root -from FreeSimpleGUI import _global_settings_get_watermark_info -from FreeSimpleGUI import _long_func_thread -from FreeSimpleGUI import _refresh_debugger -from FreeSimpleGUI import _TimerPeriodic + +# noreorder +from FreeSimpleGUI import ( + _BuildResults, + _Debugger, + _debugger_window_is_open, + _FindElementWithFocusInSubForm, + _get_hidden_master_root, + _global_settings_get_watermark_info, + _long_func_thread, + _refresh_debugger, + _TimerPeriodic, +) + +# noreorder from FreeSimpleGUI import BUTTON_TYPE_CALENDAR_CHOOSER from FreeSimpleGUI import COLOR_SYSTEM_DEFAULT from FreeSimpleGUI import ELEM_TYPE_BUTTON @@ -91,14 +104,14 @@ class Window: Represents a single Window """ - NumOpenWindows = 0 + NumOpenWindows: int = 0 _user_defined_icon = None - hidden_master_root = None # type: tk.Tk - _animated_popup_dict = {} # type: Dict - _active_windows = {} # type: Dict[Window, tk.Tk()] + hidden_master_root: tk.Tk = None + _animated_popup_dict: Dict = {} + _active_windows: Dict[Window, tk.Tk] = {} _move_all_windows = False # if one window moved, they will move - _window_that_exited = None # type: Window - _root_running_mainloop = None # type: tk.Tk() # (may be the hidden root or a window's root) + _window_that_exited: 'Window' = None + _root_running_mainloop: tk.Tk = None # (may be the hidden root or a window's root) _timeout_key = None _TKAfterID = None # timer that is used to run reads with timeouts _window_running_mainloop = None # The window that is running the mainloop @@ -109,8 +122,8 @@ class Window: _floating_debug_window_build_needed = False _main_debug_window_build_needed = False # rereouted stdout info. List of tuples (window, element, previous destination) - _rerouted_stdout_stack = [] # type: List[Tuple[Window, Element]] - _rerouted_stderr_stack = [] # type: List[Tuple[Window, Element]] + _rerouted_stdout_stack: List[Tuple[Window, Element]] = [] + _rerouted_stderr_stack: List[Tuple[Window, Element]] = [] _original_stdout = None _original_stderr = None _watermark = None @@ -119,18 +132,18 @@ class Window: def __init__( self, - title, - layout=None, - default_element_size=None, - default_button_element_size=(None, None), - auto_size_text=None, - auto_size_buttons=None, - location=(None, None), - relative_location=(None, None), - size=(None, None), - element_padding=None, - margins=(None, None), - button_color=None, + title: str, + layout: Union[List[List[Element]], Tuple[Tuple[Element, ...], ...], None] = None, + default_element_size: Optional[Tuple[int, int]] = None, + default_button_element_size: Union[Tuple[int, int], Tuple[None, None]] = (None, None), + auto_size_text: Optional[bool] = None, + auto_size_buttons: Optional[bool] = None, + location: Union[Tuple[int, int], Tuple[None, None], None] = (None, None), + relative_location: Union[Tuple[int, int], Tuple[None, None]] = (None, None), + size: Union[Tuple[int, int], Tuple[None, None]] = (None, None), + element_padding: Union[Tuple[int, int], Tuple[Tuple[int, int], Tuple[int, int]], int, None] = None, + margins: Union[Tuple[int, int], Tuple[None, None]] = (None, None), + button_color: Union[Tuple[str, str], str, None] = None, font=None, progress_bar_color=(None, None), background_color=None, @@ -138,48 +151,48 @@ def __init__( auto_close=False, auto_close_duration=FreeSimpleGUI.DEFAULT_AUTOCLOSE_TIME, icon=None, - force_toplevel=False, - alpha_channel=None, + force_toplevel: bool = False, + alpha_channel: Optional[float] = None, return_keyboard_events=False, use_default_focus=True, text_justification=None, - no_titlebar=False, - grab_anywhere=False, - grab_anywhere_using_control=True, + no_titlebar: bool = False, + grab_anywhere: bool = False, + grab_anywhere_using_control: bool = True, keep_on_top=None, - resizable=False, - disable_close=False, - disable_minimize=False, + resizable: bool = False, + disable_close: bool = False, + disable_minimize: bool = 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, + transparent_color: Optional[str] = None, + debugger_enabled: bool = True, + right_click_menu_background_color: Optional[str] = None, + right_click_menu_text_color: Optional[str] = None, + right_click_menu_disabled_text_color: Optional[str] = None, right_click_menu_selected_colors=(None, None), right_click_menu_font=None, - right_click_menu_tearoff=False, - finalize=False, - element_justification='left', - ttk_theme=None, - use_ttk_buttons=None, - modal=False, - enable_close_attempted_event=False, - enable_window_config_events=False, - titlebar_background_color=None, - titlebar_text_color=None, + right_click_menu_tearoff: bool = False, + finalize: bool = False, + element_justification: Literal['left', 'right', 'center'] = 'left', + ttk_theme: Optional[str] = None, + use_ttk_buttons: Optional[bool] = None, + modal: bool = False, + enable_close_attempted_event: bool = False, + enable_window_config_events: bool = False, + titlebar_background_color: Optional[str] = None, + titlebar_text_color: Optional[str] = None, titlebar_font=None, titlebar_icon=None, - use_custom_titlebar=None, - scaling=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, - watermark=None, + use_custom_titlebar: Optional[bool] = None, + scaling: Optional[float] = None, + sbar_trough_color: Optional[str] = None, + sbar_background_color: Optional[str] = None, + sbar_arrow_color: Optional[str] = None, + sbar_width: Optional[int] = None, + sbar_arrow_width: Optional[int] = None, + sbar_frame_color: Optional[str] = None, + sbar_relief: Optional[str] = None, + watermark: Optional[bool] = None, metadata=None, ): """ @@ -312,9 +325,9 @@ def __init__( self._metadata = None # type: Any self.AutoSizeText = auto_size_text if auto_size_text is not None else FreeSimpleGUI.DEFAULT_AUTOSIZE_TEXT self.AutoSizeButtons = auto_size_buttons if auto_size_buttons is not None else FreeSimpleGUI.DEFAULT_AUTOSIZE_BUTTONS - self.Title = str(title) + self.Title: str = str(title) self.Rows = [] # a list of ELEMENTS for this row - self.DefaultElementSize = default_element_size if default_element_size is not None else FreeSimpleGUI.DEFAULT_ELEMENT_SIZE + self.DefaultElementSize: Tuple[int, int] = default_element_size if default_element_size is not None else FreeSimpleGUI.DEFAULT_ELEMENT_SIZE self.DefaultButtonElementSize = default_button_element_size if default_button_element_size != (None, None) else FreeSimpleGUI.DEFAULT_BUTTON_ELEMENT_SIZE if FreeSimpleGUI.DEFAULT_WINDOW_LOCATION != (None, None) and location == (None, None): self.Location = FreeSimpleGUI.DEFAULT_WINDOW_LOCATION @@ -335,9 +348,9 @@ def __init__( self.WindowIcon = FreeSimpleGUI.DEFAULT_WINDOW_ICON self.AutoClose = auto_close self.NonBlocking = False - self.TKroot = None # type: tk.Tk - self.TKrootDestroyed = False - self.CurrentlyRunningMainloop = False + self.TKroot: tk.Tk = None + self.TKrootDestroyed: bool = False + self.CurrentlyRunningMainloop: bool = False self.FormRemainedOpen = False self.TKAfterID = None self.ProgressBarColor = progress_bar_color @@ -347,12 +360,12 @@ def __init__( self.ReturnValues = None self.ReturnValuesList = [] self.ReturnValuesDictionary = {} - self.DictionaryKeyCounter = 0 + self.DictionaryKeyCounter: int = 0 self.LastButtonClicked = None - self.LastButtonClickedWasRealtime = False - self.UseDictionary = False - self.UseDefaultFocus = use_default_focus - self.ReturnKeyboardEvents = return_keyboard_events + self.LastButtonClickedWasRealtime: bool = False + self.UseDictionary: bool = False + self.UseDefaultFocus: bool = use_default_focus + self.ReturnKeyboardEvents: bool = return_keyboard_events self.LastKeyboardEvent = None self.TextJustification = text_justification self.NoTitleBar = no_titlebar @@ -394,13 +407,13 @@ def __init__( self.DebuggerEnabled = debugger_enabled self.WasClosed = False self.ElementJustification = element_justification - self.FocusSet = False + self.FocusSet: bool = False self.metadata = metadata self.TtkTheme = ttk_theme or FreeSimpleGUI.DEFAULT_TTK_THEME self.UseTtkButtons = use_ttk_buttons if use_ttk_buttons is not None else FreeSimpleGUI.USE_TTK_BUTTONS self.user_bind_dict = {} # Used when user defines a tkinter binding using bind method - convert bind string to key modifier self.user_bind_event = None # Used when user defines a tkinter binding using bind method - event data from tkinter - self.modal = modal + self.modal: bool = modal self.thread_queue = None # type: queue.Queue self.thread_lock = None # type: threading.Lock self.thread_timer = None # type: tk.Misc @@ -628,7 +641,7 @@ def add_rows(self, rows): if Window._watermark is not None: self.add_row(Window._watermark(self)) - def layout(self, rows): + def layout(self, rows: List[List[Element]]): """ Second of two preferred ways of telling a Window what its layout is. The other way is to pass the layout as a parameter to Window object. The parameter method is the currently preferred method. This call to Layout @@ -826,7 +839,7 @@ def _GetElementAtLocation(self, location): element = row[col_num] return element - def _GetDefaultElementSize(self): + def _GetDefaultElementSize(self) -> Tuple[int, int]: """ Returns the default elementSize @@ -921,7 +934,7 @@ def _calendar_chooser_button_clicked(self, elem): return should_submit_window # @_timeit_summary - def read(self, timeout=None, timeout_key=TIMEOUT_KEY, close=False): + def read(self, timeout: Optional[int] = None, timeout_key=TIMEOUT_KEY, close: bool = False): """ THE biggest deal method in the Window class! This is how you get all of your data from your Window. Pass in a timeout (in milliseconds) to wait for a maximum of timeout milliseconds. Will return timeout_key @@ -990,7 +1003,7 @@ def read(self, timeout=None, timeout_key=TIMEOUT_KEY, close=False): return results # @_timeit - def _read(self, timeout=None, timeout_key=TIMEOUT_KEY): + def _read(self, timeout: Optional[int] = None, timeout_key=TIMEOUT_KEY): """ THE biggest deal method in the Window class! This is how you get all of your data from your Window. Pass in a timeout (in milliseconds) to wait for a maximum of timeout milliseconds. Will return timeout_key @@ -1157,7 +1170,7 @@ def _ReadNonBlocking(self): return None, None return _BuildResults(self, False, self) - def _start_autoclose_timer(self): + def _start_autoclose_timer(self) -> None: duration = FreeSimpleGUI.DEFAULT_AUTOCLOSE_TIME if self.AutoCloseDuration is None else self.AutoCloseDuration self.TKAfterID = self.TKroot.after(int(duration * 1000), self._AutoCloseAlarmCallback) @@ -1236,7 +1249,7 @@ def _find_closest_key(self, search_key): return k return matches[0] if len(matches) else None - def FindElement(self, key, silent_on_error=False): + def FindElement(self, key, silent_on_error: bool = False): """ ** Warning ** This call will eventually be depricated. ** @@ -1260,7 +1273,7 @@ def FindElement(self, key, silent_on_error=False): return self.find_element(key, silent_on_error=silent_on_error) - def find_element(self, key, silent_on_error=False, supress_guessing=None, supress_raise=None): + def find_element(self, key, silent_on_error: bool = False, supress_guessing: Optional[bool] = None, supress_raise: Optional[bool] = None): """ Find element object associated with the provided key. THIS METHOD IS NO LONGER NEEDED to be called by the user @@ -1331,7 +1344,7 @@ def find_element(self, key, silent_on_error=False, supress_guessing=None, supres Find = find_element # Shortcut function, most likely not used by many people. Elem = find_element # NEW for 2019! More laziness... Another shortcut - def find_element_with_focus(self): + def find_element_with_focus(self) -> Optional[Element]: """ Returns the Element that currently has focus as reported by tkinter. If no element is found None is returned! :return: An Element if one has been found with focus or None if no element found @@ -1340,12 +1353,11 @@ def find_element_with_focus(self): element = _FindElementWithFocusInSubForm(self) return element - def widget_to_element(self, widget): + def widget_to_element(self, widget) -> None: """ Returns the element that matches a supplied tkinter widget. If no matching element is found, then None is returned. - :return: Element that uses the specified widget :rtype: Element | None """ @@ -1356,7 +1368,7 @@ def widget_to_element(self, widget): return element return None - def _BuildKeyDict(self): + def _BuildKeyDict(self) -> None: """ Used internally only! Not user callable Builds a dictionary containing all elements with keys for this window. @@ -1364,7 +1376,7 @@ def _BuildKeyDict(self): dict = {} self.AllKeysDict = self._BuildKeyDictForWindow(self, self, dict) - def _BuildKeyDictForWindow(self, top_window, window, key_dict): + def _BuildKeyDictForWindow(self, top_window: 'Window', window, key_dict): """ Loop through all Rows and all Container Elements for this window and create the keys for all of them. Note that the calls are recursive as all pathes must be walked @@ -1796,7 +1808,7 @@ def _MouseWheelCallback(self, event): self.LastKeyboardEvent = 'MouseWheel:Down' if event.delta < 0 or event.num == 5 else 'MouseWheel:Up' _exit_mainloop(self) - def _Close(self, without_event=False): + def _Close(self, without_event: bool = False): """ The internal close call that does the real work of building. This method basically sets up for closing but doesn't destroy the window like the User's version of Close does @@ -1818,7 +1830,7 @@ def _Close(self, without_event=False): self.RootNeedsDestroying = True return - def close(self): + def close(self) -> None: """ Closes window. Users can safely call even if window has been destroyed. Should always call when done with a window so that resources are properly freed up within your thread. @@ -1853,7 +1865,7 @@ def close(self): self.Rows = None self.TKroot = None - def is_closed(self, quick_check=None): + def is_closed(self, quick_check: bool = False) -> bool: """ Returns True is the window is maybe closed. Can be difficult to tell sometimes NOTE - the call to TKroot.update was taking over 500 ms sometimes so added a flag to bypass the lengthy call. @@ -2629,7 +2641,7 @@ def key_dict(self): """ return self.AllKeysDict - def key_is_good(self, key): + def key_is_good(self, key) -> bool: """ Checks to see if this is a good key for this window If there's an element with the key provided, then True is returned @@ -2642,7 +2654,7 @@ def key_is_good(self, key): return True return False - def get_scaling(self): + def get_scaling(self) -> float: """ Returns the current scaling value set for this window @@ -2661,10 +2673,10 @@ def get_scaling(self): return scaling - def _custom_titlebar_restore_callback(self, event): + def _custom_titlebar_restore_callback(self, event) -> None: self._custom_titlebar_restore() - def _custom_titlebar_restore(self): + def _custom_titlebar_restore(self) -> None: if running_linux(): self.TKroot.unbind('') self.TKroot.deiconify() @@ -2684,7 +2696,7 @@ def _custom_titlebar_restore(self): self.TKroot.attributes('-fullscreen', False) self.maximized = False - def _custom_titlebar_minimize(self): + def _custom_titlebar_minimize(self) -> None: if running_linux(): self.TKroot.wm_attributes('-type', 'normal') self.TKroot.wm_overrideredirect(False) @@ -2695,7 +2707,7 @@ def _custom_titlebar_minimize(self): self.TKroot.iconify() self.TKroot.bind('', self._custom_titlebar_restore_callback) - def _custom_titlebar_callback(self, key): + def _custom_titlebar_callback(self, key) -> None: """ One of the Custom Titlbar buttons was clicked :param key: @@ -2714,7 +2726,7 @@ def _custom_titlebar_callback(self, key): if not self.DisableClose: self._OnClosingCallback() - def timer_start(self, frequency_ms, key=EVENT_TIMER, repeating=True): + def timer_start(self, frequency_ms: int, key=EVENT_TIMER, repeating: bool = True) -> int: """ Starts a timer that gnerates Timer Events. The default is to repeat the timer events until timer is stopped. You can provide your own key or a default key will be used. The default key is defined @@ -2733,7 +2745,7 @@ def timer_start(self, frequency_ms, key=EVENT_TIMER, repeating=True): timer = _TimerPeriodic(self, frequency_ms=frequency_ms, key=key, repeating=repeating) return timer.id - def timer_stop(self, timer_id): + def timer_stop(self, timer_id: int) -> None: """ Stops a timer with a given ID @@ -2743,13 +2755,13 @@ def timer_stop(self, timer_id): """ _TimerPeriodic.stop_timer_with_id(timer_id) - def timer_stop_all(self): + def timer_stop_all(self) -> None: """ Stops all timers for THIS window """ _TimerPeriodic.stop_all_timers_for_window(self) - def timer_get_active_timers(self): + def timer_get_active_timers(self) -> List[int]: """ Returns a list of currently active timers for a window :return: List of timers for the window @@ -2760,7 +2772,7 @@ def timer_get_active_timers(self): @classmethod def _restore_stdout(cls): for item in cls._rerouted_stdout_stack: - (window, element) = item # type: (Window, Element) + (window, element) = item if not window.is_closed(): sys.stdout = element break @@ -2772,7 +2784,7 @@ def _restore_stdout(cls): @classmethod def _restore_stderr(cls): for item in cls._rerouted_stderr_stack: - (window, element) = item # type: (Window, Element) + (window, element) = item if not window.is_closed(): sys.stderr = element break @@ -2805,7 +2817,7 @@ def __call__(self, *args, **kwargs): """ return self.read(*args, **kwargs) - def _is_window_created(self, additional_message=''): + def _is_window_created(self, additional_message: str = '') -> bool: msg = str(additional_message) if self.TKroot is None: warnings.warn( @@ -2821,7 +2833,7 @@ def _is_window_created(self, additional_message=''): return False return True - def _has_custom_titlebar_element(self): + def _has_custom_titlebar_element(self) -> bool: for elem in self.AllKeysDict.values(): if elem.Key in (TITLEBAR_MAXIMIZE_KEY, TITLEBAR_CLOSE_KEY, TITLEBAR_IMAGE_KEY): return True diff --git a/pyproject.toml b/pyproject.toml index 930b61bf..1e8689d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,12 +17,12 @@ maintainers = [ keywords = ["PySimpleGui", "fork", "GUI", "UI", "tkinter", "Qt", "WxPython", "Remi", "wrapper", "simple", "easy", "beginner", "novice", "student", "graphics", "progressbar", "progressmeter"] classifiers = [ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", "Topic :: Multimedia :: Graphics", "Operating System :: OS Independent",