pygkit 1.0.2__tar.gz → 1.0.4__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.
- {pygkit-1.0.2 → pygkit-1.0.4}/PKG-INFO +5 -2
- {pygkit-1.0.2 → pygkit-1.0.4}/README.md +4 -1
- {pygkit-1.0.2 → pygkit-1.0.4}/pyproject.toml +1 -1
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/__init__.py +4 -0
- pygkit-1.0.4/src/pygkit/signals/__init__.py +3 -0
- pygkit-1.0.4/src/pygkit/signals/core.py +87 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/ui/dialog.py +9 -13
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit.egg-info/PKG-INFO +5 -2
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit.egg-info/SOURCES.txt +3 -0
- pygkit-1.0.4/tests/test_signals.py +222 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/LICENSE +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/setup.cfg +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/audio/__init__.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/audio/soundmanager.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/inventory/__init__.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/inventory/assets.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/inventory/core.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/inventory/data.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/inventory/ui.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/protocols.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/transitions/__init__.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/transitions/base.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/transitions/effects.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/transitions/fades.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/transitions/wipes.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/ui/__init__.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/ui/base.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/ui/container.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/ui/cooldown.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/ui/progressbar.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/utils/__init__.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/utils/interpolation.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/utils/text.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit/utils/timer.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit.egg-info/dependency_links.txt +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit.egg-info/requires.txt +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/src/pygkit.egg-info/top_level.txt +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/tests/test_box_model.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/tests/test_dialog.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/tests/test_interpolation.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/tests/test_inventory.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/tests/test_text.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/tests/test_timer.py +0 -0
- {pygkit-1.0.2 → pygkit-1.0.4}/tests/test_transitions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygkit
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: General-purpose Pygame CE runtime kit: UI widgets, audio, timers, interpolation, and more.
|
|
5
5
|
Author: pygkit contributors
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -708,8 +708,11 @@ import pygkit
|
|
|
708
708
|
|
|
709
709
|
## Features
|
|
710
710
|
|
|
711
|
-
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers
|
|
711
|
+
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers, dialog system
|
|
712
|
+
- **Dialog**: Typewriter effect with customizable speeds, speaker names, and progress indicators
|
|
713
|
+
- **Inventory**: Grid-based item management with stacking, weight tracking, events, and serialization
|
|
712
714
|
- **Audio**: SoundManager with channel pools and explicit targeting
|
|
715
|
+
- **Transitions**: Screen transitions with fade, wipe, and custom effects
|
|
713
716
|
- **Utils**: Timer, interpolation/easing functions
|
|
714
717
|
|
|
715
718
|
## Installation
|
|
@@ -10,8 +10,11 @@ import pygkit
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers
|
|
13
|
+
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers, dialog system
|
|
14
|
+
- **Dialog**: Typewriter effect with customizable speeds, speaker names, and progress indicators
|
|
15
|
+
- **Inventory**: Grid-based item management with stacking, weight tracking, events, and serialization
|
|
14
16
|
- **Audio**: SoundManager with channel pools and explicit targeting
|
|
17
|
+
- **Transitions**: Screen transitions with fade, wipe, and custom effects
|
|
15
18
|
- **Utils**: Timer, interpolation/easing functions
|
|
16
19
|
|
|
17
20
|
## Installation
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pygkit"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.4"
|
|
8
8
|
description = "General-purpose Pygame CE runtime kit: UI widgets, audio, timers, interpolation, and more."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -2,6 +2,7 @@ from .audio import *
|
|
|
2
2
|
from .transitions import *
|
|
3
3
|
from .ui import *
|
|
4
4
|
from .utils import *
|
|
5
|
+
from .signals import *
|
|
5
6
|
|
|
6
7
|
__all__ = [
|
|
7
8
|
"SoundManager",
|
|
@@ -43,4 +44,7 @@ __all__ = [
|
|
|
43
44
|
"wrap",
|
|
44
45
|
"render_multiline",
|
|
45
46
|
"fit",
|
|
47
|
+
"Signal",
|
|
48
|
+
"SignalBus",
|
|
49
|
+
"signal",
|
|
46
50
|
]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Generic, TypeVar
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Signal(Generic[T]):
|
|
9
|
+
def __init__(self, name: str = ""):
|
|
10
|
+
self.name = name
|
|
11
|
+
self._connections: list[Callable[..., Any]] = []
|
|
12
|
+
|
|
13
|
+
def connect(self, callback: Callable[..., Any]) -> None:
|
|
14
|
+
if callback in self._connections:
|
|
15
|
+
return
|
|
16
|
+
self._connections.append(callback)
|
|
17
|
+
|
|
18
|
+
def disconnect(self, callback: Callable[..., Any]) -> None:
|
|
19
|
+
self._connections = [c for c in self._connections if c is not callback]
|
|
20
|
+
|
|
21
|
+
def disconnect_all(self) -> None:
|
|
22
|
+
self._connections.clear()
|
|
23
|
+
|
|
24
|
+
def emit(self, *args: Any, **kwargs: Any) -> None:
|
|
25
|
+
for callback in list(self._connections):
|
|
26
|
+
try:
|
|
27
|
+
callback(*args, **kwargs)
|
|
28
|
+
except Exception as e:
|
|
29
|
+
print(f"Error in signal {self.name}: {e}")
|
|
30
|
+
|
|
31
|
+
def __iadd__(self, callback: Callable[..., Any]) -> Signal[T]:
|
|
32
|
+
self.connect(callback)
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
def __isub__(self, callback: Callable[..., Any]) -> Signal[T]:
|
|
36
|
+
self.disconnect(callback)
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def __call__(self, *args: Any, **kwargs: Any) -> None:
|
|
40
|
+
self.emit(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class SignalBus:
|
|
44
|
+
def __init__(self):
|
|
45
|
+
self._signals: dict[str, Signal[Any]] = {}
|
|
46
|
+
|
|
47
|
+
def create_signal(self, name: str) -> Signal[Any]:
|
|
48
|
+
if name not in self._signals:
|
|
49
|
+
self._signals[name] = Signal(name)
|
|
50
|
+
return self._signals[name]
|
|
51
|
+
|
|
52
|
+
def get_signal(self, name: str) -> Signal[Any] | None:
|
|
53
|
+
return self._signals.get(name)
|
|
54
|
+
|
|
55
|
+
def emit(self, name: str, *args: Any, **kwargs: Any) -> None:
|
|
56
|
+
sig = self.get_signal(name)
|
|
57
|
+
if sig:
|
|
58
|
+
sig.emit(*args, **kwargs)
|
|
59
|
+
|
|
60
|
+
def connect(self, name: str, callback: Callable[..., Any]) -> None:
|
|
61
|
+
sig = self.get_signal(name)
|
|
62
|
+
if sig:
|
|
63
|
+
sig.connect(callback)
|
|
64
|
+
|
|
65
|
+
def disconnect(self, name: str, callback: Callable[..., Any]) -> None:
|
|
66
|
+
sig = self.get_signal(name)
|
|
67
|
+
if sig:
|
|
68
|
+
sig.disconnect(callback)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class signal:
|
|
72
|
+
def __init__(self, name: str | None = None):
|
|
73
|
+
self.name = name
|
|
74
|
+
|
|
75
|
+
def __set_name__(self, owner, name):
|
|
76
|
+
if self.name is None:
|
|
77
|
+
self.name = name
|
|
78
|
+
|
|
79
|
+
def __get__(self, obj, objtype=None):
|
|
80
|
+
if obj is None:
|
|
81
|
+
return self
|
|
82
|
+
try:
|
|
83
|
+
return obj.__dict__[self.name]
|
|
84
|
+
except KeyError:
|
|
85
|
+
sig = Signal(self.name)
|
|
86
|
+
obj.__dict__[self.name] = sig
|
|
87
|
+
return sig
|
|
@@ -275,25 +275,21 @@ class DialogBox(UIBase):
|
|
|
275
275
|
return
|
|
276
276
|
|
|
277
277
|
if not self._is_complete:
|
|
278
|
-
self.
|
|
279
|
-
|
|
280
|
-
chars_to_add = 0
|
|
278
|
+
text_len = len(self._current_line.text)
|
|
281
279
|
elapsed = self._typewriter_timer.elapsed()
|
|
282
280
|
interval = self._typewriter_timer.interval
|
|
283
281
|
|
|
284
|
-
if interval > 0:
|
|
282
|
+
if elapsed >= interval and interval > 0:
|
|
285
283
|
chars_to_add = max(1, int(elapsed / interval))
|
|
286
284
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
self._displayed_text = self._current_line.text[: self._char_index]
|
|
285
|
+
if self._char_index < text_len:
|
|
286
|
+
old_index = self._char_index
|
|
287
|
+
self._char_index = min(text_len, self._char_index + chars_to_add)
|
|
288
|
+
self._displayed_text = self._current_line.text[: self._char_index]
|
|
292
289
|
|
|
293
|
-
|
|
294
|
-
|
|
290
|
+
if self._char_index != old_index:
|
|
291
|
+
self._needs_rebuild = True
|
|
295
292
|
|
|
296
|
-
if self._char_index < text_len:
|
|
297
293
|
self._typewriter_timer.reset()
|
|
298
294
|
|
|
299
295
|
if self._char_index >= text_len:
|
|
@@ -400,7 +396,7 @@ class DialogBox(UIBase):
|
|
|
400
396
|
pos_offset: Position offset (x, y) on the screen
|
|
401
397
|
"""
|
|
402
398
|
|
|
403
|
-
if self._needs_rebuild
|
|
399
|
+
if self._needs_rebuild:
|
|
404
400
|
self._rebuild_text_surfaces()
|
|
405
401
|
|
|
406
402
|
self.draw_base()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygkit
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: General-purpose Pygame CE runtime kit: UI widgets, audio, timers, interpolation, and more.
|
|
5
5
|
Author: pygkit contributors
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -708,8 +708,11 @@ import pygkit
|
|
|
708
708
|
|
|
709
709
|
## Features
|
|
710
710
|
|
|
711
|
-
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers
|
|
711
|
+
- **UI**: Box-model widgets with plugin compositing — progress bars, cooldown overlays, containers, dialog system
|
|
712
|
+
- **Dialog**: Typewriter effect with customizable speeds, speaker names, and progress indicators
|
|
713
|
+
- **Inventory**: Grid-based item management with stacking, weight tracking, events, and serialization
|
|
712
714
|
- **Audio**: SoundManager with channel pools and explicit targeting
|
|
715
|
+
- **Transitions**: Screen transitions with fade, wipe, and custom effects
|
|
713
716
|
- **Utils**: Timer, interpolation/easing functions
|
|
714
717
|
|
|
715
718
|
## Installation
|
|
@@ -15,6 +15,8 @@ src/pygkit/inventory/assets.py
|
|
|
15
15
|
src/pygkit/inventory/core.py
|
|
16
16
|
src/pygkit/inventory/data.py
|
|
17
17
|
src/pygkit/inventory/ui.py
|
|
18
|
+
src/pygkit/signals/__init__.py
|
|
19
|
+
src/pygkit/signals/core.py
|
|
18
20
|
src/pygkit/transitions/__init__.py
|
|
19
21
|
src/pygkit/transitions/base.py
|
|
20
22
|
src/pygkit/transitions/effects.py
|
|
@@ -34,6 +36,7 @@ tests/test_box_model.py
|
|
|
34
36
|
tests/test_dialog.py
|
|
35
37
|
tests/test_interpolation.py
|
|
36
38
|
tests/test_inventory.py
|
|
39
|
+
tests/test_signals.py
|
|
37
40
|
tests/test_text.py
|
|
38
41
|
tests/test_timer.py
|
|
39
42
|
tests/test_transitions.py
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from pygkit.signals import Signal, SignalBus, signal
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TestSignal:
|
|
5
|
+
def test_connect_and_emit(self):
|
|
6
|
+
sig = Signal("test")
|
|
7
|
+
results = []
|
|
8
|
+
sig.connect(lambda: results.append("called"))
|
|
9
|
+
sig.emit()
|
|
10
|
+
assert results == ["called"]
|
|
11
|
+
|
|
12
|
+
def test_connect_multiple(self):
|
|
13
|
+
sig = Signal("test")
|
|
14
|
+
results = []
|
|
15
|
+
sig.connect(lambda: results.append("a"))
|
|
16
|
+
sig.connect(lambda: results.append("b"))
|
|
17
|
+
sig.emit()
|
|
18
|
+
assert results == ["a", "b"]
|
|
19
|
+
|
|
20
|
+
def test_disconnect(self):
|
|
21
|
+
sig = Signal("test")
|
|
22
|
+
results = []
|
|
23
|
+
cb = lambda: results.append("called")
|
|
24
|
+
sig.connect(cb)
|
|
25
|
+
sig.disconnect(cb)
|
|
26
|
+
sig.emit()
|
|
27
|
+
assert results == []
|
|
28
|
+
|
|
29
|
+
def test_disconnect_all(self):
|
|
30
|
+
sig = Signal("test")
|
|
31
|
+
results = []
|
|
32
|
+
sig.connect(lambda: results.append("a"))
|
|
33
|
+
sig.connect(lambda: results.append("b"))
|
|
34
|
+
sig.disconnect_all()
|
|
35
|
+
sig.emit()
|
|
36
|
+
assert results == []
|
|
37
|
+
|
|
38
|
+
def test_duplicate_connect(self):
|
|
39
|
+
sig = Signal("test")
|
|
40
|
+
results = []
|
|
41
|
+
cb = lambda: results.append("called")
|
|
42
|
+
sig.connect(cb)
|
|
43
|
+
sig.connect(cb)
|
|
44
|
+
sig.emit()
|
|
45
|
+
assert results == ["called"]
|
|
46
|
+
|
|
47
|
+
def test_emit_args(self):
|
|
48
|
+
sig = Signal("test")
|
|
49
|
+
results = []
|
|
50
|
+
sig.connect(lambda x, y: results.append((x, y)))
|
|
51
|
+
sig.emit(1, 2)
|
|
52
|
+
assert results == [(1, 2)]
|
|
53
|
+
|
|
54
|
+
def test_emit_kwargs(self):
|
|
55
|
+
sig = Signal("test")
|
|
56
|
+
results = []
|
|
57
|
+
sig.connect(lambda a=0, b=0: results.append((a, b)))
|
|
58
|
+
sig.emit(a=3, b=4)
|
|
59
|
+
assert results == [(3, 4)]
|
|
60
|
+
|
|
61
|
+
def test_iadd(self):
|
|
62
|
+
sig = Signal("test")
|
|
63
|
+
results = []
|
|
64
|
+
sig += lambda: results.append("called")
|
|
65
|
+
sig.emit()
|
|
66
|
+
assert results == ["called"]
|
|
67
|
+
|
|
68
|
+
def test_isub(self):
|
|
69
|
+
sig = Signal("test")
|
|
70
|
+
results = []
|
|
71
|
+
cb = lambda: results.append("called")
|
|
72
|
+
sig += cb
|
|
73
|
+
sig -= cb
|
|
74
|
+
sig.emit()
|
|
75
|
+
assert results == []
|
|
76
|
+
|
|
77
|
+
def test_call_syntax(self):
|
|
78
|
+
sig = Signal("test")
|
|
79
|
+
results = []
|
|
80
|
+
sig += lambda: results.append("called")
|
|
81
|
+
sig()
|
|
82
|
+
assert results == ["called"]
|
|
83
|
+
|
|
84
|
+
def test_name(self):
|
|
85
|
+
sig = Signal("my_signal")
|
|
86
|
+
assert sig.name == "my_signal"
|
|
87
|
+
|
|
88
|
+
def test_emit_error_does_not_break(self):
|
|
89
|
+
sig = Signal("test")
|
|
90
|
+
results = []
|
|
91
|
+
sig.connect(lambda: (_ for _ in ()).throw(ValueError("bad")))
|
|
92
|
+
sig.connect(lambda: results.append("ok"))
|
|
93
|
+
sig.emit()
|
|
94
|
+
assert results == ["ok"]
|
|
95
|
+
|
|
96
|
+
def test_weak_ref_allows_gc(self):
|
|
97
|
+
sig = Signal("test")
|
|
98
|
+
results = []
|
|
99
|
+
def cb():
|
|
100
|
+
results.append("called")
|
|
101
|
+
sig.connect(cb)
|
|
102
|
+
sig.emit()
|
|
103
|
+
assert results == ["called"]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class TestSignalBus:
|
|
107
|
+
def test_create_signal(self):
|
|
108
|
+
bus = SignalBus()
|
|
109
|
+
sig = bus.create_signal("test")
|
|
110
|
+
assert isinstance(sig, Signal)
|
|
111
|
+
assert sig.name == "test"
|
|
112
|
+
|
|
113
|
+
def test_create_signal_idempotent(self):
|
|
114
|
+
bus = SignalBus()
|
|
115
|
+
sig1 = bus.create_signal("test")
|
|
116
|
+
sig2 = bus.create_signal("test")
|
|
117
|
+
assert sig1 is sig2
|
|
118
|
+
|
|
119
|
+
def test_get_signal(self):
|
|
120
|
+
bus = SignalBus()
|
|
121
|
+
bus.create_signal("test")
|
|
122
|
+
sig = bus.get_signal("test")
|
|
123
|
+
assert sig is not None
|
|
124
|
+
assert sig.name == "test"
|
|
125
|
+
|
|
126
|
+
def test_get_signal_missing(self):
|
|
127
|
+
bus = SignalBus()
|
|
128
|
+
assert bus.get_signal("nonexistent") is None
|
|
129
|
+
|
|
130
|
+
def test_emit_via_bus(self):
|
|
131
|
+
bus = SignalBus()
|
|
132
|
+
bus.create_signal("test")
|
|
133
|
+
results = []
|
|
134
|
+
bus.connect("test", lambda: results.append("called"))
|
|
135
|
+
bus.emit("test")
|
|
136
|
+
assert results == ["called"]
|
|
137
|
+
|
|
138
|
+
def test_emit_missing_signal(self):
|
|
139
|
+
bus = SignalBus()
|
|
140
|
+
bus.emit("nonexistent")
|
|
141
|
+
|
|
142
|
+
def test_connect_missing_signal(self):
|
|
143
|
+
bus = SignalBus()
|
|
144
|
+
bus.connect("nonexistent", lambda: None)
|
|
145
|
+
|
|
146
|
+
def test_disconnect_missing_signal(self):
|
|
147
|
+
bus = SignalBus()
|
|
148
|
+
bus.disconnect("nonexistent", lambda: None)
|
|
149
|
+
|
|
150
|
+
def test_disconnect_via_bus(self):
|
|
151
|
+
bus = SignalBus()
|
|
152
|
+
bus.create_signal("test")
|
|
153
|
+
results = []
|
|
154
|
+
cb = lambda: results.append("called")
|
|
155
|
+
bus.connect("test", cb)
|
|
156
|
+
bus.disconnect("test", cb)
|
|
157
|
+
bus.emit("test")
|
|
158
|
+
assert results == []
|
|
159
|
+
|
|
160
|
+
def test_bus_with_args(self):
|
|
161
|
+
bus = SignalBus()
|
|
162
|
+
bus.create_signal("test")
|
|
163
|
+
results = []
|
|
164
|
+
bus.connect("test", lambda x: results.append(x))
|
|
165
|
+
bus.emit("test", 42)
|
|
166
|
+
assert results == [42]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class TestSignalDescriptor:
|
|
170
|
+
def test_descriptor_is_signal_on_instance(self):
|
|
171
|
+
class Obj:
|
|
172
|
+
sig = signal()
|
|
173
|
+
|
|
174
|
+
obj = Obj()
|
|
175
|
+
assert isinstance(obj.sig, Signal)
|
|
176
|
+
|
|
177
|
+
def test_descriptor_unique_per_instance(self):
|
|
178
|
+
class Obj:
|
|
179
|
+
sig = signal()
|
|
180
|
+
|
|
181
|
+
a = Obj()
|
|
182
|
+
b = Obj()
|
|
183
|
+
assert a.sig is not b.sig
|
|
184
|
+
|
|
185
|
+
def test_descriptor_connect_and_emit(self):
|
|
186
|
+
class Obj:
|
|
187
|
+
sig = signal()
|
|
188
|
+
|
|
189
|
+
obj = Obj()
|
|
190
|
+
results = []
|
|
191
|
+
obj.sig += lambda: results.append("called")
|
|
192
|
+
obj.sig()
|
|
193
|
+
assert results == ["called"]
|
|
194
|
+
|
|
195
|
+
def test_descriptor_assign_allows_override(self):
|
|
196
|
+
class Obj:
|
|
197
|
+
sig = signal()
|
|
198
|
+
|
|
199
|
+
obj = Obj()
|
|
200
|
+
sig = obj.sig
|
|
201
|
+
obj.sig = "nope"
|
|
202
|
+
assert obj.sig == "nope"
|
|
203
|
+
|
|
204
|
+
def test_descriptor_multiple_signals(self):
|
|
205
|
+
class Obj:
|
|
206
|
+
a = signal()
|
|
207
|
+
b = signal()
|
|
208
|
+
|
|
209
|
+
obj = Obj()
|
|
210
|
+
results = []
|
|
211
|
+
obj.a += lambda: results.append("a")
|
|
212
|
+
obj.b += lambda: results.append("b")
|
|
213
|
+
obj.a()
|
|
214
|
+
assert results == ["a"]
|
|
215
|
+
obj.b()
|
|
216
|
+
assert results == ["a", "b"]
|
|
217
|
+
|
|
218
|
+
def test_descriptor_class_access(self):
|
|
219
|
+
class Obj:
|
|
220
|
+
sig = signal()
|
|
221
|
+
|
|
222
|
+
assert isinstance(Obj.sig, signal)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|