IS-Matrix-Forge 1.0.0.dev1__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.
- is_matrix_forge/__init__.py +8 -0
- is_matrix_forge/assets/__init__.py +8 -0
- is_matrix_forge/assets/audio/__init__.py +36 -0
- is_matrix_forge/assets/audio/files/__init__.py +53 -0
- is_matrix_forge/assets/audio/files/plugged.wav +0 -0
- is_matrix_forge/assets/audio/files/unplugged.wav +0 -0
- is_matrix_forge/assets/char_map.json +2150 -0
- is_matrix_forge/assets/font_map.py +119 -0
- is_matrix_forge/common/__init__.py +0 -0
- is_matrix_forge/common/decorators/__init__.py +152 -0
- is_matrix_forge/common/dirs.py +20 -0
- is_matrix_forge/common/helpers/__init__.py +132 -0
- is_matrix_forge/common/helpers/github_api.py +67 -0
- is_matrix_forge/designer_gui/__init__.py +8 -0
- is_matrix_forge/designer_gui/main_window/__init__.py +252 -0
- is_matrix_forge/designer_gui/main_window/layout.py +117 -0
- is_matrix_forge/dev_tools/__init__.py +8 -0
- is_matrix_forge/dev_tools/debug.py +82 -0
- is_matrix_forge/dev_tools/presets.py +47 -0
- is_matrix_forge/inputmodule/__init__.py +164 -0
- is_matrix_forge/inputmodule/b1display.py +160 -0
- is_matrix_forge/inputmodule/c1minimal.py +35 -0
- is_matrix_forge/inputmodule/font.py +2166 -0
- is_matrix_forge/inputmodule/helpers/__init__.py +16 -0
- is_matrix_forge/inputmodule/ledmatrix.py +516 -0
- is_matrix_forge/led_matrix/Scripts/__init__.py +4 -0
- is_matrix_forge/led_matrix/Scripts/battery_monitor.py +152 -0
- is_matrix_forge/led_matrix/Scripts/install_presets/__init__.py +2 -0
- is_matrix_forge/led_matrix/Scripts/install_presets/arguments.py +84 -0
- is_matrix_forge/led_matrix/Scripts/install_presets/main.py +192 -0
- is_matrix_forge/led_matrix/__init__.py +7 -0
- is_matrix_forge/led_matrix/commands/__init__.py +84 -0
- is_matrix_forge/led_matrix/commands/map.py +46 -0
- is_matrix_forge/led_matrix/constants.py +82 -0
- is_matrix_forge/led_matrix/controller/__init__.py +26 -0
- is_matrix_forge/led_matrix/controller/controller.py +635 -0
- is_matrix_forge/led_matrix/controller/helpers/__init__.py +7 -0
- is_matrix_forge/led_matrix/controller/helpers/threading.py +41 -0
- is_matrix_forge/led_matrix/controller/multiton.py +32 -0
- is_matrix_forge/led_matrix/controller/threaded.py +72 -0
- is_matrix_forge/led_matrix/display/__init__.py +13 -0
- is_matrix_forge/led_matrix/display/animations/__init__.py +29 -0
- is_matrix_forge/led_matrix/display/animations/animation.py +593 -0
- is_matrix_forge/led_matrix/display/animations/effects/__init__.py +17 -0
- is_matrix_forge/led_matrix/display/animations/errors.py +23 -0
- is_matrix_forge/led_matrix/display/animations/font/__init__.py +17 -0
- is_matrix_forge/led_matrix/display/animations/font/font.py +4 -0
- is_matrix_forge/led_matrix/display/animations/font/map_builder.py +123 -0
- is_matrix_forge/led_matrix/display/animations/frame/__init__.py +47 -0
- is_matrix_forge/led_matrix/display/animations/frame/base.py +245 -0
- is_matrix_forge/led_matrix/display/animations/frame/helpers.py +67 -0
- is_matrix_forge/led_matrix/display/animations/keep_awake.py +1 -0
- is_matrix_forge/led_matrix/display/animations/plugged.py +0 -0
- is_matrix_forge/led_matrix/display/animations/text_scroller.py +104 -0
- is_matrix_forge/led_matrix/display/effects/__init__.py +17 -0
- is_matrix_forge/led_matrix/display/effects/breather.py +206 -0
- is_matrix_forge/led_matrix/display/effects/fade.py +0 -0
- is_matrix_forge/led_matrix/display/effects/scroller/__init__.py +92 -0
- is_matrix_forge/led_matrix/display/effects/scroller/base.py +76 -0
- is_matrix_forge/led_matrix/display/effects/scroller/helpers.py +62 -0
- is_matrix_forge/led_matrix/display/grid/__init__.py +9 -0
- is_matrix_forge/led_matrix/display/grid/grid.py +208 -0
- is_matrix_forge/led_matrix/display/grid/helpers.py +130 -0
- is_matrix_forge/led_matrix/display/grid/presets/__init__.py +64 -0
- is_matrix_forge/led_matrix/display/grid/presets/manifest.py +172 -0
- is_matrix_forge/led_matrix/display/helpers/__init__.py +110 -0
- is_matrix_forge/led_matrix/display/helpers/columns.py +33 -0
- is_matrix_forge/led_matrix/display/media.py +199 -0
- is_matrix_forge/led_matrix/display/patterns/__init__.py +59 -0
- is_matrix_forge/led_matrix/display/patterns/built_in/__init__.py +91 -0
- is_matrix_forge/led_matrix/display/patterns/built_in/commands.py +3 -0
- is_matrix_forge/led_matrix/display/patterns/built_in/constants.py +29 -0
- is_matrix_forge/led_matrix/display/patterns/built_in/stencils.py +70 -0
- is_matrix_forge/led_matrix/display/patterns/utils.py +2 -0
- is_matrix_forge/led_matrix/display/text/__init__.py +66 -0
- is_matrix_forge/led_matrix/display/text/scroller.py +136 -0
- is_matrix_forge/led_matrix/errors/__init__.py +4 -0
- is_matrix_forge/led_matrix/errors/base.py +14 -0
- is_matrix_forge/led_matrix/errors/grid.py +37 -0
- is_matrix_forge/led_matrix/errors/matrix.py +68 -0
- is_matrix_forge/led_matrix/errors/misc.py +36 -0
- is_matrix_forge/led_matrix/fonts.py +20 -0
- is_matrix_forge/led_matrix/hardware.py +172 -0
- is_matrix_forge/led_matrix/helpers/__init__.py +165 -0
- is_matrix_forge/led_matrix/helpers/core/__init__.py +17 -0
- is_matrix_forge/led_matrix/helpers/core/lazy_mod_load.py +84 -0
- is_matrix_forge/led_matrix/helpers/device.py +118 -0
- is_matrix_forge/led_matrix/helpers/status_handler.py +14 -0
- is_matrix_forge/led_matrix/helpers/stream/__init__.py +70 -0
- is_matrix_forge/led_matrix/helpers/stream/errors.py +17 -0
- is_matrix_forge/led_matrix/led_matrix.py +25 -0
- is_matrix_forge/led_matrix/status.py +8 -0
- is_matrix_forge/led_matrix/tools/__init__.py +8 -0
- is_matrix_forge/led_matrix/tools/identify.py +27 -0
- is_matrix_forge/led_matrix/utils/__init__.py +41 -0
- is_matrix_forge/log_engine.py +25 -0
- is_matrix_forge/monitor/__init__.py +68 -0
- is_matrix_forge/monitor/errors.py +35 -0
- is_matrix_forge/monitor/events.py +78 -0
- is_matrix_forge/monitor/gui/__init__.py +0 -0
- is_matrix_forge/monitor/gui/errors.py +35 -0
- is_matrix_forge/monitor/gui/event/__init__.py +8 -0
- is_matrix_forge/monitor/gui/event/collection.py +153 -0
- is_matrix_forge/monitor/gui/event/errors.py +58 -0
- is_matrix_forge/monitor/gui/event/event.py +226 -0
- is_matrix_forge/monitor/gui/layout/__init__.py +8 -0
- is_matrix_forge/monitor/gui/layout/base.py +147 -0
- is_matrix_forge/monitor/gui/metaclasses/__init__.py +20 -0
- is_matrix_forge/monitor/gui/windows/__init__.py +0 -0
- is_matrix_forge/monitor/gui/windows/base.py +325 -0
- is_matrix_forge/monitor/gui/windows/main/__init__.py +77 -0
- is_matrix_forge/monitor/gui/windows/main/layout.py +21 -0
- is_matrix_forge/monitor/helpers.py +55 -0
- is_matrix_forge/monitor/monitor.py +431 -0
- is_matrix_forge/notify/__init__.py +0 -0
- is_matrix_forge/notify/sounds/__init__.py +6 -0
- is_matrix_forge/notify/sounds/base.py +106 -0
- is_matrix_forge/progress.py +99 -0
- is_matrix_forge-1.0.0.dev1.dist-info/LICENSE.md +19 -0
- is_matrix_forge-1.0.0.dev1.dist-info/METADATA +190 -0
- is_matrix_forge-1.0.0.dev1.dist-info/RECORD +123 -0
- is_matrix_forge-1.0.0.dev1.dist-info/WHEEL +4 -0
- is_matrix_forge-1.0.0.dev1.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from .event import MOD_LOGGER as __MOD_LOGGER, Event, Loggable
|
|
5
|
+
from .errors import EventExistsError, EventLookupError
|
|
6
|
+
|
|
7
|
+
__initialized = False
|
|
8
|
+
|
|
9
|
+
if __initialized:
|
|
10
|
+
from ..windows.base import WindowBase
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
MOD_LOGGER = __MOD_LOGGER.parent.get_child('collection')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class EventCollection(Loggable):
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
window: WindowBase
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Initializes an EventCollection instance.
|
|
23
|
+
|
|
24
|
+
This constructor sets up the logging and initializes an empty list to hold events.
|
|
25
|
+
"""
|
|
26
|
+
super().__init__(MOD_LOGGER)
|
|
27
|
+
self.__events = []
|
|
28
|
+
|
|
29
|
+
def __iter__(self):
|
|
30
|
+
return iter(self.__events)
|
|
31
|
+
|
|
32
|
+
def __len__(self):
|
|
33
|
+
return len(self.__events)
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def event_names(self):
|
|
37
|
+
return [e.key for e in self.__events]
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def events(self):
|
|
41
|
+
return self.__events
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def exit_event_exists(self) -> bool:
|
|
45
|
+
"""
|
|
46
|
+
Checks if an ExitEvent exists in the collection.
|
|
47
|
+
|
|
48
|
+
This method checks if any events in the collection are an instance of ExitEvent.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
bool:
|
|
52
|
+
True if an ExitEvent exists in the collection, False otherwise.
|
|
53
|
+
"""
|
|
54
|
+
from ..event.event import ExitEvent
|
|
55
|
+
|
|
56
|
+
return any(isinstance(event, ExitEvent) for event in self.events)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def times_failed(self):
|
|
60
|
+
"""
|
|
61
|
+
The total number of times all events in the collection have failed.
|
|
62
|
+
|
|
63
|
+
This property is a shortcut to get the total number of times all events
|
|
64
|
+
in the collection have failed. It does this by summing up the `times_failed`
|
|
65
|
+
property of each event in the collection.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
int:
|
|
69
|
+
The total number of times all events in the collection have failed.
|
|
70
|
+
"""
|
|
71
|
+
times = 0
|
|
72
|
+
|
|
73
|
+
if self.events:
|
|
74
|
+
for event in self.events:
|
|
75
|
+
times += event.times_failed
|
|
76
|
+
else:
|
|
77
|
+
self.method_logger.warn_once('No events in collection, returning 0')
|
|
78
|
+
|
|
79
|
+
return times
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def times_handled(self) -> int:
|
|
83
|
+
times = 0
|
|
84
|
+
|
|
85
|
+
if self.events:
|
|
86
|
+
for event in self.events:
|
|
87
|
+
times += event.times_handled
|
|
88
|
+
else:
|
|
89
|
+
self.method_logger.warn_once('No events in collection, returning 0')
|
|
90
|
+
|
|
91
|
+
return times
|
|
92
|
+
|
|
93
|
+
def add_event(self, event: Event) -> None:
|
|
94
|
+
if not isinstance(event, Event):
|
|
95
|
+
raise TypeError(f"event must be of type `Event`, not {type(event)}")
|
|
96
|
+
|
|
97
|
+
if event.key in self.event_names:
|
|
98
|
+
raise EventExistsError(event_name=event.key)
|
|
99
|
+
|
|
100
|
+
self.__events.append(event)
|
|
101
|
+
|
|
102
|
+
def clear(self) -> None:
|
|
103
|
+
self.method_logger.info('Clearing all events in collection...')
|
|
104
|
+
self.events.clear()
|
|
105
|
+
|
|
106
|
+
def create_event(self, key: str, callback: Optional[callable] = None) -> None:
|
|
107
|
+
if key is None and self.exit_event_exists:
|
|
108
|
+
raise EventExistsError(event_name='ExitEvent')
|
|
109
|
+
elif key is None and not self.exit_event_exists:
|
|
110
|
+
from ..event.event import ExitEvent
|
|
111
|
+
return self.add_event(ExitEvent())
|
|
112
|
+
|
|
113
|
+
key = key.upper()
|
|
114
|
+
|
|
115
|
+
if key in self.event_names:
|
|
116
|
+
raise EventExistsError(event_name=key)
|
|
117
|
+
|
|
118
|
+
return self.add_event(Event(key=key, callback=callback))
|
|
119
|
+
|
|
120
|
+
def handle_event(self, event: str, callback_args: tuple = (), callback_kwargs: Optional[dict] = None):
|
|
121
|
+
print('Reached handle_event')
|
|
122
|
+
event = self.lookup(event)
|
|
123
|
+
|
|
124
|
+
if event is None:
|
|
125
|
+
raise EventLookupError(event_name=event)
|
|
126
|
+
|
|
127
|
+
return event.handle(callback_args=callback_args, callback_kwargs=callback_kwargs)
|
|
128
|
+
|
|
129
|
+
def lookup(self, event_key: str) -> Optional[Event]:
|
|
130
|
+
"""
|
|
131
|
+
Looks up an event in the collection by its key.
|
|
132
|
+
|
|
133
|
+
Parameters:
|
|
134
|
+
event_key (str):
|
|
135
|
+
The key of the event to be looked up.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Event or None:
|
|
139
|
+
The event with the given key, or None if not found.
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
TypeError:
|
|
143
|
+
If 'event_key' is not of type `str`.
|
|
144
|
+
"""
|
|
145
|
+
if not isinstance(event_key, str):
|
|
146
|
+
raise TypeError(f"'event_key' must be of type `str`, not {type(event_key)}")
|
|
147
|
+
|
|
148
|
+
return next(
|
|
149
|
+
(e for e in self.__events if e.key.lower() == event_key.lower()), None
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
__initialized = True
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from inspyre_toolbox.exceptional import CustomRootException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EventError(CustomRootException):
|
|
5
|
+
default_message = 'An error occurred with event handling!'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EventExistsError(EventError, ValueError):
|
|
9
|
+
"""
|
|
10
|
+
Raised when an event already exists in the collection.
|
|
11
|
+
|
|
12
|
+
Class Attributes:
|
|
13
|
+
default_message (str):
|
|
14
|
+
**Read-Only**. The default message for the error.
|
|
15
|
+
"""
|
|
16
|
+
default_message = 'Event already exists in collection!'
|
|
17
|
+
|
|
18
|
+
def __init__(self, message: str = None, event_name: str = None, skip_print: bool = False) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Initializes the EventExistsError.
|
|
21
|
+
|
|
22
|
+
Parameters:
|
|
23
|
+
message (Optional[str]):
|
|
24
|
+
The error message to be displayed. If None, uses the default message.
|
|
25
|
+
"""
|
|
26
|
+
if event_name is not None:
|
|
27
|
+
self.default_message = f"'{event_name}' already exists in collection!"
|
|
28
|
+
if message is not None:
|
|
29
|
+
self.default_message = f"{self.default_message}\n\n Additional information from caller:\n {message}"
|
|
30
|
+
|
|
31
|
+
super().__init__(message=self.default_message, skip_print=skip_print)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EventLookupError(EventError, ValueError):
|
|
35
|
+
"""
|
|
36
|
+
Raised when an event is not found in the collection.
|
|
37
|
+
|
|
38
|
+
Class Attributes:
|
|
39
|
+
default_message (str): Read-Only. The default message for the error.
|
|
40
|
+
"""
|
|
41
|
+
default_message = 'Event not found in collection!'
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str = None, event_name: str = None, skip_print: bool = False) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Initializes the EventLookupError.
|
|
46
|
+
|
|
47
|
+
Parameters:
|
|
48
|
+
message (Optional[str]):
|
|
49
|
+
The error message to be displayed. If None, uses the default message.
|
|
50
|
+
"""
|
|
51
|
+
if event_name is not None:
|
|
52
|
+
self.default_message = f"'{event_name}' not found in collection!"
|
|
53
|
+
if message is not None:
|
|
54
|
+
self.default_message = f"{self.default_message}\n\n Additional information from caller:\n {message}"
|
|
55
|
+
|
|
56
|
+
self.__additional_info = 'Test'
|
|
57
|
+
|
|
58
|
+
super().__init__(message=self.default_message, skip_print=skip_print)
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from is_matrix_forge.log_engine import ROOT_LOGGER as PARENT_LOGGER, Loggable
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
__initialized = False
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
if __initialized:
|
|
12
|
+
from ..windows.base import WindowBase
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
MOD_LOGGER = PARENT_LOGGER.get_child('monitor.gui.event.event')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ElementArgMap():
|
|
20
|
+
pos_args: tuple = field(default_factory=tuple)
|
|
21
|
+
kw_args: dict = field(default_factory=dict)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Event(Loggable):
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
key: Optional[str],
|
|
28
|
+
window: WindowBase,
|
|
29
|
+
callback: Optional[callable] = None,
|
|
30
|
+
element_arg_map: Optional[dict] = None,
|
|
31
|
+
|
|
32
|
+
) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Initializes the Event.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
key (str):
|
|
38
|
+
The unique string identifier for the event.
|
|
39
|
+
|
|
40
|
+
callback (Optional[callable]):
|
|
41
|
+
The callable to be called when the event is triggered.
|
|
42
|
+
|
|
43
|
+
pass_window_values (bool):
|
|
44
|
+
Whether to pass window values to the callback function.
|
|
45
|
+
"""
|
|
46
|
+
super().__init__(MOD_LOGGER)
|
|
47
|
+
self.__callback = None
|
|
48
|
+
self.__failed = 0
|
|
49
|
+
self.__initialized = False
|
|
50
|
+
self.__key = None
|
|
51
|
+
self.__times_handled = 0
|
|
52
|
+
|
|
53
|
+
self.key = key
|
|
54
|
+
|
|
55
|
+
if callback is not None:
|
|
56
|
+
self.callback = callback
|
|
57
|
+
|
|
58
|
+
self.__initialized = True
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def callback(self) -> callable:
|
|
62
|
+
"""
|
|
63
|
+
The callback function for the event.
|
|
64
|
+
|
|
65
|
+
The callback function is called whenever the event is triggered. The
|
|
66
|
+
function should accept zero or more arguments.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
callable:
|
|
70
|
+
The callback function for the event.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
TypeError:
|
|
74
|
+
If the callback is not callable.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
return self.__callback
|
|
78
|
+
|
|
79
|
+
@callback.setter
|
|
80
|
+
def callback(self, new: callable) -> None:
|
|
81
|
+
"""
|
|
82
|
+
The callback function for the event.
|
|
83
|
+
|
|
84
|
+
The callback function is called whenever the event is triggered. The
|
|
85
|
+
function should accept zero or more arguments.
|
|
86
|
+
|
|
87
|
+
Parameters:
|
|
88
|
+
new (callable):
|
|
89
|
+
The new value for the callback.
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
TypeError:
|
|
93
|
+
If the new value is not callable.
|
|
94
|
+
"""
|
|
95
|
+
if not callable(new):
|
|
96
|
+
raise TypeError(f"callback must be callable, not {type(new)}")
|
|
97
|
+
|
|
98
|
+
self.__callback = new
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def times_failed(self) -> int:
|
|
102
|
+
"""
|
|
103
|
+
The number of times the callback function has failed. (**Read-Only**)
|
|
104
|
+
|
|
105
|
+
If the callback function fails, an error is raised and the number of
|
|
106
|
+
times it has failed is incremented.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
int:
|
|
110
|
+
The number of times the callback function has failed.
|
|
111
|
+
"""
|
|
112
|
+
return self.__failed
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def key(self):
|
|
117
|
+
"""
|
|
118
|
+
The key of the event.
|
|
119
|
+
|
|
120
|
+
The key is the identifier for this event. It is used to look up the event
|
|
121
|
+
in the collection.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
str:
|
|
125
|
+
The key of the event.
|
|
126
|
+
"""
|
|
127
|
+
return self.__key
|
|
128
|
+
|
|
129
|
+
@key.setter
|
|
130
|
+
def key(self, new: Optional[str]):
|
|
131
|
+
"""
|
|
132
|
+
The key of the event.
|
|
133
|
+
|
|
134
|
+
The key is the identifier for this event. It is used to look up the event
|
|
135
|
+
in the collection.
|
|
136
|
+
|
|
137
|
+
If the event is an `ExitEvent` (a special event given to every window
|
|
138
|
+
handler collection), the key is allowed to be `None`. Otherwise, the key
|
|
139
|
+
must be a string.
|
|
140
|
+
|
|
141
|
+
Parameters:
|
|
142
|
+
new (Optional[str]):
|
|
143
|
+
The new key of the event.
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
RuntimeError:
|
|
147
|
+
If the key is changed after initialization.
|
|
148
|
+
TypeError:
|
|
149
|
+
If the new key is not a string.
|
|
150
|
+
"""
|
|
151
|
+
# Allow for `key` to allow `None` as a value if it is from `ExitEvent` (a special event given to every window
|
|
152
|
+
# handler collection)
|
|
153
|
+
if new is None and isinstance(self, ExitEvent):
|
|
154
|
+
self._Event__key = None
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
if self.__initialized:
|
|
158
|
+
raise RuntimeError("Cannot change key after initialization")
|
|
159
|
+
|
|
160
|
+
if not isinstance(new, str):
|
|
161
|
+
raise TypeError(f"key must be of type `str`, not {type(new)}")
|
|
162
|
+
|
|
163
|
+
self.__key = new.upper()
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def times_handled(self):
|
|
167
|
+
"""
|
|
168
|
+
The number of times the event has been handled.
|
|
169
|
+
|
|
170
|
+
This property returns the count of how many times the `handle` method
|
|
171
|
+
has been invoked for this event.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
int:
|
|
175
|
+
The number of times the event has been handled.
|
|
176
|
+
"""
|
|
177
|
+
return self.__times_handled
|
|
178
|
+
|
|
179
|
+
def handle(self, callback_args: tuple = (), callback_kwargs: Optional[dict] = None):
|
|
180
|
+
"""
|
|
181
|
+
Handles an event by calling the associated callback.
|
|
182
|
+
|
|
183
|
+
Parameters:
|
|
184
|
+
callback_args (tuple):
|
|
185
|
+
The arguments to be passed to the callback.
|
|
186
|
+
|
|
187
|
+
callback_kwargs (Optional[dict]):
|
|
188
|
+
The keyword arguments to be passed to the callback.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Any:
|
|
192
|
+
The return value of the callback, or None if no callback is associated with the event.
|
|
193
|
+
"""
|
|
194
|
+
if callback_kwargs is None:
|
|
195
|
+
callback_kwargs = {}
|
|
196
|
+
|
|
197
|
+
self.__times_handled += 1
|
|
198
|
+
|
|
199
|
+
if self.callback:
|
|
200
|
+
try:
|
|
201
|
+
return self.callback(*callback_args, **callback_kwargs)
|
|
202
|
+
except Exception as e:
|
|
203
|
+
self.__failed += 1
|
|
204
|
+
raise e from e
|
|
205
|
+
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class ExitEvent(Event):
|
|
210
|
+
"""
|
|
211
|
+
A special event that can be applied to any PySimpleGUI main loop to recognize and handle a press of the 'X' button.
|
|
212
|
+
"""
|
|
213
|
+
def __init__(self):
|
|
214
|
+
super().__init__(key=None,)
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def key(self) -> None:
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
@key.setter
|
|
221
|
+
def key(self, new: None) -> None:
|
|
222
|
+
raise ValueError('The key of an ExitEvent cannot be set.')
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
__initialized = True
|
|
226
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: Taylor B. tayjaybabee@gmail.com
|
|
3
|
+
Date: 2025-05-02 16:04:13
|
|
4
|
+
LastEditors: Taylor B. tayjaybabee@gmail.com
|
|
5
|
+
LastEditTime: 2025-05-02 16:13:48
|
|
6
|
+
FilePath: is_matrix_forge/monitor/gui/layout/__init__.py
|
|
7
|
+
Description: 这是默认设置,可以在设置》工具》File Description中进行配置
|
|
8
|
+
"""
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
import copy
|
|
3
|
+
|
|
4
|
+
from is_matrix_forge.log_engine import ROOT_LOGGER, Loggable
|
|
5
|
+
from is_matrix_forge.monitor.gui.metaclasses import SingletonABCMeta
|
|
6
|
+
from inspyre_toolbox.exceptional import CustomRootException
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LayoutError(CustomRootException):
|
|
10
|
+
default_message = 'An error occurred with the layout!'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LayoutNotBuiltError(LayoutError):
|
|
14
|
+
"""
|
|
15
|
+
Raised when the layout is not built yet.
|
|
16
|
+
|
|
17
|
+
Class Attributes:
|
|
18
|
+
default_message (str):
|
|
19
|
+
**Read-Only**. The default message for the error.
|
|
20
|
+
"""
|
|
21
|
+
default_message = 'Layout not built yet!'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LayoutAlreadyBuiltError(LayoutError):
|
|
25
|
+
"""
|
|
26
|
+
Raised when the layout is already built.
|
|
27
|
+
|
|
28
|
+
Class Attributes:
|
|
29
|
+
default_message (str):
|
|
30
|
+
**Read-Only**. The default message for the error.
|
|
31
|
+
"""
|
|
32
|
+
default_message = 'Layout already built!'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
LOGGER = ROOT_LOGGER.get_child('monitor.gui.layout.base')
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Layout(Loggable, metaclass=SingletonABCMeta):
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def BLUEPRINT(self):
|
|
43
|
+
"""Return a 2D list of PySimpleGUI elements used to build this layout."""
|
|
44
|
+
raise NotImplementedError
|
|
45
|
+
|
|
46
|
+
def __init__(self, with_logging_parent=None):
|
|
47
|
+
if with_logging_parent is None:
|
|
48
|
+
with_logging_parent = LOGGER
|
|
49
|
+
|
|
50
|
+
super().__init__(with_logging_parent)
|
|
51
|
+
|
|
52
|
+
self.__layout = None
|
|
53
|
+
self.__is_built = False
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def built(self):
|
|
57
|
+
"""
|
|
58
|
+
Check if the layout has been built.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
bool:
|
|
62
|
+
True if the layout has been built, False otherwise.
|
|
63
|
+
"""
|
|
64
|
+
return self.__is_built
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def layout(self):
|
|
68
|
+
"""
|
|
69
|
+
Get a fresh deep-copy of the built layout.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
List[List[PySimpleGUI.Element]]:
|
|
73
|
+
A 2D list of `PySimpleGUI` elements representing the layout.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
LayoutNotBuiltError:
|
|
77
|
+
If the layout has not been built yet.
|
|
78
|
+
"""
|
|
79
|
+
log = self.method_logger
|
|
80
|
+
if not self.built:
|
|
81
|
+
log.warning('Attempted to access layout before it was built!')
|
|
82
|
+
raise LayoutNotBuiltError()
|
|
83
|
+
|
|
84
|
+
if self.__layout is None:
|
|
85
|
+
err_msg = 'Layout is None, but built is True!'
|
|
86
|
+
log.warning(err_msg)
|
|
87
|
+
raise LayoutNotBuiltError(err_msg)
|
|
88
|
+
|
|
89
|
+
log.debug('Returning a deep-copy of the layout.')
|
|
90
|
+
return copy.deepcopy(self.__layout)
|
|
91
|
+
|
|
92
|
+
def build(self):
|
|
93
|
+
"""
|
|
94
|
+
Build the layout from the `BLUEPRINT` attribute once.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
List[List[PySimpleGUI.Element]]:
|
|
98
|
+
A 2D list of `PySimpleGUI` elements representing the layout, deep-copied.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
LayoutAlreadyBuiltError:
|
|
102
|
+
If the layout has already been built.
|
|
103
|
+
"""
|
|
104
|
+
log = self.method_logger
|
|
105
|
+
if self.built:
|
|
106
|
+
log.warning('Attempted to build layout when it was already built!')
|
|
107
|
+
raise LayoutAlreadyBuiltError()
|
|
108
|
+
|
|
109
|
+
blueprint = self.BLUEPRINT
|
|
110
|
+
if not isinstance(blueprint, list) or not all(isinstance(r, list) for r in blueprint):
|
|
111
|
+
raise LayoutError('BLUEPRINT must be a 2D list of PySimpleGUI elements.')
|
|
112
|
+
|
|
113
|
+
self.__layout = copy.deepcopy(blueprint)
|
|
114
|
+
log.debug('Layout built successfully.')
|
|
115
|
+
self.__is_built = True
|
|
116
|
+
log.debug('Marked layout as built.')
|
|
117
|
+
|
|
118
|
+
return self.layout
|
|
119
|
+
|
|
120
|
+
def rebuild(self):
|
|
121
|
+
"""
|
|
122
|
+
Clear the current layout and build a new one from `BLUEPRINT`.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
List[List[PySimpleGUI.Element]]:
|
|
126
|
+
A 2D list of `PySimpleGUI` elements representing the layout, deep-copied.
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
LayoutNotBuiltError:
|
|
130
|
+
If the layout has not been built yet.
|
|
131
|
+
"""
|
|
132
|
+
log = self.method_logger
|
|
133
|
+
|
|
134
|
+
if not self.built:
|
|
135
|
+
log.warning('Attempted to rebuild layout when it was not built yet!')
|
|
136
|
+
raise LayoutNotBuiltError('Cannot rebuild layout when it was not built yet!')
|
|
137
|
+
|
|
138
|
+
log.debug('Rebuilding layout...')
|
|
139
|
+
log.debug('Clearing current layout...')
|
|
140
|
+
|
|
141
|
+
self.__layout = None
|
|
142
|
+
log.debug('Layout set to None.')
|
|
143
|
+
self.__is_built = False
|
|
144
|
+
log.debug('Layout marked as not built.')
|
|
145
|
+
log.debug('Building new layout and returning...')
|
|
146
|
+
|
|
147
|
+
return self.build()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from abc import ABCMeta
|
|
2
|
+
from threading import Lock
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SingletonABCMeta(ABCMeta):
|
|
6
|
+
"""
|
|
7
|
+
A metaclass that ensures all subclasses are singletons.
|
|
8
|
+
|
|
9
|
+
This metaclass can be combined with abstract base classes
|
|
10
|
+
to make all concrete subclasses singletons.
|
|
11
|
+
"""
|
|
12
|
+
_instances = {}
|
|
13
|
+
_lock = Lock()
|
|
14
|
+
|
|
15
|
+
def __call__(cls, *args, **kwargs):
|
|
16
|
+
with SingletonABCMeta._lock:
|
|
17
|
+
if cls not in SingletonABCMeta._instances:
|
|
18
|
+
instance = super().__call__(*args, **kwargs)
|
|
19
|
+
SingletonABCMeta._instances[cls] = instance
|
|
20
|
+
return SingletonABCMeta._instances[cls]
|
|
File without changes
|