PyWebWinUI3 1.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. pywebwinui3/__init__.py +3 -0
  2. pywebwinui3/core.py +176 -0
  3. pywebwinui3/event.py +147 -0
  4. pywebwinui3/qt.py +887 -0
  5. pywebwinui3/type.py +362 -0
  6. pywebwinui3/util.py +194 -0
  7. pywebwinui3/web/Pretendard/PretendardVariable.subset.0.woff2 +0 -0
  8. pywebwinui3/web/Pretendard/PretendardVariable.subset.1.woff2 +0 -0
  9. pywebwinui3/web/Pretendard/PretendardVariable.subset.10.woff2 +0 -0
  10. pywebwinui3/web/Pretendard/PretendardVariable.subset.11.woff2 +0 -0
  11. pywebwinui3/web/Pretendard/PretendardVariable.subset.12.woff2 +0 -0
  12. pywebwinui3/web/Pretendard/PretendardVariable.subset.13.woff2 +0 -0
  13. pywebwinui3/web/Pretendard/PretendardVariable.subset.14.woff2 +0 -0
  14. pywebwinui3/web/Pretendard/PretendardVariable.subset.15.woff2 +0 -0
  15. pywebwinui3/web/Pretendard/PretendardVariable.subset.16.woff2 +0 -0
  16. pywebwinui3/web/Pretendard/PretendardVariable.subset.17.woff2 +0 -0
  17. pywebwinui3/web/Pretendard/PretendardVariable.subset.18.woff2 +0 -0
  18. pywebwinui3/web/Pretendard/PretendardVariable.subset.19.woff2 +0 -0
  19. pywebwinui3/web/Pretendard/PretendardVariable.subset.2.woff2 +0 -0
  20. pywebwinui3/web/Pretendard/PretendardVariable.subset.20.woff2 +0 -0
  21. pywebwinui3/web/Pretendard/PretendardVariable.subset.21.woff2 +0 -0
  22. pywebwinui3/web/Pretendard/PretendardVariable.subset.22.woff2 +0 -0
  23. pywebwinui3/web/Pretendard/PretendardVariable.subset.23.woff2 +0 -0
  24. pywebwinui3/web/Pretendard/PretendardVariable.subset.24.woff2 +0 -0
  25. pywebwinui3/web/Pretendard/PretendardVariable.subset.25.woff2 +0 -0
  26. pywebwinui3/web/Pretendard/PretendardVariable.subset.26.woff2 +0 -0
  27. pywebwinui3/web/Pretendard/PretendardVariable.subset.27.woff2 +0 -0
  28. pywebwinui3/web/Pretendard/PretendardVariable.subset.28.woff2 +0 -0
  29. pywebwinui3/web/Pretendard/PretendardVariable.subset.29.woff2 +0 -0
  30. pywebwinui3/web/Pretendard/PretendardVariable.subset.3.woff2 +0 -0
  31. pywebwinui3/web/Pretendard/PretendardVariable.subset.30.woff2 +0 -0
  32. pywebwinui3/web/Pretendard/PretendardVariable.subset.31.woff2 +0 -0
  33. pywebwinui3/web/Pretendard/PretendardVariable.subset.32.woff2 +0 -0
  34. pywebwinui3/web/Pretendard/PretendardVariable.subset.33.woff2 +0 -0
  35. pywebwinui3/web/Pretendard/PretendardVariable.subset.34.woff2 +0 -0
  36. pywebwinui3/web/Pretendard/PretendardVariable.subset.35.woff2 +0 -0
  37. pywebwinui3/web/Pretendard/PretendardVariable.subset.36.woff2 +0 -0
  38. pywebwinui3/web/Pretendard/PretendardVariable.subset.37.woff2 +0 -0
  39. pywebwinui3/web/Pretendard/PretendardVariable.subset.38.woff2 +0 -0
  40. pywebwinui3/web/Pretendard/PretendardVariable.subset.39.woff2 +0 -0
  41. pywebwinui3/web/Pretendard/PretendardVariable.subset.4.woff2 +0 -0
  42. pywebwinui3/web/Pretendard/PretendardVariable.subset.40.woff2 +0 -0
  43. pywebwinui3/web/Pretendard/PretendardVariable.subset.41.woff2 +0 -0
  44. pywebwinui3/web/Pretendard/PretendardVariable.subset.42.woff2 +0 -0
  45. pywebwinui3/web/Pretendard/PretendardVariable.subset.43.woff2 +0 -0
  46. pywebwinui3/web/Pretendard/PretendardVariable.subset.44.woff2 +0 -0
  47. pywebwinui3/web/Pretendard/PretendardVariable.subset.45.woff2 +0 -0
  48. pywebwinui3/web/Pretendard/PretendardVariable.subset.46.woff2 +0 -0
  49. pywebwinui3/web/Pretendard/PretendardVariable.subset.47.woff2 +0 -0
  50. pywebwinui3/web/Pretendard/PretendardVariable.subset.48.woff2 +0 -0
  51. pywebwinui3/web/Pretendard/PretendardVariable.subset.49.woff2 +0 -0
  52. pywebwinui3/web/Pretendard/PretendardVariable.subset.5.woff2 +0 -0
  53. pywebwinui3/web/Pretendard/PretendardVariable.subset.50.woff2 +0 -0
  54. pywebwinui3/web/Pretendard/PretendardVariable.subset.51.woff2 +0 -0
  55. pywebwinui3/web/Pretendard/PretendardVariable.subset.52.woff2 +0 -0
  56. pywebwinui3/web/Pretendard/PretendardVariable.subset.53.woff2 +0 -0
  57. pywebwinui3/web/Pretendard/PretendardVariable.subset.54.woff2 +0 -0
  58. pywebwinui3/web/Pretendard/PretendardVariable.subset.55.woff2 +0 -0
  59. pywebwinui3/web/Pretendard/PretendardVariable.subset.56.woff2 +0 -0
  60. pywebwinui3/web/Pretendard/PretendardVariable.subset.57.woff2 +0 -0
  61. pywebwinui3/web/Pretendard/PretendardVariable.subset.58.woff2 +0 -0
  62. pywebwinui3/web/Pretendard/PretendardVariable.subset.59.woff2 +0 -0
  63. pywebwinui3/web/Pretendard/PretendardVariable.subset.6.woff2 +0 -0
  64. pywebwinui3/web/Pretendard/PretendardVariable.subset.60.woff2 +0 -0
  65. pywebwinui3/web/Pretendard/PretendardVariable.subset.61.woff2 +0 -0
  66. pywebwinui3/web/Pretendard/PretendardVariable.subset.62.woff2 +0 -0
  67. pywebwinui3/web/Pretendard/PretendardVariable.subset.63.woff2 +0 -0
  68. pywebwinui3/web/Pretendard/PretendardVariable.subset.64.woff2 +0 -0
  69. pywebwinui3/web/Pretendard/PretendardVariable.subset.65.woff2 +0 -0
  70. pywebwinui3/web/Pretendard/PretendardVariable.subset.66.woff2 +0 -0
  71. pywebwinui3/web/Pretendard/PretendardVariable.subset.67.woff2 +0 -0
  72. pywebwinui3/web/Pretendard/PretendardVariable.subset.68.woff2 +0 -0
  73. pywebwinui3/web/Pretendard/PretendardVariable.subset.69.woff2 +0 -0
  74. pywebwinui3/web/Pretendard/PretendardVariable.subset.7.woff2 +0 -0
  75. pywebwinui3/web/Pretendard/PretendardVariable.subset.70.woff2 +0 -0
  76. pywebwinui3/web/Pretendard/PretendardVariable.subset.71.woff2 +0 -0
  77. pywebwinui3/web/Pretendard/PretendardVariable.subset.72.woff2 +0 -0
  78. pywebwinui3/web/Pretendard/PretendardVariable.subset.73.woff2 +0 -0
  79. pywebwinui3/web/Pretendard/PretendardVariable.subset.74.woff2 +0 -0
  80. pywebwinui3/web/Pretendard/PretendardVariable.subset.75.woff2 +0 -0
  81. pywebwinui3/web/Pretendard/PretendardVariable.subset.76.woff2 +0 -0
  82. pywebwinui3/web/Pretendard/PretendardVariable.subset.77.woff2 +0 -0
  83. pywebwinui3/web/Pretendard/PretendardVariable.subset.78.woff2 +0 -0
  84. pywebwinui3/web/Pretendard/PretendardVariable.subset.79.woff2 +0 -0
  85. pywebwinui3/web/Pretendard/PretendardVariable.subset.8.woff2 +0 -0
  86. pywebwinui3/web/Pretendard/PretendardVariable.subset.80.woff2 +0 -0
  87. pywebwinui3/web/Pretendard/PretendardVariable.subset.81.woff2 +0 -0
  88. pywebwinui3/web/Pretendard/PretendardVariable.subset.82.woff2 +0 -0
  89. pywebwinui3/web/Pretendard/PretendardVariable.subset.83.woff2 +0 -0
  90. pywebwinui3/web/Pretendard/PretendardVariable.subset.84.woff2 +0 -0
  91. pywebwinui3/web/Pretendard/PretendardVariable.subset.85.woff2 +0 -0
  92. pywebwinui3/web/Pretendard/PretendardVariable.subset.86.woff2 +0 -0
  93. pywebwinui3/web/Pretendard/PretendardVariable.subset.87.woff2 +0 -0
  94. pywebwinui3/web/Pretendard/PretendardVariable.subset.88.woff2 +0 -0
  95. pywebwinui3/web/Pretendard/PretendardVariable.subset.89.woff2 +0 -0
  96. pywebwinui3/web/Pretendard/PretendardVariable.subset.9.woff2 +0 -0
  97. pywebwinui3/web/Pretendard/PretendardVariable.subset.90.woff2 +0 -0
  98. pywebwinui3/web/Pretendard/PretendardVariable.subset.91.woff2 +0 -0
  99. pywebwinui3/web/Pretendard/Variable-dynamic-subset.min.css +927 -0
  100. pywebwinui3/web/SegoeFluentIcons.ttf +0 -0
  101. pywebwinui3/web/_app/env.js +1 -0
  102. pywebwinui3/web/_app/immutable/assets/2.wYBxhgNe.css +1 -0
  103. pywebwinui3/web/_app/immutable/chunks/BeVEiAlX.js +1 -0
  104. pywebwinui3/web/_app/immutable/chunks/C-ZTJ6AF.js +1 -0
  105. pywebwinui3/web/_app/immutable/chunks/D2X8AmeL.js +1 -0
  106. pywebwinui3/web/_app/immutable/chunks/DLxx5-LD.js +2 -0
  107. pywebwinui3/web/_app/immutable/chunks/DPxT1AeO.js +1 -0
  108. pywebwinui3/web/_app/immutable/chunks/DZb1s-qq.js +1 -0
  109. pywebwinui3/web/_app/immutable/chunks/DbLPBgK4.js +1 -0
  110. pywebwinui3/web/_app/immutable/chunks/d_V4Mktp.js +1 -0
  111. pywebwinui3/web/_app/immutable/chunks/pGvP_QvR.js +1 -0
  112. pywebwinui3/web/_app/immutable/entry/app.DgyHcM8N.js +2 -0
  113. pywebwinui3/web/_app/immutable/entry/start.D7vHaDGu.js +1 -0
  114. pywebwinui3/web/_app/immutable/nodes/0.DO1mLoyu.js +54 -0
  115. pywebwinui3/web/_app/immutable/nodes/1.DvyFB-Wy.js +1 -0
  116. pywebwinui3/web/_app/immutable/nodes/2.CrF9bZZd.js +94 -0
  117. pywebwinui3/web/_app/version.json +1 -0
  118. pywebwinui3/web/index.html +101 -0
  119. pywebwinui3-1.0.0.dist-info/METADATA +92 -0
  120. pywebwinui3-1.0.0.dist-info/RECORD +124 -0
  121. pywebwinui3-1.0.0.dist-info/WHEEL +5 -0
  122. pywebwinui3-1.0.0.dist-info/licenses/LICENSE +201 -0
  123. pywebwinui3-1.0.0.dist-info/licenses/NOTICE +13 -0
  124. pywebwinui3-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3 @@
