kigo-gui-framework 3.0__tar.gz → 3.2__tar.gz

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 (70) hide show
  1. kigo_gui_framework-3.2/PKG-INFO +35 -0
  2. kigo_gui_framework-3.2/README.md +13 -0
  3. kigo_gui_framework-3.2/README.txt +2 -0
  4. kigo_gui_framework-3.2/kigo/android/__init__.py +10 -0
  5. kigo_gui_framework-3.2/kigo/android/info.py +11 -0
  6. kigo_gui_framework-3.2/kigo/android/lifecycle.py +20 -0
  7. kigo_gui_framework-3.2/kigo/android/ossupport.py +181 -0
  8. kigo_gui_framework-3.2/kigo/cli/__init__.py +1 -0
  9. kigo_gui_framework-3.2/kigo/cli/doctor.py +90 -0
  10. kigo_gui_framework-3.2/kigo/cli/main.py +46 -0
  11. kigo_gui_framework-3.2/kigo/debug/__init__.py +3 -0
  12. kigo_gui_framework-3.2/kigo/debug/freeze.py +54 -0
  13. kigo_gui_framework-3.2/kigo/ecs/__init__.py +6 -0
  14. kigo_gui_framework-3.2/kigo/ecs/component.py +5 -0
  15. kigo_gui_framework-3.2/kigo/ecs/entity.py +1 -0
  16. kigo_gui_framework-3.2/kigo/ecs/render.py +38 -0
  17. kigo_gui_framework-3.2/kigo/ecs/system.py +10 -0
  18. kigo_gui_framework-3.2/kigo/ecs/world.py +59 -0
  19. kigo_gui_framework-3.2/kigo/inspector/__init__.py +3 -0
  20. kigo_gui_framework-3.2/kigo/inspector/inspector.py +50 -0
  21. kigo_gui_framework-3.2/kigo/inspector/overlay.py +36 -0
  22. kigo_gui_framework-3.2/kigo/inspector/panel.py +41 -0
  23. kigo_gui_framework-3.2/kigo/logging/__init__.py +3 -0
  24. kigo_gui_framework-3.2/kigo/logging/jsonlog.py +49 -0
  25. kigo_gui_framework-3.2/kigo/net/__init__.py +9 -0
  26. kigo_gui_framework-3.2/kigo/net/qnetwork.py +165 -0
  27. kigo_gui_framework-3.2/kigo/qt/__init__.py +3 -0
  28. kigo_gui_framework-3.2/kigo/qt/backend.py +13 -0
  29. kigo_gui_framework-3.2/kigo/studio/__init__.py +9 -0
  30. kigo_gui_framework-3.2/kigo/studio/core.py +40 -0
  31. kigo_gui_framework-3.2/kigo/studio/overlay.py +49 -0
  32. kigo_gui_framework-3.2/kigo/wasm/__init__.py +5 -0
  33. kigo_gui_framework-3.2/kigo/wasm/executor.py +45 -0
  34. kigo_gui_framework-3.2/kigo/wasm/module.py +9 -0
  35. kigo_gui_framework-3.2/kigo_gui_framework.egg-info/PKG-INFO +35 -0
  36. kigo_gui_framework-3.2/kigo_gui_framework.egg-info/SOURCES.txt +61 -0
  37. kigo_gui_framework-3.2/kigo_gui_framework.egg-info/entry_points.txt +2 -0
  38. kigo_gui_framework-3.2/kigo_gui_framework.egg-info/requires.txt +6 -0
  39. kigo_gui_framework-3.2/kigo_gui_framework.egg-info/top_level.txt +1 -0
  40. kigo_gui_framework-3.2/pyproject.toml +71 -0
  41. kigo_gui_framework-3.0/PKG-INFO +0 -35
  42. kigo_gui_framework-3.0/README.txt +0 -10
  43. kigo_gui_framework-3.0/kigo_gui_framework.egg-info/PKG-INFO +0 -35
  44. kigo_gui_framework-3.0/kigo_gui_framework.egg-info/SOURCES.txt +0 -28
  45. kigo_gui_framework-3.0/kigo_gui_framework.egg-info/requires.txt +0 -12
  46. kigo_gui_framework-3.0/kigo_gui_framework.egg-info/top_level.txt +0 -1
  47. kigo_gui_framework-3.0/pyproject.toml +0 -51
  48. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/MANIFEST.in +0 -0
  49. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/_init_.py +0 -0
  50. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/accelerate.py +0 -0
  51. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/app.py +0 -0
  52. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/fx_gl2d.py +0 -0
  53. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/fx_quick.py +0 -0
  54. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/gpu.py +0 -0
  55. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/hud.py +0 -0
  56. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/hwaccel.py +0 -0
  57. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/media.py +0 -0
  58. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/physics.py +0 -0
  59. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/physics_policy.py +0 -0
  60. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/platform.py +0 -0
  61. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/runtime.py +0 -0
  62. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/shader_demo.py +0 -0
  63. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/shim.py +0 -0
  64. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/skins.py +0 -0
  65. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/style.py +0 -0
  66. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/tree.py +0 -0
  67. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo/widgets.py +0 -0
  68. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/kigo_gui_framework.egg-info/dependency_links.txt +0 -0
  69. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/setup.cfg +0 -0
  70. {kigo_gui_framework-3.0 → kigo_gui_framework-3.2}/setup.py +0 -0
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: kigo-gui-framework
3
+ Version: 3.2
4
+ Summary: Kigo has Time Freeze Debugging and a Visual Inspector now.
5
+ Author: Anand Kumar
6
+ License: Zlib
7
+ Keywords: gui,qt,pyqt,webengine,opengl,wasm,android,chromeos,framework
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: License :: OSI Approved :: zlib/libpng License
11
+ Classifier: Operating System :: Microsoft :: Windows
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Operating System :: MacOS :: MacOS X
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: PyQt6
17
+ Requires-Dist: pybullet
18
+ Requires-Dist: pybullet-data
19
+ Requires-Dist: PyQt6-WebEngine
20
+ Requires-Dist: PyOpenGL
21
+ Requires-Dist: wasmtime
22
+
23
+ Kigo now has support for networking and
24
+ SunOS
25
+ Solaris
26
+ FreeBSD
27
+ Android
28
+ Linux
29
+ Mac
30
+ Windous
31
+ ChromeOS
32
+ Kigo also has two CLI commands:
33
+ kigo run
34
+ kigo doctor.
35
+ CLASS 5 IS TOUGH. VERY TOUGH. but you will get updates
@@ -0,0 +1,13 @@
1
+ Kigo now has support for networking and
2
+ SunOS
3
+ Solaris
4
+ FreeBSD
5
+ Android
6
+ Linux
7
+ Mac
8
+ Windous
9
+ ChromeOS
10
+ Kigo also has two CLI commands:
11
+ kigo run
12
+ kigo doctor.
13
+ CLASS 5 IS TOUGH. VERY TOUGH. but you will get updates
@@ -0,0 +1,2 @@
1
+ Kigo now has time freeze debugging and a scene inspector using my Neolog
2
+ engine (well, it is based off of Qt's things, but who cares, neolog is a good name)
@@ -0,0 +1,10 @@
1
+ from .platform import is_android
2
+ from .lifecycle import AndroidLifecycle
3
+ from .info import device_info
4
+
5
+ __all__ = [
6
+ "is_android",
7
+ "AndroidLifecycle",
8
+ "device_info",
9
+ ]
10
+
@@ -0,0 +1,11 @@
1
+ # kigo/android/info.py
2
+ from kigo.android.platform import is_android, android_api_level
3
+
4
+ def device_info():
5
+ if not is_android():
6
+ return None
7
+
8
+ return {
9
+ "api_level": android_api_level(),
10
+ "platform": "android",
11
+ }
@@ -0,0 +1,20 @@
1
+ from kigo.qt.backend import QtCore
2
+
3
+ # Alias Signal correctly for PyQt6 and PySide6
4
+ Signal = getattr(QtCore, "Signal", QtCore.pyqtSignal)
5
+
6
+
7
+ class AndroidLifecycle(QtCore.QObject):
8
+ paused = Signal()
9
+ resumed = Signal()
10
+
11
+ def __init__(self, app):
12
+ super().__init__()
13
+ self.app = app
14
+ app.applicationStateChanged.connect(self._on_state)
15
+
16
+ def _on_state(self, state):
17
+ if state == QtCore.Qt.ApplicationState.ApplicationSuspended:
18
+ self.paused.emit()
19
+ elif state == QtCore.Qt.ApplicationState.ApplicationActive:
20
+ self.resumed.emit()
@@ -0,0 +1,181 @@
1
+ # SPDX-License-Identifier: Zlib
2
+ # kigo/platform_info.py
3
+
4
+ import sys
5
+ import os
6
+ import platform as _platform
7
+
8
+
9
+ # -------------------------------------------------
10
+ # Mobile platforms
11
+ # -------------------------------------------------
12
+
13
+ def is_android() -> bool:
14
+ """True if running on Android (ARC++ or native)."""
15
+ return hasattr(sys, "getandroidapilevel")
16
+
17
+
18
+ def android_api_level():
19
+ """Return Android API level or None."""
20
+ if hasattr(sys, "getandroidapilevel"):
21
+ return sys.getandroidapilevel()
22
+ return None
23
+
24
+
25
+ def is_ios() -> bool:
26
+ """True if running on iOS (future-ready)."""
27
+ return sys.platform == "ios"
28
+
29
+
30
+ # -------------------------------------------------
31
+ # Desktop OS detection
32
+ # -------------------------------------------------
33
+
34
+ def is_windows() -> bool:
35
+ return sys.platform.startswith("win")
36
+
37
+
38
+ def is_linux() -> bool:
39
+ return sys.platform.startswith("linux")
40
+
41
+
42
+ def is_macos() -> bool:
43
+ return sys.platform == "darwin"
44
+
45
+
46
+ def is_freebsd() -> bool:
47
+ return sys.platform.startswith("freebsd")
48
+
49
+
50
+ def is_openbsd() -> bool:
51
+ return sys.platform.startswith("openbsd")
52
+
53
+
54
+ def is_sunos() -> bool:
55
+ # Solaris reports as "sunos"
56
+ return sys.platform == "sunos"
57
+
58
+
59
+ # -------------------------------------------------
60
+ # ChromeOS detection (Crostini / ARC++)
61
+ # -------------------------------------------------
62
+
63
+ def is_chromeos() -> bool:
64
+ """
65
+ Detect ChromeOS.
66
+ Covers:
67
+ - Linux (Crostini)
68
+ - Android Runtime (ARC++)
69
+ """
70
+ # Crostini exposes this env var
71
+ if "SOMMELIER_VERSION" in os.environ:
72
+ return True
73
+
74
+ # Fallback: os-release check
75
+ try:
76
+ with open("/etc/os-release", "r", encoding="utf-8") as f:
77
+ data = f.read().lower()
78
+ if "chromeos" in data or "chromiumos" in data:
79
+ return True
80
+ except Exception:
81
+ pass
82
+
83
+ return False
84
+
85
+
86
+ # -------------------------------------------------
87
+ # Platform groups
88
+ # -------------------------------------------------
89
+
90
+ def is_mobile() -> bool:
91
+ return is_android() or is_ios()
92
+
93
+
94
+ def is_desktop() -> bool:
95
+ return not is_mobile()
96
+
97
+
98
+ def is_bsd() -> bool:
99
+ return is_freebsd() or is_openbsd()
100
+
101
+
102
+ def is_unix_desktop() -> bool:
103
+ return (
104
+ is_linux()
105
+ or is_macos()
106
+ or is_bsd()
107
+ or is_sunos()
108
+ or is_chromeos()
109
+ )
110
+
111
+
112
+ # -------------------------------------------------
113
+ # Window system detection (X11 / Wayland)
114
+ # -------------------------------------------------
115
+
116
+ def window_system() -> str:
117
+ """
118
+ Returns:
119
+ 'wayland', 'x11', or 'unknown'
120
+ """
121
+ if os.environ.get("WAYLAND_DISPLAY"):
122
+ return "wayland"
123
+ if os.environ.get("DISPLAY"):
124
+ return "x11"
125
+ return "unknown"
126
+
127
+
128
+ # -------------------------------------------------
129
+ # Qt backend decision
130
+ # -------------------------------------------------
131
+
132
+ def qt_backend() -> str:
133
+ """
134
+ Android → PySide6
135
+ Everything else → PyQt6
136
+ """
137
+ if is_android():
138
+ return "pyside"
139
+ return "pyqt"
140
+
141
+
142
+ # -------------------------------------------------
143
+ # Human-readable platform name
144
+ # -------------------------------------------------
145
+
146
+ def platform_name() -> str:
147
+ if is_android():
148
+ return "android"
149
+ if is_chromeos():
150
+ return "chromeos"
151
+ if is_ios():
152
+ return "ios"
153
+ if is_windows():
154
+ return "windows"
155
+ if is_macos():
156
+ return "macos"
157
+ if is_linux():
158
+ return "linux"
159
+ if is_freebsd():
160
+ return "freebsd"
161
+ if is_openbsd():
162
+ return "openbsd"
163
+ if is_sunos():
164
+ return "sunos/solaris"
165
+ return _platform.system().lower()
166
+
167
+
168
+ # -------------------------------------------------
169
+ # Debug / dev helper
170
+ # -------------------------------------------------
171
+
172
+ def summary() -> dict:
173
+ return {
174
+ "platform": platform_name(),
175
+ "chromeos": is_chromeos(),
176
+ "mobile": is_mobile(),
177
+ "desktop": is_desktop(),
178
+ "window_system": window_system(),
179
+ "qt_backend": qt_backend(),
180
+ "android_api": android_api_level(),
181
+ }
@@ -0,0 +1 @@
1
+ __all__=[]
@@ -0,0 +1,90 @@
1
+ # SPDX-License-Identifier: Zlib
2
+ # kigo/cli/doctor.py
3
+
4
+ import sys
5
+ import os
6
+ import importlib.util
7
+
8
+
9
+ def ok(msg): print(f"✅ {msg}")
10
+ def warn(msg): print(f"⚠️ {msg}")
11
+ def err(msg): print(f"❌ {msg}")
12
+
13
+
14
+ def run_doctor():
15
+ print("Kigo Doctor running…\n")
16
+
17
+ checks = [
18
+ check_python,
19
+ check_app_file,
20
+ check_kigo_import,
21
+ check_qt_backend,
22
+ check_platform_file,
23
+ ]
24
+
25
+ failed = False
26
+ for check in checks:
27
+ try:
28
+ result = check()
29
+ if result is False:
30
+ failed = True
31
+ except Exception as e:
32
+ err(f"{check.__name__} crashed: {e}")
33
+ failed = True
34
+
35
+ print()
36
+ if failed:
37
+ err("Problems detected. Fix the issues above.")
38
+ sys.exit(1)
39
+ else:
40
+ ok("No critical issues found. You’re good to go 🚀")
41
+
42
+
43
+ # -------------------------
44
+ # Individual checks
45
+ # -------------------------
46
+
47
+ def check_python():
48
+ if sys.version_info < (3, 9):
49
+ err("Python 3.9+ is required")
50
+ return False
51
+ ok(f"Python {sys.version.split()[0]}")
52
+ return True
53
+
54
+
55
+ def check_app_file():
56
+ if not os.path.exists("app.py"):
57
+ err("app.py not found")
58
+ return False
59
+ ok("app.py found")
60
+ return True
61
+
62
+
63
+ def check_kigo_import():
64
+ try:
65
+ import kigo
66
+ ok("Kigo importable")
67
+ return True
68
+ except Exception as e:
69
+ err(f"Kigo import failed: {e}")
70
+ return False
71
+
72
+
73
+ def check_qt_backend():
74
+ try:
75
+ from kigo.qt.backend import QtCore
76
+ ok("Qt backend OK")
77
+ return True
78
+ except Exception as e:
79
+ err(f"Qt backend failed: {e}")
80
+ return False
81
+
82
+
83
+ def check_platform_file():
84
+ try:
85
+ from kigo.platform_info import platform_name
86
+ ok(f"Platform detected: {platform_name()}")
87
+ return True
88
+ except Exception as e:
89
+ err(f"Platform detection failed: {e}")
90
+ return False
@@ -0,0 +1,46 @@
1
+ # SPDX-License-Identifier: Zlib
2
+ # kigo/cli/main.py
3
+
4
+ import sys
5
+ import argparse
6
+
7
+ from kigo.cli.doctor import run_doctor
8
+
9
+
10
+ def main():
11
+ parser = argparse.ArgumentParser(
12
+ prog="kigo",
13
+ description="Kigo CLI"
14
+ )
15
+
16
+ sub = parser.add_subparsers(dest="command")
17
+
18
+ # kigo run
19
+ sub.add_parser("run", help="Run the Kigo app")
20
+
21
+ # kigo doctor
22
+ sub.add_parser("doctor", help="Diagnose common Kigo issues")
23
+
24
+ args = parser.parse_args()
25
+
26
+ if args.command == "run":
27
+ run_app()
28
+ elif args.command == "doctor":
29
+ run_doctor()
30
+ else:
31
+ parser.print_help()
32
+
33
+
34
+ def run_app():
35
+ """
36
+ Runs app.py in the current directory.
37
+ """
38
+ try:
39
+ __import__("app")
40
+ except ModuleNotFoundError:
41
+ print("❌ No app.py found in current directory. Please rename your file to app.py and try again.")
42
+ sys.exit(1)
43
+ except Exception as e:
44
+ print("❌ Failed to run app:")
45
+ print(e)
46
+ sys.exit(1)
@@ -0,0 +1,3 @@
1
+ from .freeze import freeze
2
+
3
+ __all__ = ["freeze"]
@@ -0,0 +1,54 @@
1
+ # SPDX-License-Identifier: Zlib
2
+ # kigo/debug/freeze.py
3
+
4
+ from kigo.qt.backend import QtCore, QtWidgets
5
+
6
+
7
+ def freeze(reason: str = "Execution paused", *, data: dict | None = None):
8
+ """
9
+ Freeze execution while keeping the Qt event loop alive.
10
+ Used for visual + state debugging.
11
+
12
+ This does NOT crash or block Qt internally.
13
+ """
14
+
15
+ # Dialog shown during freeze
16
+ dialog = QtWidgets.QDialog()
17
+ dialog.setWindowTitle("Kigo Time Freeze")
18
+ dialog.setModal(False)
19
+ dialog.setWindowFlags(
20
+ QtCore.Qt.WindowType.WindowStaysOnTopHint
21
+ | QtCore.Qt.WindowType.Dialog
22
+ )
23
+
24
+ layout = QtWidgets.QVBoxLayout(dialog)
25
+
26
+ label = QtWidgets.QLabel(f"🧊 {reason}")
27
+ label.setStyleSheet("font-weight: bold; font-size: 14px;")
28
+ layout.addWidget(label)
29
+
30
+ if data:
31
+ text = QtWidgets.QPlainTextEdit()
32
+ text.setReadOnly(True)
33
+ pretty = "\n".join(f"{k}: {v}" for k, v in data.items())
34
+ text.setPlainText(pretty)
35
+ layout.addWidget(text)
36
+
37
+ btn = QtWidgets.QPushButton("Resume")
38
+ layout.addWidget(btn)
39
+
40
+ # Local event loop = time freeze
41
+ loop = QtCore.QEventLoop()
42
+
43
+ btn.clicked.connect(loop.quit)
44
+ dialog.finished.connect(loop.quit)
45
+
46
+ dialog.show()
47
+
48
+ # Process paint events before freezing
49
+ QtWidgets.QApplication.processEvents()
50
+
51
+ # TIME FREEZE STARTS HERE
52
+ loop.exec()
53
+
54
+ dialog.close()
@@ -0,0 +1,6 @@
1
+ from .world import World
2
+ from .entity import EntityId
3
+ from .component import Component
4
+ from .system import System
5
+
6
+ __all__ = ["World", "EntityId", "Component", "System"]
@@ -0,0 +1,5 @@
1
+ class Component:
2
+ """
3
+ Base class for components (data-only).
4
+ """
5
+ pass
@@ -0,0 +1 @@
1
+ EntityId=int
@@ -0,0 +1,38 @@
1
+ # kigo/ecs/render.py
2
+ from kigo.ecs.system import System
3
+ from kigo.ecs.world import World
4
+ from kigo.qt.backend import QtGui
5
+ from .render import Transform, Renderable
6
+
7
+
8
+ class RenderSystem(System):
9
+ """
10
+ Pulls render data from ECS and issues draw calls.
11
+ """
12
+
13
+ def __init__(self, painter: QtGui.QPainter):
14
+ self.painter = painter
15
+
16
+ def update(self, world: World, dt: float):
17
+ entities = world.get_entities_with(Transform, Renderable)
18
+
19
+ for e in entities:
20
+ transform = world.get_component(e, Transform)
21
+ renderable = world.get_component(e, Renderable)
22
+
23
+ self._draw(transform, renderable)
24
+
25
+ def _draw(self, t: Transform, r: Renderable):
26
+ self.painter.save()
27
+ self.painter.translate(t.x, t.y)
28
+ self.painter.rotate(t.rotation)
29
+ self.painter.scale(t.scale, t.scale)
30
+
31
+ if r.kind == "rect":
32
+ self.painter.drawRect(0, 0, 50, 50)
33
+
34
+ elif r.kind == "image" and r.resource:
35
+ pix = QtGui.QPixmap(r.resource)
36
+ self.painter.drawPixmap(0, 0, pix)
37
+
38
+ self.painter.restore()
@@ -0,0 +1,10 @@
1
+ class System:
2
+ """
3
+ Base class for systems.
4
+ """
5
+
6
+ def update(self, world, dt: float):
7
+ """
8
+ Override in subclasses.
9
+ """
10
+ pass
@@ -0,0 +1,59 @@
1
+ from collections import defaultdict
2
+ from typing import Dict, Type, Set
3
+
4
+ from .entity import EntityId
5
+ from .component import Component
6
+ from .system import System
7
+
8
+
9
+ class World:
10
+ def __init__(self):
11
+ self._next_entity: EntityId = 1
12
+ self._components: Dict[Type[Component], Dict[EntityId, Component]] = defaultdict(dict)
13
+ self._systems: list[System] = []
14
+
15
+ # ----------------------------
16
+ # Entity
17
+ # ----------------------------
18
+ def create_entity(self) -> EntityId:
19
+ eid = self._next_entity
20
+ self._next_entity += 1
21
+ return eid
22
+
23
+ def remove_entity(self, entity: EntityId):
24
+ for comp_map in self._components.values():
25
+ comp_map.pop(entity, None)
26
+
27
+ # ----------------------------
28
+ # Components
29
+ # ----------------------------
30
+ def add_component(self, entity: EntityId, component: Component):
31
+ self._components[type(component)][entity] = component
32
+
33
+ def remove_component(self, entity: EntityId, component_type: Type[Component]):
34
+ self._components[component_type].pop(entity, None)
35
+
36
+ def get_component(self, entity: EntityId, component_type: Type[Component]):
37
+ return self._components[component_type].get(entity)
38
+
39
+ def get_entities_with(self, *component_types: Type[Component]) -> Set[EntityId]:
40
+ if not component_types:
41
+ return set()
42
+
43
+ entity_sets = [
44
+ set(self._components[ct].keys())
45
+ for ct in component_types
46
+ if ct in self._components
47
+ ]
48
+
49
+ return set.intersection(*entity_sets) if entity_sets else set()
50
+
51
+ # ----------------------------
52
+ # Systems
53
+ # ----------------------------
54
+ def add_system(self, system: System):
55
+ self._systems.append(system)
56
+
57
+ def update(self, dt: float):
58
+ for system in self._systems:
59
+ system.update(self, dt)
@@ -0,0 +1,3 @@
1
+ from .inspector import enable_inspector
2
+
3
+ __all__ = ["enable_inspector"]
@@ -0,0 +1,50 @@
1
+ # SPDX-License-Identifier: Zlib
2
+ # kigo/inspector/inspector.py
3
+
4
+ from kigo.qt.backend import QtCore, QtWidgets
5
+ from kigo.inspector.overlay import Overlay
6
+ from kigo.inspector.panel import InspectorPanel
7
+
8
+
9
+ class Inspector(QtCore.QObject):
10
+ def __init__(self, app: QtWidgets.QApplication):
11
+ super().__init__()
12
+ self.app = app
13
+ self.overlay = Overlay()
14
+ self.panel = InspectorPanel()
15
+
16
+ self.app.installEventFilter(self)
17
+
18
+ def attach(self):
19
+ for w in self.app.topLevelWidgets():
20
+ if isinstance(w, QtWidgets.QMainWindow):
21
+ w.addDockWidget(
22
+ QtCore.Qt.DockWidgetArea.RightDockWidgetArea,
23
+ self.panel,
24
+ )
25
+ self.overlay.setParent(w)
26
+ self.overlay.resize(w.size())
27
+
28
+ def eventFilter(self, obj, event):
29
+ if isinstance(event, QtCore.QEvent):
30
+ if event.type() == QtCore.QEvent.Type.MouseMove:
31
+ widget = self.app.widgetAt(event.globalPosition().toPoint())
32
+ if widget:
33
+ rect = widget.rect()
34
+ top_left = widget.mapToGlobal(rect.topLeft())
35
+ self.overlay.highlight(
36
+ QtCore.QRect(top_left, rect.size())
37
+ )
38
+
39
+ elif event.type() == QtCore.QEvent.Type.MouseButtonPress:
40
+ widget = self.app.widgetAt(event.globalPosition().toPoint())
41
+ self.panel.inspect(widget)
42
+ return True
43
+
44
+ return False
45
+
46
+
47
+ def enable_inspector(app: QtWidgets.QApplication):
48
+ inspector = Inspector(app)
49
+ inspector.attach()
50
+ return inspector