sticker-convert 2.8.12__py3-none-any.whl → 2.17.0.0__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 (124) hide show
  1. sticker_convert/__main__.py +24 -24
  2. sticker_convert/auth/__init__.py +0 -0
  3. sticker_convert/auth/auth_base.py +19 -0
  4. sticker_convert/auth/auth_discord.py +149 -0
  5. sticker_convert/{utils/auth/get_kakao_auth.py → auth/auth_kakao_android_login.py} +331 -300
  6. sticker_convert/auth/auth_kakao_desktop_login.py +327 -0
  7. sticker_convert/auth/auth_kakao_desktop_memdump.py +281 -0
  8. sticker_convert/{utils/auth/get_line_auth.py → auth/auth_line.py} +98 -80
  9. sticker_convert/auth/auth_signal.py +139 -0
  10. sticker_convert/auth/auth_telethon.py +161 -0
  11. sticker_convert/auth/auth_viber.py +250 -0
  12. sticker_convert/auth/telegram_api.py +736 -0
  13. sticker_convert/cli.py +623 -509
  14. sticker_convert/converter.py +1093 -962
  15. sticker_convert/definitions.py +11 -0
  16. sticker_convert/downloaders/download_band.py +111 -0
  17. sticker_convert/downloaders/download_base.py +171 -130
  18. sticker_convert/downloaders/download_discord.py +92 -0
  19. sticker_convert/downloaders/download_kakao.py +417 -255
  20. sticker_convert/downloaders/download_line.py +484 -472
  21. sticker_convert/downloaders/download_ogq.py +80 -0
  22. sticker_convert/downloaders/download_signal.py +108 -92
  23. sticker_convert/downloaders/download_telegram.py +56 -130
  24. sticker_convert/downloaders/download_viber.py +121 -95
  25. sticker_convert/gui.py +788 -795
  26. sticker_convert/gui_components/frames/comp_frame.py +180 -165
  27. sticker_convert/gui_components/frames/config_frame.py +156 -113
  28. sticker_convert/gui_components/frames/control_frame.py +32 -30
  29. sticker_convert/gui_components/frames/cred_frame.py +232 -162
  30. sticker_convert/gui_components/frames/input_frame.py +139 -137
  31. sticker_convert/gui_components/frames/output_frame.py +112 -110
  32. sticker_convert/gui_components/frames/right_clicker.py +25 -23
  33. sticker_convert/gui_components/windows/advanced_compression_window.py +757 -715
  34. sticker_convert/gui_components/windows/base_window.py +7 -2
  35. sticker_convert/gui_components/windows/discord_get_auth_window.py +79 -0
  36. sticker_convert/gui_components/windows/kakao_get_auth_window.py +511 -186
  37. sticker_convert/gui_components/windows/line_get_auth_window.py +94 -102
  38. sticker_convert/gui_components/windows/signal_get_auth_window.py +84 -135
  39. sticker_convert/gui_components/windows/viber_get_auth_window.py +168 -0
  40. sticker_convert/ios-message-stickers-template/.github/FUNDING.yml +3 -3
  41. sticker_convert/ios-message-stickers-template/.gitignore +0 -0
  42. sticker_convert/ios-message-stickers-template/README.md +10 -10
  43. sticker_convert/ios-message-stickers-template/stickers/Info.plist +43 -43
  44. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Info.plist +31 -31
  45. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Contents.json +6 -6
  46. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Contents.json +20 -20
  47. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Contents.json +9 -9
  48. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Sticker 1.png +0 -0
  49. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Contents.json +9 -9
  50. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Sticker 2.png +0 -0
  51. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Contents.json +9 -9
  52. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Sticker 3.png +0 -0
  53. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/App-Store-1024x1024pt.png +0 -0
  54. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json +91 -91
  55. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-App-Store-1024x768pt.png +0 -0
  56. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-67x50pt@2x.png +0 -0
  57. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-Pro-74x55pt@2x.png +0 -0
  58. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@2x.png +0 -0
  59. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@3x.png +0 -0
  60. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@2x.png +0 -0
  61. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@3x.png +0 -0
  62. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@2x.png +0 -0
  63. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@3x.png +0 -0
  64. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPad-Settings-29pt@2x.png +0 -0
  65. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-Settings-29pt@3x.png +0 -0
  66. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-settings-29pt@2x.png +0 -0
  67. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.pbxproj +364 -364
  68. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -7
  69. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -8
  70. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcuserdata/niklaspeterson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  71. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/xcuserdata/niklaspeterson.xcuserdatad/xcschemes/xcschememanagement.plist +14 -14
  72. sticker_convert/job.py +279 -179
  73. sticker_convert/job_option.py +15 -2
  74. sticker_convert/locales/en_US/LC_MESSAGES/base.mo +0 -0
  75. sticker_convert/locales/ja_JP/LC_MESSAGES/base.mo +0 -0
  76. sticker_convert/locales/zh_CN/LC_MESSAGES/base.mo +0 -0
  77. sticker_convert/locales/zh_TW/LC_MESSAGES/base.mo +0 -0
  78. sticker_convert/py.typed +0 -0
  79. sticker_convert/resources/NotoColorEmoji.ttf +0 -0
  80. sticker_convert/resources/compression.json +220 -16
  81. sticker_convert/resources/emoji.json +527 -77
  82. sticker_convert/resources/help.ja_JP.json +88 -0
  83. sticker_convert/resources/help.json +24 -10
  84. sticker_convert/resources/help.zh_CN.json +88 -0
  85. sticker_convert/resources/help.zh_TW.json +88 -0
  86. sticker_convert/resources/input.ja_JP.json +74 -0
  87. sticker_convert/resources/input.json +121 -71
  88. sticker_convert/resources/input.zh_CN.json +74 -0
  89. sticker_convert/resources/input.zh_TW.json +74 -0
  90. sticker_convert/resources/memdump_linux.sh +25 -0
  91. sticker_convert/resources/memdump_windows.ps1 +8 -0
  92. sticker_convert/resources/output.ja_JP.json +38 -0
  93. sticker_convert/resources/output.json +24 -0
  94. sticker_convert/resources/output.zh_CN.json +38 -0
  95. sticker_convert/resources/output.zh_TW.json +38 -0
  96. sticker_convert/uploaders/compress_wastickers.py +186 -156
  97. sticker_convert/uploaders/upload_base.py +44 -35
  98. sticker_convert/uploaders/upload_signal.py +218 -173
  99. sticker_convert/uploaders/upload_telegram.py +353 -388
  100. sticker_convert/uploaders/upload_viber.py +178 -0
  101. sticker_convert/uploaders/xcode_imessage.py +295 -285
  102. sticker_convert/utils/callback.py +238 -6
  103. sticker_convert/utils/chrome_remotedebug.py +219 -0
  104. sticker_convert/utils/chromiums/linux.py +52 -0
  105. sticker_convert/utils/chromiums/osx.py +68 -0
  106. sticker_convert/utils/chromiums/windows.py +45 -0
  107. sticker_convert/utils/emoji.py +28 -0
  108. sticker_convert/utils/files/json_resources_loader.py +24 -19
  109. sticker_convert/utils/files/metadata_handler.py +8 -7
  110. sticker_convert/utils/files/run_bin.py +1 -1
  111. sticker_convert/utils/media/codec_info.py +99 -67
  112. sticker_convert/utils/media/format_verify.py +33 -20
  113. sticker_convert/utils/process.py +231 -0
  114. sticker_convert/utils/translate.py +108 -0
  115. sticker_convert/utils/url_detect.py +40 -33
  116. sticker_convert/version.py +1 -1
  117. {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info}/METADATA +189 -96
  118. sticker_convert-2.17.0.0.dist-info/RECORD +138 -0
  119. {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info}/WHEEL +1 -1
  120. sticker_convert/utils/auth/get_signal_auth.py +0 -129
  121. sticker_convert-2.8.12.dist-info/RECORD +0 -101
  122. {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info}/entry_points.txt +0 -0
  123. {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info/licenses}/LICENSE +0 -0
  124. {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info}/top_level.txt +0 -0
