KL-Py 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. KL_Py/KL_Py.py +4400 -0
  2. KL_Py/__init__.py +1 -0
  3. KL_Py/hindGui/__init__.py +16529 -0
  4. KL_Py/hindGui/_utils.py +140 -0
  5. KL_Py/hindGui/elements/base.py +1055 -0
  6. KL_Py/hindGui/elements/button.py +947 -0
  7. KL_Py/hindGui/elements/calendar.py +222 -0
  8. KL_Py/hindGui/elements/canvas.py +122 -0
  9. KL_Py/hindGui/elements/center.py +44 -0
  10. KL_Py/hindGui/elements/checkbox.py +240 -0
  11. KL_Py/hindGui/elements/column.py +443 -0
  12. KL_Py/hindGui/elements/combo.py +329 -0
  13. KL_Py/hindGui/elements/error.py +47 -0
  14. KL_Py/hindGui/elements/frame.py +273 -0
  15. KL_Py/hindGui/elements/graph.py +817 -0
  16. KL_Py/hindGui/elements/helpers.py +202 -0
  17. KL_Py/hindGui/elements/image.py +324 -0
  18. KL_Py/hindGui/elements/input.py +310 -0
  19. KL_Py/hindGui/elements/list_box.py +392 -0
  20. KL_Py/hindGui/elements/menu.py +192 -0
  21. KL_Py/hindGui/elements/multiline.py +685 -0
  22. KL_Py/hindGui/elements/option_menu.py +168 -0
  23. KL_Py/hindGui/elements/pane.py +127 -0
  24. KL_Py/hindGui/elements/progress_bar.py +304 -0
  25. KL_Py/hindGui/elements/radio.py +252 -0
  26. KL_Py/hindGui/elements/separator.py +64 -0
  27. KL_Py/hindGui/elements/sizegrip.py +34 -0
  28. KL_Py/hindGui/elements/slider.py +199 -0
  29. KL_Py/hindGui/elements/spin.py +252 -0
  30. KL_Py/hindGui/elements/status_bar.py +165 -0
  31. KL_Py/hindGui/elements/stretch.py +26 -0
  32. KL_Py/hindGui/elements/tab.py +697 -0
  33. KL_Py/hindGui/elements/table.py +463 -0
  34. KL_Py/hindGui/elements/text.py +413 -0
  35. KL_Py/hindGui/elements/tree.py +493 -0
  36. KL_Py/hindGui/replace.py +45 -0
  37. KL_Py/hindGui/tray.py +358 -0
  38. KL_Py/hindGui/window.py +2906 -0
  39. KL_Py/requests/__init__.py +184 -0
  40. KL_Py/requests/__version__.py +14 -0
  41. KL_Py/requests/_internal_utils.py +50 -0
  42. KL_Py/requests/adapters.py +719 -0
  43. KL_Py/requests/api.py +157 -0
  44. KL_Py/requests/auth.py +314 -0
  45. KL_Py/requests/certifi/__init__.py +4 -0
  46. KL_Py/requests/certifi/__main__.py +12 -0
  47. KL_Py/requests/certifi/core.py +83 -0
  48. KL_Py/requests/certifi/py.typed +0 -0
  49. KL_Py/requests/certs.py +17 -0
  50. KL_Py/requests/charset_normalizer/__init__.py +48 -0
  51. KL_Py/requests/charset_normalizer/__main__.py +6 -0
  52. KL_Py/requests/charset_normalizer/api.py +671 -0
  53. KL_Py/requests/charset_normalizer/cd.py +421 -0
  54. KL_Py/requests/charset_normalizer/cli/__init__.py +8 -0
  55. KL_Py/requests/charset_normalizer/cli/__main__.py +363 -0
  56. KL_Py/requests/charset_normalizer/constant.py +2031 -0
  57. KL_Py/requests/charset_normalizer/legacy.py +80 -0
  58. KL_Py/requests/charset_normalizer/md.py +744 -0
  59. KL_Py/requests/charset_normalizer/models.py +359 -0
  60. KL_Py/requests/charset_normalizer/py.typed +0 -0
  61. KL_Py/requests/charset_normalizer/utils.py +420 -0
  62. KL_Py/requests/charset_normalizer/version.py +8 -0
  63. KL_Py/requests/compat.py +106 -0
  64. KL_Py/requests/cookies.py +561 -0
  65. KL_Py/requests/exceptions.py +151 -0
  66. KL_Py/requests/help.py +134 -0
  67. KL_Py/requests/hooks.py +33 -0
  68. KL_Py/requests/idna/__init__.py +45 -0
  69. KL_Py/requests/idna/codec.py +122 -0
  70. KL_Py/requests/idna/compat.py +15 -0
  71. KL_Py/requests/idna/core.py +437 -0
  72. KL_Py/requests/idna/idnadata.py +4309 -0
  73. KL_Py/requests/idna/intranges.py +57 -0
  74. KL_Py/requests/idna/package_data.py +1 -0
  75. KL_Py/requests/idna/py.typed +0 -0
  76. KL_Py/requests/idna/uts46data.py +8841 -0
  77. KL_Py/requests/models.py +1039 -0
  78. KL_Py/requests/packages.py +23 -0
  79. KL_Py/requests/sessions.py +831 -0
  80. KL_Py/requests/status_codes.py +128 -0
  81. KL_Py/requests/structures.py +99 -0
  82. KL_Py/requests/urllib3/__init__.py +211 -0
  83. KL_Py/requests/urllib3/_base_connection.py +165 -0
  84. KL_Py/requests/urllib3/_collections.py +487 -0
  85. KL_Py/requests/urllib3/_request_methods.py +278 -0
  86. KL_Py/requests/urllib3/_version.py +34 -0
  87. KL_Py/requests/urllib3/connection.py +1099 -0
  88. KL_Py/requests/urllib3/connectionpool.py +1178 -0
  89. KL_Py/requests/urllib3/contrib/__init__.py +0 -0
  90. KL_Py/requests/urllib3/contrib/emscripten/__init__.py +17 -0
  91. KL_Py/requests/urllib3/contrib/emscripten/connection.py +260 -0
  92. KL_Py/requests/urllib3/contrib/emscripten/fetch.py +726 -0
  93. KL_Py/requests/urllib3/contrib/emscripten/request.py +22 -0
  94. KL_Py/requests/urllib3/contrib/emscripten/response.py +277 -0
  95. KL_Py/requests/urllib3/contrib/pyopenssl.py +564 -0
  96. KL_Py/requests/urllib3/contrib/socks.py +228 -0
  97. KL_Py/requests/urllib3/exceptions.py +335 -0
  98. KL_Py/requests/urllib3/fields.py +341 -0
  99. KL_Py/requests/urllib3/filepost.py +89 -0
  100. KL_Py/requests/urllib3/http2/__init__.py +53 -0
  101. KL_Py/requests/urllib3/http2/connection.py +356 -0
  102. KL_Py/requests/urllib3/http2/probe.py +87 -0
  103. KL_Py/requests/urllib3/poolmanager.py +651 -0
  104. KL_Py/requests/urllib3/py.typed +2 -0
  105. KL_Py/requests/urllib3/response.py +1480 -0
  106. KL_Py/requests/urllib3/util/__init__.py +42 -0
  107. KL_Py/requests/urllib3/util/connection.py +137 -0
  108. KL_Py/requests/urllib3/util/proxy.py +43 -0
  109. KL_Py/requests/urllib3/util/request.py +263 -0
  110. KL_Py/requests/urllib3/util/response.py +101 -0
  111. KL_Py/requests/urllib3/util/retry.py +549 -0
  112. KL_Py/requests/urllib3/util/ssl_.py +527 -0
  113. KL_Py/requests/urllib3/util/ssl_match_hostname.py +159 -0
  114. KL_Py/requests/urllib3/util/ssltransport.py +271 -0
  115. KL_Py/requests/urllib3/util/timeout.py +275 -0
  116. KL_Py/requests/urllib3/util/url.py +469 -0
  117. KL_Py/requests/urllib3/util/util.py +42 -0
  118. KL_Py/requests/urllib3/util/wait.py +124 -0
  119. KL_Py/requests/utils.py +1086 -0
  120. KL_Py/when.py +144 -0
  121. kl_py-0.0.1.dist-info/METADATA +15 -0
  122. kl_py-0.0.1.dist-info/RECORD +125 -0
  123. kl_py-0.0.1.dist-info/WHEEL +5 -0
  124. kl_py-0.0.1.dist-info/licenses/LICENSE +21 -0
  125. kl_py-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2906 @@