1
+ __all__ = ["core", "event", "util", "type"]
2
+
3
+ __version__ = '1.0.0'
pywebwinui3/core.py ADDED
@@ -0,0 +1,176 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING
7
+
8
+ from .event import Event
9
+ from .type import Status
10
+ from .util import AccentColorWatcher, SyncDict, loadPage
11
+
12
+ logger = logging.getLogger("pywebwinui3")
13
+
14
+ from .qt import WebviewAPI
15
+
16
+ DEFAULT_WINDOW_WIDTH = 900
17
+ DEFAULT_WINDOW_HEIGHT = 600
18
+ DEFAULT_WINDOW_MIN_WIDTH = 100
19
+ DEFAULT_WINDOW_MIN_HEIGHT = 100
20
+ ABSOLUTE_WINDOW_MIN_WIDTH = 1
21
+ ABSOLUTE_WINDOW_MIN_HEIGHT = 1
22
+
23
+
24
+ class WindowEvents:
25
+ def __init__(self) -> None:
26
+ self.windowReady = Event()
27
+ self.closed = Event()
28
+ self.accentColorChange = Event()
29
+ self.themeChange = Event()
30
+ self.valueChange = Event()
31
+
32
+
33
+ class MainWindow:
34
+ def __init__(self, title: str, icon: str | None = None):
35
+ self.rootPath = Path(inspect.currentframe().f_back.f_code.co_filename).parent.resolve()
36
+ self.packagePath = Path(__file__).parent.resolve() / "web"
37
+ self._title = title
38
+ self._icon = icon
39
+
40
+ self.accent = AccentColorWatcher()
41
+ self.events = WindowEvents()
42
+ self.api = WebviewAPI(self, self._title, self._icon)
43
+
44
+ self.values = SyncDict(
45
+ {
46
+ "system_title": title,
47
+ "system_icon": icon,
48
+ "system_theme": "system",
49
+ "system_theme_resolved": self.accent.theme,
50
+ "system_accent": self.accent.palette,
51
+ "system_pages": None,
52
+ "system_settings": None,
53
+ "system_nofication": [],
54
+ "system_pin": False,
55
+ "system_window_width": 900,
56
+ "system_window_height": 600,
57
+ }
58
+ )
59
+ self.values.sync = self.api.queue_sync_value
60
+
61
+ self.events.accentColorChange = self.accent.event
62
+ self.events.themeChange = self.accent.theme_event
63
+ self.events.valueChange = self.values.event
64
+ self.events.accentColorChange += lambda palette: self.values.set("system_accent", palette)
65
+ self.events.themeChange += lambda theme: self.values.set("system_theme_resolved", theme)
66
+
67
+ def onValueChange(self, key):
68
+ def decorator(func):
69
+ self.events.valueChange += (key, func)
70
+ return func
71
+
72
+ return decorator
73
+
74
+ def onAccentColorChange(self):
75
+ def decorator(func):
76
+ self.events.accentColorChange += func
77
+ return func
78
+
79
+ return decorator
80
+
81
+ def onThemeChange(self):
82
+ def decorator(func):
83
+ self.events.themeChange += func
84
+ return func
85
+
86
+ return decorator
87
+
88
+ def onSetup(self):
89
+ def decorator(func):
90
+ self.events.windowReady += func
91
+ return func
92
+
93
+ return decorator
94
+
95
+ def onExit(self):
96
+ def decorator(func):
97
+ self.events.closed += func
98
+ return func
99
+
100
+ return decorator
101
+
102
+ def notice(self, level: Status, title: str, description: str, item: dict | None = None):
103
+ self.values["system_nofication"] = [
104
+ *(self.values["system_nofication"] or []),
105
+ [level, title, description, item],
106
+ ]
107
+
108
+ def init(self) -> dict:
109
+ return dict(self.values)
110
+
111
+ def pin(self, state: bool):
112
+ state = bool(state)
113
+ self.values.set("system_pin", state, self.api is not None)
114
+ if self.api is not None:
115
+ self.api.set_on_top(state)
116
+ return state
117
+
118
+ def syncValue(self, key, value):
119
+ return self.values.set(key, value, False)
120
+
121
+ def addSettings(self, pageFile: str | Path | None = None, pageData: dict | None = None):
122
+ if pageFile and not pageData:
123
+ pageData = loadPage(pageFile)
124
+ logger.debug("Setting page: %s", pageData["attr"]["path"])
125
+ self.values["system_settings"] = pageData
126
+
127
+ def addPage(self, pageFile: str | Path | None = None, pageData: dict | None = None):
128
+ if pageFile and not pageData:
129
+ pageData = loadPage(pageFile)
130
+ logger.debug("Page added: %s", pageData["attr"]["path"])
131
+ self.values["system_pages"] = {
132
+ **(self.values["system_pages"] or {}),
133
+ pageData["attr"]["path"]: pageData,
134
+ }
135
+
136
+ def resolve_path(self, value: str | Path | None) -> Path | None:
137
+ if value is None:
138
+ return None
139
+
140
+ path = Path(value)
141
+ if path.is_absolute():
142
+ return path
143
+
144
+ root_candidate = self.rootPath / path
145
+ if root_candidate.exists():
146
+ return root_candidate.resolve()
147
+
148
+ package_candidate = self.packagePath / path
149
+ if package_candidate.exists():
150
+ return package_candidate.resolve()
151
+
152
+ return root_candidate.resolve()
153
+
154
+ def resolve_resource_url(self, value):
155
+ if not isinstance(value, (str, Path)):
156
+ return value
157
+
158
+ raw_value = str(value).strip()
159
+ if not raw_value:
160
+ return value
161
+
162
+ lowered = raw_value.lower()
163
+ if lowered.startswith(("http://", "https://", "file://", "data:", "qrc://", "qrc:", "about:")):
164
+ return raw_value
165
+
166
+ resolved = self.resolve_path(raw_value)
167
+ if resolved is None or not resolved.exists():
168
+ return raw_value
169
+
170
+ return resolved.as_uri()
171
+
172
+ def start(self, debug: bool = False, min_width=900, min_height=600):
173
+ # if self.api is not None and getattr(self.api, "_window", None) is not None:
174
+ self.api.set_window_minimum_size(min_width, min_height)
175
+ self.accent.start()
176
+ self.api.start(debug=debug)
pywebwinui3/event.py ADDED
@@ -0,0 +1,147 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ import fnmatch
5
+ import logging
6
+ import traceback
7
+ from typing import Any, Callable
8
+
9
+ logger = logging.getLogger("pywebwinui3.eventmanager")
10
+
11
+ class Event:
12
+ def __init__(self) -> None:
13
+ self.items: list[Callable[..., Any]] = []
14
+
15
+ def set(self, *args: Any):
16
+ listeners = tuple(self.items)
17
+ if not listeners:
18
+ return
19
+
20
+ for func in listeners:
21
+ try:
22
+ func(*args)
23
+ except Exception:
24
+ logger.error(traceback.format_exc())
25
+
26
+ def __add__(self, item: Callable[..., Any]):
27
+ self.items.append(item)
28
+ return self
29
+
30
+ def __sub__(self, item: Callable[..., Any]):
31
+ self.items.remove(item)
32
+ return self
33
+
34
+ def __iadd__(self, item: Callable[..., Any]):
35
+ self.items.append(item)
36
+ return self
37
+
38
+ def __isub__(self, item: Callable[..., Any]):
39
+ self.items.remove(item)
40
+ return self
41
+
42
+ def __len__(self) -> int:
43
+ return len(self.items)
44
+
45
+
46
+ class PathEvent:
47
+ def __init__(self) -> None:
48
+ self.exact_items: dict[str, Event] = {}
49
+ self.pattern_items: dict[str, Event] = {}
50
+ self._compiled_patterns: dict[str, re.Pattern[str]] = {}
51
+ self._pattern_cache: dict[str, tuple[Event, ...]] = {}
52
+
53
+ @staticmethod
54
+ def _is_pattern(target: str) -> bool:
55
+ return any(char in target for char in "*?[]")
56
+
57
+ def _bucket(self, target: str) -> dict[str, Event]:
58
+ return self.pattern_items if self._is_pattern(target) else self.exact_items
59
+
60
+ def _clear_pattern_cache(self) -> None:
61
+ self._pattern_cache.clear()
62
+
63
+ def _compile_pattern(self, pattern: str) -> re.Pattern[str]:
64
+ regex = self._compiled_patterns.get(pattern)
65
+ if regex is None:
66
+ regex = re.compile(fnmatch.translate(pattern))
67
+ self._compiled_patterns[pattern] = regex
68
+ return regex
69
+
70
+ def _get_pattern_events(self, target: str) -> tuple[Event, ...]:
71
+ cached = self._pattern_cache.get(target)
72
+ if cached is not None:
73
+ return cached
74
+
75
+ matched_events: list[Event] = []
76
+ for key, event in tuple(self.pattern_items.items()):
77
+ try:
78
+ if self._compile_pattern(key).match(target):
79
+ matched_events.append(event)
80
+ except Exception:
81
+ logger.error(traceback.format_exc())
82
+
83
+ result = tuple(matched_events)
84
+ self._pattern_cache[target] = result
85
+ return result
86
+
87
+ def set(self, target: str, *args: Any):
88
+ if not self.exact_items and not self.pattern_items:
89
+ return
90
+
91
+ exact_event = self.exact_items.get(target)
92
+ if exact_event is not None:
93
+ try:
94
+ exact_event.set(target, *args)
95
+ except Exception:
96
+ logger.error(traceback.format_exc())
97
+
98
+ for event in self._get_pattern_events(target):
99
+ try:
100
+ event.set(target, *args)
101
+ except Exception:
102
+ logger.error(traceback.format_exc())
103
+
104
+ def __add__(self, item: list):
105
+ target, callback = item[0], item[1]
106
+ bucket = self._bucket(target)
107
+ bucket.setdefault(target, Event()).__iadd__(callback)
108
+
109
+ if self._is_pattern(target):
110
+ self._compiled_patterns.setdefault(target, self._compile_pattern(target))
111
+ self._clear_pattern_cache()
112
+
113
+ return self
114
+
115
+ def __sub__(self, item: list):
116
+ target, callback = item[0], item[1]
117
+ bucket = self._bucket(target)
118
+ bucket.setdefault(target, Event()).__isub__(callback)
119
+
120
+ if self._is_pattern(target):
121
+ self._clear_pattern_cache()
122
+
123
+ return self
124
+
125
+ def __iadd__(self, item: list):
126
+ target, callback = item[0], item[1]
127
+ bucket = self._bucket(target)
128
+ bucket.setdefault(target, Event()).__iadd__(callback)
129
+
130
+ if self._is_pattern(target):
131
+ self._compiled_patterns.setdefault(target, self._compile_pattern(target))
132
+ self._clear_pattern_cache()
133
+
134
+ return self
135
+
136
+ def __isub__(self, item: list):
137
+ target, callback = item[0], item[1]
138
+ bucket = self._bucket(target)
139
+ bucket.setdefault(target, Event()).__isub__(callback)
140
+
141
+ if self._is_pattern(target):
142
+ self._clear_pattern_cache()
143
+
144
+ return self
145
+
146
+ def __len__(self) -> int:
147
+ return len(self.exact_items) + len(self.pattern_items)