@@ -1,715 +1,757 @@
1
- #!/usr/bin/env python3
2
- from __future__ import annotations
3
-
4
- import re
5
- from functools import partial
6
- from tkinter import colorchooser
7
- from typing import Any, List, Tuple
8
-
9
- from PIL import Image, ImageDraw, ImageTk
10
- from ttkbootstrap import Button, Canvas, Checkbutton, Entry, Frame, Label, LabelFrame, OptionMenu, Scrollbar, StringVar # type: ignore
11
-
12
- from sticker_convert.gui_components.frames.right_clicker import RightClicker
13
- from sticker_convert.gui_components.gui_utils import GUIUtils
14
- from sticker_convert.gui_components.windows.base_window import BaseWindow
15
-
16
-
17
- class AdvancedCompressionWindow(BaseWindow):
18
- emoji_column_per_row = 8
19
- emoji_visible_rows = 5
20
- emoji_btns: List[Tuple[Button, ImageTk.PhotoImage]] = []
21
-
22
- def __init__(self, *args: Any, **kwargs: Any) -> None:
23
- super().__init__(*args, **kwargs)
24
- self.categories = list({entry["category"] for entry in self.gui.emoji_list})
25
-
26
- self.title("Advanced compression options")
27
-
28
- self.frame_advcomp = LabelFrame(
29
- self.scrollable_frame, text="Advanced compression option"
30
- )
31
- self.frame_emoji_search = LabelFrame(
32
- self.scrollable_frame, text="Setting default emoji"
33
- )
34
- self.frame_emoji_canvas = Frame(self.scrollable_frame)
35
-
36
- self.frame_advcomp.grid_columnconfigure(6, weight=1)
37
-
38
- self.fps_help_btn = self.add_help_btn(self.gui.help["comp"]["fps"])
39
- self.fps_lbl = Label(self.frame_advcomp, text="Output FPS")
40
- self.fps_min_lbl = Label(self.frame_advcomp, text="Min:")
41
- self.fps_min_entry = Entry(
42
- self.frame_advcomp, textvariable=self.gui.fps_min_var, width=8
43
- )
44
- self.fps_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
45
- self.fps_max_lbl = Label(self.frame_advcomp, text="Max:")
46
- self.fps_max_entry = Entry(
47
- self.frame_advcomp, textvariable=self.gui.fps_max_var, width=8
48
- )
49
- self.fps_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
50
- self.fps_disable_cbox = Checkbutton(
51
- self.frame_advcomp,
52
- text="X",
53
- variable=self.gui.fps_disable_var,
54
- command=self.cb_disable_fps,
55
- onvalue=True,
56
- offvalue=False,
57
- bootstyle="danger-round-toggle", # type: ignore
58
- )
59
-
60
- self.res_w_help_btn = self.add_help_btn(self.gui.help["comp"]["res"])
61
- self.res_w_lbl = Label(self.frame_advcomp, text="Output resolution (Width)")
62
- self.res_w_min_lbl = Label(self.frame_advcomp, text="Min:")
63
- self.res_w_min_entry = Entry(
64
- self.frame_advcomp, textvariable=self.gui.res_w_min_var, width=8
65
- )
66
- self.res_w_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
67
- self.res_w_max_lbl = Label(self.frame_advcomp, text="Max:")
68
- self.res_w_max_entry = Entry(
69
- self.frame_advcomp, textvariable=self.gui.res_w_max_var, width=8
70
- )
71
- self.res_w_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
72
- self.res_w_disable_cbox = Checkbutton(
73
- self.frame_advcomp,
74
- text="X",
75
- variable=self.gui.res_w_disable_var,
76
- command=self.cb_disable_res_w,
77
- onvalue=True,
78
- offvalue=False,
79
- bootstyle="danger-round-toggle", # type: ignore
80
- )
81
-
82
- self.res_h_help_btn = self.add_help_btn(self.gui.help["comp"]["res"])
83
- self.res_h_lbl = Label(self.frame_advcomp, text="Output resolution (Height)")
84
- self.res_h_min_lbl = Label(self.frame_advcomp, text="Min:")
85
- self.res_h_min_entry = Entry(
86
- self.frame_advcomp, textvariable=self.gui.res_h_min_var, width=8
87
- )
88
- self.res_h_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
89
- self.res_h_max_lbl = Label(self.frame_advcomp, text="Max:")
90
- self.res_h_max_entry = Entry(
91
- self.frame_advcomp, textvariable=self.gui.res_h_max_var, width=8
92
- )
93
- self.res_h_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
94
- self.res_h_disable_cbox = Checkbutton(
95
- self.frame_advcomp,
96
- text="X",
97
- variable=self.gui.res_h_disable_var,
98
- command=self.cb_disable_res_h,
99
- onvalue=True,
100
- offvalue=False,
101
- bootstyle="danger-round-toggle", # type: ignore
102
- )
103
-
104
- self.quality_help_btn = self.add_help_btn(self.gui.help["comp"]["quality"])
105
- self.quality_lbl = Label(self.frame_advcomp, text="Output quality (0-100)")
106
- self.quality_min_lbl = Label(self.frame_advcomp, text="Min:")
107
- self.quality_min_entry = Entry(
108
- self.frame_advcomp, textvariable=self.gui.quality_min_var, width=8
109
- )
110
- self.quality_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
111
- self.quality_max_lbl = Label(self.frame_advcomp, text="Max:")
112
- self.quality_max_entry = Entry(
113
- self.frame_advcomp, textvariable=self.gui.quality_max_var, width=8
114
- )
115
- self.quality_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
116
- self.quality_disable_cbox = Checkbutton(
117
- self.frame_advcomp,
118
- text="X",
119
- variable=self.gui.quality_disable_var,
120
- command=self.cb_disable_quality,
121
- onvalue=True,
122
- offvalue=False,
123
- bootstyle="danger-round-toggle", # type: ignore
124
- )
125
-
126
- self.color_help_btn = self.add_help_btn(self.gui.help["comp"]["color"])
127
- self.color_lbl = Label(self.frame_advcomp, text="Colors (0-256)")
128
- self.color_min_lbl = Label(self.frame_advcomp, text="Min:")
129
- self.color_min_entry = Entry(
130
- self.frame_advcomp, textvariable=self.gui.color_min_var, width=8
131
- )
132
- self.color_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
133
- self.color_max_lbl = Label(self.frame_advcomp, text="Max:")
134
- self.color_max_entry = Entry(
135
- self.frame_advcomp, textvariable=self.gui.color_max_var, width=8
136
- )
137
- self.color_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
138
- self.color_disable_cbox = Checkbutton(
139
- self.frame_advcomp,
140
- text="X",
141
- variable=self.gui.color_disable_var,
142
- command=self.cb_disable_color,
143
- onvalue=True,
144
- offvalue=False,
145
- bootstyle="danger-round-toggle", # type: ignore
146
- )
147
-
148
- self.duration_help_btn = self.add_help_btn(self.gui.help["comp"]["duration"])
149
- self.duration_lbl = Label(self.frame_advcomp, text="Duration (Miliseconds)")
150
- self.duration_min_lbl = Label(self.frame_advcomp, text="Min:")
151
- self.duration_min_entry = Entry(
152
- self.frame_advcomp, textvariable=self.gui.duration_min_var, width=8
153
- )
154
- self.duration_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
155
- self.duration_max_lbl = Label(self.frame_advcomp, text="Max:")
156
- self.duration_max_entry = Entry(
157
- self.frame_advcomp, textvariable=self.gui.duration_max_var, width=8
158
- )
159
- self.duration_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
160
- self.duration_disable_cbox = Checkbutton(
161
- self.frame_advcomp,
162
- text="X",
163
- variable=self.gui.duration_disable_var,
164
- command=self.cb_disable_duration,
165
- onvalue=True,
166
- offvalue=False,
167
- bootstyle="danger-round-toggle", # type: ignore
168
- )
169
-
170
- self.size_help_btn = self.add_help_btn(self.gui.help["comp"]["size"])
171
- self.size_lbl = Label(self.frame_advcomp, text="Maximum file size (bytes)")
172
- self.img_size_max_lbl = Label(self.frame_advcomp, text="Img:")
173
- self.img_size_max_entry = Entry(
174
- self.frame_advcomp, textvariable=self.gui.img_size_max_var, width=8
175
- )
176
- self.img_size_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
177
- self.vid_size_max_lbl = Label(self.frame_advcomp, text="Vid:")
178
- self.vid_size_max_entry = Entry(
179
- self.frame_advcomp, textvariable=self.gui.vid_size_max_var, width=8
180
- )
181
- self.vid_size_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
182
- self.size_disable_cbox = Checkbutton(
183
- self.frame_advcomp,
184
- text="X",
185
- variable=self.gui.size_disable_var,
186
- command=self.cb_disable_size,
187
- onvalue=True,
188
- offvalue=False,
189
- bootstyle="danger-round-toggle", # type: ignore
190
- )
191
-
192
- self.format_help_btn = self.add_help_btn(self.gui.help["comp"]["format"])
193
- self.format_lbl = Label(self.frame_advcomp, text="File format")
194
- self.img_format_lbl = Label(self.frame_advcomp, text="Img:")
195
- self.img_format_entry = Entry(
196
- self.frame_advcomp, textvariable=self.gui.img_format_var, width=8
197
- )
198
- self.img_format_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
199
- self.vid_format_lbl = Label(self.frame_advcomp, text="Vid:")
200
- self.vid_format_entry = Entry(
201
- self.frame_advcomp, textvariable=self.gui.vid_format_var, width=8
202
- )
203
- self.vid_format_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
204
-
205
- self.power_help_btn1 = self.add_help_btn(self.gui.help["comp"]["fps_power"])
206
- self.power_lbl1 = Label(self.frame_advcomp, text="Power (Importance)")
207
- self.fps_power_lbl = Label(self.frame_advcomp, text="FPS:")
208
- self.fps_power_entry = Entry(
209
- self.frame_advcomp, textvariable=self.gui.fps_power_var, width=8
210
- )
211
- self.fps_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
212
- self.res_power_lbl = Label(self.frame_advcomp, text="Res:")
213
- self.res_power_entry = Entry(
214
- self.frame_advcomp, textvariable=self.gui.res_power_var, width=8
215
- )
216
- self.res_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
217
-
218
- self.power_help_btn2 = self.add_help_btn(self.gui.help["comp"]["fps_power"])
219
- self.power_lbl2 = Label(self.frame_advcomp, text="Power (Importance)")
220
- self.quality_power_lbl = Label(self.frame_advcomp, text="Quality:")
221
- self.quality_power_entry = Entry(
222
- self.frame_advcomp, textvariable=self.gui.quality_power_var, width=8
223
- )
224
- self.quality_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
225
- self.color_power_lbl = Label(self.frame_advcomp, text="Color:")
226
- self.color_power_entry = Entry(
227
- self.frame_advcomp, textvariable=self.gui.color_power_var, width=8
228
- )
229
- self.color_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
230
-
231
- self.padding_percent_help_btn = self.add_help_btn(
232
- self.gui.help["comp"]["padding_percent"]
233
- )
234
- self.padding_percent_lbl1 = Label(self.frame_advcomp, text="Padding")
235
- self.padding_percent_lbl2 = Label(self.frame_advcomp, text="0-100%")
236
- self.padding_percent_entry = Entry(
237
- self.frame_advcomp, textvariable=self.gui.padding_percent_var, width=8
238
- )
239
- self.padding_percent_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
240
-
241
- self.fake_vid_help_btn = self.add_help_btn(self.gui.help["comp"]["fake_vid"])
242
- self.fake_vid_lbl = Label(
243
- self.frame_advcomp, text="Convert (faking) image to video"
244
- )
245
- self.fake_vid_cbox = Checkbutton(
246
- self.frame_advcomp,
247
- variable=self.gui.fake_vid_var,
248
- onvalue=True,
249
- offvalue=False,
250
- bootstyle="success-round-toggle", # type: ignore
251
- )
252
-
253
- self.bg_color_help_btn = self.add_help_btn(self.gui.help["comp"]["bg_color"])
254
- self.bg_color_lbl = Label(self.frame_advcomp, text="Background color")
255
- self.bg_color_entry = Entry(
256
- self.frame_advcomp, textvariable=self.gui.bg_color_var, width=8
257
- )
258
- self.bg_color_btn = Button(
259
- self.frame_advcomp,
260
- text="Set",
261
- command=self.cb_bg_color,
262
- bootstyle="secondary", # type: ignore
263
- )
264
-
265
- self.scale_filter_help_btn = self.add_help_btn(
266
- self.gui.help["comp"]["scale_filter"]
267
- )
268
- self.scale_filter_lbl = Label(self.frame_advcomp, text="Scale filter")
269
- self.scale_filter_opt = OptionMenu(
270
- self.frame_advcomp,
271
- self.gui.scale_filter_var,
272
- self.gui.scale_filter_var.get(),
273
- "nearest",
274
- "box",
275
- "bilinear",
276
- "hamming",
277
- "bicubic",
278
- "lanczos",
279
- bootstyle="secondary", # type: ignore
280
- )
281
-
282
- self.quantize_method_help_btn = self.add_help_btn(
283
- self.gui.help["comp"]["quantize_method"]
284
- )
285
- self.quantize_method_lbl = Label(self.frame_advcomp, text="Quantize method")
286
- self.quantize_method_opt = OptionMenu(
287
- self.frame_advcomp,
288
- self.gui.quantize_method_var,
289
- self.gui.quantize_method_var.get(),
290
- "imagequant",
291
- "fastoctree",
292
- "none",
293
- bootstyle="secondary", # type: ignore
294
- )
295
-
296
- self.cache_dir_help_btn = self.add_help_btn(self.gui.help["comp"]["cache_dir"])
297
- self.cache_dir_lbl = Label(self.frame_advcomp, text="Custom cache directory")
298
- self.cache_dir_entry = Entry(
299
- self.frame_advcomp, textvariable=self.gui.cache_dir_var, width=30
300
- )
301
- self.cache_dir_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
302
-
303
- self.default_emoji_help_btn = self.add_help_btn(
304
- self.gui.help["comp"]["default_emoji"]
305
- )
306
- self.default_emoji_lbl = Label(self.frame_advcomp, text="Default emoji")
307
- self.im: Image.Image = Image.new("RGBA", (32, 32), (255, 255, 255, 0))
308
- self.ph_im = ImageTk.PhotoImage(self.im)
309
- self.default_emoji_dsp = Label(self.frame_advcomp, image=self.ph_im)
310
-
311
- r = 0
312
- self.fps_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
313
- self.fps_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
314
- self.fps_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
315
- self.fps_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
316
- self.fps_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
317
- self.fps_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
318
- self.fps_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
319
- r += 1
320
- self.res_w_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
321
- self.res_w_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
322
- self.res_w_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
323
- self.res_w_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
324
- self.res_w_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
325
- self.res_w_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
326
- self.res_w_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
327
- r += 1
328
- self.res_h_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
329
- self.res_h_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
330
- self.res_h_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
331
- self.res_h_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
332
- self.res_h_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
333
- self.res_h_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
334
- self.res_h_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
335
- r += 1
336
- self.quality_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
337
- self.quality_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
338
- self.quality_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
339
- self.quality_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
340
- self.quality_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
341
- self.quality_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
342
- self.quality_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
343
- r += 1
344
- self.color_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
345
- self.color_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
346
- self.color_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
347
- self.color_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
348
- self.color_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
349
- self.color_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
350
- self.color_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
351
- r += 1
352
- self.duration_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
353
- self.duration_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
354
- self.duration_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
355
- self.duration_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
356
- self.duration_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
357
- self.duration_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
358
- self.duration_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
359
- r += 1
360
- self.size_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
361
- self.size_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
362
- self.img_size_max_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
363
- self.img_size_max_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
364
- self.vid_size_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
365
- self.vid_size_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
366
- self.size_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
367
- r += 1
368
- self.format_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
369
- self.format_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
370
- self.img_format_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
371
- self.img_format_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
372
- self.vid_format_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
373
- self.vid_format_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
374
- r += 1
375
- self.power_help_btn1.grid(column=0, row=r, sticky="w", padx=3, pady=3)
376
- self.power_lbl1.grid(column=1, row=r, sticky="w", padx=3, pady=3)
377
- self.fps_power_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
378
- self.fps_power_entry.grid(column=3, row=r, sticky="w", padx=3, pady=3)
379
- self.res_power_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
380
- self.res_power_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
381
- r += 1
382
- self.power_help_btn2.grid(column=0, row=r, sticky="w", padx=3, pady=3)
383
- self.power_lbl2.grid(column=1, row=r, sticky="w", padx=3, pady=3)
384
- self.quality_power_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
385
- self.quality_power_entry.grid(column=3, row=r, sticky="w", padx=3, pady=3)
386
- self.color_power_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
387
- self.color_power_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
388
- r += 1
389
- self.padding_percent_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
390
- self.padding_percent_lbl1.grid(column=1, row=r, sticky="w", padx=3, pady=3)
391
- self.padding_percent_lbl2.grid(column=2, row=r, sticky="w", padx=3, pady=3)
392
- self.padding_percent_entry.grid(column=3, row=r, sticky="w", padx=3, pady=3)
393
- r += 1
394
- self.fake_vid_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
395
- self.fake_vid_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
396
- self.fake_vid_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
397
- r += 1
398
- self.bg_color_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
399
- self.bg_color_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
400
- self.bg_color_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
401
- self.bg_color_btn.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
402
- r += 1
403
- self.scale_filter_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
404
- self.scale_filter_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
405
- self.scale_filter_opt.grid(
406
- column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
407
- )
408
- r += 1
409
- self.quantize_method_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
410
- self.quantize_method_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
411
- self.quantize_method_opt.grid(
412
- column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
413
- )
414
- r += 1
415
- self.cache_dir_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
416
- self.cache_dir_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
417
- self.cache_dir_entry.grid(
418
- column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
419
- )
420
- r += 1
421
- self.default_emoji_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
422
- self.default_emoji_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
423
- self.default_emoji_dsp.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
424
-
425
- # https://stackoverflow.com/questions/43731784/tkinter-canvas-scrollbar-with-grid
426
- # Create a frame for the canvas with non-zero row&column weights
427
- self.frame_emoji_canvas.grid_rowconfigure(0, weight=1)
428
- self.frame_emoji_canvas.grid_columnconfigure(0, weight=1)
429
- # Set grid_propagate to False to allow buttons resizing later
430
- self.frame_emoji_canvas.grid_propagate(False)
431
-
432
- self.frame_advcomp.grid(column=0, row=0, sticky="news", padx=3, pady=3)
433
- self.frame_emoji_search.grid(column=0, row=1, sticky="news", padx=3, pady=3)
434
- self.frame_emoji_canvas.grid(column=0, row=2, sticky="news", padx=3, pady=3)
435
-
436
- self.categories_lbl = Label(
437
- self.frame_emoji_search,
438
- text="Category",
439
- width=15,
440
- justify="left",
441
- anchor="w",
442
- )
443
- self.categories_var = StringVar(self.scrollable_frame)
444
- self.categories_var.set("Smileys & Emotion")
445
- self.categories_opt = OptionMenu(
446
- self.frame_emoji_search,
447
- self.categories_var,
448
- "Smileys & Emotion",
449
- *self.categories,
450
- command=self.render_emoji_list,
451
- bootstyle="secondary", # type: ignore
452
- )
453
- self.categories_opt.config(width=30)
454
-
455
- self.search_lbl = Label(self.frame_emoji_search, text="Search")
456
- self.search_var = StringVar(self.frame_emoji_search)
457
- self.search_var.trace_add("write", self.render_emoji_list)
458
- self.search_entry = Entry(self.frame_emoji_search, textvariable=self.search_var)
459
- self.search_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
460
-
461
- self.categories_lbl.grid(column=0, row=0, sticky="nsw", padx=3, pady=3)
462
- self.categories_opt.grid(column=1, row=0, sticky="news", padx=3, pady=3)
463
- self.search_lbl.grid(column=0, row=1, sticky="nsw", padx=3, pady=3)
464
- self.search_entry.grid(column=1, row=1, sticky="news", padx=3, pady=3)
465
-
466
- # Add a canvas in frame_emoji_canvas
467
- self.emoji_canvas = Canvas(self.frame_emoji_canvas)
468
- self.emoji_canvas.grid(row=0, column=0, sticky="news")
469
-
470
- # Link a scrollbar to the canvas
471
- self.vsb = Scrollbar(
472
- self.frame_emoji_canvas,
473
- orient="vertical",
474
- command=self.emoji_canvas.yview, # type: ignore
475
- )
476
- self.vsb.grid(row=0, column=1, sticky="ns")
477
- self.emoji_canvas.configure(yscrollcommand=self.vsb.set)
478
-
479
- self.frame_buttons = Frame(self.emoji_canvas)
480
- self.emoji_canvas.create_window((0, 0), window=self.frame_buttons, anchor="nw")
481
-
482
- self.render_emoji_list()
483
-
484
- self.set_emoji_btn()
485
- self.cb_disable_fps()
486
- self.cb_disable_res_w()
487
- self.cb_disable_res_h()
488
- self.cb_disable_quality()
489
- self.cb_disable_color()
490
- self.cb_disable_duration()
491
- self.cb_disable_size()
492
- self.cb_disable_format()
493
- self.cb_disable_fake_vid()
494
-
495
- GUIUtils.finalize_window(self)
496
-
497
- def cb_disable_fps(self, *_: Any) -> None:
498
- if self.gui.fps_disable_var.get() is True:
499
- state = "disabled"
500
- else:
501
- state = "normal"
502
-
503
- self.fps_min_entry.config(state=state)
504
- self.fps_max_entry.config(state=state)
505
-
506
- def cb_disable_res_w(self, *_: Any) -> None:
507
- if self.gui.res_w_disable_var.get() is True:
508
- state = "disabled"
509
- else:
510
- state = "normal"
511
-
512
- self.res_w_min_entry.config(state=state)
513
- self.res_w_max_entry.config(state=state)
514
-
515
- def cb_disable_res_h(self, *_: Any) -> None:
516
- if self.gui.res_h_disable_var.get() is True:
517
- state = "disabled"
518
- else:
519
- state = "normal"
520
-
521
- self.res_h_min_entry.config(state=state)
522
- self.res_h_max_entry.config(state=state)
523
-
524
- def cb_disable_quality(self, *_: Any) -> None:
525
- if self.gui.quality_disable_var.get() is True:
526
- state = "disabled"
527
- else:
528
- state = "normal"
529
-
530
- self.quality_min_entry.config(state=state)
531
- self.quality_max_entry.config(state=state)
532
-
533
- def cb_disable_color(self, *_: Any) -> None:
534
- if self.gui.color_disable_var.get() is True:
535
- state = "disabled"
536
- else:
537
- state = "normal"
538
-
539
- self.color_min_entry.config(state=state)
540
- self.color_max_entry.config(state=state)
541
-
542
- def cb_disable_duration(self, *_: Any) -> None:
543
- if (
544
- self.gui.duration_disable_var.get() is True
545
- or self.gui.comp_preset_var.get() == "auto"
546
- ):
547
- state = "disabled"
548
- else:
549
- state = "normal"
550
-
551
- self.duration_min_entry.config(state=state)
552
- self.duration_max_entry.config(state=state)
553
-
554
- def cb_disable_size(self, *_: Any) -> None:
555
- if (
556
- self.gui.size_disable_var.get() is True
557
- or self.gui.comp_preset_var.get() == "auto"
558
- ):
559
- state = "disabled"
560
- else:
561
- state = "normal"
562
-
563
- self.img_size_max_entry.config(state=state)
564
- self.vid_size_max_entry.config(state=state)
565
-
566
- def cb_disable_format(self, *_: Any) -> None:
567
- if self.gui.comp_preset_var.get() == "auto":
568
- state = "disabled"
569
- else:
570
- state = "normal"
571
-
572
- self.img_format_entry.config(state=state)
573
- self.vid_format_entry.config(state=state)
574
-
575
- def cb_disable_fake_vid(self, *_: Any) -> None:
576
- if self.gui.comp_preset_var.get() == "auto":
577
- state = "disabled"
578
- else:
579
- state = "normal"
580
-
581
- self.fake_vid_cbox.config(state=state)
582
-
583
- def cb_bg_color(self, *_: Any) -> None:
584
- color_init = "#" + self.gui.bg_color_var.get()[:6]
585
- color_init = (
586
- color_init if re.search(r"^#(?:[0-9a-fA-F]{3}){1,2}$", color_init) else None
587
- )
588
- color = colorchooser.askcolor(
589
- title="Choose color",
590
- color=color_init,
591
- parent=self,
592
- )[1]
593
- if color:
594
- self.gui.bg_color_var.set(color.replace("#", "") + "00")
595
- self.lift()
596
- self.attributes("-topmost", True) # type: ignore
597
- self.attributes("-topmost", False) # type: ignore
598
-
599
- def cb_bound_to_mousewheel(self, event: Any) -> None:
600
- for i in self.mousewheel:
601
- self.emoji_canvas.bind_all(i, self.cb_on_mousewheel)
602
-
603
- def cb_unbound_to_mousewheel(self, event: Any) -> None:
604
- for i in self.mousewheel:
605
- self.emoji_canvas.unbind_all(i)
606
-
607
- def cb_on_mousewheel(self, event: Any) -> None:
608
- self.emoji_canvas.yview_scroll(
609
- int(-1 * (event.delta / self.delta_divide)), "units"
610
- ) # type: ignore
611
-
612
- def cb_set_emoji(self, emoji: str) -> None:
613
- self.gui.default_emoji_var.set(emoji)
614
- self.set_emoji_btn()
615
-
616
- def add_help_btn(self, msg: str) -> Button:
617
- cb_msg_block_adv_comp_win = partial(self.gui.cb_msg_block, parent=self)
618
-
619
- return Button(
620
- self.frame_advcomp,
621
- text="?",
622
- width=1,
623
- command=lambda: cb_msg_block_adv_comp_win(msg),
624
- bootstyle="secondary", # type: ignore
625
- )
626
-
627
- def set_emoji_btn(self) -> None:
628
- self.im = Image.new("RGBA", (128, 128), (255, 255, 255, 0))
629
- ImageDraw.Draw(self.im).text( # type: ignore
630
- (0, 0),
631
- self.gui.default_emoji_var.get(),
632
- embedded_color=True,
633
- font=self.gui.emoji_font,
634
- ) # type: ignore
635
- self.im = self.im.resize((32, 32))
636
- self.ph_im = ImageTk.PhotoImage(self.im)
637
- self.default_emoji_dsp.config(image=self.ph_im)
638
-
639
- def render_emoji_list(self, *_: Any) -> None:
640
- category = self.categories_var.get()
641
-
642
- for emoji_btn, ph_im in self.emoji_btns:
643
- emoji_btn.destroy()
644
- del ph_im
645
-
646
- column = 0
647
- row = 0
648
-
649
- self.emoji_btns = []
650
- for entry in self.gui.emoji_list:
651
- # Filtering
652
- search_term = self.search_var.get().lower()
653
- emoji = entry["emoji"]
654
- keywords = entry["aliases"] + entry["tags"] + [emoji]
655
- if search_term == "":
656
- if entry["category"] != category:
657
- continue
658
- else:
659
- ok = False
660
-
661
- if search_term in keywords:
662
- ok = True
663
- elif len(search_term) >= 3:
664
- for i in keywords:
665
- if search_term in i:
666
- ok = True
667
-
668
- if ok is False:
669
- continue
670
-
671
- im = Image.new("RGBA", (196, 196), (255, 255, 255, 0))
672
- ImageDraw.Draw(im).text( # type: ignore
673
- (16, 16), emoji, embedded_color=True, font=self.gui.emoji_font
674
- ) # type: ignore
675
- im = im.resize((32, 32))
676
- ph_im = ImageTk.PhotoImage(im)
677
-
678
- button = Button(
679
- self.frame_buttons,
680
- command=lambda i=emoji: self.cb_set_emoji(i), # type: ignore
681
- bootstyle="dark", # type: ignore
682
- )
683
- button.config(image=ph_im)
684
- button.grid(column=column, row=row)
685
-
686
- column += 1
687
-
688
- if column == self.emoji_column_per_row:
689
- column = 0
690
- row += 1
691
-
692
- self.emoji_btns.append((button, ph_im))
693
-
694
- # Update buttons frames idle tasks to let tkinter calculate buttons sizes
695
- self.frame_buttons.update_idletasks()
696
-
697
- # Resize the canvas frame to show specified number of buttons and the scrollbar
698
- if len(self.emoji_btns) > 0:
699
- in_view_columns_width = (
700
- self.emoji_btns[0][0].winfo_width() * self.emoji_column_per_row
701
- )
702
- in_view_rows_height = (
703
- self.emoji_btns[0][0].winfo_height() * self.emoji_visible_rows
704
- )
705
- self.frame_emoji_canvas.config(
706
- width=in_view_columns_width + self.vsb.winfo_width(),
707
- height=in_view_rows_height,
708
- )
709
-
710
- # Set the canvas scrolling region
711
- self.emoji_canvas.config(scrollregion=self.emoji_canvas.bbox("all"))
712
-
713
- # https://stackoverflow.com/questions/17355902/tkinter-binding-mousewheel-to-scrollbar
714
- self.emoji_canvas.bind("<Enter>", self.cb_bound_to_mousewheel)
715
- self.emoji_canvas.bind("<Leave>", self.cb_unbound_to_mousewheel)
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import re
5
+ from tkinter import colorchooser
6
+ from typing import Any, List, Tuple
7
+
8
+ from PIL import Image, ImageDraw, ImageTk
9
+ from ttkbootstrap import Button, Canvas, Checkbutton, Entry, Frame, Label, LabelFrame, OptionMenu, Scrollbar, StringVar # type: ignore
10
+
11
+ from sticker_convert.gui_components.frames.right_clicker import RightClicker
12
+ from sticker_convert.gui_components.gui_utils import GUIUtils
13
+ from sticker_convert.gui_components.windows.base_window import BaseWindow
14
+ from sticker_convert.utils.translate import I
15
+
16
+
17
+ class AdvancedCompressionWindow(BaseWindow):
18
+ emoji_visible_rows = 5
19
+ emoji_btns: List[Tuple[Button, ImageTk.PhotoImage]] = []
20
+
21
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
22
+ super().__init__(*args, **kwargs)
23
+ self.categories = list({entry["category"] for entry in self.gui.emoji_list})
24
+
25
+ self.title(I("Advanced compression options"))
26
+
27
+ self.frame_advcomp = LabelFrame(
28
+ self.scrollable_frame, text=I("Advanced compression option")
29
+ )
30
+ self.frame_emoji_search = LabelFrame(
31
+ self.scrollable_frame, text=I("Setting default emoji")
32
+ )
33
+ self.frame_emoji_canvas = Frame(self.scrollable_frame)
34
+
35
+ self.frame_advcomp.grid_columnconfigure(6, weight=1)
36
+
37
+ self.fps_help_btn = self.add_help_btn(self.gui.help["comp"]["fps"])
38
+ self.fps_lbl = Label(self.frame_advcomp, text=I("Output FPS"))
39
+ self.fps_min_lbl = Label(self.frame_advcomp, text=I("Min:"))
40
+ self.fps_min_entry = Entry(
41
+ self.frame_advcomp, textvariable=self.gui.fps_min_var, width=8
42
+ )
43
+ self.fps_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
44
+ self.fps_max_lbl = Label(self.frame_advcomp, text=I("Max:"))
45
+ self.fps_max_entry = Entry(
46
+ self.frame_advcomp, textvariable=self.gui.fps_max_var, width=8
47
+ )
48
+ self.fps_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
49
+ self.fps_disable_cbox = Checkbutton(
50
+ self.frame_advcomp,
51
+ text="X",
52
+ variable=self.gui.fps_disable_var,
53
+ command=self.cb_disable_fps,
54
+ onvalue=True,
55
+ offvalue=False,
56
+ bootstyle="danger-round-toggle", # type: ignore
57
+ )
58
+
59
+ self.res_w_help_btn = self.add_help_btn(self.gui.help["comp"]["res"])
60
+ self.res_w_lbl = Label(self.frame_advcomp, text=I("Output resolution (Width)"))
61
+ self.res_w_min_lbl = Label(self.frame_advcomp, text=I("Min:"))
62
+ self.res_w_min_entry = Entry(
63
+ self.frame_advcomp, textvariable=self.gui.res_w_min_var, width=8
64
+ )
65
+ self.res_w_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
66
+ self.res_w_max_lbl = Label(self.frame_advcomp, text=I("Max:"))
67
+ self.res_w_max_entry = Entry(
68
+ self.frame_advcomp, textvariable=self.gui.res_w_max_var, width=8
69
+ )
70
+ self.res_w_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
71
+ self.res_w_disable_cbox = Checkbutton(
72
+ self.frame_advcomp,
73
+ text="X",
74
+ variable=self.gui.res_w_disable_var,
75
+ command=self.cb_disable_res_w,
76
+ onvalue=True,
77
+ offvalue=False,
78
+ bootstyle="danger-round-toggle", # type: ignore
79
+ )
80
+
81
+ self.res_h_help_btn = self.add_help_btn(self.gui.help["comp"]["res"])
82
+ self.res_h_lbl = Label(self.frame_advcomp, text=I("Output resolution (Height)"))
83
+ self.res_h_min_lbl = Label(self.frame_advcomp, text=I("Min:"))
84
+ self.res_h_min_entry = Entry(
85
+ self.frame_advcomp, textvariable=self.gui.res_h_min_var, width=8
86
+ )
87
+ self.res_h_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
88
+ self.res_h_max_lbl = Label(self.frame_advcomp, text=I("Max:"))
89
+ self.res_h_max_entry = Entry(
90
+ self.frame_advcomp, textvariable=self.gui.res_h_max_var, width=8
91
+ )
92
+ self.res_h_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
93
+ self.res_h_disable_cbox = Checkbutton(
94
+ self.frame_advcomp,
95
+ text="X",
96
+ variable=self.gui.res_h_disable_var,
97
+ command=self.cb_disable_res_h,
98
+ onvalue=True,
99
+ offvalue=False,
100
+ bootstyle="danger-round-toggle", # type: ignore
101
+ )
102
+
103
+ self.res_snap_pow2_help_btn = self.add_help_btn(
104
+ self.gui.help["comp"]["res_snap_pow2"]
105
+ )
106
+ self.res_snap_pow2_lbl = Label(
107
+ self.frame_advcomp, text=I("Snap resolution to power of 2")
108
+ )
109
+ self.res_snap_pow2_cbox = Checkbutton(
110
+ self.frame_advcomp,
111
+ variable=self.gui.res_snap_pow2_var,
112
+ onvalue=True,
113
+ offvalue=False,
114
+ bootstyle="success-round-toggle", # type: ignore
115
+ )
116
+
117
+ self.quality_help_btn = self.add_help_btn(self.gui.help["comp"]["quality"])
118
+ self.quality_lbl = Label(self.frame_advcomp, text=I("Output quality (0-100)"))
119
+ self.quality_min_lbl = Label(self.frame_advcomp, text=I("Min:"))
120
+ self.quality_min_entry = Entry(
121
+ self.frame_advcomp, textvariable=self.gui.quality_min_var, width=8
122
+ )
123
+ self.quality_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
124
+ self.quality_max_lbl = Label(self.frame_advcomp, text=I("Max:"))
125
+ self.quality_max_entry = Entry(
126
+ self.frame_advcomp, textvariable=self.gui.quality_max_var, width=8
127
+ )
128
+ self.quality_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
129
+ self.quality_disable_cbox = Checkbutton(
130
+ self.frame_advcomp,
131
+ text="X",
132
+ variable=self.gui.quality_disable_var,
133
+ command=self.cb_disable_quality,
134
+ onvalue=True,
135
+ offvalue=False,
136
+ bootstyle="danger-round-toggle", # type: ignore
137
+ )
138
+
139
+ self.color_help_btn = self.add_help_btn(self.gui.help["comp"]["color"])
140
+ self.color_lbl = Label(self.frame_advcomp, text=I("Colors (0-256)"))
141
+ self.color_min_lbl = Label(self.frame_advcomp, text=I("Min:"))
142
+ self.color_min_entry = Entry(
143
+ self.frame_advcomp, textvariable=self.gui.color_min_var, width=8
144
+ )
145
+ self.color_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
146
+ self.color_max_lbl = Label(self.frame_advcomp, text=I("Max:"))
147
+ self.color_max_entry = Entry(
148
+ self.frame_advcomp, textvariable=self.gui.color_max_var, width=8
149
+ )
150
+ self.color_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
151
+ self.color_disable_cbox = Checkbutton(
152
+ self.frame_advcomp,
153
+ text="X",
154
+ variable=self.gui.color_disable_var,
155
+ command=self.cb_disable_color,
156
+ onvalue=True,
157
+ offvalue=False,
158
+ bootstyle="danger-round-toggle", # type: ignore
159
+ )
160
+
161
+ self.duration_help_btn = self.add_help_btn(self.gui.help["comp"]["duration"])
162
+ self.duration_lbl = Label(self.frame_advcomp, text=I("Duration (Miliseconds)"))
163
+ self.duration_min_lbl = Label(self.frame_advcomp, text=I("Min:"))
164
+ self.duration_min_entry = Entry(
165
+ self.frame_advcomp, textvariable=self.gui.duration_min_var, width=8
166
+ )
167
+ self.duration_min_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
168
+ self.duration_max_lbl = Label(self.frame_advcomp, text=I("Max:"))
169
+ self.duration_max_entry = Entry(
170
+ self.frame_advcomp, textvariable=self.gui.duration_max_var, width=8
171
+ )
172
+ self.duration_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
173
+ self.duration_disable_cbox = Checkbutton(
174
+ self.frame_advcomp,
175
+ text="X",
176
+ variable=self.gui.duration_disable_var,
177
+ command=self.cb_disable_duration,
178
+ onvalue=True,
179
+ offvalue=False,
180
+ bootstyle="danger-round-toggle", # type: ignore
181
+ )
182
+
183
+ self.size_help_btn = self.add_help_btn(self.gui.help["comp"]["size"])
184
+ self.size_lbl = Label(self.frame_advcomp, text=I("Maximum file size (bytes)"))
185
+ self.img_size_max_lbl = Label(self.frame_advcomp, text=I("Img:"))
186
+ self.img_size_max_entry = Entry(
187
+ self.frame_advcomp, textvariable=self.gui.img_size_max_var, width=8
188
+ )
189
+ self.img_size_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
190
+ self.vid_size_max_lbl = Label(self.frame_advcomp, text=I("Vid:"))
191
+ self.vid_size_max_entry = Entry(
192
+ self.frame_advcomp, textvariable=self.gui.vid_size_max_var, width=8
193
+ )
194
+ self.vid_size_max_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
195
+ self.size_disable_cbox = Checkbutton(
196
+ self.frame_advcomp,
197
+ text="X",
198
+ variable=self.gui.size_disable_var,
199
+ command=self.cb_disable_size,
200
+ onvalue=True,
201
+ offvalue=False,
202
+ bootstyle="danger-round-toggle", # type: ignore
203
+ )
204
+
205
+ self.format_help_btn = self.add_help_btn(self.gui.help["comp"]["format"])
206
+ self.format_lbl = Label(self.frame_advcomp, text=I("File format"))
207
+ self.img_format_lbl = Label(self.frame_advcomp, text="Img:")
208
+ self.img_format_entry = Entry(
209
+ self.frame_advcomp, textvariable=self.gui.img_format_var, width=8
210
+ )
211
+ self.img_format_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
212
+ self.vid_format_lbl = Label(self.frame_advcomp, text=I("Vid:"))
213
+ self.vid_format_entry = Entry(
214
+ self.frame_advcomp, textvariable=self.gui.vid_format_var, width=8
215
+ )
216
+ self.vid_format_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
217
+
218
+ self.power_help_btn1 = self.add_help_btn(self.gui.help["comp"]["fps_power"])
219
+ self.power_lbl1 = Label(self.frame_advcomp, text=I("Power (Importance)"))
220
+ self.fps_power_lbl = Label(self.frame_advcomp, text=I("FPS:"))
221
+ self.fps_power_entry = Entry(
222
+ self.frame_advcomp, textvariable=self.gui.fps_power_var, width=8
223
+ )
224
+ self.fps_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
225
+ self.res_power_lbl = Label(self.frame_advcomp, text=I("Res:"))
226
+ self.res_power_entry = Entry(
227
+ self.frame_advcomp, textvariable=self.gui.res_power_var, width=8
228
+ )
229
+ self.res_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
230
+
231
+ self.power_help_btn2 = self.add_help_btn(self.gui.help["comp"]["fps_power"])
232
+ self.power_lbl2 = Label(self.frame_advcomp, text="Power (Importance)")
233
+ self.quality_power_lbl = Label(self.frame_advcomp, text="Quality:")
234
+ self.quality_power_entry = Entry(
235
+ self.frame_advcomp, textvariable=self.gui.quality_power_var, width=8
236
+ )
237
+ self.quality_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
238
+ self.color_power_lbl = Label(self.frame_advcomp, text=I("Color:"))
239
+ self.color_power_entry = Entry(
240
+ self.frame_advcomp, textvariable=self.gui.color_power_var, width=8
241
+ )
242
+ self.color_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
243
+
244
+ self.padding_percent_help_btn = self.add_help_btn(
245
+ self.gui.help["comp"]["padding_percent"]
246
+ )
247
+ self.padding_percent_lbl1 = Label(self.frame_advcomp, text=I("Padding"))
248
+ self.padding_percent_lbl2 = Label(self.frame_advcomp, text=I("0-100%"))
249
+ self.padding_percent_entry = Entry(
250
+ self.frame_advcomp, textvariable=self.gui.padding_percent_var, width=8
251
+ )
252
+ self.padding_percent_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
253
+
254
+ self.fake_vid_help_btn = self.add_help_btn(self.gui.help["comp"]["fake_vid"])
255
+ self.fake_vid_lbl = Label(
256
+ self.frame_advcomp, text=I("Convert (faking) image to video")
257
+ )
258
+ self.fake_vid_cbox = Checkbutton(
259
+ self.frame_advcomp,
260
+ variable=self.gui.fake_vid_var,
261
+ onvalue=True,
262
+ offvalue=False,
263
+ bootstyle="success-round-toggle", # type: ignore
264
+ )
265
+
266
+ self.bg_color_help_btn = self.add_help_btn(self.gui.help["comp"]["bg_color"])
267
+ self.bg_color_lbl = Label(self.frame_advcomp, text=I("Background color"))
268
+ self.bg_color_entry = Entry(
269
+ self.frame_advcomp, textvariable=self.gui.bg_color_var, width=8
270
+ )
271
+ self.bg_color_btn = Button(
272
+ self.frame_advcomp,
273
+ text=I("Set"),
274
+ command=self.cb_bg_color,
275
+ bootstyle="secondary", # type: ignore
276
+ )
277
+
278
+ self.scale_filter_help_btn = self.add_help_btn(
279
+ self.gui.help["comp"]["scale_filter"]
280
+ )
281
+ self.scale_filter_lbl = Label(self.frame_advcomp, text=I("Scale filter"))
282
+ self.scale_filter_opt = OptionMenu(
283
+ self.frame_advcomp,
284
+ self.gui.scale_filter_var,
285
+ self.gui.scale_filter_var.get(),
286
+ "nearest",
287
+ "box",
288
+ "bilinear",
289
+ "hamming",
290
+ "bicubic",
291
+ "lanczos",
292
+ bootstyle="secondary", # type: ignore
293
+ )
294
+
295
+ self.quantize_method_help_btn = self.add_help_btn(
296
+ self.gui.help["comp"]["quantize_method"]
297
+ )
298
+ self.quantize_method_lbl = Label(self.frame_advcomp, text=I("Quantize method"))
299
+ self.quantize_method_opt = OptionMenu(
300
+ self.frame_advcomp,
301
+ self.gui.quantize_method_var,
302
+ self.gui.quantize_method_var.get(),
303
+ "imagequant",
304
+ "fastoctree",
305
+ "maxcoverage",
306
+ "mediancut",
307
+ "none",
308
+ bootstyle="secondary", # type: ignore
309
+ )
310
+
311
+ self.chromium_path_help_btn = self.add_help_btn(
312
+ self.gui.help["comp"]["chromium_path"]
313
+ )
314
+ self.chromium_path_lbl = Label(
315
+ self.frame_advcomp, text=I("Chromium/Chrome directory")
316
+ )
317
+ self.chromium_path_entry = Entry(
318
+ self.frame_advcomp, textvariable=self.gui.chromium_path_var, width=30
319
+ )
320
+ self.chromium_path_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
321
+
322
+ self.cache_dir_help_btn = self.add_help_btn(self.gui.help["comp"]["cache_dir"])
323
+ self.cache_dir_lbl = Label(self.frame_advcomp, text=I("Custom cache directory"))
324
+ self.cache_dir_entry = Entry(
325
+ self.frame_advcomp, textvariable=self.gui.cache_dir_var, width=30
326
+ )
327
+ self.cache_dir_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
328
+
329
+ self.default_emoji_help_btn = self.add_help_btn(
330
+ self.gui.help["comp"]["default_emoji"]
331
+ )
332
+ self.default_emoji_lbl = Label(self.frame_advcomp, text=I("Default emoji"))
333
+ self.im: Image.Image = Image.new("RGBA", (32, 32), (255, 255, 255, 0))
334
+ self.ph_im = ImageTk.PhotoImage(self.im)
335
+ self.default_emoji_dsp = Label(self.frame_advcomp, image=self.ph_im)
336
+
337
+ r = 0
338
+ self.fps_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
339
+ self.fps_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
340
+ self.fps_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
341
+ self.fps_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
342
+ self.fps_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
343
+ self.fps_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
344
+ self.fps_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
345
+ r += 1
346
+ self.res_w_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
347
+ self.res_w_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
348
+ self.res_w_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
349
+ self.res_w_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
350
+ self.res_w_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
351
+ self.res_w_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
352
+ self.res_w_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
353
+ r += 1
354
+ self.res_h_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
355
+ self.res_h_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
356
+ self.res_h_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
357
+ self.res_h_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
358
+ self.res_h_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
359
+ self.res_h_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
360
+ self.res_h_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
361
+ r += 1
362
+ self.res_snap_pow2_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
363
+ self.res_snap_pow2_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
364
+ self.res_snap_pow2_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
365
+ r += 1
366
+ self.quality_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
367
+ self.quality_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
368
+ self.quality_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
369
+ self.quality_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
370
+ self.quality_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
371
+ self.quality_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
372
+ self.quality_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
373
+ r += 1
374
+ self.color_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
375
+ self.color_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
376
+ self.color_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
377
+ self.color_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
378
+ self.color_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
379
+ self.color_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
380
+ self.color_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
381
+ r += 1
382
+ self.duration_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
383
+ self.duration_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
384
+ self.duration_min_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
385
+ self.duration_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
386
+ self.duration_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
387
+ self.duration_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
388
+ self.duration_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
389
+ r += 1
390
+ self.size_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
391
+ self.size_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
392
+ self.img_size_max_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
393
+ self.img_size_max_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
394
+ self.vid_size_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
395
+ self.vid_size_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
396
+ self.size_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
397
+ r += 1
398
+ self.format_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
399
+ self.format_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
400
+ self.img_format_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
401
+ self.img_format_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
402
+ self.vid_format_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
403
+ self.vid_format_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
404
+ r += 1
405
+ self.power_help_btn1.grid(column=0, row=r, sticky="w", padx=3, pady=3)
406
+ self.power_lbl1.grid(column=1, row=r, sticky="w", padx=3, pady=3)
407
+ self.fps_power_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
408
+ self.fps_power_entry.grid(column=3, row=r, sticky="w", padx=3, pady=3)
409
+ self.res_power_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
410
+ self.res_power_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
411
+ r += 1
412
+ self.power_help_btn2.grid(column=0, row=r, sticky="w", padx=3, pady=3)
413
+ self.power_lbl2.grid(column=1, row=r, sticky="w", padx=3, pady=3)
414
+ self.quality_power_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
415
+ self.quality_power_entry.grid(column=3, row=r, sticky="w", padx=3, pady=3)
416
+ self.color_power_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
417
+ self.color_power_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
418
+ r += 1
419
+ self.padding_percent_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
420
+ self.padding_percent_lbl1.grid(column=1, row=r, sticky="w", padx=3, pady=3)
421
+ self.padding_percent_lbl2.grid(column=2, row=r, sticky="w", padx=3, pady=3)
422
+ self.padding_percent_entry.grid(column=3, row=r, sticky="w", padx=3, pady=3)
423
+ r += 1
424
+ self.fake_vid_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
425
+ self.fake_vid_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
426
+ self.fake_vid_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
427
+ r += 1
428
+ self.bg_color_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
429
+ self.bg_color_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
430
+ self.bg_color_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
431
+ self.bg_color_btn.grid(column=6, row=r, sticky="w", padx=3, pady=3)
432
+ r += 1
433
+ self.scale_filter_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
434
+ self.scale_filter_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
435
+ self.scale_filter_opt.grid(
436
+ column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
437
+ )
438
+ r += 1
439
+ self.quantize_method_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
440
+ self.quantize_method_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
441
+ self.quantize_method_opt.grid(
442
+ column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
443
+ )
444
+ r += 1
445
+ self.chromium_path_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
446
+ self.chromium_path_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
447
+ self.chromium_path_entry.grid(
448
+ column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
449
+ )
450
+ r += 1
451
+ self.cache_dir_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
452
+ self.cache_dir_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
453
+ self.cache_dir_entry.grid(
454
+ column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
455
+ )
456
+ r += 1
457
+ self.default_emoji_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
458
+ self.default_emoji_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
459
+ self.default_emoji_dsp.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
460
+
461
+ # https://stackoverflow.com/questions/43731784/tkinter-canvas-scrollbar-with-grid
462
+ # Create a frame for the canvas with non-zero row&column weights
463
+ self.frame_emoji_canvas.grid_rowconfigure(0, weight=1)
464
+ self.frame_emoji_canvas.grid_columnconfigure(0, weight=1)
465
+ # Set grid_propagate to False to allow buttons resizing later
466
+ self.frame_emoji_canvas.grid_propagate(False)
467
+
468
+ self.frame_advcomp.grid(column=0, row=0, sticky="news", padx=3, pady=3)
469
+ self.frame_emoji_search.grid(column=0, row=1, sticky="news", padx=3, pady=3)
470
+ self.frame_emoji_canvas.grid(column=0, row=2, sticky="news", padx=3, pady=3)
471
+
472
+ self.categories_lbl = Label(
473
+ self.frame_emoji_search,
474
+ text=I("Category"),
475
+ width=15,
476
+ justify="left",
477
+ anchor="w",
478
+ )
479
+ self.categories_var = StringVar(self.scrollable_frame)
480
+ self.categories_var.set("Smileys & Emotion")
481
+ self.categories_opt = OptionMenu(
482
+ self.frame_emoji_search,
483
+ self.categories_var,
484
+ "Smileys & Emotion",
485
+ *self.categories,
486
+ command=self.render_emoji_list,
487
+ bootstyle="secondary", # type: ignore
488
+ )
489
+ self.categories_opt.config(width=30)
490
+
491
+ self.search_lbl = Label(self.frame_emoji_search, text=I("Search"))
492
+ self.search_var = StringVar(self.frame_emoji_search)
493
+ self.search_var.trace_add("write", self.render_emoji_list)
494
+ self.search_entry = Entry(self.frame_emoji_search, textvariable=self.search_var)
495
+ self.search_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
496
+
497
+ self.categories_lbl.grid(column=0, row=0, sticky="nsw", padx=3, pady=3)
498
+ self.categories_opt.grid(column=1, row=0, sticky="news", padx=3, pady=3)
499
+ self.search_lbl.grid(column=0, row=1, sticky="nsw", padx=3, pady=3)
500
+ self.search_entry.grid(column=1, row=1, sticky="news", padx=3, pady=3)
501
+
502
+ # Add a canvas in frame_emoji_canvas
503
+ self.emoji_canvas = Canvas(self.frame_emoji_canvas)
504
+ self.emoji_canvas.grid(row=0, column=0, sticky="news")
505
+
506
+ # Link a scrollbar to the canvas
507
+ self.vsb = Scrollbar(
508
+ self.frame_emoji_canvas,
509
+ orient="vertical",
510
+ command=self.emoji_canvas.yview, # type: ignore
511
+ )
512
+ self.vsb.grid(row=0, column=1, sticky="ns")
513
+ self.emoji_canvas.configure(yscrollcommand=self.vsb.set)
514
+
515
+ self.frame_buttons = Frame(self.emoji_canvas)
516
+ self.emoji_canvas.create_window((0, 0), window=self.frame_buttons, anchor="nw")
517
+
518
+ self.render_emoji_list()
519
+
520
+ self.set_emoji_btn()
521
+ self.cb_disable_fps()
522
+ self.cb_disable_res_w()
523
+ self.cb_disable_res_h()
524
+ self.cb_disable_quality()
525
+ self.cb_disable_color()
526
+ self.cb_disable_duration()
527
+ self.cb_disable_size()
528
+ self.cb_disable_format()
529
+ self.cb_disable_fake_vid()
530
+
531
+ GUIUtils.finalize_window(self)
532
+
533
+ def cb_disable_fps(self, *_: Any) -> None:
534
+ if self.gui.fps_disable_var.get() is True:
535
+ state = "disabled"
536
+ else:
537
+ state = "normal"
538
+
539
+ self.fps_min_entry.config(state=state)
540
+ self.fps_max_entry.config(state=state)
541
+
542
+ def cb_disable_res_w(self, *_: Any) -> None:
543
+ if self.gui.res_w_disable_var.get() is True:
544
+ state = "disabled"
545
+ else:
546
+ state = "normal"
547
+
548
+ self.res_w_min_entry.config(state=state)
549
+ self.res_w_max_entry.config(state=state)
550
+
551
+ def cb_disable_res_h(self, *_: Any) -> None:
552
+ if self.gui.res_h_disable_var.get() is True:
553
+ state = "disabled"
554
+ else:
555
+ state = "normal"
556
+
557
+ self.res_h_min_entry.config(state=state)
558
+ self.res_h_max_entry.config(state=state)
559
+
560
+ def cb_disable_quality(self, *_: Any) -> None:
561
+ if self.gui.quality_disable_var.get() is True:
562
+ state = "disabled"
563
+ else:
564
+ state = "normal"
565
+
566
+ self.quality_min_entry.config(state=state)
567
+ self.quality_max_entry.config(state=state)
568
+
569
+ def cb_disable_color(self, *_: Any) -> None:
570
+ if self.gui.color_disable_var.get() is True:
571
+ state = "disabled"
572
+ else:
573
+ state = "normal"
574
+
575
+ self.color_min_entry.config(state=state)
576
+ self.color_max_entry.config(state=state)
577
+
578
+ def cb_disable_duration(self, *_: Any) -> None:
579
+ if (
580
+ self.gui.duration_disable_var.get() is True
581
+ or self.gui.comp_preset_var.get() == "auto"
582
+ ):
583
+ state = "disabled"
584
+ else:
585
+ state = "normal"
586
+
587
+ self.duration_min_entry.config(state=state)
588
+ self.duration_max_entry.config(state=state)
589
+
590
+ def cb_disable_size(self, *_: Any) -> None:
591
+ if (
592
+ self.gui.size_disable_var.get() is True
593
+ or self.gui.comp_preset_var.get() == "auto"
594
+ ):
595
+ state = "disabled"
596
+ else:
597
+ state = "normal"
598
+
599
+ self.img_size_max_entry.config(state=state)
600
+ self.vid_size_max_entry.config(state=state)
601
+
602
+ def cb_disable_format(self, *_: Any) -> None:
603
+ if self.gui.comp_preset_var.get() == "auto":
604
+ state = "disabled"
605
+ else:
606
+ state = "normal"
607
+
608
+ self.img_format_entry.config(state=state)
609
+ self.vid_format_entry.config(state=state)
610
+
611
+ def cb_disable_fake_vid(self, *_: Any) -> None:
612
+ if self.gui.comp_preset_var.get() == "auto":
613
+ state = "disabled"
614
+ else:
615
+ state = "normal"
616
+
617
+ self.fake_vid_cbox.config(state=state)
618
+
619
+ def cb_bg_color(self, *_: Any) -> None:
620
+ color_init = "#" + self.gui.bg_color_var.get()[:6]
621
+ color_init = (
622
+ color_init if re.search(r"^#(?:[0-9a-fA-F]{3}){1,2}$", color_init) else None
623
+ )
624
+ color = colorchooser.askcolor(
625
+ title="Choose color",
626
+ color=color_init,
627
+ parent=self,
628
+ )[1]
629
+ if color:
630
+ self.gui.bg_color_var.set(color.replace("#", "") + "00")
631
+ self.lift() # type: ignore
632
+ self.attributes("-topmost", True) # type: ignore
633
+ self.attributes("-topmost", False) # type: ignore
634
+
635
+ def cb_bound_to_mousewheel(self, event: Any) -> None:
636
+ for i in self.mousewheel:
637
+ self.emoji_canvas.bind_all(i, self.cb_on_mousewheel)
638
+
639
+ def cb_unbound_to_mousewheel(self, event: Any) -> None:
640
+ for i in self.mousewheel:
641
+ self.emoji_canvas.unbind_all(i)
642
+
643
+ def cb_on_mousewheel(self, event: Any) -> None:
644
+ self.emoji_canvas.yview_scroll(
645
+ int(-1 * (event.delta / self.delta_divide)), "units"
646
+ ) # type: ignore
647
+
648
+ def cb_set_emoji(self, emoji: str) -> None:
649
+ self.gui.default_emoji_var.set(emoji)
650
+ self.set_emoji_btn()
651
+
652
+ def add_help_btn(self, msg: str) -> Button:
653
+ return Button(
654
+ self.frame_advcomp,
655
+ text="?",
656
+ width=1,
657
+ command=lambda: self.gui.cb.put(
658
+ ("msg_block", None, {"message": msg, "parent": self})
659
+ ),
660
+ bootstyle="secondary", # type: ignore
661
+ )
662
+
663
+ def set_emoji_btn(self) -> None:
664
+ self.im = Image.new("RGBA", (128, 128), (255, 255, 255, 0))
665
+ ImageDraw.Draw(self.im).text( # type: ignore
666
+ (0, 0),
667
+ self.gui.default_emoji_var.get(),
668
+ embedded_color=True,
669
+ font=self.gui.emoji_font,
670
+ ) # type: ignore
671
+ self.im = self.im.resize((32, 32))
672
+ self.ph_im = ImageTk.PhotoImage(self.im)
673
+ self.default_emoji_dsp.config(image=self.ph_im)
674
+
675
+ def render_emoji_list(self, *_: Any) -> None:
676
+ category = self.categories_var.get()
677
+
678
+ for emoji_btn, ph_im in self.emoji_btns:
679
+ emoji_btn.destroy()
680
+ del ph_im
681
+
682
+ column = 0
683
+ row = 0
684
+ emoji_column_per_row = 0
685
+
686
+ self.emoji_btns = []
687
+ for entry in self.gui.emoji_list:
688
+ # Filtering
689
+ search_term = self.search_var.get().lower()
690
+ emoji = entry["emoji"]
691
+ keywords = entry["aliases"] + entry["tags"] + [emoji]
692
+ if search_term == "":
693
+ if entry["category"] != category:
694
+ continue
695
+ else:
696
+ ok = False
697
+
698
+ if search_term in keywords:
699
+ ok = True
700
+ elif len(search_term) >= 3:
701
+ for i in keywords:
702
+ if search_term in i:
703
+ ok = True
704
+
705
+ if ok is False:
706
+ continue
707
+
708
+ im = Image.new("RGBA", (196, 196), (255, 255, 255, 0))
709
+ ImageDraw.Draw(im).text( # type: ignore
710
+ (16, 16), emoji, embedded_color=True, font=self.gui.emoji_font
711
+ ) # type: ignore
712
+ im = im.resize((32, 32))
713
+ ph_im = ImageTk.PhotoImage(im)
714
+
715
+ button = Button(
716
+ self.frame_buttons,
717
+ command=lambda i=emoji: self.cb_set_emoji(i), # type: ignore
718
+ bootstyle="dark", # type: ignore
719
+ )
720
+ button.config(image=ph_im)
721
+ button.grid(column=column, row=row)
722
+
723
+ column += 1
724
+
725
+ if emoji_column_per_row == 0:
726
+ self.emoji_canvas.update_idletasks()
727
+ emoji_column_per_row = int(
728
+ self.emoji_canvas.winfo_width() / button.winfo_width()
729
+ )
730
+ if column == emoji_column_per_row:
731
+ column = 0
732
+ row += 1
733
+
734
+ self.emoji_btns.append((button, ph_im))
735
+
736
+ # Update buttons frames idle tasks to let tkinter calculate buttons sizes
737
+ self.frame_buttons.update_idletasks()
738
+
739
+ # Resize the canvas frame to show specified number of buttons and the scrollbar
740
+ if len(self.emoji_btns) > 0:
741
+ in_view_columns_width = (
742
+ self.emoji_btns[0][0].winfo_width() * emoji_column_per_row
743
+ )
744
+ in_view_rows_height = (
745
+ self.emoji_btns[0][0].winfo_height() * self.emoji_visible_rows
746
+ )
747
+ self.frame_emoji_canvas.config(
748
+ width=in_view_columns_width + self.vsb.winfo_width(),
749
+ height=in_view_rows_height,
750
+ )
751
+
752
+ # Set the canvas scrolling region
753
+ self.emoji_canvas.config(scrollregion=self.emoji_canvas.bbox("all"))
754
+
755
+ # https://stackoverflow.com/questions/17355902/tkinter-binding-mousewheel-to-scrollbar
756
+ self.emoji_canvas.bind("<Enter>", self.cb_bound_to_mousewheel)
757
+ self.emoji_canvas.bind("<Leave>", self.cb_unbound_to_mousewheel)