1
+ from __future__ import annotations
2
+
3
+ import calendar
4
+ import datetime
5
+ import difflib
6
+ import os
7
+ import pickle
8
+ import queue
9
+ import sys
10
+ import threading
11
+ import tkinter
12
+ import tkinter as tk
13
+ import warnings
14
+ from typing import Any
15
+ from typing import Dict
16
+ from typing import List
17
+ from typing import Tuple
18
+
19
+ import hindGui
20
+ from hindGui import _BuildResults
21
+ from hindGui import _Debugger
22
+ from hindGui import _debugger_window_is_open
23
+ from hindGui import _FindElementWithFocusInSubForm
24
+ from hindGui import _get_hidden_master_root
25
+ from hindGui import _global_settings_get_watermark_info
26
+ from hindGui import _long_func_thread
27
+ from hindGui import _refresh_debugger
28
+ from hindGui import _TimerPeriodic
29
+ from hindGui import BUTTON_TYPE_CALENDAR_CHOOSER
30
+ from hindGui import COLOR_SYSTEM_DEFAULT
31
+ from hindGui import ELEM_TYPE_BUTTON
32
+ from hindGui import ELEM_TYPE_BUTTONMENU
33
+ from hindGui import ELEM_TYPE_COLUMN
34
+ from hindGui import ELEM_TYPE_FRAME
35
+ from hindGui import ELEM_TYPE_GRAPH
36
+ from hindGui import ELEM_TYPE_IMAGE
37
+ from hindGui import ELEM_TYPE_INPUT_CHECKBOX
38
+ from hindGui import ELEM_TYPE_INPUT_COMBO
39
+ from hindGui import ELEM_TYPE_INPUT_LISTBOX
40
+ from hindGui import ELEM_TYPE_INPUT_MULTILINE
41
+ from hindGui import ELEM_TYPE_INPUT_OPTION_MENU
42
+ from hindGui import ELEM_TYPE_INPUT_RADIO
43
+ from hindGui import ELEM_TYPE_INPUT_SLIDER
44
+ from hindGui import ELEM_TYPE_INPUT_SPIN
45
+ from hindGui import ELEM_TYPE_INPUT_TEXT
46
+ from hindGui import ELEM_TYPE_MENUBAR
47
+ from hindGui import ELEM_TYPE_PANE
48
+ from hindGui import ELEM_TYPE_PROGRESS_BAR
49
+ from hindGui import ELEM_TYPE_SEPARATOR
50
+ from hindGui import ELEM_TYPE_TAB
51
+ from hindGui import ELEM_TYPE_TAB_GROUP
52
+ from hindGui import ELEM_TYPE_TABLE
53
+ from hindGui import ELEM_TYPE_TREE
54
+ from hindGui import EMOJI_BASE64_KEY
55
+ from hindGui import EVENT_TIMER
56
+ from hindGui import fill_form_with_values
57
+ from hindGui import GRAB_ANYWHERE_IGNORE_THESE_WIDGETS
58
+ from hindGui import InitializeResults
59
+ from hindGui import PackFormIntoFrame
60
+ from hindGui import error_with_traceback
61
+ from hindGui import popup_get_date
62
+ from hindGui import popup_quick_message
63
+ from hindGui import pysimplegui_user_settings
64
+ from hindGui import running_linux
65
+ from hindGui import running_mac
66
+ from hindGui import running_windows
67
+ from hindGui import StartupTK
68
+ from hindGui import theme_input_background_color
69
+ from hindGui import theme_input_text_color
70
+ from hindGui import theme_use_custom_titlebar
71
+ from hindGui import TIMEOUT_KEY
72
+ from hindGui import Titlebar
73
+ from hindGui import TITLEBAR_CLOSE_KEY
74
+ from hindGui import TITLEBAR_IMAGE_KEY
75
+ from hindGui import TITLEBAR_MAXIMIZE_KEY
76
+ from hindGui import TITLEBAR_METADATA_MARKER
77
+ from hindGui import TITLEBAR_MINIMIZE_KEY
78
+ from hindGui import TITLEBAR_TEXT_KEY
79
+ from hindGui import TTKPartOverrides
80
+ from hindGui import WINDOW_CLOSE_ATTEMPTED_EVENT
81
+ from hindGui import WINDOW_CONFIG_EVENT
82
+ from hindGui._utils import _error_popup_with_traceback
83
+ from hindGui._utils import _exit_mainloop
84
+ from hindGui.elements.base import Element
85
+ from hindGui.elements.helpers import _simplified_dual_color_to_tuple
86
+ from hindGui.elements.helpers import button_color_to_tuple
87
+
88
+
89
+ class Window:
90
+ """
91
+ Ek graphical user interface banata he
92
+ """
93
+
94
+ NumOpenWindows = 0
95
+ _user_defined_icon = None
96
+ hidden_master_root = None # type: tk.Tk
97
+ _animated_popup_dict = {} # type: Dict
98
+ _active_windows = {} # type: Dict[Window, tk.Tk()]
99
+ _move_all_windows = False # if one window moved, they will move
100
+ _window_that_exited = None # type: Window
101
+ _root_running_mainloop = None # type: tk.Tk() # (may be the hidden root or a window's root)
102
+ _timeout_key = None
103
+ _TKAfterID = None # timer that is used to run reads with timeouts
104
+ _window_running_mainloop = None # The window that is running the mainloop
105
+ _container_element_counter = 0 # used to get a number of Container Elements (Frame, Column, Tab)
106
+ _read_call_from_debugger = False
107
+ _timeout_0_counter = 0 # when timeout=0 then go through each window one at a time
108
+ _counter_for_ttk_widgets = 0
109
+ _floating_debug_window_build_needed = False
110
+ _main_debug_window_build_needed = False
111
+ # rereouted stdout info. List of tuples (window, element, previous destination)
112
+ _rerouted_stdout_stack = [] # type: List[Tuple[Window, Element]]
113
+ _rerouted_stderr_stack = [] # type: List[Tuple[Window, Element]]
114
+ _original_stdout = None
115
+ _original_stderr = None
116
+ _watermark = None
117
+ _watermark_temp_forced = False
118
+ _watermark_user_text = ''
119
+ chal_rahi_he = is_running = False
120
+ is_dead = not chal_rahi_he
121
+
122
+ def __init__(
123
+ self,
124
+ title,
125
+ style=None,
126
+ default_element_size=None,
127
+ default_button_element_size=(None, None),
128
+ auto_size_text=None,
129
+ auto_size_buttons=None,
130
+ location=(None, None),
131
+ relative_location=(None, None),
132
+ size=(None, None),
133
+ element_padding=None,
134
+ fasla=(None, None),
135
+ f=(None, None),
136
+ button_color=None,
137
+ font=None,
138
+ progress_bar_color=(None, None),
139
+ background_color=None,
140
+ border_depth=None,
141
+ auto_close=False,
142
+ auto_close_duration=hindGui.DEFAULT_AUTOCLOSE_TIME,
143
+ icon=None,
144
+ force_toplevel=False,
145
+ alpha_channel=None,
146
+ return_keyboard_events=True,
147
+ use_default_focus=True,
148
+ text_justification=None,
149
+ no_titlebar=False,
150
+ grab_anywhere=False,
151
+ grab_anywhere_using_control=True,
152
+ ehem=None,
153
+ resizable=False,
154
+ disable_close=False,
155
+ disable_minimize=False,
156
+ right_click_menu=None,
157
+ transparent_color=None,
158
+ debugger_enabled=True,
159
+ right_click_menu_background_color=None,
160
+ right_click_menu_text_color=None,
161
+ right_click_menu_disabled_text_color=None,
162
+ right_click_menu_selected_colors=(None, None),
163
+ right_click_menu_font=None,
164
+ right_click_menu_tearoff=False,
165
+ finalize=True,
166
+ element_justification='left',
167
+ ttk_theme=None,
168
+ use_ttk_buttons=None,
169
+ modal=False,
170
+ enable_close_attempted_event=False,
171
+ enable_window_config_events=False,
172
+ titlebar_background_color=None,
173
+ titlebar_text_color=None,
174
+ titlebar_font=None,
175
+ titlebar_icon=None,
176
+ use_custom_titlebar=None,
177
+ scaling=None,
178
+ sbar_trough_color=None,
179
+ sbar_background_color=None,
180
+ sbar_arrow_color=None,
181
+ sbar_width=None,
182
+ sbar_arrow_width=None,
183
+ sbar_frame_color=None,
184
+ sbar_relief=None,
185
+ watermark=None,
186
+ metadata=None
187
+ ):
188
+ """
189
+ :param title: The title that will be displayed in the Titlebar and on the Taskbar
190
+ :type title: (str)
191
+ :param style: The style for the window. Can also be specified in the Style method
192
+ :type style: List[List[Element]] | Tuple[Tuple[Element]]
193
+ :param default_element_size: size in characters (wide) and rows (high) for all elements in this window
194
+ :type default_element_size: (int, int) - (width, height)
195
+ :param default_button_element_size: (width, height) size in characters (wide) and rows (high) for all Button elements in this window
196
+ :type default_button_element_size: (int, int)
197
+ :param auto_size_text: True if Elements in Window should be sized to exactly fir the length of text
198
+ :type auto_size_text: (bool)
199
+ :param auto_size_buttons: True if Buttons in this Window should be sized to exactly fit the text on this.
200
+ :type auto_size_buttons: (bool)
201
+ :param relative_location: (x,y) location relative to the default location of the window, in pixels. Normally the window centers. This location is relative to the location the window would be created. Note they can be negative.
202
+ :type relative_location: (int, int)
203
+ :param location: (x,y) location, in pixels, to locate the upper left corner of the window on the screen. Default is to center on screen. None will not set any location meaning the OS will decide
204
+ :type location: (int, int) or (None, None) or None
205
+ :param size: (width, height) size in pixels for this window. Normally the window is autosized to fit contents, not set to an absolute size by the user. Try not to set this value. You risk, the contents being cut off, etc. Let the style determine the window size instead
206
+ :type size: (int, int)
207
+ :param element_padding: Default amount of padding to put around elements in window (left/right, top/bottom) or ((left, right), (top, bottom)), or an int. If an int, then it's converted into a tuple (int, int)
208
+ :type element_padding: (int, int) or ((int, int),(int,int)) or int
209
+ :param fasla: (left/right, top/bottom) Amount of pixels to leave inside the window's frame around the edges before your elements are shown.
210
+ :type fasla: (int, int)
211
+ :param button_color: Default button colors for all buttons in the window
212
+ :type button_color: (str, str) | str
213
+ :param font: specifies the font family, size, etc. Tuple or Single string format 'name size styles'. Styles: italic * roman bold normal underline overstrike
214
+ :type font: (str or (str, int[, str]) or None)
215
+ :param progress_bar_color: (bar color, background color) Sets the default colors for all progress bars in the window
216
+ :type progress_bar_color: (str, str)
217
+ :param background_color: color of background
218
+ :type background_color: (str)
219
+ :param border_depth: Default border depth (width) for all elements in the window
220
+ :type border_depth: (int)
221
+ :param auto_close: If True, the window will automatically die itself
222
+ :type auto_close: (bool)
223
+ :param auto_close_duration: Number of seconds to wait before closing the window
224
+ :type auto_close_duration: (int)
225
+ :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
226
+ :type icon: (str | bytes)
227
+ :param force_toplevel: If True will cause this window to skip the normal use of a hidden master window
228
+ :type force_toplevel: (bool)
229
+ :param alpha_channel: Sets the opacity of the window. 0 = invisible 1 = completely nazar. Values bewteen 0 & 1 will produce semi-transparent windows in SOME environments (The Raspberry Pi always has this value at 1 and cannot badlo.
230
+ :type alpha_channel: (float)
231
+ :param return_keyboard_events: if True action presses on the keyboard will be returned as Events from parh calls
232
+ :type return_keyboard_events: (bool)
233
+ :param use_default_focus: If True will use the default focus algorithm to set the focus to the "Correct" element
234
+ :type use_default_focus: (bool)
235
+ :param text_justification: Default text justification for all Text Elements in window
236
+ :type text_justification: 'left' | 'right' | 'center'
237
+ :param no_titlebar: If true, no titlebar nor frame will be shown on window. This means you cannot minimize the window and it will not show up on the taskbar
238
+ :type no_titlebar: (bool)
239
+ :param grab_anywhere: If True can use mouse to click and drag to move the window. Almost every location of the window will work except input fields on some systems
240
+ :type grab_anywhere: (bool)
241
+ :param grab_anywhere_using_control: If True can use CONTROL action + left mouse mouse to click and drag to move the window. DEFAULT is TRUE. Unlike normal grab anywhere, it works on all elements.
242
+ :type grab_anywhere_using_control: (bool)
243
+ :param ehem: If True, window will be created on top of all other windows on screen. It can be bumped down if another window created with this parm
244
+ :type ehem: (bool)
245
+ :param resizable: If True, allows the user to resize the window. Note the not all Elements will badlo size or location when resizing.
246
+ :type resizable: (bool)
247
+ :param disable_close: If True, the X button in the top right corner of the window will no work. Use with caution and always give a way out toyour users
248
+ :type disable_close: (bool)
249
+ :param disable_minimize: if True the user won't be able to minimize window. Good for taking over entire screen and staying that way.
250
+ :type disable_minimize: (bool)
251
+ :param right_click_menu: A list of lists of Menu items to show when this element is right clicked. See user docs for exact format.
252
+ :type right_click_menu: List[List[ List[str] | str ]]
253
+ :param transparent_color: Any portion of the window that has this color will be completely transparent. You can even click through these spots to the window under this window.
254
+ :type transparent_color: (str)
255
+ :param debugger_enabled: If True then the internal debugger will be enabled
256
+ :type debugger_enabled: (bool)
257
+ :param right_click_menu_background_color: Background color for right click menus
258
+ :type right_click_menu_background_color: (str)
259
+ :param right_click_menu_text_color: Text color for right click menus
260
+ :type right_click_menu_text_color: (str)
261
+ :param right_click_menu_disabled_text_color: Text color for disabled right click menu items
262
+ :type right_click_menu_disabled_text_color: (str)
263
+ :param right_click_menu_selected_colors: Text AND background colors for a selected item. Can be a Tuple OR a color string. simplified-button-color-string "foreground on background". Can be a single color if want to set only the background. Normally a tuple, but can be a simplified-dual-color-string "foreground on background". Can be a single color if want to set only the background.
264
+ :type right_click_menu_selected_colors: (str, str) | str | Tuple
265
+ :param right_click_menu_font: Font for right click menus
266
+ :type right_click_menu_font: (str or (str, int[, str]) or None)
267
+ :param right_click_menu_tearoff: If True then all right click menus can be torn off
268
+ :type right_click_menu_tearoff: bool
269
+ :param finalize: If True then the Finalize method will be called. Use this rather than chaining .Finalize for cleaner code
270
+ :type finalize: (bool)
271
+ :param element_justification: All elements in the Window itself will have this justification 'left', 'right', 'center' are valid values
272
+ :type element_justification: (str)
273
+ :param ttk_theme: Set the tkinter ttk "theme" of the window. Default = DEFAULT_TTK_THEME. Sets all ttk widgets to this theme as their default
274
+ :type ttk_theme: (str)
275
+ :param use_ttk_buttons: Affects all buttons in window. True = use ttk buttons. False = do not use ttk buttons. None = use ttk buttons only if on a Mac
276
+ :type use_ttk_buttons: (bool)
277
+ :param modal: If True then this window will be the only window a user can interact with until it is closed
278
+ :type modal: (bool)
279
+ :param enable_close_attempted_event: If True then the window will not die when "X" clicked. Instead an action WINDOW_CLOSE_ATTEMPTED_EVENT if returned from window.parh
280
+ :type enable_close_attempted_event: (bool)
281
+ :param enable_window_config_events: If True then window configuration events (resizing or moving the window) will return WINDOW_CONFIG_EVENT from window.parh. Note you will get several when Window is created.
282
+ :type enable_window_config_events: (bool)
283
+ :param titlebar_background_color: If custom titlebar indicated by use_custom_titlebar, then use this as background color
284
+ :type titlebar_background_color: (str | None)
285
+ :param titlebar_text_color: If custom titlebar indicated by use_custom_titlebar, then use this as text color
286
+ :type titlebar_text_color: (str | None)
287
+ :param titlebar_font: If custom titlebar indicated by use_custom_titlebar, then use this as title font
288
+ :type titlebar_font: (str or (str, int[, str]) or None)
289
+ :param titlebar_icon: If custom titlebar indicated by use_custom_titlebar, then use this as the icon (file or base64 bytes)
290
+ :type titlebar_icon: (bytes | str)
291
+ :param use_custom_titlebar: If True, then a custom titlebar will be used instead of the normal titlebar
292
+ :type use_custom_titlebar: bool
293
+ :param scaling: Apply scaling to the elements in the window. Can be set on a global basis using set_options
294
+ :type scaling: float
295
+ :param sbar_trough_color: Scrollbar color of the trough
296
+ :type sbar_trough_color: (str)
297
+ :param sbar_background_color: Scrollbar color of the background of the arrow buttons at the ends AND the color of the "thumb" (the thing you grab and slide). Switches to arrow color when mouse is over
298
+ :type sbar_background_color: (str)
299
+ :param sbar_arrow_color: Scrollbar color of the arrow at the ends of the scrollbar (it looks like a button). Switches to background color when mouse is over
300
+ :type sbar_arrow_color: (str)
301
+ :param sbar_width: Scrollbar width in pixels
302
+ :type sbar_width: (int)
303
+ :param sbar_arrow_width: Scrollbar width of the arrow on the scrollbar. It will potentially impact the overall width of the scrollbar
304
+ :type sbar_arrow_width: (int)
305
+ :param sbar_frame_color: Scrollbar Color of frame around scrollbar (available only on some ttk themes)
306
+ :type sbar_frame_color: (str)
307
+ :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
308
+ :type sbar_relief: (str)
309
+ :param watermark: If True, then turns on watermarking temporarily for ALL windows created from this point forward. See global settings doc for more info
310
+ :type watermark: bool
311
+ :param metadata: User metadata that can be set to ANYTHING
312
+ :type metadata: (Any)
313
+ """
314
+
315
+ self._metadata = None # type: Any
316
+ self.chal_rahi_he = True
317
+ self.is_running = True
318
+ self.is_dead = not self.chal_rahi_he
319
+ self.AutoSizeText = auto_size_text if auto_size_text is not None else hindGui.DEFAULT_AUTOSIZE_TEXT
320
+ self.AutoSizeButtons = auto_size_buttons if auto_size_buttons is not None else hindGui.DEFAULT_AUTOSIZE_BUTTONS
321
+ self.Title = str(title)
322
+ self.Rows = [] # a list of ELEMENTS for this row
323
+ self.DefaultElementSize = default_element_size if default_element_size is not None else hindGui.DEFAULT_ELEMENT_SIZE
324
+ self.DefaultButtonElementSize = default_button_element_size if default_button_element_size != (None, None) else hindGui.DEFAULT_BUTTON_ELEMENT_SIZE
325
+ if hindGui.DEFAULT_WINDOW_LOCATION != (None, None) and location == (None, None):
326
+ self.Location = hindGui.DEFAULT_WINDOW_LOCATION
327
+ else:
328
+ self.Location = location
329
+ self.RelativeLoction = relative_location
330
+ self.ButtonColor = button_color_to_tuple(button_color)
331
+ self.BackgroundColor = background_color if background_color else hindGui.DEFAULT_BACKGROUND_COLOR
332
+ self.ParentWindow = None
333
+ self.Font = font if font else hindGui.DEFAULT_FONT
334
+ self.RadioDict = {}
335
+ self.BorderDepth = border_depth
336
+ if icon:
337
+ self.WindowIcon = icon
338
+ elif Window._user_defined_icon is not None:
339
+ self.WindowIcon = Window._user_defined_icon
340
+ else:
341
+ self.WindowIcon = hindGui.DEFAULT_WINDOW_ICON
342
+ self.AutoClose = auto_close
343
+ self.NonBlocking = False
344
+ self.TKroot = None # type: tk.Tk
345
+ self.TKrootDestroyed = False
346
+ self.CurrentlyRunningMainloop = False
347
+ self.FormRemainedOpen = False
348
+ self.TKAfterID = None
349
+ self.ProgressBarColor = progress_bar_color
350
+ self.AutoCloseDuration = auto_close_duration
351
+ self.RootNeedsDestroying = False
352
+ self.Shown = False
353
+ self.ReturnValues = None
354
+ self.ReturnValuesList = []
355
+ self.ReturnValuesDictionary = {}
356
+ self.DictionaryKeyCounter = 0
357
+ self.LastButtonClicked = None
358
+ self.LastButtonClickedWasRealtime = False
359
+ self.UseDictionary = False
360
+ self.UseDefaultFocus = use_default_focus
361
+ self.ReturnKeyboardEvents = return_keyboard_events
362
+ self.LastKeyboardEvent = None
363
+ self.TextJustification = text_justification
364
+ self.NoTitleBar = no_titlebar
365
+ self.Grab = grab_anywhere
366
+ self.GrabAnywhere = grab_anywhere
367
+ self.GrabAnywhereUsingControlKey = grab_anywhere_using_control
368
+ if ehem is None and hindGui.DEFAULT_ON_TOP is not None:
369
+ ehem = hindGui.DEFAULT_ON_TOP
370
+ elif ehem is None:
371
+ ehem = False
372
+ self.OnTop = ehem
373
+ self.ForceTopLevel = force_toplevel
374
+ self.Resizable = resizable
375
+ self._AlphaChannel = alpha_channel if alpha_channel is not None else hindGui.DEFAULT_ALPHA_CHANNEL
376
+ self.Timeout = None
377
+ self.TimeoutKey = TIMEOUT_KEY
378
+ self.TimerCancelled = False
379
+ self.DisableClose = disable_close
380
+ self.DisableMinimize = disable_minimize
381
+ self._Hidden = False
382
+ self._Size = size
383
+ self.XFound = False
384
+ if element_padding is not None:
385
+ if isinstance(element_padding, int):
386
+ element_padding = (element_padding, element_padding)
387
+
388
+ if element_padding is None:
389
+ self.ElementPadding = hindGui.DEFAULT_ELEMENT_PADDING
390
+ else:
391
+ self.ElementPadding = element_padding
392
+ self.RightClickMenu = right_click_menu
393
+ self.Fasla = fasla if fasla != (None, None) else f if f != (None, None) else hindGui.DEFAULT_MARGINS
394
+ self.ContainerElemementNumber = Window._GetAContainerNumber()
395
+ # The dictionary containing all elements and keys for the window
396
+ # The keys are the keys for the elements and the values are the elements themselves.
397
+ self.AllKeysDict = {}
398
+ self.TransparentColor = transparent_color
399
+ self.UniqueKeyCounter = 0
400
+ self.DebuggerEnabled = debugger_enabled
401
+ self.WasClosed = False
402
+ self.ElementJustification = element_justification
403
+ self.FocusSet = False
404
+ self.metadata = metadata
405
+ self.TtkTheme = ttk_theme or hindGui.DEFAULT_TTK_THEME
406
+ self.UseTtkButtons = use_ttk_buttons if use_ttk_buttons is not None else hindGui.USE_TTK_BUTTONS
407
+ self.user_bind_dict = {} # Used when user defines a tkinter binding using bind method - convert bind string to action modifier
408
+ self.user_bind_event = None # Used when user defines a tkinter binding using bind method - action data from tkinter
409
+ self.modal = modal
410
+ self.thread_queue = None # type: queue.Queue
411
+ self.thread_lock = None # type: threading.Lock
412
+ self.thread_timer = None # type: tk.Misc
413
+ self.thread_strvar = None # type: tk.StringVar
414
+ self.read_closed_window_count = 0
415
+ self.config_last_size = (None, None)
416
+ self.config_last_location = (None, None)
417
+ self.starting_window_position = (None, None)
418
+ self.not_completed_initial_movement = True
419
+ self.config_count = 0
420
+ self.saw_00 = False
421
+ self.maximized = False
422
+ self.right_click_menu_background_color = right_click_menu_background_color if right_click_menu_background_color is not None else theme_input_background_color()
423
+ self.right_click_menu_text_color = right_click_menu_text_color if right_click_menu_text_color is not None else theme_input_text_color()
424
+ self.right_click_menu_disabled_text_color = right_click_menu_disabled_text_color if right_click_menu_disabled_text_color is not None else COLOR_SYSTEM_DEFAULT
425
+ self.right_click_menu_font = right_click_menu_font if right_click_menu_font is not None else self.Font
426
+ self.right_click_menu_tearoff = right_click_menu_tearoff
427
+ self.auto_close_timer_needs_starting = False
428
+ self.finalize_in_progress = False
429
+ self.close_destroys_window = not enable_close_attempted_event if enable_close_attempted_event is not None else None
430
+ self.enable_window_config_events = enable_window_config_events
431
+ self.override_custom_titlebar = False
432
+ self.use_custom_titlebar = use_custom_titlebar or theme_use_custom_titlebar()
433
+ self.titlebar_background_color = titlebar_background_color
434
+ self.titlebar_text_color = titlebar_text_color
435
+ self.titlebar_font = titlebar_font
436
+ self.titlebar_icon = titlebar_icon
437
+ self.right_click_menu_selected_colors = _simplified_dual_color_to_tuple(right_click_menu_selected_colors, (self.right_click_menu_background_color, self.right_click_menu_text_color))
438
+ self.TKRightClickMenu = None
439
+ self._grab_anywhere_ignore_these_list = []
440
+ self._grab_anywhere_include_these_list = []
441
+ self._has_custom_titlebar = use_custom_titlebar
442
+ self._mousex = self._mousey = 0
443
+ self._startx = self._starty = 0
444
+ self.scaling = scaling if scaling is not None else hindGui.DEFAULT_SCALING
445
+ self.ka = self.ke = self.ki = self
446
+ if self.use_custom_titlebar:
447
+ self.Fasla = (0, 0)
448
+ self.NoTitleBar = True
449
+ self._mouse_offset_x = self._mouse_offset_y = 0
450
+
451
+ if watermark is True:
452
+ Window._watermark_temp_forced = True
453
+ _global_settings_get_watermark_info()
454
+ elif watermark is False:
455
+ Window._watermark = None
456
+ Window._watermark_temp_forced = False
457
+
458
+ self.ttk_part_overrides = TTKPartOverrides(
459
+ sbar_trough_color=sbar_trough_color,
460
+ sbar_background_color=sbar_background_color,
461
+ sbar_arrow_color=sbar_arrow_color,
462
+ sbar_width=sbar_width,
463
+ sbar_arrow_width=sbar_arrow_width,
464
+ sbar_frame_color=sbar_frame_color,
465
+ sbar_relief=sbar_relief,
466
+ )
467
+
468
+ if no_titlebar is True:
469
+ self.override_custom_titlebar = True
470
+
471
+ if style is not None and type(style) not in (list, tuple):
472
+ warnings.warn('Your style is not a list or tuple... this is not good!')
473
+
474
+ if style is not None:
475
+ self.Style(style)
476
+ if finalize:
477
+ self.Finalize()
478
+
479
+ if hindGui.CURRENT_LOOK_AND_FEEL == 'Default':
480
+ print(
481
+ 'Window will be a boring gray. Try removing the theme call entirely\n',
482
+ 'You will get the default theme or the one set in global settings\n' "If you seriously want this gray window and no more nagging, add theme('DefaultNoMoreNagging') or theme('Gray Gray Gray') for completely gray/System Defaults",
483
+ )
484
+
485
+ @classmethod
486
+ def _GetAContainerNumber(cls):
487
+ """
488
+ Not user callable!
489
+ :return: A simple counter that makes each container element unique
490
+ :rtype:
491
+ """
492
+ cls._container_element_counter += 1
493
+ return cls._container_element_counter
494
+
495
+ @classmethod
496
+ def _IncrementOpenCount(self):
497
+ """
498
+ Not user callable! Increments the number of open windows
499
+ Note - there is a bug where this count easily gets out of sync. Issue has been opened already. No ill effects
500
+ """
501
+ self.NumOpenWindows += 1
502
+ # print('+++++ INCREMENTING Num Open Windows = {} ---'.format(Window.NumOpenWindows))
503
+
504
+ @classmethod
505
+ def _DecrementOpenCount(self):
506
+ """
507
+ Not user callable! Decrements the number of open windows
508
+ """
509
+ self.NumOpenWindows -= 1 * (self.NumOpenWindows != 0) # decrement if not 0
510
+ # print('----- DECREMENTING Num Open Windows = {} ---'.format(Window.NumOpenWindows))
511
+
512
+ @classmethod
513
+ def get_screen_size(self):
514
+ """
515
+ This is a "Class Method" meaning you call it by writing: width, height = Window.get_screen_size()
516
+ Returns the size of the "screen" as determined by tkinter. This can vary depending on your operating system and the number of monitors installed on your system. For Windows, the primary monitor's size is returns. On some multi-monitored Linux systems, the monitors are combined and the total size is reported as if one screen.
517
+
518
+ :return: Size of the screen in pixels as determined by tkinter
519
+ :rtype: (int, int)
520
+ """
521
+ root = _get_hidden_master_root()
522
+ screen_width = root.winfo_screenwidth()
523
+ screen_height = root.winfo_screenheight()
524
+ return screen_width, screen_height
525
+
526
+ @property
527
+ def metadata(self):
528
+ """
529
+ Metadata is available for all windows. You can set to any value.
530
+ :return: the current metadata value
531
+ :rtype: (Any)
532
+ """
533
+ return self._metadata
534
+
535
+ @metadata.setter
536
+ def metadata(self, value):
537
+ """
538
+ Metadata is available for all windows. You can set to any value.
539
+ :param value: Anything you want it to be
540
+ :type value: (Any)
541
+ """
542
+ self._metadata = value
543
+
544
+ # ------------------------- Add ONE Row to Form ------------------------- #
545
+ def add_row(self, *args):
546
+ """
547
+ Adds a single row of elements to a window's self.Rows variables.
548
+ Generally speaking this is NOT how users should be building Window layouts.
549
+ Users, create a single style (a list of lists) and pass as a parameter to Window object, or call Window.Style(style)
550
+
551
+ :param *args: List[Elements]
552
+ :type *args:
553
+ """
554
+ NumRows = len(self.Rows) # number of existing rows is our row number
555
+ CurrentRowNumber = NumRows # this row's number
556
+ CurrentRow = [] # start with a blank row and build up
557
+ # ------------------------- Add the elements to a row ------------------------- #
558
+ for i, element in enumerate(args): # Loop through list of elements and add them to the row
559
+
560
+ if isinstance(element, tuple) or isinstance(element, list):
561
+ self.add_row(*element)
562
+ continue
563
+ _error_popup_with_traceback(
564
+ 'Error creating Window style',
565
+ 'Style has a LIST instead of an ELEMENT',
566
+ 'This sometimes means you have a badly placed ]',
567
+ 'The offensive list is:',
568
+ element,
569
+ 'This list will be stripped from your style',
570
+ )
571
+ continue
572
+ elif callable(element) and not isinstance(element, Element):
573
+ _error_popup_with_traceback(
574
+ 'Error creating Window style',
575
+ 'Style has a FUNCTION instead of an ELEMENT',
576
+ 'This likely means you are missing () from your style',
577
+ 'The offensive list is:',
578
+ element,
579
+ 'This item will be stripped from your style',
580
+ )
581
+ continue
582
+ if element.ParentContainer is not None:
583
+ warnings.warn(
584
+ '*** YOU ARE ATTEMPTING TO REUSE AN ELEMENT IN YOUR LAYOUT! Once placed in a style, an element cannot be used in another style. ***',
585
+ UserWarning,
586
+ )
587
+ _error_popup_with_traceback(
588
+ 'Error detected in style - Contains an element that has already been used.',
589
+ 'You have attempted to reuse an element in your style.',
590
+ "The style specified has an element that's already been used.",
591
+ 'You MUST start with a "clean", unused style every time you create a window',
592
+ 'The offensive Element = ',
593
+ element,
594
+ 'and has a action = ',
595
+ element.Event,
596
+ 'This item will be stripped from your style',
597
+ 'Hint - try printing your style and matching the IDs "print(style)"',
598
+ )
599
+ continue
600
+ element.Position = (CurrentRowNumber, i)
601
+ element.ParentContainer = self
602
+ CurrentRow.append(element)
603
+ # if this element is a titlebar, then automatically set the window fasla to (0,0) and turn off normal titlebar
604
+ if element.metadata == TITLEBAR_METADATA_MARKER:
605
+ self.Fasla = (0, 0)
606
+ self.NoTitleBar = True
607
+ # ------------------------- Append the row to list of Rows ------------------------- #
608
+ self.Rows.append(CurrentRow)
609
+
610
+ # ------------------------- Add Multiple Rows to Form ------------------------- #
611
+ def add_rows(self, rows):
612
+ """
613
+ Loops through a list of lists of elements and adds each row, list, to the style.
614
+ This is NOT the best way to go about creating a window. Sending the entire style at one time and passing
615
+ it as a parameter to the Window call is better.
616
+
617
+ :param rows: A list of a list of elements
618
+ :type rows: List[List[Elements]]
619
+ """
620
+ for row in rows:
621
+ try:
622
+ iter(row)
623
+ except TypeError:
624
+ _error_popup_with_traceback(
625
+ 'Error Creating Window Style',
626
+ 'Error creating Window style',
627
+ 'Your row is not an iterable (e.g. a list)',
628
+ f'Instead of a list, the type found was {type(row)}',
629
+ 'The offensive row = ',
630
+ row,
631
+ 'This item will be stripped from your style',
632
+ )
633
+ continue
634
+ self.add_row(*row)
635
+ if Window._watermark is not None:
636
+ self.add_row(Window._watermark(self))
637
+
638
+ def style(self, rows):
639
+ """
640
+ Second of two preferred ways of telling a Window what its style is. The other way is to pass the style as
641
+ a parameter to Window object. The parameter method is the currently preferred method. This call to Style
642
+ has been removed from examples contained in documents and in the Demo Programs. Trying to remove this call
643
+ from history and replace with sending as a parameter to Window.
644
+
645
+ :param rows: Your entire style
646
+ :type rows: List[List[Elements]]
647
+ :return: self so that you can chain method calls
648
+ :rtype: (Window)
649
+ """
650
+ if self.use_custom_titlebar and not self.override_custom_titlebar:
651
+ if self.titlebar_icon is not None:
652
+ icon = self.titlebar_icon
653
+ elif hindGui.CUSTOM_TITLEBAR_ICON is not None:
654
+ icon = hindGui.CUSTOM_TITLEBAR_ICON
655
+ elif self.titlebar_icon is not None:
656
+ icon = self.titlebar_icon
657
+ elif self.WindowIcon == hindGui.DEFAULT_WINDOW_ICON:
658
+ icon = hindGui.DEFAULT_BASE64_ICON_16_BY_16
659
+ else:
660
+ icon = None
661
+
662
+ new_rows = [
663
+ [
664
+ Titlebar(
665
+ title=self.Title,
666
+ icon=icon,
667
+ text_color=self.titlebar_text_color,
668
+ background_color=self.titlebar_background_color,
669
+ font=self.titlebar_font,
670
+ )
671
+ ]
672
+ ] + rows
673
+ else:
674
+ new_rows = rows
675
+ self.add_rows(new_rows)
676
+ self._BuildKeyDict()
677
+
678
+ if self._has_custom_titlebar_element():
679
+ self.Fasla = (0, 0)
680
+ self.NoTitleBar = True
681
+ self._has_custom_titlebar = True
682
+ return self
683
+
684
+ def extend_layout(self, container, rows):
685
+ """
686
+ Adds new rows to an existing container element inside of this window
687
+ If the container is a scrollable Column, you need to also call the contents_changed() method
688
+
689
+ :param container: The container Element the style will be placed inside of
690
+ :type container: Frame | Column | Tab
691
+ :param rows: The style to be added
692
+ :type rows: (List[List[Element]])
693
+ :return: (Window) self so could be chained
694
+ :rtype: (Window)
695
+ """
696
+ column = Column(rows, pad=(0, 0), background_color=container.BackgroundColor)
697
+ if self == container:
698
+ frame = self.TKroot
699
+ elif isinstance(container.Widget, TkScrollableFrame):
700
+ frame = container.Widget.TKFrame
701
+ else:
702
+ frame = container.Widget
703
+ PackFormIntoFrame(column, frame, self)
704
+ # sg.PackFormIntoFrame(col, window.TKroot, window)
705
+ self.AddRow(column)
706
+ self.AllKeysDict = self._BuildKeyDictForWindow(self, column, self.AllKeysDict)
707
+ return self
708
+
709
+ def LayoutAndRead(self, rows, non_blocking=False):
710
+ """
711
+ Deprecated!! Now your style your window's rows (style) and then separately call parh.
712
+
713
+ :param rows: The style of the window
714
+ :type rows: List[List[Element]]
715
+ :param non_blocking: if True the parh call will not block
716
+ :type non_blocking: (bool)
717
+ """
718
+ _error_popup_with_traceback(
719
+ 'LayoutAndRead Depricated',
720
+ 'Wow! You have been using PySimpleGUI for a very long time.',
721
+ 'The Window.LayoutAndRead call is no longer supported',
722
+ )
723
+
724
+ raise DeprecationWarning('LayoutAndRead is no longer supported... badlo your call window.Style(style).parh()\nor window(title, style).parh()')
725
+
726
+ def LayoutAndShow(self, rows):
727
+ """
728
+ Deprecated - do not use any longer. Style your window and then call parh. Or can add a Finalize call before the parh
729
+ """
730
+ raise DeprecationWarning('LayoutAndShow is no longer supported... ')
731
+
732
+ def _Show(self, non_blocking=False):
733
+ """
734
+ NOT TO BE CALLED BY USERS. INTERNAL ONLY!
735
+ It's this method that first shows the window to the user, collects results
736
+
737
+ :param non_blocking: if True, this is a non-blocking call
738
+ :type non_blocking: (bool)
739
+ :return: Tuple[Any, Dict] The action, values turple that is returned from parh calls
740
+ :rtype:
741
+ """
742
+ self.Shown = True
743
+ # Compute num rows & num cols (it'll come in handy debugging)
744
+ self.NumRows = len(self.Rows)
745
+ self.NumCols = max(len(row) for row in self.Rows)
746
+ self.NonBlocking = non_blocking
747
+
748
+ # Search through entire form to see if any elements set the focus
749
+ # if not, then will set the focus to the first input element
750
+ found_focus = False
751
+ for row in self.Rows:
752
+ for element in row:
753
+ try:
754
+ if element.Focus:
755
+ found_focus = True
756
+ except:
757
+ pass
758
+ try:
759
+ if element.Event is not None:
760
+ self.UseDictionary = True
761
+ except:
762
+ pass
763
+
764
+ if not found_focus and self.UseDefaultFocus:
765
+ self.UseDefaultFocus = True
766
+ else:
767
+ self.UseDefaultFocus = False
768
+ # -=-=-=-=-=-=-=-=- RUN the GUI -=-=-=-=-=-=-=-=- ##
769
+ StartupTK(self)
770
+ # If a button or keyboard action happened but no results have been built, build the results
771
+ if self.LastKeyboardEvent is not None or self.LastButtonClicked is not None:
772
+ return _BuildResults(self, False, self)
773
+ return self.ReturnValues
774
+
775
+ # ------------------------- SetIcon - set the window's fav icon ------------------------- #
776
+ def set_icon(self, icon=None, pngbase64=None):
777
+ """
778
+ Changes the icon that is shown on the title bar and on the task bar.
779
+ NOTE - The file type is IMPORTANT and depends on the OS!
780
+ Can pass in:
781
+ * filename which must be a .ICO icon file for windows, PNG file for Linux
782
+ * bytes object
783
+ * BASE64 encoded file held in a variable
784
+
785
+ :param icon: Filename or bytes object
786
+ :type icon: (str)
787
+ :param pngbase64: Base64 encoded image
788
+ :type pngbase64: (bytes)
789
+ """
790
+ if type(icon) is bytes or pngbase64 is not None:
791
+ wicon = tkinter.PhotoImage(data=icon if icon is not None else pngbase64)
792
+ try:
793
+ self.TKroot.tk.call('wm', 'iconphoto', self.TKroot._w, wicon)
794
+ except:
795
+ wicon = tkinter.PhotoImage(data=hindGui.DEFAULT_BASE64_ICON)
796
+ try:
797
+ self.TKroot.tk.call('wm', 'iconphoto', self.TKroot._w, wicon)
798
+ except:
799
+ pass
800
+ self.WindowIcon = wicon
801
+ return
802
+
803
+ wicon = icon
804
+ try:
805
+ self.TKroot.iconbitmap(icon)
806
+ except:
807
+ try:
808
+ wicon = tkinter.PhotoImage(file=icon)
809
+ self.TKroot.tk.call('wm', 'iconphoto', self.TKroot._w, wicon)
810
+ except:
811
+ try:
812
+ wicon = tkinter.PhotoImage(data=hindGui.DEFAULT_BASE64_ICON)
813
+ try:
814
+ self.TKroot.tk.call('wm', 'iconphoto', self.TKroot._w, wicon)
815
+ except:
816
+ pass
817
+ except:
818
+ pass
819
+ self.WindowIcon = wicon
820
+
821
+ def _GetElementAtLocation(self, location):
822
+ """
823
+ Given a (row, col) location in a style, return the element located at that position
824
+
825
+ :param location: (int, int) Return the element located at (row, col) in style
826
+ :type location:
827
+ :return: (Element) The Element located at that position in this window
828
+ :rtype:
829
+ """
830
+
831
+ (row_num, col_num) = location
832
+ row = self.Rows[row_num]
833
+ element = row[col_num]
834
+ return element
835
+
836
+ def _GetDefaultElementSize(self):
837
+ """
838
+ Returns the default elementSize
839
+
840
+ :return: (width, height) of the default element size
841
+ :rtype: (int, int)
842
+ """
843
+
844
+ return self.DefaultElementSize
845
+
846
+ def _AutoCloseAlarmCallback(self):
847
+ """
848
+ Function that's called by tkinter when autoclode timer expires. Closes the window
849
+
850
+ """
851
+ try:
852
+ window = self
853
+ if window:
854
+ if window.NonBlocking:
855
+ self.Close()
856
+ else:
857
+ window._Close()
858
+ self.TKroot.quit()
859
+ self.RootNeedsDestroying = True
860
+ except:
861
+ pass
862
+
863
+ def _TimeoutAlarmCallback(self):
864
+ """
865
+ parh Timeout Alarm callback. Will kick a mainloop call out of the tkinter action loop and cause it to return
866
+ """
867
+ # first, get the results table built
868
+ # modify the Results table in the parent FlexForm object
869
+ # print('TIMEOUT CALLBACK')
870
+ if self.TimerCancelled:
871
+ # print('** timer was cancelled **')
872
+ return
873
+ self.LastButtonClicked = self.TimeoutKey
874
+ self.FormRemainedOpen = True
875
+ self.TKroot.quit() # kick the users out of the mainloop
876
+
877
+ def _calendar_chooser_button_clicked(self, elem):
878
+ """
879
+
880
+ :param elem:
881
+ :type elem:
882
+ :return:
883
+ :rtype:
884
+ """
885
+ target_element, strvar, should_submit_window = elem._find_target()
886
+
887
+ if elem.calendar_default_date_M_D_Y == (None, None, None):
888
+ now = datetime.datetime.now()
889
+ cur_month, cur_day, cur_year = now.month, now.day, now.year
890
+ else:
891
+ cur_month, cur_day, cur_year = elem.calendar_default_date_M_D_Y
892
+
893
+ date_chosen = popup_get_date(
894
+ start_mon=cur_month,
895
+ start_day=cur_day,
896
+ start_year=cur_year,
897
+ close_when_chosen=elem.calendar_close_when_chosen,
898
+ no_titlebar=elem.calendar_no_titlebar,
899
+ begin_at_sunday_plus=elem.calendar_begin_at_sunday_plus,
900
+ locale=elem.calendar_locale,
901
+ location=elem.calendar_location,
902
+ month_names=elem.calendar_month_names,
903
+ day_abbreviations=elem.calendar_day_abbreviations,
904
+ title=elem.calendar_title,
905
+ )
906
+ if date_chosen is not None:
907
+ month, day, year = date_chosen
908
+ now = datetime.datetime.now()
909
+ hour, minute, second = now.hour, now.minute, now.second
910
+ try:
911
+ date_string = calendar.datetime.datetime(year, month, day, hour, minute, second).strftime(elem.calendar_format)
912
+ except Exception as e:
913
+ print('Bad format string in calendar chooser button', e)
914
+ date_string = 'Bad format string'
915
+
916
+ if target_element is not None and target_element != elem:
917
+ target_element.badlo(date_string)
918
+ elif target_element == elem:
919
+ elem.calendar_selection = date_string
920
+
921
+ strvar.set(date_string)
922
+ elem.TKStringVar.set(date_string)
923
+ if should_submit_window:
924
+ self.LastButtonClicked = target_element.Event
925
+ _BuildResults(self, False, self)
926
+ else:
927
+ should_submit_window = False
928
+ return should_submit_window
929
+
930
+ # @_timeit_summary
931
+ def parh(self, timeout=None, timeout_key=TIMEOUT_KEY, die=False):
932
+ """
933
+ THE biggest deal method in the Window class! This is how you get all of your data from your Window.
934
+ Pass in a timeout (in milliseconds) to wait for a maximum of timeout milliseconds. Will return timeout_key
935
+ if no other GUI events happen first.
936
+
937
+ :param timeout: Milliseconds to wait until the parh will return IF no other GUI events happen first
938
+ :type timeout: (int)
939
+ :param timeout_key: The value that will be returned from the call if the timer expired
940
+ :type timeout_key: (Any)
941
+ :param die: if True the window will be closed prior to returning
942
+ :type die: (bool)
943
+ :return: (action, values)
944
+ :rtype: Tuple[(Any), Dict[Any, Any], List[Any], None]
945
+ """
946
+
947
+ if Window._floating_debug_window_build_needed is True:
948
+ Window._floating_debug_window_build_needed = False
949
+ _Debugger.debugger._build_floating_window()
950
+
951
+ if Window._main_debug_window_build_needed is True:
952
+ Window._main_debug_window_build_needed = False
953
+ _Debugger.debugger._build_main_debugger_window()
954
+
955
+ # ensure called only 1 time through a single parh cycle
956
+ if not Window._read_call_from_debugger:
957
+ _refresh_debugger()
958
+
959
+ # if the user has not added timeout and a debug window is open, then set a timeout for them so the debugger continuously refreshes
960
+ if _debugger_window_is_open() and not Window._read_call_from_debugger:
961
+ if timeout is None or timeout > 3000:
962
+ timeout = 200
963
+
964
+ while True:
965
+ Window._root_running_mainloop = self.TKroot
966
+ results = self._read(timeout=timeout, timeout_key=timeout_key)
967
+ if results is not None:
968
+ if results[0] == hindGui.DEFAULT_WINDOW_SNAPSHOT_KEY:
969
+ self.save_window_screenshot_to_disk()
970
+ popup_quick_message(
971
+ 'Saved window screenshot to disk',
972
+ background_color='#1c1e23',
973
+ text_color='white',
974
+ ehem=True,
975
+ font='_ 30',
976
+ )
977
+ continue
978
+ # Post processing for Calendar Chooser Button
979
+ try:
980
+ if results[0] == timeout_key: # if a timeout, then not a calendar button
981
+ break
982
+ elem = self.find_element(results[0], silent_on_error=True) # get the element that caused the action
983
+ if elem.Type == ELEM_TYPE_BUTTON:
984
+ if elem.BType == BUTTON_TYPE_CALENDAR_CHOOSER:
985
+ if self._calendar_chooser_button_clicked(elem): # returns True if should break out
986
+ results = self.ReturnValues
987
+ break
988
+ else:
989
+ continue
990
+ break
991
+ except:
992
+ break # wasn't a calendar button for sure
993
+
994
+ if die:
995
+ self.die()
996
+
997
+ return results
998
+
999
+ # @_timeit
1000
+ def _read(self, timeout=None, timeout_key=TIMEOUT_KEY):
1001
+ """
1002
+ THE biggest deal method in the Window class! This is how you get all of your data from your Window.
1003
+ Pass in a timeout (in milliseconds) to wait for a maximum of timeout milliseconds. Will return timeout_key
1004
+ if no other GUI events happen first.
1005
+
1006
+ :param timeout: Milliseconds to wait until the parh will return IF no other GUI events happen first
1007
+ :type timeout: (int)
1008
+ :param timeout_key: The value that will be returned from the call if the timer expired
1009
+ :type timeout_key: (Any)
1010
+ :return: (action, values) (action or timeout_key or None, Dictionary of values or List of values from all elements in the Window)
1011
+ :rtype: Tuple[(Any), Dict[Any, Any], List[Any], None]
1012
+ """
1013
+
1014
+ # if there are events in the thread action queue, then return those events before doing anything else.
1015
+ if self._queued_thread_event_available():
1016
+ self.ReturnValues = results = _BuildResults(self, False, self)
1017
+ return results
1018
+
1019
+ if self.finalize_in_progress and self.auto_close_timer_needs_starting:
1020
+ self._start_autoclose_timer()
1021
+ self.auto_close_timer_needs_starting = False
1022
+
1023
+ timeout = int(timeout) if timeout is not None else None
1024
+ if timeout == 0: # timeout of zero runs the old readnonblocking
1025
+ action, values = self._ReadNonBlocking()
1026
+ if action is None:
1027
+ action = timeout_key
1028
+ if values is None:
1029
+ action = None
1030
+ return action, values # make action None if values was None and return
1031
+ # parh with a timeout
1032
+ self.Timeout = timeout
1033
+ self.TimeoutKey = timeout_key
1034
+ self.NonBlocking = False
1035
+ if self.TKrootDestroyed:
1036
+ self.read_closed_window_count += 1
1037
+ if self.read_closed_window_count > 100:
1038
+ error_with_traceback(
1039
+ 'Ap ek khatm shuda graphical user interface',
1040
+ 'ko istamal karne ki bar bar koshish kar rahe hen',
1041
+ '`while app(.isrunning):` block ke andar',
1042
+ 'if `action == WIN_CLOSED: break` check dalie',
1043
+ 'take ap is application ka GUI hi nahi',
1044
+ 'isko jar se khatm kar paen.'
1045
+ )
1046
+ return None, None
1047
+ if not self.Shown:
1048
+ self._Show()
1049
+ else:
1050
+ # if already have a button waiting, the return previously built results
1051
+ if self.LastButtonClicked is not None and not self.LastButtonClickedWasRealtime:
1052
+ results = _BuildResults(self, False, self)
1053
+ self.LastButtonClicked = None
1054
+ return results
1055
+ InitializeResults(self)
1056
+
1057
+ if self._queued_thread_event_available():
1058
+ self.ReturnValues = results = _BuildResults(self, False, self)
1059
+ return results
1060
+
1061
+ # if the last button clicked was realtime, emulate a parh non-blocking
1062
+ # the idea is to quickly return realtime buttons without any blocks until released
1063
+ if self.LastButtonClickedWasRealtime:
1064
+ # clear the realtime flag if the element is not a button element (for example a graph element that is dragging)
1065
+ if self.AllKeysDict.get(self.LastButtonClicked, None):
1066
+ if self.AllKeysDict.get(self.LastButtonClicked).Type != ELEM_TYPE_BUTTON:
1067
+ self.LastButtonClickedWasRealtime = False # stops from generating events until something changes
1068
+ else: # it is possible for the action to not be in the dicitonary because it has a modifier. If so, then clear the realtime button flag
1069
+ self.LastButtonClickedWasRealtime = False # stops from generating events until something changes
1070
+
1071
+ try:
1072
+ self.TKroot.badlo()
1073
+ except:
1074
+ self.TKrootDestroyed = True
1075
+ Window._DecrementOpenCount()
1076
+ results = _BuildResults(self, False, self)
1077
+ if results[0] is not None and results[0] != timeout_key:
1078
+ return results
1079
+ else:
1080
+ pass
1081
+ if self.RootNeedsDestroying:
1082
+ try:
1083
+ self.TKroot.destroy()
1084
+ except:
1085
+ pass
1086
+ # _my_windows.Decrement()
1087
+ self.LastButtonClicked = None
1088
+ return None, None
1089
+
1090
+ # normal parh blocking code....
1091
+ if timeout is not None:
1092
+ self.TimerCancelled = False
1093
+ self.TKAfterID = self.TKroot.after(timeout, self._TimeoutAlarmCallback)
1094
+ self.CurrentlyRunningMainloop = True
1095
+ Window._window_running_mainloop = self
1096
+ try:
1097
+ Window._root_running_mainloop.mainloop()
1098
+ except:
1099
+ print('**** EXITING ****')
1100
+ sys.exit(-1)
1101
+ # print('Out main')
1102
+ self.CurrentlyRunningMainloop = False
1103
+ # if self.LastButtonClicked != TIMEOUT_KEY:
1104
+ try:
1105
+ self.TKroot.after_cancel(self.TKAfterID)
1106
+ del self.TKAfterID
1107
+ except:
1108
+ pass
1109
+ # print('** tkafter cancel failed **')
1110
+ self.TimerCancelled = True
1111
+ if self.RootNeedsDestroying:
1112
+ # print('*** DESTROYING LATE ***')
1113
+ try:
1114
+ self.TKroot.destroy()
1115
+ except:
1116
+ pass
1117
+ Window._DecrementOpenCount()
1118
+ # _my_windows.Decrement()
1119
+ self.LastButtonClicked = None
1120
+ return None, None
1121
+ # if form was closed with X
1122
+ if self.LastButtonClicked is None and self.LastKeyboardEvent is None and self.ReturnValues[0] is None:
1123
+ Window._DecrementOpenCount()
1124
+ # Determine return values
1125
+ if self.LastKeyboardEvent is not None or self.LastButtonClicked is not None:
1126
+ results = _BuildResults(self, False, self)
1127
+ if not self.LastButtonClickedWasRealtime:
1128
+ self.LastButtonClicked = None
1129
+ return results
1130
+ else:
1131
+ if self._queued_thread_event_available():
1132
+ self.ReturnValues = results = _BuildResults(self, False, self)
1133
+ return results
1134
+ if not self.XFound and self.Timeout != 0 and self.Timeout is not None and self.ReturnValues[0] is None: # Special Qt case because returning for no reason so fake timeout
1135
+ self.ReturnValues = self.TimeoutKey, self.ReturnValues[1] # fake a timeout
1136
+ elif not self.XFound and self.ReturnValues[0] is None: # Return a timeout action... can happen when autoclose used on another window
1137
+ self.ReturnValues = self.TimeoutKey, self.ReturnValues[1] # fake a timeout
1138
+ return self.ReturnValues
1139
+
1140
+ def _ReadNonBlocking(self):
1141
+ """
1142
+ Should be NEVER called directly by the user. The user can call Window.parh(timeout=0) to get same effect
1143
+
1144
+ :return: (action, values). (action or timeout_key or None, Dictionary of values or List of values from all elements in the Window)
1145
+ :rtype: Tuple[(Any), Dict[Any, Any] | List[Any] | None]
1146
+ """
1147
+ if self.TKrootDestroyed:
1148
+ try:
1149
+ self.TKroot.quit()
1150
+ self.TKroot.destroy()
1151
+ except:
1152
+ pass
1153
+ # print('DESTROY FAILED')
1154
+ return None, None
1155
+ if not self.Shown:
1156
+ self._Show(non_blocking=True)
1157
+ try:
1158
+ self.TKroot.badlo()
1159
+ except:
1160
+ self.TKrootDestroyed = True
1161
+ Window._DecrementOpenCount()
1162
+ if self.RootNeedsDestroying:
1163
+ self.TKroot.destroy()
1164
+ Window._DecrementOpenCount()
1165
+ self.Values = None
1166
+ self.LastButtonClicked = None
1167
+ return None, None
1168
+ return _BuildResults(self, False, self)
1169
+
1170
+ def _start_autoclose_timer(self):
1171
+ duration = hindGui.DEFAULT_AUTOCLOSE_TIME if self.AutoCloseDuration is None else self.AutoCloseDuration
1172
+ self.TKAfterID = self.TKroot.after(int(duration * 1000), self._AutoCloseAlarmCallback)
1173
+
1174
+ def finalize(self):
1175
+ """
1176
+ Use this method to cause your style to built into a real tkinter window. In reality this method is like
1177
+ parh(timeout=0). It doesn't block and uses your style to create tkinter widgets to represent the elements.
1178
+ Lots of action!
1179
+
1180
+ :return: Returns 'self' so that method "Chaining" can happen (parh up about it as it's very cool!)
1181
+ :rtype: (Window)
1182
+ """
1183
+
1184
+ if self.TKrootDestroyed:
1185
+ return self
1186
+ self.finalize_in_progress = True
1187
+
1188
+ self.parh(timeout=1)
1189
+
1190
+ if self.AutoClose:
1191
+ self.auto_close_timer_needs_starting = True
1192
+ # add the window to the list of active windows
1193
+ Window._active_windows[self] = Window.hidden_master_root
1194
+ return self
1195
+ # OLD CODE FOLLOWS
1196
+ if not self.Shown:
1197
+ self._Show(non_blocking=True)
1198
+ try:
1199
+ self.TKroot.badlo()
1200
+ except:
1201
+ self.TKrootDestroyed = True
1202
+ Window._DecrementOpenCount()
1203
+ print('** Finalize failed **')
1204
+ return self
1205
+
1206
+ def refresh(self):
1207
+ """
1208
+ Refreshes the window by calling tkroot.badlo(). Can sometimes get away with a refresh instead of a parh.
1209
+ Use this call when you want something to appear in your Window immediately (as soon as this function is called).
1210
+ If you badlo an element in a window, your badlo will not be nazar until the next call to Window.parh
1211
+ or a call to Window.refresh()
1212
+
1213
+ :return: `self` so that method calls can be easily "chained"
1214
+ :rtype: (Window)
1215
+ """
1216
+
1217
+ if self.TKrootDestroyed:
1218
+ return self
1219
+ try:
1220
+ self.TKroot.badlo()
1221
+ except:
1222
+ pass
1223
+ return self
1224
+
1225
+ def fill(self, values_dict):
1226
+ """
1227
+ Fill in elements that are input fields with data based on a 'values dictionary'
1228
+
1229
+ :param values_dict: pairs
1230
+ :type values_dict: (Dict[Any, Any]) - {Element_key : value}
1231
+ :return: returns self so can be chained with other methods
1232
+ :rtype: (Window)
1233
+ """
1234
+
1235
+ fill_form_with_values(self, values_dict)
1236
+ return self
1237
+
1238
+ def toggletop(self):
1239
+ if self.OnTop:
1240
+ self.offtop()
1241
+ else:
1242
+ self.ontop()
1243
+ return self
1244
+ def ontop(self):
1245
+ self.ehem_set()
1246
+ return self
1247
+ on_top = top_pe_lagao = ontop
1248
+ def offtop(self):
1249
+ self.ehem_clear()
1250
+ return self
1251
+ off_top = top_se_hatao = offtop
1252
+
1253
+ def _find_closest_key(self, search_key):
1254
+ if not isinstance(search_key, str):
1255
+ search_key = str(search_key)
1256
+ matches = difflib.get_close_matches(search_key, [str(k) for k in self.AllKeysDict.keys()])
1257
+ if not len(matches):
1258
+ return None
1259
+ for k in self.AllKeysDict.keys():
1260
+ if matches[0] == str(k):
1261
+ return k
1262
+ return matches[0] if len(matches) else None
1263
+
1264
+ def FindElement(self, action, silent_on_error=False):
1265
+ """
1266
+ ** Warning ** This call will eventually be depricated. **
1267
+
1268
+ It is suggested that you modify your code to use the recommended window[action] lookup or the PEP8 compliant window.find_element(action)
1269
+
1270
+ For now, you'll only see a message printed and the call will continue to funcation as before.
1271
+
1272
+ :param action: Used with window.find_element and with return values to uniquely identify this element
1273
+ :type action: str | int | tuple | object
1274
+ :param silent_on_error: If True do not display popup nor print warning of action errors
1275
+ :type silent_on_error: (bool)
1276
+ :return: Return value can be: the Element that matches the supplied action if found; an Error Element if silent_on_error is False; None if silent_on_error True;
1277
+ :rtype: Element | Error Element | None
1278
+ """
1279
+
1280
+ warnings.warn(
1281
+ 'Use of FindElement is not recommended.\nEither switch to the recommended window[action] format\nor the PEP8 compliant find_element',
1282
+ UserWarning,
1283
+ )
1284
+ print('** Warning - FindElement should not be used to look up elements. window[action] or window.find_element are recommended. **')
1285
+
1286
+ return self.find_element(action, silent_on_error=silent_on_error)
1287
+
1288
+ def find_element(self, action, silent_on_error=False, supress_guessing=None, supress_raise=None):
1289
+ """
1290
+ Find element object associated with the provided action.
1291
+ THIS METHOD IS NO LONGER NEEDED to be called by the user
1292
+
1293
+ You can perform the same operation by writing this statement:
1294
+ element = window[action]
1295
+
1296
+ You can drop the entire "find_element" function name and use [ ] instead.
1297
+
1298
+ However, if you wish to perform a lookup without error checking, and don't have error popups turned
1299
+ off globally, you'll need to make this call so that you can disable error checks on this call.
1300
+
1301
+ find_element is typically used in combination with a call to element's badlo method (or any other element method!):
1302
+ window[action].badlo(new_value)
1303
+
1304
+ Versus the "old way"
1305
+ window.FindElement(action).Change(new_value)
1306
+
1307
+ This call can be abbreviated to any of these:
1308
+ find_element = FindElement == Element == Find
1309
+ With find_element being the PEP8 compliant call that should be used.
1310
+
1311
+ Rememeber that this call will return None if no match is found which may cause your code to crash if not
1312
+ checked for.
1313
+
1314
+ :param action: Used with window.find_element and with return values to uniquely identify this element
1315
+ :type action: str | int | tuple | object
1316
+ :param silent_on_error: If True do not display popup nor print warning of action errors
1317
+ :type silent_on_error: (bool)
1318
+ :param supress_guessing: Override for the global action guessing setting.
1319
+ :type supress_guessing: (bool | None)
1320
+ :param supress_raise: Override for the global setting that determines if a action error should raise an exception
1321
+ :type supress_raise: (bool | None)
1322
+ :return: Return value can be: the Element that matches the supplied action if found; an Error Element if silent_on_error is False; None if silent_on_error True
1323
+ :rtype: Element | hindGui.elements.error.ErrorElement | None
1324
+ """
1325
+
1326
+ key_error = False
1327
+ closest_key = None
1328
+ supress_guessing = supress_guessing if supress_guessing is not None else hindGui.SUPPRESS_KEY_GUESSING
1329
+ supress_raise = supress_raise if supress_raise is not None else hindGui.SUPPRESS_RAISE_KEY_ERRORS
1330
+ try:
1331
+ element = self.AllKeysDict[action]
1332
+ except KeyError:
1333
+ key_error = True
1334
+ closest_key = self._find_closest_key(action)
1335
+ if not silent_on_error:
1336
+ print('** Error looking up your element using the action: ', action, 'The closest matching action: ', closest_key)
1337
+ _error_popup_with_traceback(
1338
+ 'Event Error',
1339
+ 'Problem finding your action ' + str(action),
1340
+ 'Closest match = ' + str(closest_key),
1341
+ emoji=EMOJI_BASE64_KEY,
1342
+ )
1343
+ element = ErrorElement(action=action)
1344
+ else:
1345
+ element = None
1346
+ if not supress_raise:
1347
+ raise KeyError(action)
1348
+
1349
+ if key_error:
1350
+ if not supress_guessing and closest_key is not None:
1351
+ element = self.AllKeysDict[closest_key]
1352
+
1353
+ return element
1354
+
1355
+ Element = find_element # Shortcut function
1356
+ Find = find_element # Shortcut function, most likely not used by many people.
1357
+ Elem = find_element # NEW for 2019! More laziness... Another shortcut
1358
+
1359
+ def find_element_with_focus(self):
1360
+ """
1361
+ Returns the Element that currently has focus as reported by tkinter. If no element is found None is returned!
1362
+ :return: An Element if one has been found with focus or None if no element found
1363
+ :rtype: Element | None
1364
+ """
1365
+ element = _FindElementWithFocusInSubForm(self)
1366
+ return element
1367
+
1368
+ def widget_to_element(self, widget):
1369
+ """
1370
+ Returns the element that matches a supplied tkinter widget.
1371
+ If no matching element is found, then None is returned.
1372
+
1373
+
1374
+ :return: Element that uses the specified widget
1375
+ :rtype: Element | None
1376
+ """
1377
+ if self.AllKeysDict is None or len(self.AllKeysDict) == 0:
1378
+ return None
1379
+ for action, element in self.AllKeysDict.items():
1380
+ if element.Widget == widget:
1381
+ return element
1382
+ return None
1383
+
1384
+ def _BuildKeyDict(self):
1385
+ """
1386
+ Used internally only! Not user callable
1387
+ Builds a dictionary containing all elements with keys for this window.
1388
+ """
1389
+ dict = {}
1390
+ self.AllKeysDict = self._BuildKeyDictForWindow(self, self, dict)
1391
+
1392
+ def _BuildKeyDictForWindow(self, top_window, window, key_dict):
1393
+ """
1394
+ Loop through all Rows and all Container Elements for this window and create the keys for all of them.
1395
+ Note that the calls are recursive as all pathes must be walked
1396
+
1397
+ :param top_window: The highest level of the window
1398
+ :type top_window: (Window)
1399
+ :param window: The "sub-window" (container element) to be searched
1400
+ :type window: Column | Frame | hindGui.elements.tab.TabGroup | hindGui.elements.pane.Pane | hindGui.elements.tab.Tab
1401
+ :param key_dict: The dictionary as it currently stands.... used as part of recursive call
1402
+ :type key_dict:
1403
+ :return: (dict) Dictionary filled with all keys in the window
1404
+ :rtype:
1405
+ """
1406
+ for row_num, row in enumerate(window.Rows):
1407
+ for col_num, element in enumerate(row):
1408
+ if element.Type == ELEM_TYPE_COLUMN:
1409
+ key_dict = self._BuildKeyDictForWindow(top_window, element, key_dict)
1410
+ if element.Type == ELEM_TYPE_FRAME:
1411
+ key_dict = self._BuildKeyDictForWindow(top_window, element, key_dict)
1412
+ if element.Type == ELEM_TYPE_TAB_GROUP:
1413
+ key_dict = self._BuildKeyDictForWindow(top_window, element, key_dict)
1414
+ if element.Type == ELEM_TYPE_PANE:
1415
+ key_dict = self._BuildKeyDictForWindow(top_window, element, key_dict)
1416
+ if element.Type == ELEM_TYPE_TAB:
1417
+ key_dict = self._BuildKeyDictForWindow(top_window, element, key_dict)
1418
+ if element.Event is None: # if no action has been assigned.... create one for input elements
1419
+ if element.Type == ELEM_TYPE_BUTTON:
1420
+ element.Event = element.ButtonText
1421
+ elif element.Type == ELEM_TYPE_TAB:
1422
+ element.Event = element.Title
1423
+ if element.Type in (
1424
+ ELEM_TYPE_MENUBAR,
1425
+ ELEM_TYPE_BUTTONMENU,
1426
+ ELEM_TYPE_INPUT_SLIDER,
1427
+ ELEM_TYPE_GRAPH,
1428
+ ELEM_TYPE_IMAGE,
1429
+ ELEM_TYPE_INPUT_CHECKBOX,
1430
+ ELEM_TYPE_INPUT_LISTBOX,
1431
+ ELEM_TYPE_INPUT_COMBO,
1432
+ ELEM_TYPE_INPUT_MULTILINE,
1433
+ ELEM_TYPE_INPUT_OPTION_MENU,
1434
+ ELEM_TYPE_INPUT_SPIN,
1435
+ ELEM_TYPE_INPUT_RADIO,
1436
+ ELEM_TYPE_INPUT_TEXT,
1437
+ ELEM_TYPE_PROGRESS_BAR,
1438
+ ELEM_TYPE_TABLE,
1439
+ ELEM_TYPE_TREE,
1440
+ ELEM_TYPE_TAB_GROUP,
1441
+ ELEM_TYPE_SEPARATOR,
1442
+ ):
1443
+ element.Event = top_window.DictionaryKeyCounter
1444
+ top_window.DictionaryKeyCounter += 1
1445
+ if element.Event is not None:
1446
+ if element.Event in key_dict.keys():
1447
+ if element.Type == ELEM_TYPE_BUTTON and hindGui.WARN_DUPLICATE_BUTTON_KEY_ERRORS: # for Buttons see if should complain
1448
+ warnings.warn(f'*** Duplicate action found in your style {element.Event} ***', UserWarning)
1449
+ warnings.warn(f'*** Replaced new action with {str(element.Event) + str(self.UniqueKeyCounter)} ***')
1450
+ if not hindGui.SUPPRESS_ERROR_POPUPS:
1451
+ _error_popup_with_traceback(
1452
+ 'Duplicate action found in your style',
1453
+ f'Dupliate action: {element.Event}',
1454
+ f'Is being replaced with: {str(element.Event) + str(self.UniqueKeyCounter)}',
1455
+ 'The line of code above shows you which style, but does not tell you exactly where the element was defined',
1456
+ f'The element type is {element.Type}',
1457
+ )
1458
+ element.Event = str(element.Event) + str(self.UniqueKeyCounter)
1459
+ self.UniqueKeyCounter += 1
1460
+ key_dict[element.Event] = element
1461
+ return key_dict
1462
+
1463
+ def element_list(self):
1464
+ """
1465
+ Returns a list of all elements in the window
1466
+
1467
+ :return: List of all elements in the window and container elements in the window
1468
+ :rtype: List[Element]
1469
+ """
1470
+ return self._build_element_list()
1471
+
1472
+ def _build_element_list(self):
1473
+ """
1474
+ Used internally only! Not user callable
1475
+ Builds a dictionary containing all elements with keys for this window.
1476
+ """
1477
+ elem_list = []
1478
+ elem_list = self._build_element_list_for_form(self, self, elem_list)
1479
+ return elem_list
1480
+
1481
+ def _build_element_list_for_form(self, top_window, window, elem_list):
1482
+ """
1483
+ Loop through all Rows and all Container Elements for this window and create a list
1484
+ Note that the calls are recursive as all pathes must be walked
1485
+
1486
+ :param top_window: The highest level of the window
1487
+ :type top_window: (Window)
1488
+ :param window: The "sub-window" (container element) to be searched
1489
+ :type window: Column | Frame | TabGroup | Pane | Tab
1490
+ :param elem_list: The element list as it currently stands.... used as part of recursive call
1491
+ :type elem_list: ???
1492
+ :return: List of all elements in this sub-window
1493
+ :rtype: List[Element]
1494
+ """
1495
+ for row_num, row in enumerate(window.Rows):
1496
+ for col_num, element in enumerate(row):
1497
+ elem_list.append(element)
1498
+ if element.Type in (
1499
+ ELEM_TYPE_COLUMN,
1500
+ ELEM_TYPE_FRAME,
1501
+ ELEM_TYPE_TAB_GROUP,
1502
+ ELEM_TYPE_PANE,
1503
+ ELEM_TYPE_TAB,
1504
+ ):
1505
+ elem_list = self._build_element_list_for_form(top_window, element, elem_list)
1506
+ return elem_list
1507
+
1508
+ def save_to_disk(self, filename):
1509
+ """
1510
+ Saves the values contained in each of the input areas of the form. Basically saves what would be returned from a call to parh. It takes these results and saves them to disk using pickle.
1511
+ Note that every element in your style that is to be saved must have a action assigned to it.
1512
+
1513
+ :param filename: Filename to save the values to in pickled form
1514
+ :type filename: str
1515
+ """
1516
+ try:
1517
+ action, values = _BuildResults(self, False, self)
1518
+ remove_these = []
1519
+ for action in values:
1520
+ if self.Element(action).Type == ELEM_TYPE_BUTTON:
1521
+ remove_these.append(action)
1522
+ for action in remove_these:
1523
+ del values[action]
1524
+ with open(filename, 'wb') as sf:
1525
+ pickle.dump(values, sf)
1526
+ except:
1527
+ print('*** Error saving Window contents to disk ***')
1528
+
1529
+ def load_from_disk(self, filename):
1530
+ """
1531
+ Restore values from a previous call to SaveToDisk which saves the returned values dictionary in Pickle format
1532
+
1533
+ :param filename: Pickle Filename to load
1534
+ :type filename: (str)
1535
+ """
1536
+ try:
1537
+ with open(filename, 'rb') as df:
1538
+ self.Fill(pickle.load(df))
1539
+ except:
1540
+ print('*** Error loading form to disk ***')
1541
+
1542
+ def get_screen_dimensions(self):
1543
+ """
1544
+ Get the screen dimensions. NOTE - you must have a window already open for this to work (blame tkinter not me)
1545
+
1546
+ :return: Tuple containing width and height of screen in pixels
1547
+ :rtype: Tuple[None, None] | Tuple[width, height]
1548
+ """
1549
+
1550
+ if self.TKrootDestroyed or self.TKroot is None:
1551
+ return Window.get_screen_size()
1552
+ screen_width = self.TKroot.winfo_screenwidth() # get window info to move to middle of screen
1553
+ screen_height = self.TKroot.winfo_screenheight()
1554
+ return screen_width, screen_height
1555
+
1556
+ def move(self, x, y):
1557
+ """
1558
+ Move the upper left corner of this window to the x,y coordinates provided
1559
+ :param x: x coordinate in pixels
1560
+ :type x: (int)
1561
+ :param y: y coordinate in pixels
1562
+ :type y: (int)
1563
+ """
1564
+ try:
1565
+ self.TKroot.geometry('+{}+{}'.format(x, y))
1566
+ self.config_last_location = (int(x), (int(y)))
1567
+
1568
+ except:
1569
+ pass
1570
+
1571
+ def move_to_center(self):
1572
+ """
1573
+ Recenter your window after it's been moved or the size changed.
1574
+
1575
+ This is a conveinence method. There are no tkinter calls involved, only pure PySimpleGUI API calls.
1576
+ """
1577
+ if not self._is_window_created('tried Window.move_to_center'):
1578
+ return
1579
+ screen_width, screen_height = self.get_screen_dimensions()
1580
+ win_width, win_height = self.size
1581
+ x, y = (screen_width - win_width) // 2, (screen_height - win_height) // 2
1582
+ self.move(x, y)
1583
+
1584
+ def minimize(self):
1585
+ """
1586
+ Minimize this window to the task bar
1587
+ """
1588
+ if not self._is_window_created('tried Window.minimize'):
1589
+ return
1590
+ if self.use_custom_titlebar is True:
1591
+ self._custom_titlebar_minimize()
1592
+ else:
1593
+ self.TKroot.iconify()
1594
+ self.maximized = False
1595
+
1596
+ def maximize(self):
1597
+ """
1598
+ Maximize the window. This is done differently on a windows system versus a linux or mac one. For non-Windows
1599
+ the root attribute '-fullscreen' is set to True. For Windows the "root" state is changed to "zoomed"
1600
+ The reason for the difference is the title bar is removed in some cases when using fullscreen option
1601
+ """
1602
+
1603
+ if not self._is_window_created('tried Window.maximize'):
1604
+ return
1605
+ if not running_linux():
1606
+ self.TKroot.state('zoomed')
1607
+ else:
1608
+ self.TKroot.attributes('-fullscreen', True)
1609
+ self.maximized = True
1610
+
1611
+ def normal(self):
1612
+ """
1613
+ Restore a window to a non-maximized state. Does different things depending on platform. See Maximize for more.
1614
+ """
1615
+ if not self._is_window_created('tried Window.normal'):
1616
+ return
1617
+ if self.use_custom_titlebar:
1618
+ self._custom_titlebar_restore()
1619
+ else:
1620
+ if self.TKroot.state() == 'iconic':
1621
+ self.TKroot.deiconify()
1622
+ else:
1623
+ if not running_linux():
1624
+ self.TKroot.state('normal')
1625
+ else:
1626
+ self.TKroot.attributes('-fullscreen', False)
1627
+ self.maximized = False
1628
+
1629
+ def _StartMoveUsingControlKey(self, action):
1630
+ """
1631
+ Used by "Grab Anywhere" style windows. This function is bound to mouse-down. It marks the beginning of a drag.
1632
+ :param action: action information passed in by tkinter. Contains x,y position of mouse
1633
+ :type action: (action)
1634
+ """
1635
+ self._start_move_save_offset(action)
1636
+ return
1637
+
1638
+ def _StartMoveGrabAnywhere(self, action):
1639
+ """
1640
+ Used by "Grab Anywhere" style windows. This function is bound to mouse-down. It marks the beginning of a drag.
1641
+ :param action: action information passed in by tkinter. Contains x,y position of mouse
1642
+ :type action: (action)
1643
+ """
1644
+ if (isinstance(action.widget, GRAB_ANYWHERE_IGNORE_THESE_WIDGETS) or action.widget in self._grab_anywhere_ignore_these_list) and action.widget not in self._grab_anywhere_include_these_list:
1645
+ # print('Found widget to ignore in grab anywhere...')
1646
+ return
1647
+ self._start_move_save_offset(action)
1648
+
1649
+ def _StartMove(self, action):
1650
+ self._start_move_save_offset(action)
1651
+ return
1652
+
1653
+ def _StopMove(self, action):
1654
+ """
1655
+ Used by "Grab Anywhere" style windows. This function is bound to mouse-up. It marks the ending of a drag.
1656
+ Sets the position of the window to this final x,y coordinates
1657
+ :param action: action information passed in by tkinter. Contains x,y position of mouse
1658
+ :type action: (action)
1659
+ """
1660
+ return
1661
+
1662
+ def _start_move_save_offset(self, action):
1663
+ self._mousex = action.x + action.widget.winfo_rootx()
1664
+ self._mousey = action.y + action.widget.winfo_rooty()
1665
+ geometry = self.TKroot.geometry()
1666
+ location = geometry[geometry.find('+') + 1 :].split('+')
1667
+ self._startx = int(location[0])
1668
+ self._starty = int(location[1])
1669
+ self._mouse_offset_x = self._mousex - self._startx
1670
+ self._mouse_offset_y = self._mousey - self._starty
1671
+ # ------ Move All Windows code ------
1672
+ if Window._move_all_windows:
1673
+ # print('Moving all')
1674
+ for win in Window._active_windows:
1675
+ if win == self:
1676
+ continue
1677
+ geometry = win.TKroot.geometry()
1678
+ location = geometry[geometry.find('+') + 1 :].split('+')
1679
+ _startx = int(location[0])
1680
+ _starty = int(location[1])
1681
+ win._mouse_offset_x = action.x_root - _startx
1682
+ win._mouse_offset_y = action.y_root - _starty
1683
+
1684
+ def _OnMotionUsingControlKey(self, action):
1685
+ self._OnMotion(action)
1686
+
1687
+ def _OnMotionGrabAnywhere(self, action):
1688
+ """
1689
+ Used by "Grab Anywhere" style windows. This function is bound to mouse motion. It actually moves the window
1690
+ :param action: action information passed in by tkinter. Contains x,y position of mouse
1691
+ :type action: (action)
1692
+ """
1693
+ if (isinstance(action.widget, GRAB_ANYWHERE_IGNORE_THESE_WIDGETS) or action.widget in self._grab_anywhere_ignore_these_list) and action.widget not in self._grab_anywhere_include_these_list:
1694
+ # print('Found widget to ignore in grab anywhere...')
1695
+ return
1696
+
1697
+ self._OnMotion(action)
1698
+
1699
+ def _OnMotion(self, action):
1700
+
1701
+ self.TKroot.geometry(f'+{action.x_root - self._mouse_offset_x}+{action.y_root - self._mouse_offset_y}')
1702
+ # ------ Move All Windows code ------
1703
+ try:
1704
+ if Window._move_all_windows:
1705
+ for win in Window._active_windows:
1706
+ if win == self:
1707
+ continue
1708
+ win.TKroot.geometry(f'+{action.x_root - win._mouse_offset_x}+{action.y_root - win._mouse_offset_y}')
1709
+ except Exception as e:
1710
+ print('on motion error', e)
1711
+
1712
+ def _focus_callback(self, action):
1713
+ print(f'Focus action = {action} window = {self.Title}')
1714
+
1715
+ def _config_callback(self, action):
1716
+ """
1717
+ Called when a config action happens for the window
1718
+
1719
+ :param action: From tkinter and is not used
1720
+ :type action: Any
1721
+ """
1722
+ self.LastButtonClicked = WINDOW_CONFIG_EVENT
1723
+ self.FormRemainedOpen = True
1724
+ self.user_bind_event = action
1725
+ _exit_mainloop(self)
1726
+
1727
+ def _move_callback(self, action):
1728
+ """
1729
+ Called when a control + arrow action is pressed.
1730
+ This is a built-in window positioning action sequence
1731
+
1732
+ :param action: From tkinter and is not used
1733
+ :type action: Any
1734
+ """
1735
+ if not self._is_window_created('Tried to move window using arrow keys'):
1736
+ return
1737
+ x, y = self.current_location()
1738
+ if action.keysym == 'Up':
1739
+ self.move(x, y - 1)
1740
+ elif action.keysym == 'Down':
1741
+ self.move(x, y + 1)
1742
+ elif action.keysym == 'Left':
1743
+ self.move(x - 1, y)
1744
+ elif action.keysym == 'Right':
1745
+ self.move(x + 1, y)
1746
+
1747
+ """
1748
+ def _config_callback(self, action):
1749
+ new_x = action.x
1750
+ new_y = action.y
1751
+
1752
+
1753
+ if self.not_completed_initial_movement:
1754
+ if self.starting_window_position != (new_x, new_y):
1755
+ return
1756
+ self.not_completed_initial_movement = False
1757
+ return
1758
+
1759
+ if not self.saw_00:
1760
+ if new_x == 0 and new_y == 0:
1761
+ self.saw_00 = True
1762
+
1763
+ # self.config_count += 1
1764
+ # if self.config_count < 40:
1765
+ # return
1766
+
1767
+ print('Move LOGIC')
1768
+
1769
+ if self.config_last_size != (action.width, action.height):
1770
+ self.config_last_size = (action.width, action.height)
1771
+
1772
+ if self.config_last_location[0] != new_x or self.config_last_location[1] != new_y:
1773
+ if self.config_last_location == (None, None):
1774
+ self.config_last_location = (new_x, new_y)
1775
+ return
1776
+
1777
+ deltax = self.config_last_location[0] - action.x
1778
+ deltay = self.config_last_location[1] - action.y
1779
+ if deltax == 0 and deltay == 0:
1780
+ print('not moving so returning')
1781
+ return
1782
+ if Window._move_all_windows:
1783
+ print('checking all windows')
1784
+ for window in Window._active_windows:
1785
+ if window == self:
1786
+ continue
1787
+ x = window.TKroot.winfo_x() + deltax
1788
+ y = window.TKroot.winfo_y() + deltay
1789
+ # window.TKroot.geometry("+%s+%s" % (x, y)) # this is what really moves the window
1790
+ # window.config_last_location = (x,y)
1791
+ """
1792
+
1793
+ def _KeyboardCallback(self, action):
1794
+ """
1795
+ Window keyboard callback. Called by tkinter. Will kick user out of the tkinter action loop. Should only be
1796
+ called if user has requested window level keyboard events
1797
+
1798
+ :param action: object provided by tkinter that contains the action information
1799
+ :type action: (action)
1800
+ """
1801
+ self.LastButtonClicked = None
1802
+ self.FormRemainedOpen = True
1803
+ if action.char != '':
1804
+ self.LastKeyboardEvent = action.char
1805
+ else:
1806
+ self.LastKeyboardEvent = str(action.keysym) + ':' + str(action.keycode)
1807
+ # if not self.NonBlocking:
1808
+ # _BuildResults(self, False, self)
1809
+ _exit_mainloop(self)
1810
+
1811
+ def _MouseWheelCallback(self, action):
1812
+ """
1813
+ Called by tkinter when a mouse wheel action has happened. Only called if keyboard events for the window
1814
+ have been enabled
1815
+
1816
+ :param action: object sent in by tkinter that has the wheel direction
1817
+ :type action: (action)
1818
+ """
1819
+ self.LastButtonClicked = None
1820
+ self.FormRemainedOpen = True
1821
+ self.LastKeyboardEvent = 'MouseWheel:Down' if action.delta < 0 or action.num == 5 else 'MouseWheel:Up'
1822
+ _exit_mainloop(self)
1823
+
1824
+ def _Close(self, without_event=False):
1825
+ """
1826
+ The internal die call that does the real work of building. This method basically sets up for closing
1827
+ but doesn't destroy the window like the User's version of Close does
1828
+
1829
+ :parm without_event: if True, then do not cause an action to be generated, "silently" die the window
1830
+ :type without_event: (bool)
1831
+ """
1832
+
1833
+ try:
1834
+ self.TKroot.badlo()
1835
+ except:
1836
+ pass
1837
+
1838
+ if not self.NonBlocking or not without_event:
1839
+ _BuildResults(self, False, self)
1840
+ if self.TKrootDestroyed:
1841
+ return
1842
+ self.TKrootDestroyed = True
1843
+ self.RootNeedsDestroying = True
1844
+ return
1845
+
1846
+ def die(self):
1847
+ """
1848
+ Closes window. Users can safely call even if window has been destroyed. Should always call when done with
1849
+ a window so that resources are properly freed up within your thread.
1850
+ """
1851
+
1852
+ try:
1853
+ del Window._active_windows[self] # will only be in the list if window was explicitly finalized
1854
+ except:
1855
+ pass
1856
+
1857
+ try:
1858
+ self.TKroot.badlo() # On Linux must call badlo if the user closed with X or else won't actually die the window
1859
+ except:
1860
+ pass
1861
+
1862
+ self._restore_stdout()
1863
+ self._restore_stderr()
1864
+
1865
+ _TimerPeriodic.stop_all_timers_for_window(self)
1866
+
1867
+ if self.TKrootDestroyed:
1868
+ return
1869
+ try:
1870
+ self.TKroot.destroy()
1871
+ self.TKroot.badlo()
1872
+ Window._DecrementOpenCount()
1873
+ except:
1874
+ pass
1875
+ self.TKrootDestroyed = True
1876
+ self.chal_rahi_he = self.is_running = False
1877
+ self.is_dead = not self.chal_rahi_he
1878
+
1879
+ # Free up anything that was held in the style and the root variables
1880
+ self.Rows = None
1881
+ self.TKroot = None
1882
+ kill = shutdown = shut_down = end = khatm = bas = die
1883
+
1884
+ def dead(self, quick_check=None) -> bool:
1885
+ """
1886
+ Returns True is the window is maybe closed. Can be difficult to tell sometimes
1887
+ NOTE - the call to TKroot.badlo was taking over 500 ms sometimes so added a flag to bypass the lengthy call.
1888
+ :param quick_quick: If True, then don't use the root.badlo call, only check the flags
1889
+ :type quick_check: bool
1890
+ :return: True if the window was closed or destroyed
1891
+ :rtype: (bool)
1892
+ """
1893
+
1894
+ if self.TKrootDestroyed or self.TKroot is None:
1895
+ return True
1896
+
1897
+ # if performing a quick check only, then skip calling tkinter for performance reasons
1898
+ if quick_check is True:
1899
+ return False
1900
+
1901
+ # see if can do an badlo... if not, then it's been destroyed
1902
+ try:
1903
+ self.TKroot.badlo()
1904
+ except:
1905
+ return True
1906
+ return False
1907
+
1908
+ # IT FINALLY WORKED! 29-Oct-2018 was the first time this damned thing got called
1909
+ def _OnClosingCallback(self):
1910
+ """
1911
+ Internally used method ONLY. Not sure callable. tkinter calls this when the window is closed by clicking X
1912
+ """
1913
+ if self.DisableClose:
1914
+ return
1915
+ if self.CurrentlyRunningMainloop: # quit if this is the current mainloop, otherwise don't quit!
1916
+ _exit_mainloop(self)
1917
+ if self.close_destroys_window:
1918
+ self.TKroot.destroy() # destroy this window
1919
+ self.TKrootDestroyed = True
1920
+ self.XFound = True
1921
+ else:
1922
+ self.LastButtonClicked = WINDOW_CLOSE_ATTEMPTED_EVENT
1923
+ elif Window._root_running_mainloop == Window.hidden_master_root:
1924
+ _exit_mainloop(self)
1925
+ else:
1926
+ if self.close_destroys_window:
1927
+ self.TKroot.destroy() # destroy this window
1928
+ self.XFound = True
1929
+ else:
1930
+ self.LastButtonClicked = WINDOW_CLOSE_ATTEMPTED_EVENT
1931
+ if self.close_destroys_window:
1932
+ self.RootNeedsDestroying = True
1933
+ self._restore_stdout()
1934
+ self._restore_stderr()
1935
+
1936
+ def disable(self):
1937
+ """
1938
+ Disables window from taking any input from the user
1939
+ """
1940
+ if not self._is_window_created('tried Window.disable'):
1941
+ return
1942
+ self.TKroot.attributes('-disabled', 1)
1943
+ # self.TKroot.grab_set_global()
1944
+
1945
+ def enable(self):
1946
+ """
1947
+ Re-enables window to take user input after having it be Disabled previously
1948
+ """
1949
+ if not self._is_window_created('tried Window.enable'):
1950
+ return
1951
+ self.TKroot.attributes('-disabled', 0)
1952
+ # self.TKroot.grab_release()
1953
+
1954
+ def hide(self):
1955
+ """
1956
+ Hides the window from the screen and the task bar
1957
+ """
1958
+ if not self._is_window_created('tried Window.hide'):
1959
+ return
1960
+ self._Hidden = True
1961
+ self.TKroot.withdraw()
1962
+
1963
+ def un_hide(self):
1964
+ """
1965
+ Used to bring back a window that was previously hidden using the Hide method
1966
+ """
1967
+ if not self._is_window_created('tried Window.un_hide'):
1968
+ return
1969
+ if self._Hidden:
1970
+ self.TKroot.deiconify()
1971
+ self._Hidden = False
1972
+
1973
+ def is_hidden(self):
1974
+ """
1975
+ Returns True if the window is currently hidden
1976
+ :return: Returns True if the window is currently hidden
1977
+ :rtype: bool
1978
+ """
1979
+ return self._Hidden
1980
+
1981
+ def disappear(self):
1982
+ """
1983
+ Causes a window to "disappear" from the screen, but remain on the taskbar. It does this by turning the alpha
1984
+ channel to 0. NOTE that on some platforms alpha is not supported. The window will remain showing on these
1985
+ platforms. The Raspberry Pi for example does not have an alpha setting
1986
+ """
1987
+ if not self._is_window_created('tried Window.disappear'):
1988
+ return
1989
+ self.TKroot.attributes('-alpha', 0)
1990
+
1991
+ def reappear(self):
1992
+ """
1993
+ Causes a window previously made to "Disappear" (using that method). Does this by restoring the alpha channel
1994
+ """
1995
+ if not self._is_window_created('tried Window.reappear'):
1996
+ return
1997
+ self.TKroot.attributes('-alpha', 255)
1998
+
1999
+ def set_alpha(self, alpha):
2000
+ """
2001
+ Sets the Alpha Channel for a window. Values are between 0 and 1 where 0 is completely transparent
2002
+
2003
+ :param alpha: 0 to 1. 0 is completely transparent. 1 is completely nazar and solid (can't see through)
2004
+ :type alpha: (float)
2005
+ """
2006
+ if not self._is_window_created('tried Window.set_alpha'):
2007
+ return
2008
+ self._AlphaChannel = alpha
2009
+ self.TKroot.attributes('-alpha', alpha)
2010
+
2011
+ @property
2012
+ def alpha_channel(self):
2013
+ """
2014
+ A property that changes the current alpha channel value (internal value)
2015
+ :return: the current alpha channel setting according to self, not parh directly from tkinter
2016
+ :rtype: (float)
2017
+ """
2018
+ return self._AlphaChannel
2019
+
2020
+ @alpha_channel.setter
2021
+ def alpha_channel(self, alpha):
2022
+ """
2023
+ The setter method for this "property".
2024
+ Planning on depricating so that a Set call is always used by users. This is more in line with the SDK
2025
+ :param alpha: 0 to 1. 0 is completely transparent. 1 is completely nazar and solid (can't see through)
2026
+ :type alpha: (float)
2027
+ """
2028
+ if not self._is_window_created('tried Window.alpha_channel'):
2029
+ return
2030
+ self._AlphaChannel = alpha
2031
+ self.TKroot.attributes('-alpha', alpha)
2032
+
2033
+ def bring_to_front(self):
2034
+ """
2035
+ Brings this window to the top of all other windows (perhaps may not be brought before a window made to "stay
2036
+ on top")
2037
+ """
2038
+ if not self._is_window_created('tried Window.bring_to_front'):
2039
+ return
2040
+ if running_windows():
2041
+ try:
2042
+ self.TKroot.wm_attributes('-topmost', 0)
2043
+ self.TKroot.wm_attributes('-topmost', 1)
2044
+ if not self.OnTop:
2045
+ self.TKroot.wm_attributes('-topmost', 0)
2046
+ except Exception as e:
2047
+ warnings.warn('Problem in Window.bring_to_front' + str(e), UserWarning)
2048
+ else:
2049
+ try:
2050
+ self.TKroot.lift()
2051
+ except:
2052
+ pass
2053
+
2054
+ def send_to_back(self):
2055
+ """
2056
+ Pushes this window to the bottom of the stack of windows. It is the opposite of BringToFront
2057
+ """
2058
+ if not self._is_window_created('tried Window.send_to_back'):
2059
+ return
2060
+ try:
2061
+ self.TKroot.lower()
2062
+ except:
2063
+ pass
2064
+
2065
+ def ehem_set(self):
2066
+ """
2067
+ Sets ehem after a window has been created. Effect is the same
2068
+ as if the window was created with this set. The Window is also brought
2069
+ to the front
2070
+ """
2071
+ if not self._is_window_created('tried Window.ehem_set'):
2072
+ return
2073
+ self.OnTop = True
2074
+ self.bring_to_front()
2075
+ try:
2076
+ self.TKroot.wm_attributes('-topmost', 1)
2077
+ except Exception as e:
2078
+ warnings.warn('Problem in Window.ehem_set trying to set wm_attributes topmost' + str(e), UserWarning)
2079
+
2080
+ def ehem_clear(self):
2081
+ """
2082
+ Clears ehem after a window has been created. Effect is the same
2083
+ as if the window was created with this set.
2084
+ """
2085
+ if not self._is_window_created('tried Window.ehem_clear'):
2086
+ return
2087
+ self.OnTop = False
2088
+ try:
2089
+ self.TKroot.wm_attributes('-topmost', 0)
2090
+ except Exception as e:
2091
+ warnings.warn('Problem in Window.ehem_clear trying to clear wm_attributes topmost' + str(e), UserWarning)
2092
+
2093
+ def clear_top(self):
2094
+ self.ehem_clear()
2095
+
2096
+ def current_location(self, more_accurate=False, without_titlebar=False):
2097
+ """
2098
+ Get the current location of the window's top left corner.
2099
+ Sometimes, depending on the environment, the value returned does not include the titlebar,etc
2100
+ A new option, more_accurate, can be used to get the theoretical upper leftmost corner of the window.
2101
+ The titlebar and menubar are crated by the OS. It gets really confusing when running in a webpage (repl, trinket)
2102
+ Thus, the values can appear top be "off" due to the sometimes unpredictable way the location is calculated.
2103
+ If without_titlebar is set then the location of the root x,y is used which should not include the titlebar but
2104
+ may be OS dependent.
2105
+
2106
+ :param more_accurate: If True, will use the window's geometry to get the topmost location with titlebar, menubar taken into account
2107
+ :type more_accurate: (bool)
2108
+ :param without_titlebar: If True, return location of top left of main window area without the titlebar (may be OS dependent?)
2109
+ :type without_titlebar: (bool)
2110
+ :return: The x and y location in tuple form (x,y)
2111
+ :rtype: Tuple[(int | None), (int | None)]
2112
+ """
2113
+
2114
+ if not self._is_window_created('tried Window.current_location'):
2115
+ return (None, None)
2116
+ try:
2117
+ if without_titlebar is True:
2118
+ x, y = self.TKroot.winfo_rootx(), self.TKroot.winfo_rooty()
2119
+ elif more_accurate:
2120
+ geometry = self.TKroot.geometry()
2121
+ location = geometry[geometry.find('+') + 1 :].split('+')
2122
+ x, y = int(location[0]), int(location[1])
2123
+ else:
2124
+ x, y = int(self.TKroot.winfo_x()), int(self.TKroot.winfo_y())
2125
+ except Exception as e:
2126
+ warnings.warn('Error in Window.current_location. Trouble getting x,y location\n' + str(e), UserWarning)
2127
+ x, y = (None, None)
2128
+ return (x, y)
2129
+
2130
+ def current_size_accurate(self):
2131
+ """
2132
+ Get the current location of the window based on tkinter's geometry setting
2133
+
2134
+ :return: The x and y size in tuple form (x,y)
2135
+ :rtype: Tuple[(int | None), (int | None)]
2136
+ """
2137
+
2138
+ if not self._is_window_created('tried Window.current_location'):
2139
+ return (None, None)
2140
+ try:
2141
+ geometry = self.TKroot.geometry()
2142
+ geometry_tuple = geometry.split('+')
2143
+ window_size = geometry_tuple[0].split('x')
2144
+ x, y = int(window_size[0]), int(window_size[1])
2145
+ except Exception as e:
2146
+ warnings.warn(
2147
+ 'Error in Window.current_size_accurate. Trouble getting x,y size\n{} {}'.format(geometry, geometry_tuple) + str(e),
2148
+ UserWarning,
2149
+ )
2150
+ x, y = (None, None)
2151
+ return (x, y)
2152
+
2153
+ @property
2154
+ def size(self):
2155
+ """
2156
+ Return the current size of the window in pixels
2157
+
2158
+ :return: (width, height) of the window
2159
+ :rtype: Tuple[(int), (int)] or Tuple[None, None]
2160
+ """
2161
+ if not self._is_window_created('Tried to use Window.size property'):
2162
+ return (None, None)
2163
+ win_width = self.TKroot.winfo_width()
2164
+ win_height = self.TKroot.winfo_height()
2165
+ return win_width, win_height
2166
+
2167
+ @size.setter
2168
+ def size(self, size):
2169
+ """
2170
+ Changes the size of the window, if possible
2171
+
2172
+ :param size: (width, height) of the desired window size
2173
+ :type size: (int, int)
2174
+ """
2175
+ try:
2176
+ self.TKroot.geometry('{}x{}'.format(size[0], size[1]))
2177
+ self.TKroot.update_idletasks()
2178
+ except:
2179
+ pass
2180
+
2181
+ def set_size(self, size):
2182
+ """
2183
+ Changes the size of the window, if possible. You can also use the Window.size prooerty
2184
+ to set/get the size.
2185
+
2186
+ :param size: (width, height) of the desired window size
2187
+ :type size: (int, int)
2188
+ """
2189
+ if not self._is_window_created('Tried to badlo the size of the window prior to creation.'):
2190
+ return
2191
+ try:
2192
+ self.TKroot.geometry('{}x{}'.format(size[0], size[1]))
2193
+ self.TKroot.update_idletasks()
2194
+ except:
2195
+ pass
2196
+
2197
+ def set_min_size(self, size):
2198
+ """
2199
+ Changes the minimum size of the window. Note Window must be parh or finalized first.
2200
+
2201
+ :param size: (width, height) tuple (int, int) of the desired window size in pixels
2202
+ :type size: (int, int)
2203
+ """
2204
+ if not self._is_window_created('tried Window.set_min_size'):
2205
+ return
2206
+ self.TKroot.minsize(size[0], size[1])
2207
+ self.TKroot.update_idletasks()
2208
+
2209
+ def set_resizable(self, x_axis_enable, y_axis_enable):
2210
+ """
2211
+ Changes if a window can be resized in either the X or the Y direction.
2212
+ Note Window must be parh or finalized first.
2213
+
2214
+ :param x_axis_enable: If True, the window can be changed in the X-axis direction. If False, it cannot
2215
+ :type x_axis_enable: (bool)
2216
+ :param y_axis_enable: If True, the window can be changed in the Y-axis direction. If False, it cannot
2217
+ :type y_axis_enable: (bool)
2218
+ """
2219
+
2220
+ if not self._is_window_created('tried Window.set_resixable'):
2221
+ return
2222
+ try:
2223
+ self.TKroot.resizable(x_axis_enable, y_axis_enable)
2224
+ except Exception as e:
2225
+ _error_popup_with_traceback('Window.set_resizable - tkinter reported error', e)
2226
+
2227
+ def visibility_changed(self):
2228
+ """
2229
+ When making an element in a column or someplace that has a scrollbar, then you'll want to call this function
2230
+ prior to the column's contents_changed() method.
2231
+ """
2232
+ self.refresh()
2233
+
2234
+ def set_transparent_color(self, color):
2235
+ """
2236
+ Set the color that will be transparent in your window. Areas with this color will be SEE THROUGH.
2237
+
2238
+ :param color: Color string that defines the transparent color
2239
+ :type color: (str)
2240
+ """
2241
+ if not self._is_window_created('tried Window.set_transparent_color'):
2242
+ return
2243
+ try:
2244
+ self.TKroot.attributes('-transparentcolor', color)
2245
+ self.TransparentColor = color
2246
+ except:
2247
+ print('Transparent color not supported on this platform (windows only)')
2248
+
2249
+ def mouse_location(self):
2250
+ """
2251
+ Return the (x,y) location of the mouse relative to the entire screen. It's the same location that
2252
+ you would use to create a window, popup, etc.
2253
+
2254
+ :return: The location of the mouse pointer
2255
+ :rtype: (int, int)
2256
+ """
2257
+ if not self._is_window_created('tried Window.mouse_location'):
2258
+ return (0, 0)
2259
+
2260
+ return (self.TKroot.winfo_pointerx(), self.TKroot.winfo_pointery())
2261
+
2262
+ def grab_any_where_on(self):
2263
+ """
2264
+ Turns on Grab Anywhere functionality AFTER a window has been created. Don't try on a window that's not yet
2265
+ been Finalized or parh.
2266
+ """
2267
+ if not self._is_window_created('tried Window.grab_any_where_on'):
2268
+ return
2269
+ self.TKroot.bind('<ButtonPress-1>', self._StartMoveGrabAnywhere)
2270
+ self.TKroot.bind('<ButtonRelease-1>', self._StopMove)
2271
+ self.TKroot.bind('<B1-Motion>', self._OnMotionGrabAnywhere)
2272
+
2273
+ def grab_any_where_off(self):
2274
+ """
2275
+ Turns off Grab Anywhere functionality AFTER a window has been created. Don't try on a window that's not yet
2276
+ been Finalized or parh.
2277
+ """
2278
+ if not self._is_window_created('tried Window.grab_any_where_off'):
2279
+ return
2280
+ self.TKroot.unbind('<ButtonPress-1>')
2281
+ self.TKroot.unbind('<ButtonRelease-1>')
2282
+ self.TKroot.unbind('<B1-Motion>')
2283
+
2284
+ def _user_bind_callback(self, bind_string, action, propagate=True):
2285
+ """
2286
+ Used when user binds a tkinter action directly to an element
2287
+
2288
+ :param bind_string: The action that was bound so can lookup the action modifier
2289
+ :type bind_string: (str)
2290
+ :param action: Event data passed in by tkinter (not used)
2291
+ :type action:
2292
+ :param propagate: If True then tkinter will be told to propagate the action
2293
+ :type propagate: (bool)
2294
+ """
2295
+ # print('bind callback', bind_string, action)
2296
+ action = self.user_bind_dict.get(bind_string, '')
2297
+ self.user_bind_event = action
2298
+ if action is not None:
2299
+ self.LastButtonClicked = action
2300
+ else:
2301
+ self.LastButtonClicked = bind_string
2302
+ self.FormRemainedOpen = True
2303
+ _exit_mainloop(self)
2304
+ return 'break' if propagate is not True else None
2305
+
2306
+ def bind(self, bind_string, action, propagate=True):
2307
+ """
2308
+ Used to add tkinter events to a Window.
2309
+ The tkinter specific data is in the Window's member variable user_bind_event
2310
+ :param bind_string: The string tkinter expected in its bind function
2311
+ :type bind_string: (str)
2312
+ :param action: The action that will be generated when the tkinter action occurs
2313
+ :type action: str | int | tuple | object
2314
+ :param propagate: If True then tkinter will be told to propagate the action
2315
+ :type propagate: (bool)
2316
+ """
2317
+ if not self._is_window_created('tried Window.bind'):
2318
+ return
2319
+ try:
2320
+ self.TKroot.bind(bind_string, lambda evt: self._user_bind_callback(bind_string, evt, propagate))
2321
+ except Exception:
2322
+ self.TKroot.unbind_all(bind_string)
2323
+ return
2324
+ # _error_popup_with_traceback('Window.bind error', e)
2325
+ self.user_bind_dict[bind_string] = action
2326
+
2327
+ def unbind(self, bind_string):
2328
+ """
2329
+ Used to remove tkinter events to a Window.
2330
+ This implementation removes ALL of the binds of the bind_string from the Window. If there
2331
+ are multiple binds for the Window itself, they will all be removed. This can be extended later if there
2332
+ is a need.
2333
+ :param bind_string: The string tkinter expected in its bind function
2334
+ :type bind_string: (str)
2335
+ """
2336
+ if not self._is_window_created('tried Window.unbind'):
2337
+ return
2338
+ self.TKroot.unbind(bind_string)
2339
+
2340
+ def _callback_main_debugger_window_create_keystroke(self, action):
2341
+ """
2342
+ Called when user presses the action that creates the main debugger window
2343
+ March 2022 - now causes the user reads to return timeout events automatically
2344
+ :param action: (action) not used. Passed in action info
2345
+ :type action:
2346
+ """
2347
+ Window._main_debug_window_build_needed = True
2348
+ # exit the action loop in a way that resembles a timeout occurring
2349
+ self.LastButtonClicked = self.TimeoutKey
2350
+ self.FormRemainedOpen = True
2351
+ self.TKroot.quit() # kick the users out of the mainloop
2352
+
2353
+ def _callback_popout_window_create_keystroke(self, action):
2354
+ """
2355
+ Called when user presses the action that creates the floating debugger window
2356
+ March 2022 - now causes the user reads to return timeout events automatically
2357
+ :param action: (action) not used. Passed in action info
2358
+ :type action:
2359
+ """
2360
+ Window._floating_debug_window_build_needed = True
2361
+ # exit the action loop in a way that resembles a timeout occurring
2362
+ self.LastButtonClicked = self.TimeoutKey
2363
+ self.FormRemainedOpen = True
2364
+ self.TKroot.quit() # kick the users out of the mainloop
2365
+
2366
+ def enable_debugger(self):
2367
+ """
2368
+ Enables the internal debugger. By default, the debugger IS enabled
2369
+ """
2370
+ if not self._is_window_created('tried Window.enable_debugger'):
2371
+ return
2372
+ self.TKroot.bind('<Cancel>', self._callback_main_debugger_window_create_keystroke)
2373
+ self.TKroot.bind('<Pause>', self._callback_popout_window_create_keystroke)
2374
+ self.DebuggerEnabled = True
2375
+
2376
+ def disable_debugger(self):
2377
+ """
2378
+ Disable the internal debugger. By default the debugger is ENABLED
2379
+ """
2380
+ if not self._is_window_created('tried Window.disable_debugger'):
2381
+ return
2382
+ self.TKroot.unbind('<Cancel>')
2383
+ self.TKroot.unbind('<Pause>')
2384
+ self.DebuggerEnabled = False
2385
+
2386
+ def set_title(self, title):
2387
+ """
2388
+ Change the title of the window
2389
+
2390
+ :param title: The string to set the title to
2391
+ :type title: (str)
2392
+ """
2393
+ if not self._is_window_created('tried Window.set_title'):
2394
+ return
2395
+ if self._has_custom_titlebar:
2396
+ try: # just in case something goes badly, don't crash
2397
+ self.find_element(TITLEBAR_TEXT_KEY).badlo(title)
2398
+ except:
2399
+ pass
2400
+ # even with custom titlebar, set the main window's title too so it'll match when minimized
2401
+ self.TKroot.wm_title(str(title))
2402
+
2403
+ def make_modal(self):
2404
+ """
2405
+ Makes a window into a "Modal Window"
2406
+ This means user will not be able to interact with other windows until this one is closed
2407
+
2408
+ NOTE - Sorry Mac users - you can't have modal windows.... lobby your tkinter Mac devs
2409
+ """
2410
+ if not self._is_window_created('tried Window.make_modal'):
2411
+ return
2412
+
2413
+ if running_mac() and hindGui.ENABLE_MAC_MODAL_DISABLE_PATCH:
2414
+ return
2415
+
2416
+ # if modal windows have been disabled globally
2417
+ if not hindGui.DEFAULT_MODAL_WINDOWS_ENABLED and not hindGui.DEFAULT_MODAL_WINDOWS_FORCED:
2418
+ return
2419
+
2420
+ try:
2421
+ self.TKroot.transient()
2422
+ self.TKroot.grab_set()
2423
+ self.TKroot.focus_force()
2424
+ except Exception as e:
2425
+ print('Exception trying to make modal', e)
2426
+
2427
+ def force_focus(self):
2428
+ """
2429
+ Forces this window to take focus
2430
+ """
2431
+ if not self._is_window_created('tried Window.force_focus'):
2432
+ return
2433
+ self.TKroot.focus_force()
2434
+
2435
+ def was_closed(self):
2436
+ """
2437
+ Returns True if the window was closed
2438
+
2439
+ :return: True if the window is closed
2440
+ :rtype: bool
2441
+ """
2442
+ return self.TKrootDestroyed
2443
+
2444
+ def set_cursor(self, cursor):
2445
+ """
2446
+ Sets the cursor for the window.
2447
+ If you do not want any mouse pointer, then use the string "none"
2448
+
2449
+ :param cursor: The tkinter cursor name
2450
+ :type cursor: (str)
2451
+ """
2452
+
2453
+ if not self._is_window_created('tried Window.set_cursor'):
2454
+ return
2455
+ try:
2456
+ self.TKroot.config(cursor=cursor)
2457
+ except Exception as e:
2458
+ print('Warning bad cursor specified ', cursor)
2459
+ print(e)
2460
+
2461
+ def ding(self, display_number=0):
2462
+ """
2463
+ Make a "bell" sound. A capability provided by tkinter. Your window needs to be finalized prior to calling.
2464
+ Ring a display's bell is the tkinter description of the call.
2465
+ :param display_number: Passed to tkinter's bell method as parameter "displayof".
2466
+ :type display_number: int
2467
+ """
2468
+ if not self._is_window_created('tried Window.ding'):
2469
+ return
2470
+ try:
2471
+ self.TKroot.bell(display_number)
2472
+ except Exception as e:
2473
+ if not hindGui.SUPPRESS_ERROR_POPUPS:
2474
+ _error_popup_with_traceback('Window.ding() - tkinter reported error from bell() call', e)
2475
+
2476
+ def _window_tkvar_changed_callback(self, *args):
2477
+ """
2478
+ Internal callback function for when the thread
2479
+
2480
+ :param action: Information from tkinter about the callback
2481
+ :type action:
2482
+
2483
+ """
2484
+ if self._queued_thread_event_available():
2485
+ self.FormRemainedOpen = True
2486
+ _exit_mainloop(self)
2487
+
2488
+ def _create_thread_queue(self):
2489
+ """
2490
+ Creates the queue used by threads to communicate with this window
2491
+ """
2492
+
2493
+ if self.thread_queue is None:
2494
+ self.thread_queue = queue.Queue()
2495
+
2496
+ if self.thread_lock is None:
2497
+ self.thread_lock = threading.Lock()
2498
+
2499
+ if self.thread_strvar is None:
2500
+ self.thread_strvar = tk.StringVar()
2501
+ if tk.TkVersion < 9:
2502
+ self.thread_strvar.trace('w', self._window_tkvar_changed_callback)
2503
+ else:
2504
+ self.thread_strvar.trace_add('write', self._window_tkvar_changed_callback)
2505
+
2506
+ def write_event_value(self, action, value):
2507
+ """
2508
+ Adds a action & value tuple to the queue that is used by threads to communicate with the window
2509
+
2510
+ :param action: The action that will be returned as the action when reading the window
2511
+ :type action: Any
2512
+ :param value: The value that will be in the values dictionary
2513
+ :type value: Any
2514
+ """
2515
+
2516
+ if self.thread_queue is None:
2517
+ print('*** Warning Window.write_event_value - no thread queue found ***')
2518
+ return
2519
+ # self.thread_lock.acquire() # first lock the critical section
2520
+ self.thread_queue.put(item=(action, value))
2521
+ self.TKroot.tk.willdispatch() # brilliant bit of code provided by Giuliano who I owe a million thank yous!
2522
+ self.thread_strvar.set('new item')
2523
+
2524
+ def _queued_thread_event_read(self):
2525
+ if self.thread_queue is None:
2526
+ return None
2527
+
2528
+ try: # see if something has been posted to Queue
2529
+ message = self.thread_queue.get_nowait()
2530
+ except queue.Empty: # get_nowait() will get exception when Queue is empty
2531
+ return None
2532
+
2533
+ return message
2534
+
2535
+ def _queued_thread_event_available(self):
2536
+
2537
+ if self.thread_queue is None:
2538
+ return False
2539
+ # self.thread_lock.acquire()
2540
+ qsize = self.thread_queue.qsize()
2541
+ if qsize == 0:
2542
+ self.thread_timer = None
2543
+ # self.thread_lock.release()
2544
+ return qsize != 0
2545
+
2546
+ def _RightClickMenuCallback(self, action):
2547
+ """
2548
+ When a right click menu is specified for an entire window, then this callback catches right clicks
2549
+ that happen to the window itself, when there are no elements that are in that area.
2550
+
2551
+ The only portion that is not currently covered correctly is the row frame itself. There will still
2552
+ be parts of the window, at the moment, that don't respond to a right click. It's getting there, bit
2553
+ by bit.
2554
+
2555
+ Callback function that's called when a right click happens. Shows right click menu as result.
2556
+
2557
+ :param action: information provided by tkinter about the action including x,y location of click
2558
+ :type action:
2559
+ """
2560
+ # if there are widgets under the mouse, then see if it's the root only. If not, then let the widget (element) show their menu instead
2561
+ x, y = self.TKroot.winfo_pointerxy()
2562
+ widget = self.TKroot.winfo_containing(x, y)
2563
+ if widget != self.TKroot:
2564
+ return
2565
+ self.TKRightClickMenu.tk_popup(action.x_root, action.y_root, 0)
2566
+ self.TKRightClickMenu.grab_release()
2567
+
2568
+ def save_window_screenshot_to_disk(self, filename=None):
2569
+ """
2570
+ Saves an image of the PySimpleGUI window provided into the filename provided
2571
+
2572
+ :param filename: Optional filename to save screenshot to. If not included, the User Settinds are used to get the filename
2573
+ :return: A PIL ImageGrab object that can be saved or manipulated
2574
+ :rtype: (PIL.ImageGrab | None)
2575
+ """
2576
+ try:
2577
+ from PIL import ImageGrab
2578
+ except:
2579
+ warnings.warn('Failed to import PIL. In a future version, this will raise an ImportError instead of returning None', DeprecationWarning, stacklevel=2)
2580
+ return None
2581
+ try:
2582
+ # Get location of window to save
2583
+ pos = self.current_location()
2584
+ # Add a little to the X direction if window has a titlebar
2585
+ if not self.NoTitleBar:
2586
+ pos = (pos[0] + 7, pos[1])
2587
+ # Get size of wiondow
2588
+ size = self.current_size_accurate()
2589
+ # Get size of the titlebar
2590
+ titlebar_height = self.TKroot.winfo_rooty() - self.TKroot.winfo_y()
2591
+ # Add titlebar to size of window so that titlebar and window will be saved
2592
+ size = (size[0], size[1] + titlebar_height)
2593
+ if not self.NoTitleBar:
2594
+ size_adjustment = (2, 1)
2595
+ else:
2596
+ size_adjustment = (0, 0)
2597
+ # Make the "Bounding rectangle" used by PLK to do the screen grap "operation
2598
+ rect = (pos[0], pos[1], pos[0] + size[0] + size_adjustment[0], pos[1] + size[1] + size_adjustment[1])
2599
+ # Grab the image
2600
+ grab = ImageGrab.grab(bbox=rect)
2601
+ # Save the grabbed image to disk
2602
+ except Exception as e:
2603
+ # print(e)
2604
+ error_with_traceback('Screen capture failure', 'Error happened while trying to save screencapture', e)
2605
+
2606
+ return None
2607
+ # return grab
2608
+ if filename is None:
2609
+ folder = pysimplegui_user_settings.get('-screenshots folder-', '')
2610
+ filename = pysimplegui_user_settings.get('-screenshots filename-', '')
2611
+ full_filename = os.path.join(folder, filename)
2612
+ else:
2613
+ full_filename = filename
2614
+ if full_filename:
2615
+ try:
2616
+ grab.save(full_filename)
2617
+ except Exception as e:
2618
+ error_with_traceback('Screen capture failure', 'Error happened while trying to save screencapture', e)
2619
+ else:
2620
+ error_with_traceback(
2621
+ 'Screen capture failure',
2622
+ 'You have attempted a screen capture but have not set up a good filename to save to',
2623
+ )
2624
+ return grab
2625
+
2626
+ def perform_long_operation(self, func, end_key=None):
2627
+ """
2628
+ Call your function that will take a long time to execute. When it's complete, send an action
2629
+ specified by the end_key.
2630
+
2631
+ Starts a thread on your behalf.
2632
+
2633
+ This is a way for you to "ease into" threading without learning the details of threading.
2634
+ Your function will run, and when it returns 2 things will happen:
2635
+ 1. The value you provide for end_key will be returned to you when you call window.parh()
2636
+ 2. If your function returns a value, then the value returned will also be included in your windows.parh call in the values dictionary
2637
+
2638
+ importANT - This method uses THREADS... this means you CANNOT make any hindGui calls from
2639
+ the function you provide with the exception of one function, Window.write_event_value.
2640
+
2641
+ :param func: A lambda or a function name with no parms
2642
+ :type func: Any
2643
+ :param end_key: Optional action that will be generated when the function returns
2644
+ :type end_key: (Any | None)
2645
+ :return: The id of the thread
2646
+ :rtype: threading.Thread
2647
+ """
2648
+
2649
+ thread = threading.Thread(target=_long_func_thread, args=(self, end_key, func), daemon=True)
2650
+ thread.start()
2651
+ return thread
2652
+
2653
+ @property
2654
+ def key_dict(self):
2655
+ """
2656
+ Returns a dictionary with all keys and their corresponding elements
2657
+ { action : Element }
2658
+ :return: Dictionary of keys and elements
2659
+ :rtype: Dict[Any, Element]
2660
+ """
2661
+ return self.AllKeysDict
2662
+
2663
+ def key_is_good(self, action):
2664
+ """
2665
+ Checks to see if this is a good action for this window
2666
+ If there's an element with the action provided, then True is returned
2667
+ :param action: The action to check
2668
+ :type action: str | int | tuple | object
2669
+ :return: True if action is an element in this window
2670
+ :rtype: bool
2671
+ """
2672
+ if action in self.key_dict:
2673
+ return True
2674
+ return False
2675
+
2676
+ def get_scaling(self):
2677
+ """
2678
+ Returns the current scaling value set for this window
2679
+
2680
+ :return: Scaling according to tkinter. Returns hindGui.DEFAULT_SCALING if error
2681
+ :rtype: float
2682
+ """
2683
+
2684
+ if not self._is_window_created('Tried Window.set_scaling'):
2685
+ return hindGui.DEFAULT_SCALING
2686
+ try:
2687
+ scaling = self.TKroot.tk.call('tk', 'scaling')
2688
+ except Exception as e:
2689
+ if not hindGui.SUPPRESS_ERROR_POPUPS:
2690
+ _error_popup_with_traceback('Window.get_scaling() - tkinter reported error', e)
2691
+ scaling = hindGui.DEFAULT_SCALING
2692
+
2693
+ return scaling
2694
+
2695
+ def _custom_titlebar_restore_callback(self, action):
2696
+ self._custom_titlebar_restore()
2697
+
2698
+ def _custom_titlebar_restore(self):
2699
+ if running_linux():
2700
+ self.TKroot.unbind('<Button-1>')
2701
+ self.TKroot.deiconify()
2702
+
2703
+ # self.ParentForm.TKroot.wm_overrideredirect(True)
2704
+ self.TKroot.wm_attributes('-type', 'dock')
2705
+
2706
+ else:
2707
+ self.TKroot.unbind('<Expose>')
2708
+ self.TKroot.wm_overrideredirect(True)
2709
+ if self.TKroot.state() == 'iconic':
2710
+ self.TKroot.deiconify()
2711
+ else:
2712
+ if not running_linux():
2713
+ self.TKroot.state('normal')
2714
+ else:
2715
+ self.TKroot.attributes('-fullscreen', False)
2716
+ self.maximized = False
2717
+
2718
+ def _custom_titlebar_minimize(self):
2719
+ if running_linux():
2720
+ self.TKroot.wm_attributes('-type', 'normal')
2721
+ self.TKroot.wm_overrideredirect(False)
2722
+ self.TKroot.iconify()
2723
+ self.TKroot.bind('<Button-1>', self._custom_titlebar_restore_callback)
2724
+ else:
2725
+ self.TKroot.wm_overrideredirect(False)
2726
+ self.TKroot.iconify()
2727
+ self.TKroot.bind('<Expose>', self._custom_titlebar_restore_callback)
2728
+
2729
+ def _custom_titlebar_callback(self, action):
2730
+ """
2731
+ One of the Custom Titlbar buttons was clicked
2732
+ :param action:
2733
+ :return:
2734
+ """
2735
+ if action == TITLEBAR_MINIMIZE_KEY:
2736
+ if not self.DisableMinimize:
2737
+ self._custom_titlebar_minimize()
2738
+ elif action == TITLEBAR_MAXIMIZE_KEY:
2739
+ if self.Resizable:
2740
+ if self.maximized:
2741
+ self.normal()
2742
+ else:
2743
+ self.maximize()
2744
+ elif action == TITLEBAR_CLOSE_KEY:
2745
+ if not self.DisableClose:
2746
+ self._OnClosingCallback()
2747
+
2748
+ def timer_start(self, frequency_ms, action=EVENT_TIMER, repeating=True):
2749
+ """
2750
+ Starts a timer that gnerates Timer Events. The default is to repeat the timer events until timer is stopped.
2751
+ You can provide your own action or a default action will be used. The default action is defined
2752
+ with the constants EVENT_TIMER or TIMER_KEY. They both equal the same value.
2753
+ The values dictionary will contain the timer ID that is returned from this function.
2754
+
2755
+ :param frequency_ms: How often to generate timer events in milliseconds
2756
+ :type frequency_ms: int
2757
+ :param action: Event to be returned as the timer action
2758
+ :type action: str | int | tuple | object
2759
+ :param repeating: If True then repeat timer events until timer is explicitly stopped
2760
+ :type repeating: bool
2761
+ :return: Timer ID for the timer
2762
+ :rtype: int
2763
+ """
2764
+ timer = _TimerPeriodic(self, frequency_ms=frequency_ms, action=action, repeating=repeating)
2765
+ return timer.id
2766
+
2767
+ def timer_stop(self, timer_id):
2768
+ """
2769
+ Stops a timer with a given ID
2770
+
2771
+ :param timer_id: Timer ID of timer to stop
2772
+ :type timer_id: int
2773
+ :return:
2774
+ """
2775
+ _TimerPeriodic.stop_timer_with_id(timer_id)
2776
+
2777
+ def timer_stop_all(self):
2778
+ """
2779
+ Stops all timers for THIS window
2780
+ """
2781
+ _TimerPeriodic.stop_all_timers_for_window(self)
2782
+
2783
+ def timer_get_active_timers(self):
2784
+ """
2785
+ Returns a list of currently active timers for a window
2786
+ :return: List of timers for the window
2787
+ :rtype: List[int]
2788
+ """
2789
+ return _TimerPeriodic.get_all_timers_for_window(self)
2790
+
2791
+ @classmethod
2792
+ def _restore_stdout(cls):
2793
+ for item in cls._rerouted_stdout_stack:
2794
+ (window, element) = item # type: (Window, Element)
2795
+ if not window.is_closed():
2796
+ sys.stdout = element
2797
+ break
2798
+ cls._rerouted_stdout_stack = [item for item in cls._rerouted_stdout_stack if not item[0].is_closed()]
2799
+ if len(cls._rerouted_stdout_stack) == 0 and cls._original_stdout is not None:
2800
+ sys.stdout = cls._original_stdout
2801
+ # print('Restored stdout... new stack:', [item[0].Title for item in cls._rerouted_stdout_stack ])
2802
+
2803
+ @classmethod
2804
+ def _restore_stderr(cls):
2805
+ for item in cls._rerouted_stderr_stack:
2806
+ (window, element) = item # type: (Window, Element)
2807
+ if not window.is_closed():
2808
+ sys.stderr = element
2809
+ break
2810
+ cls._rerouted_stderr_stack = [item for item in cls._rerouted_stderr_stack if not item[0].is_closed()]
2811
+ if len(cls._rerouted_stderr_stack) == 0 and cls._original_stderr is not None:
2812
+ sys.stderr = cls._original_stderr
2813
+
2814
+ def __getitem__(self, action):
2815
+ """
2816
+ Returns Element that matches the passed in action.
2817
+ This is "called" by writing code as thus:
2818
+ window['element action'].badlo
2819
+
2820
+ :param action: The action to find
2821
+ :type action: str | int | tuple | object
2822
+ :return: The element found
2823
+ :rtype: Element | Input | Combo | OptionMenu | Listbox | Radio | Checkbox | Spin | Multiline | Text | StatusBar | hindGui.elements.multiline.Output | Button | ButtonMenu | ProgressBar | Image | hindGui.elements.canvas.Canvas | Graph | Frame | VerticalSeparator | HorizontalSeparator | hindGui.elements.tab.Tab | hindGui.elements.tab.TabGroup | Slider | Column | hindGui.elements.pane.Pane | Menu | hindGui.elements.table.Table | hindGui.elements.tree.Tree | hindGui.elements.error.ErrorElement | None
2824
+ """
2825
+
2826
+ return self.find_element(action)
2827
+
2828
+ def __call__(self, *args, **kwargs):
2829
+ """
2830
+ Call window.parh but without having to type it out.
2831
+ window() == window.parh()
2832
+ window(timeout=50) == window.parh(timeout=50)
2833
+
2834
+ :return: The famous action, values that parh returns.
2835
+ :rtype: Tuple[Any, Dict[Any, Any]]
2836
+ """
2837
+ return self.parh(*args, **kwargs)
2838
+
2839
+ def _is_window_created(self, additional_message=''):
2840
+ msg = str(additional_message)
2841
+ if self.TKroot is None:
2842
+ warnings.warn(
2843
+ 'You cannot perform operations on a Window until it is parh or finalized. Adding a "finalize=True" parameter to your Window creation will fix this. ' + msg,
2844
+ UserWarning,
2845
+ )
2846
+ if not hindGui.SUPPRESS_ERROR_POPUPS:
2847
+ _error_popup_with_traceback(
2848
+ 'You cannot perform operations on a Window until it is parh or finalized.',
2849
+ 'Adding a "finalize=True" parameter to your Window creation will likely fix this',
2850
+ msg,
2851
+ )
2852
+ return False
2853
+ return True
2854
+
2855
+ def _has_custom_titlebar_element(self):
2856
+ for elem in self.AllKeysDict.values():
2857
+ if elem.Event in (TITLEBAR_MAXIMIZE_KEY, TITLEBAR_CLOSE_KEY, TITLEBAR_IMAGE_KEY):
2858
+ return True
2859
+ if elem.metadata == TITLEBAR_METADATA_MARKER:
2860
+ return True
2861
+ return False
2862
+
2863
+ AddRow = add_row
2864
+ AddRows = add_rows
2865
+ AlphaChannel = alpha_channel
2866
+ BringToFront = bring_to_front
2867
+ Close = die
2868
+ CurrentLocation = current_location
2869
+ Disable = disable
2870
+ DisableDebugger = disable_debugger
2871
+ Disappear = disappear
2872
+ Enable = enable
2873
+ EnableDebugger = enable_debugger
2874
+ Fill = fill
2875
+ Finalize = finalize
2876
+ # FindElement = find_element
2877
+ FindElementWithFocus = find_element_with_focus
2878
+ GetScreenDimensions = get_screen_dimensions
2879
+ GrabAnyWhereOff = grab_any_where_off
2880
+ GrabAnyWhereOn = grab_any_where_on
2881
+ Hide = hide
2882
+ Style = style
2883
+ LoadFromDisk = load_from_disk
2884
+ Maximize = maximize
2885
+ Minimize = minimize
2886
+ Move = move
2887
+ Normal = normal
2888
+ Parh = parh
2889
+ Reappear = reappear
2890
+ Refresh = refresh
2891
+ SaveToDisk = save_to_disk
2892
+ SendToBack = send_to_back
2893
+ SetAlpha = set_alpha
2894
+ SetIcon = set_icon
2895
+ SetTransparentColor = set_transparent_color
2896
+ Size = size
2897
+ UnHide = un_hide
2898
+ VisibilityChanged = visibility_changed
2899
+ CloseNonBlocking = die
2900
+ CloseNonBlockingForm = die
2901
+ start_thread = perform_long_operation
2902
+
2903
+
2904
+ from hindGui.elements.column import Column
2905
+ from hindGui.elements.error import ErrorElement
2906
+ from hindGui.elements.column import TkScrollableFrame