absfuyu 5.0.0__py3-none-any.whl → 6.1.2__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.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

Files changed (103) hide show
  1. absfuyu/__init__.py +5 -3
  2. absfuyu/__main__.py +3 -3
  3. absfuyu/cli/__init__.py +13 -2
  4. absfuyu/cli/audio_group.py +98 -0
  5. absfuyu/cli/color.py +30 -14
  6. absfuyu/cli/config_group.py +9 -2
  7. absfuyu/cli/do_group.py +23 -6
  8. absfuyu/cli/game_group.py +27 -2
  9. absfuyu/cli/tool_group.py +81 -11
  10. absfuyu/config/__init__.py +3 -3
  11. absfuyu/core/__init__.py +12 -8
  12. absfuyu/core/baseclass.py +929 -96
  13. absfuyu/core/baseclass2.py +44 -3
  14. absfuyu/core/decorator.py +70 -4
  15. absfuyu/core/docstring.py +64 -41
  16. absfuyu/core/dummy_cli.py +3 -3
  17. absfuyu/core/dummy_func.py +19 -6
  18. absfuyu/dxt/__init__.py +2 -2
  19. absfuyu/dxt/base_type.py +93 -0
  20. absfuyu/dxt/dictext.py +204 -16
  21. absfuyu/dxt/dxt_support.py +2 -2
  22. absfuyu/dxt/intext.py +151 -34
  23. absfuyu/dxt/listext.py +969 -127
  24. absfuyu/dxt/strext.py +77 -17
  25. absfuyu/extra/__init__.py +2 -2
  26. absfuyu/extra/audio/__init__.py +8 -0
  27. absfuyu/extra/audio/_util.py +57 -0
  28. absfuyu/extra/audio/convert.py +192 -0
  29. absfuyu/extra/audio/lossless.py +281 -0
  30. absfuyu/extra/beautiful.py +3 -2
  31. absfuyu/extra/da/__init__.py +72 -0
  32. absfuyu/extra/da/dadf.py +1600 -0
  33. absfuyu/extra/da/dadf_base.py +186 -0
  34. absfuyu/extra/da/df_func.py +181 -0
  35. absfuyu/extra/da/mplt.py +219 -0
  36. absfuyu/extra/ggapi/__init__.py +8 -0
  37. absfuyu/extra/ggapi/gdrive.py +223 -0
  38. absfuyu/extra/ggapi/glicense.py +148 -0
  39. absfuyu/extra/ggapi/glicense_df.py +186 -0
  40. absfuyu/extra/ggapi/gsheet.py +88 -0
  41. absfuyu/extra/img/__init__.py +30 -0
  42. absfuyu/extra/img/converter.py +402 -0
  43. absfuyu/extra/img/dup_check.py +291 -0
  44. absfuyu/extra/pdf.py +87 -0
  45. absfuyu/extra/rclone.py +253 -0
  46. absfuyu/extra/xml.py +90 -0
  47. absfuyu/fun/__init__.py +7 -20
  48. absfuyu/fun/rubik.py +442 -0
  49. absfuyu/fun/tarot.py +2 -2
  50. absfuyu/game/__init__.py +2 -2
  51. absfuyu/game/game_stat.py +2 -2
  52. absfuyu/game/schulte.py +78 -0
  53. absfuyu/game/sudoku.py +2 -2
  54. absfuyu/game/tictactoe.py +2 -3
  55. absfuyu/game/wordle.py +6 -4
  56. absfuyu/general/__init__.py +4 -4
  57. absfuyu/general/content.py +4 -4
  58. absfuyu/general/human.py +2 -2
  59. absfuyu/general/resrel.py +213 -0
  60. absfuyu/general/shape.py +3 -8
  61. absfuyu/general/tax.py +344 -0
  62. absfuyu/logger.py +806 -59
  63. absfuyu/numbers/__init__.py +13 -0
  64. absfuyu/numbers/number_to_word.py +321 -0
  65. absfuyu/numbers/shorten_number.py +303 -0
  66. absfuyu/numbers/time_duration.py +217 -0
  67. absfuyu/pkg_data/__init__.py +2 -2
  68. absfuyu/pkg_data/deprecated.py +2 -2
  69. absfuyu/pkg_data/logo.py +1462 -0
  70. absfuyu/sort.py +4 -4
  71. absfuyu/tools/__init__.py +28 -2
  72. absfuyu/tools/checksum.py +144 -9
  73. absfuyu/tools/converter.py +120 -34
  74. absfuyu/tools/generator.py +461 -0
  75. absfuyu/tools/inspector.py +752 -0
  76. absfuyu/tools/keygen.py +2 -2
  77. absfuyu/tools/obfuscator.py +47 -9
  78. absfuyu/tools/passwordlib.py +89 -25
  79. absfuyu/tools/shutdownizer.py +3 -8
  80. absfuyu/tools/sw.py +718 -0
  81. absfuyu/tools/web.py +10 -13
  82. absfuyu/typings.py +138 -0
  83. absfuyu/util/__init__.py +114 -6
  84. absfuyu/util/api.py +41 -18
  85. absfuyu/util/cli.py +119 -0
  86. absfuyu/util/gui.py +91 -0
  87. absfuyu/util/json_method.py +43 -14
  88. absfuyu/util/lunar.py +2 -2
  89. absfuyu/util/package.py +124 -0
  90. absfuyu/util/path.py +702 -82
  91. absfuyu/util/performance.py +122 -7
  92. absfuyu/util/shorten_number.py +244 -21
  93. absfuyu/util/text_table.py +481 -0
  94. absfuyu/util/zipped.py +8 -7
  95. absfuyu/version.py +79 -59
  96. {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/METADATA +52 -11
  97. absfuyu-6.1.2.dist-info/RECORD +105 -0
  98. {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/WHEEL +1 -1
  99. absfuyu/extra/data_analysis.py +0 -1078
  100. absfuyu/general/generator.py +0 -303
  101. absfuyu-5.0.0.dist-info/RECORD +0 -68
  102. {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/entry_points.txt +0 -0
  103. {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/licenses/LICENSE +0 -0
absfuyu/tools/web.py CHANGED
@@ -3,21 +3,14 @@ Absfuyu: Web
3
3
  ------------
4
4
  Web, ``request``, ``BeautifulSoup`` stuff
5
5
 
6
- Version: 1.0.2
7
- Date updated: 05/04/2024 (dd/mm/yyyy)
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
- # Library
11
- # ---------------------------------------------------------------------------
12
- import requests
13
- from bs4 import BeautifulSoup
14
-
15
- from absfuyu.logger import logger
16
-
17
10
 
18
11
  # Function
19
12
  # ---------------------------------------------------------------------------
20
- def soup_link(link: str) -> BeautifulSoup:
13
+ def soup_link(link: str):
21
14
  """
22
15
  ``BeautifulSoup`` the link
23
16
 
@@ -32,12 +25,17 @@ def soup_link(link: str) -> BeautifulSoup:
32
25
  ``BeautifulSoup`` instance
33
26
  """
34
27
  try:
28
+ import requests
29
+ from bs4 import BeautifulSoup
30
+
35
31
  page = requests.get(link)
36
32
  soup = BeautifulSoup(page.content, "html.parser")
37
- logger.debug("Soup completed!")
38
33
  return soup
34
+
35
+ except ImportError:
36
+ raise ImportError("Please install bs4, requests package")
37
+
39
38
  except Exception:
40
- logger.error("Can't soup")
41
39
  raise SystemExit("Something wrong") # noqa: B904
42
40
 
43
41
 
@@ -51,5 +49,4 @@ def gen_random_commit_msg() -> str:
51
49
  Random commit message
52
50
  """
53
51
  out = soup_link("https://whatthecommit.com/").get_text()[34:-20]
54
- logger.debug(out)
55
52
  return out # type: ignore
absfuyu/typings.py ADDED
@@ -0,0 +1,138 @@
1
+ """
2
+ Absfuyu: Core
3
+ -------------
4
+ Pre-defined typing
5
+
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module Package
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = [
13
+ # Type
14
+ "T",
15
+ "T_co",
16
+ "T_contra",
17
+ "KT",
18
+ "VT",
19
+ "P",
20
+ "R",
21
+ "_CALLABLE",
22
+ "CT",
23
+ "N",
24
+ "_Number",
25
+ "override",
26
+ # Protocol
27
+ "SupportsShowMethods",
28
+ ]
29
+
30
+
31
+ # Library
32
+ # ---------------------------------------------------------------------------
33
+ from collections.abc import Callable
34
+ from typing import Any, ParamSpec, Protocol, TypeVar, overload
35
+
36
+ # Type
37
+ # ---------------------------------------------------------------------------
38
+ # Types where neither is possible are invariant
39
+ # Type safety must be strict
40
+ T = TypeVar("T") # Type invariant
41
+ # Type variables that are covariant can be substituted
42
+ # with a more specific type without causing errors
43
+ # Safe to treat a Box[int] as a Box[object]
44
+ T_co = TypeVar("T_co", covariant=True) # Type covariant
45
+ # Type variables that are contravariant can be substituted
46
+ # with a more general type without causing errors
47
+ # Safe to use a Box[object] where a Box[int] is needed
48
+ T_contra = TypeVar("T_contra", contravariant=True) # Type contravariant
49
+
50
+ KT = TypeVar("KT")
51
+ VT = TypeVar("VT")
52
+
53
+ # Callable
54
+ P = ParamSpec("P") # Parameter type
55
+ R = TypeVar("R") # Return type - Can be anything
56
+ _CALLABLE = Callable[P, R]
57
+
58
+ # Class type - Can be any subtype of `type`
59
+ CT = TypeVar("CT", bound=type)
60
+
61
+ # Number type
62
+ N = TypeVar("N", int, float) # Must be int or float
63
+ _Number = int | float
64
+
65
+
66
+ # Protocol
67
+ # ---------------------------------------------------------------------------
68
+ class SupportsShowMethods(Protocol):
69
+ """
70
+ Support class with ``show_all_methods()``
71
+ and ``show_all_properties()`` method
72
+ from ``absfuyu.core.basclass.ShowAllMethodsMixin``
73
+ """
74
+
75
+ @overload
76
+ @classmethod
77
+ def show_all_methods(cls) -> dict[str, list[str]]: ...
78
+
79
+ @overload
80
+ @classmethod
81
+ def show_all_methods(
82
+ cls,
83
+ print_result: bool = False,
84
+ include_classmethod: bool = True,
85
+ classmethod_indicator: str = "<classmethod>",
86
+ include_staticmethod: bool = True,
87
+ staticmethod_indicator: str = "<staticmethod>",
88
+ include_private_method: bool = False,
89
+ ) -> dict[str, list[str]]: ...
90
+
91
+ @classmethod
92
+ def show_all_methods(cls, *args, **kwargs) -> Any: ...
93
+
94
+ @overload
95
+ @classmethod
96
+ def show_all_properties(cls) -> dict[str, list[str]]: ...
97
+
98
+ @overload
99
+ @classmethod
100
+ def show_all_properties(
101
+ cls, print_result: bool = False
102
+ ) -> dict[str, list[str]]: ...
103
+
104
+ @classmethod
105
+ def show_all_properties(cls, *args, **kwargs) -> Any: ...
106
+
107
+
108
+ # Note
109
+ # ---------------------------------------------------------------------------
110
+ # Iterable : __iter__
111
+ # Iterator(Iterable) : __next__, __iter__
112
+ # Reversible(Iterable) : __reversed__, __iter__
113
+ # Sized : __len__
114
+ # Container : __contains__
115
+ # Collection(Sized, Iterable, Container) : __len__, __iter__, __contains__
116
+ # Set(Collection) : __contains__, __iter__, __len__
117
+ # MutableSet(Set) : __contains__, __iter__, __len__
118
+ # Mapping(Collection) : __getitem__, __iter__, and __len__
119
+ # MutableMapping(Mapping) : __getitem__, __setitem__, __delitem__,__iter__, __len__
120
+ # Sequence(Reversible, Collection) : __reversed__, __len__, __iter__, __contains__
121
+ # MutableSequence(Sequence) : __getitem__, __setitem__, __delitem__, __reversed__, __len__, __iter__, __contains__
122
+
123
+ # Iterable : str, dict, list, tuple, set
124
+ # Iterator(Iterable) : Generator
125
+ # Reversible(Iterable) : str, dict, list, tuple
126
+ # Sized : str, dict, list, tuple, set
127
+ # Container : str, dict, list, tuple, set
128
+ # Collection(Sized, Iterable, Container) : str, dict, list, tuple, set
129
+ # Set(Collection) : set
130
+ # MutableSet(Set) : set
131
+ # Mapping(Collection) : dict
132
+ # MutableMapping(Mapping) : dict
133
+ # Sequence(Reversible, Collection) : str, list, tuple
134
+ # MutableSequence(Sequence) : list
135
+
136
+ # __iter__: for <...> in <...>
137
+ # __len__: len(<...>)
138
+ # __contains__: if <...> in <...>
absfuyu/util/__init__.py CHANGED
@@ -3,16 +3,34 @@ Absufyu: Utilities
3
3
  ------------------
4
4
  Some random utilities
5
5
 
6
- Version: 1.5.2
7
- Date updated: 25/11/2024 (dd/mm/yyyy)
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
+ # Module Package
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = [
13
+ # util
14
+ "get_installed_package",
15
+ "set_min",
16
+ "set_max",
17
+ "set_min_max",
18
+ "stop_after_day",
19
+ "convert_to_raw_unicode",
20
+ # shorten number
21
+ "Decimal",
22
+ ]
23
+
24
+
10
25
  # Library
11
26
  # ---------------------------------------------------------------------------
12
27
  import pkgutil
28
+ import subprocess
13
29
  from datetime import datetime
30
+ from string import printable
14
31
 
15
- from absfuyu.core import deprecated, versionadded, versionchanged
32
+ from absfuyu.core.docstring import deprecated, versionadded, versionchanged
33
+ from absfuyu.util.shorten_number import Decimal
16
34
 
17
35
 
18
36
  # Function
@@ -137,14 +155,23 @@ def set_min_max(
137
155
  >>> set_min_max(808)
138
156
  100
139
157
  """
140
- current_value = set_min(current_value, min_value=min_value)
141
- current_value = set_max(current_value, max_value=max_value)
158
+ # Set min
159
+ # current_value = set_min(current_value, min_value=min_value)
160
+ current_value = max(current_value, min_value)
161
+ # Set max
162
+ # current_value = set_max(current_value, max_value=max_value)
163
+ current_value = min(current_value, max_value)
142
164
  return current_value
143
165
 
144
166
 
167
+ @versionchanged("5.6.0", reason="New `custom_msg` parameter")
145
168
  @versionadded("3.2.0")
146
169
  def stop_after_day(
147
- year: int | None = None, month: int | None = None, day: int | None = None
170
+ year: int | None = None,
171
+ month: int | None = None,
172
+ day: int | None = None,
173
+ *,
174
+ custom_msg: str | None = None,
148
175
  ) -> None:
149
176
  """
150
177
  Stop working after specified day.
@@ -163,6 +190,10 @@ def stop_after_day(
163
190
  day : int
164
191
  Desired day
165
192
  (Default: ``None`` - 1 day trial)
193
+
194
+ custom_msg : str
195
+ Custom exit message
196
+ (Default: ``None``)
166
197
  """
167
198
  # None checking - By default: 1 day trial
168
199
  now = datetime.now()
@@ -177,4 +208,81 @@ def stop_after_day(
177
208
  end_date = datetime(year, month, day)
178
209
  result = end_date - now
179
210
  if result.days < 0:
211
+ if custom_msg:
212
+ raise SystemExit(custom_msg)
180
213
  raise SystemExit("End of time")
214
+
215
+
216
+ @versionadded("5.4.0")
217
+ def convert_to_raw_unicode(text: str, partial: bool = True) -> str:
218
+ r"""
219
+ Convert text to raw unicode variant.
220
+
221
+ Parameters
222
+ ----------
223
+ text : str
224
+ Text to convert
225
+
226
+ partial : bool, optional
227
+ Only convert characters that not in ``string.printable``,
228
+ by default ``True``
229
+
230
+ Returns
231
+ -------
232
+ str
233
+ Converted text.
234
+
235
+
236
+ Example:
237
+ --------
238
+ >>> convert_to_raw_unicode("résumé")
239
+ r\u00E9sum\u00E9
240
+
241
+ >>> convert_to_raw_unicode("résumé", partial=False)
242
+ \u0072\u00E9\u0073\u0075\u006D\u00E9
243
+ """
244
+
245
+ character_set = printable
246
+
247
+ def _convert(character: str) -> str:
248
+ """Get unicode value"""
249
+ # ord(c): Returns the Unicode code point for a one-character string c.
250
+ _ord = ord(character)
251
+ # f"\\u{_ord:04X}": Formats the Unicode code point as a four-digit
252
+ # hexadecimal number for code points less than 0x10000.
253
+ # f"\\U{_ord:08X}": Formats the Unicode code point as an eight-digit
254
+ # hexadecimal number for code points greater than or equal to 0x10000.
255
+ return f"\\u{_ord:04X}" if _ord < 0x10000 else f"\\U{_ord:08X}"
256
+
257
+ return "".join(
258
+ character if character in character_set and partial else _convert(character)
259
+ for character in text
260
+ )
261
+
262
+
263
+ @versionadded("5.9.0")
264
+ def is_command_available(cmd: list[str] | str, err_msg: str = "") -> None:
265
+ """
266
+ Checks if the desired command available
267
+
268
+ Parameters
269
+ ----------
270
+ cmd : list[str] | str
271
+ Command to check
272
+
273
+ err_msg : str, optional
274
+ Custom error message, by default ""
275
+
276
+ Raises
277
+ ------
278
+ ValueError
279
+ When command is unvailable
280
+ """
281
+ try:
282
+ subprocess.run(
283
+ cmd,
284
+ stdout=subprocess.DEVNULL,
285
+ stderr=subprocess.DEVNULL,
286
+ )
287
+ except Exception:
288
+ raise ValueError(err_msg)
absfuyu/util/api.py CHANGED
@@ -3,8 +3,8 @@ Absufyu: API
3
3
  ------------
4
4
  Fetch data stuff
5
5
 
6
- Version: 5.0.0
7
- Date updated: 13/02/2025 (dd/mm/yyyy)
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -23,18 +23,29 @@ import subprocess
23
23
  from pathlib import Path
24
24
  from typing import NamedTuple
25
25
 
26
- import requests
27
-
28
- from absfuyu.core import versionadded, versionchanged
26
+ from absfuyu.core.baseclass import BaseClass
27
+ from absfuyu.core.docstring import versionadded, versionchanged
29
28
  from absfuyu.logger import logger
30
29
 
30
+ try:
31
+ import requests
32
+ except ImportError:
33
+ raise ImportError("Please install requests package")
34
+
31
35
 
32
36
  # Function
33
37
  # ---------------------------------------------------------------------------
34
38
  class PingResult(NamedTuple):
35
39
  """
36
- :param host: Host name/IP
37
- :param result: Ping result in ms
40
+ Ping result
41
+
42
+ Parameters
43
+ ----------
44
+ host : str
45
+ Host name/IP
46
+
47
+ result : str
48
+ Ping result in ms
38
49
  """
39
50
 
40
51
  host: str
@@ -90,28 +101,40 @@ def ping_windows(host: list[str], ping_count: int = 3) -> list[PingResult]:
90
101
 
91
102
  # Class
92
103
  # ---------------------------------------------------------------------------
93
- class APIRequest:
94
- """API data with cache feature"""
104
+ class APIRequest(BaseClass):
105
+ """
106
+ API data with cache feature
107
+
108
+ Parameters
109
+ ----------
110
+ api_url : str
111
+ API link
112
+
113
+ encoding : str | None, optional
114
+ Data encoding, by default ``"utf-8"``
115
+ """
95
116
 
96
117
  def __init__(
97
118
  self,
98
119
  api_url: str,
99
- *, # Use "*" to force using keyword in function parameter | Example: APIRequest(url, encoding="utf-8")
120
+ *,
100
121
  encoding: str | None = "utf-8",
101
122
  ) -> None:
102
123
  """
103
- :param api_url: api link
104
- :param encoding: data encoding (Default: utf-8)
124
+ Create APIRequest instance
125
+
126
+ Parameters
127
+ ----------
128
+ api_url : str
129
+ API link
130
+
131
+ encoding : str | None, optional
132
+ Data encoding, by default ``"utf-8"``
105
133
  """
134
+
106
135
  self.url = api_url
107
136
  self.encoding = encoding
108
137
 
109
- def __str__(self) -> str:
110
- return f"{self.__class__.__name__}({self.url})"
111
-
112
- def __repr__(self) -> str:
113
- return self.__str__()
114
-
115
138
  def fetch_data(self, *, update: bool = False, json_cache: str | Path):
116
139
  """
117
140
  Fetch data from an API then cache it for later use
absfuyu/util/cli.py ADDED
@@ -0,0 +1,119 @@
1
+ """
2
+ Absfuyu: CLI
3
+ ------------
4
+ Custom Argument Parser
5
+
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["FuyuArgumentParser"]
13
+
14
+
15
+ # Library
16
+ # ---------------------------------------------------------------------------
17
+ import logging
18
+ import sys
19
+ from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
20
+ from collections.abc import Callable
21
+ from typing import Any
22
+
23
+
24
+ # Class
25
+ # ---------------------------------------------------------------------------
26
+ class FuyuArgumentParser(ArgumentParser):
27
+ """
28
+ Usage:
29
+ ------
30
+ >>> import sys
31
+ >>> args = FuyuArgumentParser()
32
+ >>> args.start()
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ prog: str | None = None,
38
+ description: str | None = None,
39
+ epilog: str | None = None,
40
+ version: str = "0.0.1",
41
+ formatter_class: Any = ArgumentDefaultsHelpFormatter,
42
+ prefix_chars: str = "-",
43
+ argument_default: Any = None,
44
+ conflict_handler: str = "error",
45
+ add_help: bool = True,
46
+ allow_abbrev: bool = True,
47
+ exit_on_error: bool = True,
48
+ ) -> None:
49
+ # Desc
50
+ if description is None:
51
+ description = f"Absfuyu CLI {version}"
52
+
53
+ # Default
54
+ self._default_args = ["--help"]
55
+
56
+ # Super
57
+ super().__init__(
58
+ prog=prog,
59
+ description=description,
60
+ epilog=epilog,
61
+ formatter_class=formatter_class,
62
+ prefix_chars=prefix_chars,
63
+ argument_default=argument_default,
64
+ conflict_handler=conflict_handler,
65
+ add_help=add_help,
66
+ allow_abbrev=allow_abbrev,
67
+ exit_on_error=exit_on_error,
68
+ )
69
+
70
+ # Add version
71
+ self.add_argument("-v", "--version", action="version", version=f"%(prog)s {version}")
72
+
73
+ # Add log level
74
+ _ll_val = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
75
+ self.add_argument(
76
+ "-ll",
77
+ "--log-level",
78
+ metavar="LOG_LEVEL",
79
+ dest="log_level",
80
+ choices=_ll_val,
81
+ default="ERROR",
82
+ help=f"Log level: {', '.join(_ll_val)}",
83
+ )
84
+
85
+ def _fuyu_set_log_level(self, log_level: str = "ERROR") -> None:
86
+ log_levels = {
87
+ "DEBUG": logging.DEBUG,
88
+ "INFO": logging.INFO,
89
+ "WARNING": logging.WARNING,
90
+ "ERROR": logging.ERROR,
91
+ "CRITICAL": logging.CRITICAL,
92
+ }
93
+ logging.basicConfig(
94
+ level=log_levels[log_level],
95
+ format="[%(asctime)s] [%(module)s] [%(name)s] [%(funcName)s] [%(levelname)-s] %(message)s",
96
+ datefmt="%Y-%m-%d %H:%M:%S",
97
+ )
98
+
99
+ def start(self, default: Callable[[], Any] | None = None):
100
+ """
101
+ Quick start for argument parser
102
+
103
+ Parameters
104
+ ----------
105
+ default : Callable[[], Any] | None, optional
106
+ | Default callable to run, by default ``None``
107
+ | Shows help on default behavior (default=None)
108
+
109
+ Returns
110
+ -------
111
+ Namespace
112
+ Namespace
113
+ """
114
+ if default is None:
115
+ args = self.parse_args(args=None if sys.argv[1:] else self._default_args)
116
+ else:
117
+ args = self.parse_args(args=None if sys.argv[1:] else default())
118
+ self._fuyu_set_log_level(getattr(args, "log_level", "ERROR"))
119
+ return args
absfuyu/util/gui.py ADDED
@@ -0,0 +1,91 @@
1
+ """
2
+ Absfuyu: GUI
3
+ ------------
4
+ Custom tkinter GUI
5
+
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["CustomTkinterApp"]
13
+
14
+
15
+ # Library
16
+ # ---------------------------------------------------------------------------
17
+ import tkinter as tk
18
+
19
+ from absfuyu.pkg_data.logo import AbsfuyuLogo
20
+
21
+
22
+ # Class
23
+ # ---------------------------------------------------------------------------
24
+ class CustomTkinterApp(tk.Tk):
25
+
26
+ def __init__(self, title: str | None = None, size: tuple[int, int] | None = None) -> None:
27
+ """
28
+ Custom Tkinter GUI
29
+
30
+ Parameters
31
+ ----------
32
+ title : str | None, optional
33
+ Title of the app, by default None
34
+
35
+ size : tuple[int, int] | None, optional
36
+ Size of the app (width, height), by default None
37
+ """
38
+ super().__init__()
39
+
40
+ # Set custom icon
41
+ self.iconphoto(True, tk.PhotoImage(data=AbsfuyuLogo.SHORT))
42
+
43
+ # Title
44
+ self.title(title)
45
+
46
+ # Set size
47
+ self._absfuyu_set_width_height(size)
48
+
49
+ # @versionadded("5.10.0")
50
+ def _absfuyu_set_width_height(self, size: tuple[int, int] | None = None) -> None:
51
+ """
52
+ Set width and height for the app.
53
+
54
+ Parameters
55
+ ----------
56
+ size : tuple[int, int] | None, optional
57
+ Size of the app (width, height), by default ``None``
58
+ (420 x 250)
59
+ """
60
+ # Set GUI appears in center
61
+ if size is None:
62
+ _width = 420
63
+ _height = 250
64
+ else:
65
+ _width, _height = size
66
+
67
+ _width_screen = self.winfo_screenwidth()
68
+ _width_screen_offset = 0.0052 # x offset mutiplier
69
+ _x_offset = int(_width_screen * _width_screen_offset)
70
+
71
+ _height_screen = self.winfo_screenheight()
72
+ _height_screen_offset = 0.0926 # y offset mutiplier
73
+ _y_offset = int(_height_screen * _height_screen_offset)
74
+
75
+ if _width > _width_screen:
76
+ _width = int(_width_screen * (1 - 0.001))
77
+ # _x_offset = 0
78
+
79
+ if _height > (_height_screen - _y_offset):
80
+ _height = int(_height_screen * (1 - 0.001))
81
+ _y_offset = 0
82
+
83
+ _x = (_width_screen / 2) - (_width / 2) - _x_offset
84
+ _y = (_height_screen / 2) - (_height / 2) - _y_offset
85
+
86
+ self.geometry(f"{_width}x{_height}+{int(_x)}+{int(_y)}")
87
+
88
+
89
+ if __name__ == "__main__":
90
+ app = CustomTkinterApp("absfuyu")
91
+ app.mainloop()