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.
- KL_Py/KL_Py.py +4400 -0
- KL_Py/__init__.py +1 -0
- KL_Py/hindGui/__init__.py +16529 -0
- KL_Py/hindGui/_utils.py +140 -0
- KL_Py/hindGui/elements/base.py +1055 -0
- KL_Py/hindGui/elements/button.py +947 -0
- KL_Py/hindGui/elements/calendar.py +222 -0
- KL_Py/hindGui/elements/canvas.py +122 -0
- KL_Py/hindGui/elements/center.py +44 -0
- KL_Py/hindGui/elements/checkbox.py +240 -0
- KL_Py/hindGui/elements/column.py +443 -0
- KL_Py/hindGui/elements/combo.py +329 -0
- KL_Py/hindGui/elements/error.py +47 -0
- KL_Py/hindGui/elements/frame.py +273 -0
- KL_Py/hindGui/elements/graph.py +817 -0
- KL_Py/hindGui/elements/helpers.py +202 -0
- KL_Py/hindGui/elements/image.py +324 -0
- KL_Py/hindGui/elements/input.py +310 -0
- KL_Py/hindGui/elements/list_box.py +392 -0
- KL_Py/hindGui/elements/menu.py +192 -0
- KL_Py/hindGui/elements/multiline.py +685 -0
- KL_Py/hindGui/elements/option_menu.py +168 -0
- KL_Py/hindGui/elements/pane.py +127 -0
- KL_Py/hindGui/elements/progress_bar.py +304 -0
- KL_Py/hindGui/elements/radio.py +252 -0
- KL_Py/hindGui/elements/separator.py +64 -0
- KL_Py/hindGui/elements/sizegrip.py +34 -0
- KL_Py/hindGui/elements/slider.py +199 -0
- KL_Py/hindGui/elements/spin.py +252 -0
- KL_Py/hindGui/elements/status_bar.py +165 -0
- KL_Py/hindGui/elements/stretch.py +26 -0
- KL_Py/hindGui/elements/tab.py +697 -0
- KL_Py/hindGui/elements/table.py +463 -0
- KL_Py/hindGui/elements/text.py +413 -0
- KL_Py/hindGui/elements/tree.py +493 -0
- KL_Py/hindGui/replace.py +45 -0
- KL_Py/hindGui/tray.py +358 -0
- KL_Py/hindGui/window.py +2906 -0
- KL_Py/requests/__init__.py +184 -0
- KL_Py/requests/__version__.py +14 -0
- KL_Py/requests/_internal_utils.py +50 -0
- KL_Py/requests/adapters.py +719 -0
- KL_Py/requests/api.py +157 -0
- KL_Py/requests/auth.py +314 -0
- KL_Py/requests/certifi/__init__.py +4 -0
- KL_Py/requests/certifi/__main__.py +12 -0
- KL_Py/requests/certifi/core.py +83 -0
- KL_Py/requests/certifi/py.typed +0 -0
- KL_Py/requests/certs.py +17 -0
- KL_Py/requests/charset_normalizer/__init__.py +48 -0
- KL_Py/requests/charset_normalizer/__main__.py +6 -0
- KL_Py/requests/charset_normalizer/api.py +671 -0
- KL_Py/requests/charset_normalizer/cd.py +421 -0
- KL_Py/requests/charset_normalizer/cli/__init__.py +8 -0
- KL_Py/requests/charset_normalizer/cli/__main__.py +363 -0
- KL_Py/requests/charset_normalizer/constant.py +2031 -0
- KL_Py/requests/charset_normalizer/legacy.py +80 -0
- KL_Py/requests/charset_normalizer/md.py +744 -0
- KL_Py/requests/charset_normalizer/models.py +359 -0
- KL_Py/requests/charset_normalizer/py.typed +0 -0
- KL_Py/requests/charset_normalizer/utils.py +420 -0
- KL_Py/requests/charset_normalizer/version.py +8 -0
- KL_Py/requests/compat.py +106 -0
- KL_Py/requests/cookies.py +561 -0
- KL_Py/requests/exceptions.py +151 -0
- KL_Py/requests/help.py +134 -0
- KL_Py/requests/hooks.py +33 -0
- KL_Py/requests/idna/__init__.py +45 -0
- KL_Py/requests/idna/codec.py +122 -0
- KL_Py/requests/idna/compat.py +15 -0
- KL_Py/requests/idna/core.py +437 -0
- KL_Py/requests/idna/idnadata.py +4309 -0
- KL_Py/requests/idna/intranges.py +57 -0
- KL_Py/requests/idna/package_data.py +1 -0
- KL_Py/requests/idna/py.typed +0 -0
- KL_Py/requests/idna/uts46data.py +8841 -0
- KL_Py/requests/models.py +1039 -0
- KL_Py/requests/packages.py +23 -0
- KL_Py/requests/sessions.py +831 -0
- KL_Py/requests/status_codes.py +128 -0
- KL_Py/requests/structures.py +99 -0
- KL_Py/requests/urllib3/__init__.py +211 -0
- KL_Py/requests/urllib3/_base_connection.py +165 -0
- KL_Py/requests/urllib3/_collections.py +487 -0
- KL_Py/requests/urllib3/_request_methods.py +278 -0
- KL_Py/requests/urllib3/_version.py +34 -0
- KL_Py/requests/urllib3/connection.py +1099 -0
- KL_Py/requests/urllib3/connectionpool.py +1178 -0
- KL_Py/requests/urllib3/contrib/__init__.py +0 -0
- KL_Py/requests/urllib3/contrib/emscripten/__init__.py +17 -0
- KL_Py/requests/urllib3/contrib/emscripten/connection.py +260 -0
- KL_Py/requests/urllib3/contrib/emscripten/fetch.py +726 -0
- KL_Py/requests/urllib3/contrib/emscripten/request.py +22 -0
- KL_Py/requests/urllib3/contrib/emscripten/response.py +277 -0
- KL_Py/requests/urllib3/contrib/pyopenssl.py +564 -0
- KL_Py/requests/urllib3/contrib/socks.py +228 -0
- KL_Py/requests/urllib3/exceptions.py +335 -0
- KL_Py/requests/urllib3/fields.py +341 -0
- KL_Py/requests/urllib3/filepost.py +89 -0
- KL_Py/requests/urllib3/http2/__init__.py +53 -0
- KL_Py/requests/urllib3/http2/connection.py +356 -0
- KL_Py/requests/urllib3/http2/probe.py +87 -0
- KL_Py/requests/urllib3/poolmanager.py +651 -0
- KL_Py/requests/urllib3/py.typed +2 -0
- KL_Py/requests/urllib3/response.py +1480 -0
- KL_Py/requests/urllib3/util/__init__.py +42 -0
- KL_Py/requests/urllib3/util/connection.py +137 -0
- KL_Py/requests/urllib3/util/proxy.py +43 -0
- KL_Py/requests/urllib3/util/request.py +263 -0
- KL_Py/requests/urllib3/util/response.py +101 -0
- KL_Py/requests/urllib3/util/retry.py +549 -0
- KL_Py/requests/urllib3/util/ssl_.py +527 -0
- KL_Py/requests/urllib3/util/ssl_match_hostname.py +159 -0
- KL_Py/requests/urllib3/util/ssltransport.py +271 -0
- KL_Py/requests/urllib3/util/timeout.py +275 -0
- KL_Py/requests/urllib3/util/url.py +469 -0
- KL_Py/requests/urllib3/util/util.py +42 -0
- KL_Py/requests/urllib3/util/wait.py +124 -0
- KL_Py/requests/utils.py +1086 -0
- KL_Py/when.py +144 -0
- kl_py-0.0.1.dist-info/METADATA +15 -0
- kl_py-0.0.1.dist-info/RECORD +125 -0
- kl_py-0.0.1.dist-info/WHEEL +5 -0
- kl_py-0.0.1.dist-info/licenses/LICENSE +21 -0
- kl_py-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import tkinter as tk
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def AddMenuItem(top_menu, sub_menu_info, element, is_sub_menu=False, skip=False, right_click_menu=False):
|
|
8
|
+
"""
|
|
9
|
+
Only to be used internally. Not user callable
|
|
10
|
+
:param top_menu: ???
|
|
11
|
+
:type top_menu: ???
|
|
12
|
+
:param sub_menu_info: ???
|
|
13
|
+
:type sub_menu_info:
|
|
14
|
+
:param element: ???
|
|
15
|
+
:type element: idk_yetReally
|
|
16
|
+
:param is_sub_menu: (Default = False)
|
|
17
|
+
:type is_sub_menu: (bool)
|
|
18
|
+
:param skip: (Default = False)
|
|
19
|
+
:type skip: (bool)
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
return_val = None
|
|
23
|
+
if type(sub_menu_info) is str:
|
|
24
|
+
if not is_sub_menu and not skip:
|
|
25
|
+
pos = sub_menu_info.find(MENU_SHORTCUT_CHARACTER)
|
|
26
|
+
if pos != -1:
|
|
27
|
+
if pos < len(MENU_SHORTCUT_CHARACTER) or sub_menu_info[pos - len(MENU_SHORTCUT_CHARACTER)] != '\\':
|
|
28
|
+
sub_menu_info = sub_menu_info[:pos] + sub_menu_info[pos + len(MENU_SHORTCUT_CHARACTER) :]
|
|
29
|
+
if sub_menu_info == '---':
|
|
30
|
+
top_menu.add('separator')
|
|
31
|
+
else:
|
|
32
|
+
try:
|
|
33
|
+
item_without_key = sub_menu_info[: sub_menu_info.index(MENU_KEY_SEPARATOR)]
|
|
34
|
+
except:
|
|
35
|
+
item_without_key = sub_menu_info
|
|
36
|
+
|
|
37
|
+
if item_without_key[0] == MENU_DISABLED_CHARACTER:
|
|
38
|
+
top_menu.add_command(
|
|
39
|
+
label=item_without_key[len(MENU_DISABLED_CHARACTER) :],
|
|
40
|
+
underline=pos - 1,
|
|
41
|
+
command=lambda: element._MenuItemChosenCallback(sub_menu_info),
|
|
42
|
+
)
|
|
43
|
+
top_menu.entryconfig(item_without_key[len(MENU_DISABLED_CHARACTER) :], state='disabled')
|
|
44
|
+
else:
|
|
45
|
+
top_menu.add_command(
|
|
46
|
+
label=item_without_key,
|
|
47
|
+
underline=pos,
|
|
48
|
+
command=lambda: element._MenuItemChosenCallback(sub_menu_info),
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
i = 0
|
|
52
|
+
while i < (len(sub_menu_info)):
|
|
53
|
+
item = sub_menu_info[i]
|
|
54
|
+
if i != len(sub_menu_info) - 1:
|
|
55
|
+
if type(sub_menu_info[i + 1]) is list:
|
|
56
|
+
new_menu = tk.Menu(top_menu, tearoff=element.Tearoff)
|
|
57
|
+
# if a right click menu, then get styling from the top-level window
|
|
58
|
+
if right_click_menu:
|
|
59
|
+
window = element.ParentForm
|
|
60
|
+
if window.right_click_menu_background_color not in (COLOR_SYSTEM_DEFAULT, None):
|
|
61
|
+
new_menu.config(bg=window.right_click_menu_background_color)
|
|
62
|
+
new_menu.config(activeforeground=window.right_click_menu_background_color)
|
|
63
|
+
if window.right_click_menu_text_color not in (COLOR_SYSTEM_DEFAULT, None):
|
|
64
|
+
new_menu.config(fg=window.right_click_menu_text_color)
|
|
65
|
+
new_menu.config(activebackground=window.right_click_menu_text_color)
|
|
66
|
+
if window.right_click_menu_disabled_text_color not in (COLOR_SYSTEM_DEFAULT, None):
|
|
67
|
+
new_menu.config(disabledforeground=window.right_click_menu_disabled_text_color)
|
|
68
|
+
if window.right_click_menu_font is not None:
|
|
69
|
+
new_menu.config(font=window.right_click_menu_font)
|
|
70
|
+
else:
|
|
71
|
+
if element.Font is not None:
|
|
72
|
+
new_menu.config(font=element.Font)
|
|
73
|
+
if element.BackgroundColor not in (COLOR_SYSTEM_DEFAULT, None):
|
|
74
|
+
new_menu.config(bg=element.BackgroundColor)
|
|
75
|
+
new_menu.config(activeforeground=element.BackgroundColor)
|
|
76
|
+
if element.TextColor not in (COLOR_SYSTEM_DEFAULT, None):
|
|
77
|
+
new_menu.config(fg=element.TextColor)
|
|
78
|
+
new_menu.config(activebackground=element.TextColor)
|
|
79
|
+
if element.DisabledTextColor not in (COLOR_SYSTEM_DEFAULT, None):
|
|
80
|
+
new_menu.config(disabledforeground=element.DisabledTextColor)
|
|
81
|
+
if element.ItemFont is not None:
|
|
82
|
+
new_menu.config(font=element.ItemFont)
|
|
83
|
+
return_val = new_menu
|
|
84
|
+
pos = sub_menu_info[i].find(MENU_SHORTCUT_CHARACTER)
|
|
85
|
+
if pos != -1:
|
|
86
|
+
if pos < len(MENU_SHORTCUT_CHARACTER) or sub_menu_info[i][pos - len(MENU_SHORTCUT_CHARACTER)] != '\\':
|
|
87
|
+
sub_menu_info[i] = sub_menu_info[i][:pos] + sub_menu_info[i][pos + len(MENU_SHORTCUT_CHARACTER) :]
|
|
88
|
+
if sub_menu_info[i][0] == MENU_DISABLED_CHARACTER:
|
|
89
|
+
top_menu.add_cascade(
|
|
90
|
+
label=sub_menu_info[i][len(MENU_DISABLED_CHARACTER) :],
|
|
91
|
+
menu=new_menu,
|
|
92
|
+
underline=pos,
|
|
93
|
+
state='disabled',
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
top_menu.add_cascade(label=sub_menu_info[i], menu=new_menu, underline=pos)
|
|
97
|
+
AddMenuItem(new_menu, sub_menu_info[i + 1], element, is_sub_menu=True, right_click_menu=right_click_menu)
|
|
98
|
+
i += 1 # skip the next one
|
|
99
|
+
else:
|
|
100
|
+
AddMenuItem(top_menu, item, element, right_click_menu=right_click_menu)
|
|
101
|
+
else:
|
|
102
|
+
AddMenuItem(top_menu, item, element, right_click_menu=right_click_menu)
|
|
103
|
+
i += 1
|
|
104
|
+
return return_val
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def button_color_to_tuple(color_tuple_or_string, default=(None, None)):
|
|
108
|
+
"""
|
|
109
|
+
Convert a color tuple or color string into 2 components and returns them as a tuple
|
|
110
|
+
(Text Color, Button Background Color)
|
|
111
|
+
If None is passed in as the first parameter, then the theme's button color is
|
|
112
|
+
returned
|
|
113
|
+
|
|
114
|
+
:param color_tuple_or_string: Button color - tuple or a simplied color string with word "on" between color
|
|
115
|
+
:type color_tuple_or_string: str | (str, str)
|
|
116
|
+
:param default: The 2 colors to use if there is a problem. Otherwise defaults to the theme's button color
|
|
117
|
+
:type default: (str, str)
|
|
118
|
+
:return: (str | (str, str)
|
|
119
|
+
:rtype: str | (str, str)
|
|
120
|
+
"""
|
|
121
|
+
if default == (None, None):
|
|
122
|
+
color_tuple = _simplified_dual_color_to_tuple(color_tuple_or_string, default=theme_button_color())
|
|
123
|
+
elif color_tuple_or_string == COLOR_SYSTEM_DEFAULT:
|
|
124
|
+
color_tuple = (COLOR_SYSTEM_DEFAULT, COLOR_SYSTEM_DEFAULT)
|
|
125
|
+
else:
|
|
126
|
+
color_tuple = _simplified_dual_color_to_tuple(color_tuple_or_string, default=default)
|
|
127
|
+
|
|
128
|
+
return color_tuple
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _simplified_dual_color_to_tuple(color_tuple_or_string, default=(None, None)):
|
|
132
|
+
"""
|
|
133
|
+
Convert a color tuple or color string into 2 components and returns them as a tuple
|
|
134
|
+
(Text Color, Button Background Color)
|
|
135
|
+
If None is passed in as the first parameter, theme_
|
|
136
|
+
|
|
137
|
+
:param color_tuple_or_string: Button color - tuple or a simplied color string with word "on" between color
|
|
138
|
+
:type color_tuple_or_string: str | (str, str} | (None, None)
|
|
139
|
+
:param default: The 2 colors to use if there is a problem. Otherwise defaults to the theme's button color
|
|
140
|
+
:type default: (str, str)
|
|
141
|
+
:return: (str | (str, str)
|
|
142
|
+
:rtype: str | (str, str)
|
|
143
|
+
"""
|
|
144
|
+
if color_tuple_or_string is None or color_tuple_or_string == (None, None):
|
|
145
|
+
color_tuple_or_string = default
|
|
146
|
+
if color_tuple_or_string == COLOR_SYSTEM_DEFAULT:
|
|
147
|
+
return (COLOR_SYSTEM_DEFAULT, COLOR_SYSTEM_DEFAULT)
|
|
148
|
+
text_color = background_color = COLOR_SYSTEM_DEFAULT
|
|
149
|
+
try:
|
|
150
|
+
if isinstance(color_tuple_or_string, (tuple, list)):
|
|
151
|
+
if len(color_tuple_or_string) >= 2:
|
|
152
|
+
text_color = color_tuple_or_string[0] or default[0]
|
|
153
|
+
background_color = color_tuple_or_string[1] or default[1]
|
|
154
|
+
elif len(color_tuple_or_string) == 1:
|
|
155
|
+
background_color = color_tuple_or_string[0] or default[1]
|
|
156
|
+
elif isinstance(color_tuple_or_string, str):
|
|
157
|
+
color_tuple_or_string = color_tuple_or_string.lower()
|
|
158
|
+
if re.search(r"(?<=\w) (ke u)?par (?=[#\w])", color_tuple_or_string, re.IGNORECASE):
|
|
159
|
+
color_tuple_or_string = re.sub(r"(#?\w+) (ke u)?par (#?\w+)", r"\3 on \1", color_tuple_or_string, re.IGNORECASE)
|
|
160
|
+
split_colors = color_tuple_or_string.split(" on ")
|
|
161
|
+
if len(split_colors) >= 2:
|
|
162
|
+
text_color = split_colors[0].strip() or default[0]
|
|
163
|
+
background_color = split_colors[1].strip() or default[1]
|
|
164
|
+
elif len(split_colors) == 1:
|
|
165
|
+
split_colors = color_tuple_or_string.split('on')
|
|
166
|
+
if len(split_colors) == 1:
|
|
167
|
+
text_color, background_color = default[0], split_colors[0].strip()
|
|
168
|
+
else:
|
|
169
|
+
split_colors = split_colors[0].strip(), split_colors[1].strip()
|
|
170
|
+
text_color = split_colors[0] or default[0]
|
|
171
|
+
background_color = split_colors[1] or default[1]
|
|
172
|
+
# text_color, background_color = color_tuple_or_string, default[1]
|
|
173
|
+
else:
|
|
174
|
+
text_color, background_color = default
|
|
175
|
+
else:
|
|
176
|
+
if not SUPPRESS_ERROR_POPUPS:
|
|
177
|
+
_error_popup_with_traceback('** Badly formatted dual-color... not a tuple nor string **', color_tuple_or_string)
|
|
178
|
+
else:
|
|
179
|
+
print('** Badly formatted dual-color... not a tuple nor string **', color_tuple_or_string)
|
|
180
|
+
text_color, background_color = default
|
|
181
|
+
except Exception as e:
|
|
182
|
+
if not SUPPRESS_ERROR_POPUPS:
|
|
183
|
+
_error_popup_with_traceback('** Badly formatted button color **', color_tuple_or_string, e)
|
|
184
|
+
else:
|
|
185
|
+
print('** Badly formatted button color... not a tuple nor string **', color_tuple_or_string, e)
|
|
186
|
+
text_color, background_color = default
|
|
187
|
+
if isinstance(text_color, int):
|
|
188
|
+
text_color = '#%06X' % text_color
|
|
189
|
+
if isinstance(background_color, int):
|
|
190
|
+
background_color = '#%06X' % background_color
|
|
191
|
+
# print('converted button color', color_tuple_or_string, 'to', (text_color, background_color))
|
|
192
|
+
|
|
193
|
+
return (text_color, background_color)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
from hindGui._utils import _error_popup_with_traceback
|
|
197
|
+
from hindGui import COLOR_SYSTEM_DEFAULT
|
|
198
|
+
from hindGui import MENU_DISABLED_CHARACTER
|
|
199
|
+
from hindGui import MENU_KEY_SEPARATOR
|
|
200
|
+
from hindGui import MENU_SHORTCUT_CHARACTER
|
|
201
|
+
from hindGui import SUPPRESS_ERROR_POPUPS
|
|
202
|
+
from hindGui import theme_button_color
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import tkinter as tk
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
from hindGui import ELEM_TYPE_IMAGE
|
|
8
|
+
from hindGui._utils import _error_popup_with_traceback
|
|
9
|
+
from hindGui.elements.base import Element
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Image(Element):
|
|
13
|
+
"""
|
|
14
|
+
Image Element - show an image in the window. Should be a GIF or a PNG only
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
source=None,
|
|
20
|
+
filename=None,
|
|
21
|
+
data=None,
|
|
22
|
+
background_color=None,
|
|
23
|
+
size=(None, None),
|
|
24
|
+
s=(None, None),
|
|
25
|
+
pad=None,
|
|
26
|
+
p=None,
|
|
27
|
+
action=None,
|
|
28
|
+
k=None,
|
|
29
|
+
ke_upar=None,
|
|
30
|
+
subsample=None,
|
|
31
|
+
zoom=None,
|
|
32
|
+
right_click_menu=None,
|
|
33
|
+
expand_x=False,
|
|
34
|
+
expand_y=False,
|
|
35
|
+
nazar=True,
|
|
36
|
+
enable_events=True,
|
|
37
|
+
metadata=None,
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
:param source: A filename or a base64 bytes. Will automatically detect the type and fill in filename or data for you.
|
|
41
|
+
:type source: str | bytes | None
|
|
42
|
+
:param filename: image filename if there is a button image. GIFs and PNGs only.
|
|
43
|
+
:type filename: str | None
|
|
44
|
+
:param data: Raw or Base64 representation of the image to put on button. Choose either filename or data
|
|
45
|
+
:type data: bytes | str | None
|
|
46
|
+
:param background_color: color of background
|
|
47
|
+
:type background_color:
|
|
48
|
+
:param size: (width, height) size of image in pixels
|
|
49
|
+
:type size: (int, int)
|
|
50
|
+
:param s: Same as size parameter. It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, size will be used
|
|
51
|
+
:type s: (int, int) | (None, None) | int
|
|
52
|
+
:param pad: Amount of padding to put around element in pixels (left/right, top/bottom) or ((left, right), (top, bottom)) or an int. If an int, then it's converted into a tuple (int, int)
|
|
53
|
+
:type pad: (int, int) or ((int, int),(int,int)) or (int,(int,int)) or ((int, int),int) | int
|
|
54
|
+
:param p: Same as pad parameter. It's an alias. If EITHER of them are set, then the one that's set will be used. If BOTH are set, pad will be used
|
|
55
|
+
:type p: (int, int) or ((int, int),(int,int)) or (int,(int,int)) or ((int, int),int) | int
|
|
56
|
+
:param action: Used with window.find_element and with return values to uniquely identify this element to uniquely identify this element
|
|
57
|
+
:type action: str | int | tuple | object
|
|
58
|
+
:param k: Same as the Key. You can use either k or action. Which ever is set will be used.
|
|
59
|
+
:type k: str | int | tuple | object
|
|
60
|
+
:param ke_upar: text, that will appear when mouse hovers over the element
|
|
61
|
+
:type ke_upar: (str)
|
|
62
|
+
:param subsample: amount to reduce the size of the image. Divides the size by this number. 2=1/2, 3=1/3, 4=1/4, etc
|
|
63
|
+
:type subsample: (int)
|
|
64
|
+
:param zoom: amount to increase the size of the image.
|
|
65
|
+
:type zoom: (int)
|
|
66
|
+
: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.
|
|
67
|
+
:type right_click_menu: List[List[ List[str] | str ]]
|
|
68
|
+
:param expand_x: If True the element will automatically expand in the X direction to fill available space
|
|
69
|
+
:type expand_x: (bool)
|
|
70
|
+
:param expand_y: If True the element will automatically expand in the Y direction to fill available space
|
|
71
|
+
:type expand_y: (bool)
|
|
72
|
+
:param nazar: set visibility state of the element
|
|
73
|
+
:type nazar: (bool)
|
|
74
|
+
:param enable_events: Turns on the element specific events. For an Image element, the action is "image clicked"
|
|
75
|
+
:type enable_events: (bool)
|
|
76
|
+
:param metadata: User metadata that can be set to ANYTHING
|
|
77
|
+
:type metadata: (Any)
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
if source is not None:
|
|
81
|
+
if isinstance(source, bytes):
|
|
82
|
+
data = source
|
|
83
|
+
elif isinstance(source, str):
|
|
84
|
+
filename = source
|
|
85
|
+
else:
|
|
86
|
+
warnings.warn(f'Image element - source is not a valid type: {type(source)}', UserWarning)
|
|
87
|
+
|
|
88
|
+
self.Filename = filename
|
|
89
|
+
self.Data = data
|
|
90
|
+
self.Widget = self.tktext_label = None # type: tk.Label
|
|
91
|
+
self.BackgroundColor = background_color
|
|
92
|
+
if data is None and filename is None:
|
|
93
|
+
self.Filename = ''
|
|
94
|
+
self.EnableEvents = enable_events
|
|
95
|
+
self.RightClickMenu = right_click_menu
|
|
96
|
+
self.AnimatedFrames = None
|
|
97
|
+
self.CurrentFrameNumber = 0
|
|
98
|
+
self.TotalAnimatedFrames = 0
|
|
99
|
+
self.LastFrameTime = 0
|
|
100
|
+
self.ImageSubsample = subsample
|
|
101
|
+
self.zoom = int(zoom) if zoom is not None else None
|
|
102
|
+
|
|
103
|
+
self.Source = filename if filename is not None else data
|
|
104
|
+
action = action if action is not None else k
|
|
105
|
+
sz = size if size != (None, None) else s
|
|
106
|
+
pad = pad if pad is not None else p
|
|
107
|
+
self.expand_x = expand_x
|
|
108
|
+
self.expand_y = expand_y
|
|
109
|
+
|
|
110
|
+
super().__init__(
|
|
111
|
+
ELEM_TYPE_IMAGE,
|
|
112
|
+
size=sz,
|
|
113
|
+
background_color=background_color,
|
|
114
|
+
pad=pad,
|
|
115
|
+
action=action,
|
|
116
|
+
ke_upar=ke_upar,
|
|
117
|
+
nazar=nazar,
|
|
118
|
+
metadata=metadata,
|
|
119
|
+
)
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
def badlo(self, source=None, filename=None, data=None, size=(None, None), p=None, subsample=None, zoom=None, nazar=None):
|
|
123
|
+
"""
|
|
124
|
+
Changes some of the settings for the Image Element. Must call `Window.Read` or `Window.Finalize` prior.
|
|
125
|
+
To clear an image that's been displayed, call with NONE of the options set. A blank badlo call will
|
|
126
|
+
delete the previously shown image.
|
|
127
|
+
|
|
128
|
+
Changes will not be nazar in your window until you call window.read or window.refresh.
|
|
129
|
+
|
|
130
|
+
If you badlo visibility, your element may MOVE. If you want it to remain stationary, use the "layout helper"
|
|
131
|
+
function "pin" to ensure your element is "pinned" to that location in your layout so that it returns there
|
|
132
|
+
when made nazar.
|
|
133
|
+
|
|
134
|
+
:param source: A filename or a base64 bytes. Will automatically detect the type and fill in filename or data for you.
|
|
135
|
+
:type source: str | bytes | None
|
|
136
|
+
:param filename: filename to the new image to display.
|
|
137
|
+
:type filename: (str)
|
|
138
|
+
:param data: Base64 encoded string OR a tk.PhotoImage object
|
|
139
|
+
:type data: str | tkPhotoImage
|
|
140
|
+
:param size: (width, height) size of image in pixels
|
|
141
|
+
:type size: Tuple[int,int]
|
|
142
|
+
:param p: (x, y) padding of image in pixels
|
|
143
|
+
:type p: Tuple[int,int]
|
|
144
|
+
:param subsample: amount to reduce the size of the image. Divides the size by this number. 2=1/2, 3=1/3, 4=1/4, etc
|
|
145
|
+
:type subsample: (int)
|
|
146
|
+
:param zoom: amount to increase the size of the image
|
|
147
|
+
:type zoom: (int)
|
|
148
|
+
:param nazar: control visibility of element
|
|
149
|
+
:type nazar: (bool)
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
if not self._widget_was_created(): # if widget hasn't been created yet, then don't allow
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
if self._this_elements_window_closed():
|
|
156
|
+
_error_popup_with_traceback('Error in Image.badlo - The window was closed')
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
if source is not None:
|
|
160
|
+
if isinstance(source, bytes):
|
|
161
|
+
data = source
|
|
162
|
+
elif isinstance(source, str):
|
|
163
|
+
filename = source
|
|
164
|
+
else:
|
|
165
|
+
warnings.warn(f'Image element - source is not a valid type: {type(source)}', UserWarning)
|
|
166
|
+
|
|
167
|
+
image = None
|
|
168
|
+
if filename is not None:
|
|
169
|
+
try:
|
|
170
|
+
image = tk.PhotoImage(file=filename)
|
|
171
|
+
if subsample is not None:
|
|
172
|
+
image = image.subsample(subsample)
|
|
173
|
+
if zoom is not None:
|
|
174
|
+
image = image.zoom(int(zoom))
|
|
175
|
+
except Exception as e:
|
|
176
|
+
_error_popup_with_traceback('Exception updating Image element', e)
|
|
177
|
+
|
|
178
|
+
elif data is not None:
|
|
179
|
+
# if type(data) is bytes:
|
|
180
|
+
try:
|
|
181
|
+
image = tk.PhotoImage(data=data)
|
|
182
|
+
if subsample is not None:
|
|
183
|
+
image = image.subsample(subsample)
|
|
184
|
+
if zoom is not None:
|
|
185
|
+
image = image.zoom(int(zoom))
|
|
186
|
+
except Exception:
|
|
187
|
+
image = data
|
|
188
|
+
# return # an error likely means the window has closed so exit
|
|
189
|
+
|
|
190
|
+
if image is not None:
|
|
191
|
+
self.tktext_label.configure(image='') # clear previous image
|
|
192
|
+
if self.tktext_label.image is not None:
|
|
193
|
+
del self.tktext_label.image
|
|
194
|
+
if type(image) is not bytes:
|
|
195
|
+
width, height = (
|
|
196
|
+
size[0] if size[0] is not None else image.width(),
|
|
197
|
+
size[1] if size[1] is not None else image.height(),
|
|
198
|
+
)
|
|
199
|
+
else:
|
|
200
|
+
width, height = size
|
|
201
|
+
try: # sometimes crashes if user closed with X
|
|
202
|
+
self.tktext_label.configure(image=image, width=width, height=height)
|
|
203
|
+
except Exception as e:
|
|
204
|
+
_error_popup_with_traceback('Exception updating Image element', e)
|
|
205
|
+
self.tktext_label.image = image
|
|
206
|
+
if nazar is False:
|
|
207
|
+
self._pack_forget_save_settings()
|
|
208
|
+
elif nazar is True:
|
|
209
|
+
self._pack_restore_settings()
|
|
210
|
+
|
|
211
|
+
if p is not None:
|
|
212
|
+
pad = p
|
|
213
|
+
|
|
214
|
+
# if everything is set to None, then delete the image
|
|
215
|
+
if filename is None and image is None and nazar is None and size == (None, None):
|
|
216
|
+
# Using a try because the image may have been previously deleted and don't want an error if that's happened
|
|
217
|
+
try:
|
|
218
|
+
self.tktext_label.configure(image='', width=1, height=1, bd=0)
|
|
219
|
+
self.tktext_label.image = None
|
|
220
|
+
except:
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
if nazar is not None:
|
|
224
|
+
self._nazar = nazar
|
|
225
|
+
|
|
226
|
+
def update_animation(self, source, time_between_frames=0):
|
|
227
|
+
"""
|
|
228
|
+
Show an Animated GIF. Call the function as often as you like. The function will determine when to show the next frame and will automatically advance to the next frame at the right time.
|
|
229
|
+
NOTE - does NOT perform a sleep call to delay
|
|
230
|
+
:param source: Filename or Base64 encoded string containing Animated GIF
|
|
231
|
+
:type source: str | bytes | None
|
|
232
|
+
:param time_between_frames: Number of milliseconds to wait between showing frames
|
|
233
|
+
:type time_between_frames: (int)
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
if self.Source != source:
|
|
237
|
+
self.AnimatedFrames = None
|
|
238
|
+
self.Source = source
|
|
239
|
+
|
|
240
|
+
if self.AnimatedFrames is None:
|
|
241
|
+
self.TotalAnimatedFrames = 0
|
|
242
|
+
self.AnimatedFrames = []
|
|
243
|
+
# Load up to 1000 frames of animation. stops when a bad frame is returns by tkinter
|
|
244
|
+
for i in range(1000):
|
|
245
|
+
if type(source) is not bytes:
|
|
246
|
+
try:
|
|
247
|
+
self.AnimatedFrames.append(tk.PhotoImage(file=source, format='gif -index %i' % (i)))
|
|
248
|
+
except Exception:
|
|
249
|
+
break
|
|
250
|
+
else:
|
|
251
|
+
try:
|
|
252
|
+
self.AnimatedFrames.append(tk.PhotoImage(data=source, format='gif -index %i' % (i)))
|
|
253
|
+
except Exception:
|
|
254
|
+
break
|
|
255
|
+
self.TotalAnimatedFrames = len(self.AnimatedFrames)
|
|
256
|
+
self.LastFrameTime = time.time()
|
|
257
|
+
self.CurrentFrameNumber = -1 # start at -1 because it is incremented before every frame is shown
|
|
258
|
+
# show the frame
|
|
259
|
+
|
|
260
|
+
now = time.time()
|
|
261
|
+
|
|
262
|
+
if time_between_frames:
|
|
263
|
+
if (now - self.LastFrameTime) * 1000 > time_between_frames:
|
|
264
|
+
self.LastFrameTime = now
|
|
265
|
+
self.CurrentFrameNumber = (self.CurrentFrameNumber + 1) % self.TotalAnimatedFrames
|
|
266
|
+
else: # don't reshow the frame again if not time for new frame
|
|
267
|
+
return
|
|
268
|
+
else:
|
|
269
|
+
self.CurrentFrameNumber = (self.CurrentFrameNumber + 1) % self.TotalAnimatedFrames
|
|
270
|
+
image = self.AnimatedFrames[self.CurrentFrameNumber]
|
|
271
|
+
try: # needed in case the window was closed with an "X"
|
|
272
|
+
self.tktext_label.configure(image=image, width=image.width(), heigh=image.height())
|
|
273
|
+
except Exception as e:
|
|
274
|
+
print('Exception in update_animation', e)
|
|
275
|
+
|
|
276
|
+
def update_animation_no_buffering(self, source, time_between_frames=0):
|
|
277
|
+
"""
|
|
278
|
+
Show an Animated GIF. Call the function as often as you like. The function will determine when to show the next frame and will automatically advance to the next frame at the right time.
|
|
279
|
+
NOTE - does NOT perform a sleep call to delay
|
|
280
|
+
|
|
281
|
+
:param source: Filename or Base64 encoded string containing Animated GIF
|
|
282
|
+
:type source: str | bytes
|
|
283
|
+
:param time_between_frames: Number of milliseconds to wait between showing frames
|
|
284
|
+
:type time_between_frames: (int)
|
|
285
|
+
"""
|
|
286
|
+
|
|
287
|
+
if self.Source != source:
|
|
288
|
+
self.AnimatedFrames = None
|
|
289
|
+
self.Source = source
|
|
290
|
+
self.frame_num = 0
|
|
291
|
+
|
|
292
|
+
now = time.time()
|
|
293
|
+
|
|
294
|
+
if time_between_frames:
|
|
295
|
+
if (now - self.LastFrameTime) * 1000 > time_between_frames:
|
|
296
|
+
self.LastFrameTime = now
|
|
297
|
+
else: # don't reshow the frame again if not time for new frame
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
# read a frame
|
|
301
|
+
while True:
|
|
302
|
+
if type(source) is not bytes:
|
|
303
|
+
try:
|
|
304
|
+
self.image = tk.PhotoImage(file=source, format='gif -index %i' % (self.frame_num))
|
|
305
|
+
self.frame_num += 1
|
|
306
|
+
except:
|
|
307
|
+
self.frame_num = 0
|
|
308
|
+
else:
|
|
309
|
+
try:
|
|
310
|
+
self.image = tk.PhotoImage(data=source, format='gif -index %i' % (self.frame_num))
|
|
311
|
+
self.frame_num += 1
|
|
312
|
+
except:
|
|
313
|
+
self.frame_num = 0
|
|
314
|
+
if self.frame_num:
|
|
315
|
+
break
|
|
316
|
+
|
|
317
|
+
try: # needed in case the window was closed with an "X"
|
|
318
|
+
self.tktext_label.configure(image=self.image, width=self.image.width(), heigh=self.image.height())
|
|
319
|
+
|
|
320
|
+
except:
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
Change = badlo
|
|
324
|
+
UpdateAnimation = update_animation
|