IS-Matrix-Forge 1.0.0.dev8__tar.gz → 1.0.0.dev9__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.
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/PKG-INFO +5 -1
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/Untitled.ipynb +109 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/assets/digit_map.py +82 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/designer_gui/main_window/__init__.py +47 -1
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/designer_gui/main_window/layout/menu_bar.py +43 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/controller/controller.py +81 -23
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/controller/helpers/__init__.py +45 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/controller/helpers/threading.py +67 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/animations/font/font.py +2 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/assets/__init__.py +2 -0
- {is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix → is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/assets}/fonts.py +2 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/effects/breather.py +34 -13
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/grid/composite/__init__.py +13 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/grid/composite/background.py +11 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/grid/composite/composite.py +91 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/grid/composite/foreground.py +53 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/grid/grid.py +21 -5
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/patterns/built_in/stencils/__init__.py +2 -2
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/scene/__init__.py +17 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/scene/errors.py +12 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/scene/scene.py +93 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/text/__init__.py +1 -1
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/text/scroller.py +0 -2
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/errors/composite_grid.py +1 -0
- is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix/helpers/device.py → is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/helpers/device/__init__.py +28 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/helpers/device/bootloader/__init__.py +47 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/helpers/device/bootloader/constants.py +3 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/helpers/device/bootloader/unix.py +33 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/helpers/device/bootloader/win.py +44 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/pyproject.toml +5 -1
- is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix/controller/helpers/__init__.py +0 -7
- is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix/controller/helpers/threading.py +0 -41
- is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix/display/animations/font/font.py +0 -4
- is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix/helpers/core/__init__.py +0 -17
- is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix/helpers/core/lazy_mod_load.py +0 -84
- is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix/helpers/stream/__init__.py +0 -70
- is_matrix_forge-1.0.0.dev8/is_matrix_forge/led_matrix/helpers/stream/errors.py +0 -17
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/LICENSE.md +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/README.md +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/assets/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/assets/audio/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/assets/audio/files/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/assets/audio/files/plugged.wav +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/assets/audio/files/unplugged.wav +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/assets/char_map.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/assets/font_map.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/common/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/common/decorators/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/common/decorators/freeze_setter.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/common/dirs.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/common/helpers/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/common/helpers/github_api.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/designer_gui/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/designer_gui/main_window/layout/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/designer_gui/main_window/layout/button_grid/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/designer_gui/main_window/layout/button_grid/column.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/designer_gui/main_window/layout/button_grid/grid.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/designer_gui/main_window/streamlit_app.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/dev_tools/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/dev_tools/debug.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/dev_tools/presets.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/inputmodule/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/inputmodule/b1display.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/inputmodule/c1minimal.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/inputmodule/font.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/inputmodule/helpers/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/inputmodule/ledmatrix.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/Scripts/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/Scripts/battery_monitor.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/Scripts/identify_matrices.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/Scripts/install_presets/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/Scripts/install_presets/arguments.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/Scripts/install_presets/main.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/Scripts/pixel_grid.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/commands/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/commands/map.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/constants.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/controller/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/controller/multiton.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/controller/threaded.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/animation.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/audio_visualizer.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/effects/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/font/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/font/map_builder.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/frame/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/frame/base.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/frame/helpers.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/keep_awake.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/plugged.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/animations/text_scroller.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/effects/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/effects/fade.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/effects/scroller/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/effects/scroller/base.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/effects/scroller/helpers.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/grid/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/grid/helpers.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/grid/presets/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/grid/presets/manifest.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/helpers/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/helpers/columns.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/media.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/patterns/built_in/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/patterns/built_in/commands.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/patterns/built_in/constants.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/display/patterns/built_in/stencils/res.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/errors/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/errors/base.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/errors/grid.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/errors/matrix.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/errors/misc.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/hardware.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/helpers/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/helpers/status_handler.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/led_matrix.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/status.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/tools/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/tools/identify.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/led_matrix/utils/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/log_engine.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/events.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/event/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/event/collection.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/event/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/event/event.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/layout/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/layout/base.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/metaclasses/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/windows/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/windows/base.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/windows/main/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/gui/windows/main/layout.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/helpers.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/monitor/monitor.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/notify/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/notify/sounds/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/notify/sounds/base.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/progress.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/scripts/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/scripts/preset_installer.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/serial_com/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/serial_com/base/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/serial_com/base/answer.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/serial_com/base/message.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/serial_com/base/request.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/is_matrix_forge/serial_com/exchange.py +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/blank.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/manifest.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/shock_face.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/test1.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/test2.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/test3.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/test4.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/test5.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/test6.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/test7.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/x_animation II.json +0 -0
- {is_matrix_forge-1.0.0.dev8 → is_matrix_forge-1.0.0.dev9}/presets/x_animation.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: IS-Matrix-Forge
|
|
3
|
-
Version: 1.0.0.
|
|
3
|
+
Version: 1.0.0.dev9
|
|
4
4
|
Summary:
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: led,matrix,framework,hardware
|
|
@@ -19,12 +19,16 @@ Requires-Dist: easy-exit-calls (>=1.0.0.dev1,<2.0.0)
|
|
|
19
19
|
Requires-Dist: imapclient (==3.0.1)
|
|
20
20
|
Requires-Dist: inspy-logger (>=3.2.3,<4.0.0)
|
|
21
21
|
Requires-Dist: inspyre-toolbox (>=1.6.0.dev23)
|
|
22
|
+
Requires-Dist: ipywidgets (==8.1.7)
|
|
23
|
+
Requires-Dist: notebook (>=7.4.4,<8.0.0)
|
|
22
24
|
Requires-Dist: opencv-python (>=4.11.0.86,<5.0.0.0)
|
|
23
25
|
Requires-Dist: pillow (>=11.2.1,<12.0.0)
|
|
26
|
+
Requires-Dist: psutil (==6.1.1)
|
|
24
27
|
Requires-Dist: pyserial (>=3.5,<4.0)
|
|
25
28
|
Requires-Dist: pysimplegui-4-foss (==4.60.4.1)
|
|
26
29
|
Requires-Dist: pysimpleguiweb (==0.39.0)
|
|
27
30
|
Requires-Dist: requests (>=2.32.4,<3.0.0)
|
|
31
|
+
Requires-Dist: wmi (>=1.5.1,<2.0.0)
|
|
28
32
|
Project-URL: Homepage, https://github.com/Inspyre-Softworks/IS-Matrix-Forge
|
|
29
33
|
Project-URL: Repository, https://github.com/Inspyre-Softworks/IS-Matrix-Forge
|
|
30
34
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": 5,
|
|
6
|
+
"id": "a6d553a0-fca1-411a-a7bd-cf15dce52e97",
|
|
7
|
+
"metadata": {},
|
|
8
|
+
"outputs": [
|
|
9
|
+
{
|
|
10
|
+
"data": {
|
|
11
|
+
"application/vnd.jupyter.widget-view+json": {
|
|
12
|
+
"model_id": "a9c234aa78a641018ba78e54e9a3755b",
|
|
13
|
+
"version_major": 2,
|
|
14
|
+
"version_minor": 0
|
|
15
|
+
},
|
|
16
|
+
"text/plain": [
|
|
17
|
+
"VBox(children=(Dropdown(description='Pick a pattern to render...', options=('All LEDs on', 'Gradient (0-13% Br…"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"metadata": {},
|
|
21
|
+
"output_type": "display_data"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"source": [
|
|
25
|
+
"from is_matrix_forge.led_matrix import get_controllers\n",
|
|
26
|
+
"import ipywidgets as widgets\n",
|
|
27
|
+
"CONTROLLERS = get_controllers(threaded = True)\n",
|
|
28
|
+
"PATTERNS = CONTROLLERS[0].built_in_pattern_names\n",
|
|
29
|
+
"\n",
|
|
30
|
+
"\n",
|
|
31
|
+
"class DropdownWithButton:\n",
|
|
32
|
+
" def __init__(self, options, description='Choose:', button_desc='Run', callback=None):\n",
|
|
33
|
+
" self.output = widgets.Output()\n",
|
|
34
|
+
" self.dropdown = widgets.Dropdown(\n",
|
|
35
|
+
" options=options, description=description, disabled=False\n",
|
|
36
|
+
" )\n",
|
|
37
|
+
" self.button = widgets.Button(\n",
|
|
38
|
+
" description=button_desc, disabled=True, button_style='primary'\n",
|
|
39
|
+
" )\n",
|
|
40
|
+
" self.callback = callback or self.default_callback\n",
|
|
41
|
+
"\n",
|
|
42
|
+
" # Set up observers\n",
|
|
43
|
+
" self.dropdown.observe(self._on_select, names='value')\n",
|
|
44
|
+
" self.button.on_click(self._on_click)\n",
|
|
45
|
+
"\n",
|
|
46
|
+
" # Layout\n",
|
|
47
|
+
" self.container = widgets.VBox([self.dropdown, self.button, self.output])\n",
|
|
48
|
+
" display(self.container)\n",
|
|
49
|
+
"\n",
|
|
50
|
+
" def _on_select(self, change):\n",
|
|
51
|
+
" self.button.disabled = False # Enable button once a selection is made\n",
|
|
52
|
+
"\n",
|
|
53
|
+
" def _on_click(self, b):\n",
|
|
54
|
+
" with self.output:\n",
|
|
55
|
+
" # Clear prior output\n",
|
|
56
|
+
" self.output.clear_output()\n",
|
|
57
|
+
" self.callback(self.dropdown.value)\n",
|
|
58
|
+
"\n",
|
|
59
|
+
" @staticmethod\n",
|
|
60
|
+
" def default_callback(value):\n",
|
|
61
|
+
" print(f\"You selected: {value}\")\n",
|
|
62
|
+
"\n",
|
|
63
|
+
"\n",
|
|
64
|
+
"\n",
|
|
65
|
+
"def render(selection):\n",
|
|
66
|
+
" CONTROLLERS[0].draw_pattern(selection)\n",
|
|
67
|
+
"\n",
|
|
68
|
+
"\n",
|
|
69
|
+
"\n",
|
|
70
|
+
"drp_bttn = DropdownWithButton(\n",
|
|
71
|
+
" options=PATTERNS,\n",
|
|
72
|
+
" description='Pick a pattern to render...',\n",
|
|
73
|
+
" button_desc='Render',\n",
|
|
74
|
+
" callback=render\n",
|
|
75
|
+
"\n",
|
|
76
|
+
")\n"
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"cell_type": "code",
|
|
81
|
+
"execution_count": null,
|
|
82
|
+
"id": "8533a241-ef3b-4a44-99cd-2e88cabb9fbb",
|
|
83
|
+
"metadata": {},
|
|
84
|
+
"outputs": [],
|
|
85
|
+
"source": []
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"metadata": {
|
|
89
|
+
"kernelspec": {
|
|
90
|
+
"display_name": "Python 3 (ipykernel)",
|
|
91
|
+
"language": "python",
|
|
92
|
+
"name": "python3"
|
|
93
|
+
},
|
|
94
|
+
"language_info": {
|
|
95
|
+
"codemirror_mode": {
|
|
96
|
+
"name": "ipython",
|
|
97
|
+
"version": 3
|
|
98
|
+
},
|
|
99
|
+
"file_extension": ".py",
|
|
100
|
+
"mimetype": "text/x-python",
|
|
101
|
+
"name": "python",
|
|
102
|
+
"nbconvert_exporter": "python",
|
|
103
|
+
"pygments_lexer": "ipython3",
|
|
104
|
+
"version": "3.12.10"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"nbformat": 4,
|
|
108
|
+
"nbformat_minor": 5
|
|
109
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains the pixel representation of the ld states within a 3x5 grid.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
DIGITS = {
|
|
7
|
+
"0": [
|
|
8
|
+
[1, 1, 1],
|
|
9
|
+
[1, 0, 1],
|
|
10
|
+
[1, 0, 1],
|
|
11
|
+
[1, 0, 1],
|
|
12
|
+
[1, 1, 1],
|
|
13
|
+
],
|
|
14
|
+
"1": [
|
|
15
|
+
[0, 1, 0],
|
|
16
|
+
[1, 1, 0],
|
|
17
|
+
[0, 1, 0],
|
|
18
|
+
[0, 1, 0],
|
|
19
|
+
[1, 1, 1],
|
|
20
|
+
],
|
|
21
|
+
"2": [
|
|
22
|
+
[1, 1, 1],
|
|
23
|
+
[0, 0, 1],
|
|
24
|
+
[1, 1, 1],
|
|
25
|
+
[1, 0, 0],
|
|
26
|
+
[1, 1, 1],
|
|
27
|
+
],
|
|
28
|
+
"3": [
|
|
29
|
+
[1, 1, 1],
|
|
30
|
+
[0, 0, 1],
|
|
31
|
+
[1, 1, 1],
|
|
32
|
+
[0, 0, 1],
|
|
33
|
+
[1, 1, 1],
|
|
34
|
+
],
|
|
35
|
+
"4": [
|
|
36
|
+
[1, 0, 1],
|
|
37
|
+
[1, 0, 1],
|
|
38
|
+
[1, 1, 1],
|
|
39
|
+
[0, 0, 1],
|
|
40
|
+
[0, 0, 1],
|
|
41
|
+
],
|
|
42
|
+
"5": [
|
|
43
|
+
[1, 1, 1],
|
|
44
|
+
[1, 0, 0],
|
|
45
|
+
[1, 1, 1],
|
|
46
|
+
[0, 0, 1],
|
|
47
|
+
[1, 1, 1],
|
|
48
|
+
],
|
|
49
|
+
"6": [
|
|
50
|
+
[1, 1, 1],
|
|
51
|
+
[1, 0, 0],
|
|
52
|
+
[1, 1, 1],
|
|
53
|
+
[1, 0, 1],
|
|
54
|
+
[1, 1, 1],
|
|
55
|
+
],
|
|
56
|
+
"7": [
|
|
57
|
+
[1, 1, 1],
|
|
58
|
+
[0, 0, 1],
|
|
59
|
+
[0, 0, 1],
|
|
60
|
+
[0, 0, 1],
|
|
61
|
+
[0, 0, 1],
|
|
62
|
+
],
|
|
63
|
+
"8": [
|
|
64
|
+
[1, 1, 1],
|
|
65
|
+
[1, 0, 1],
|
|
66
|
+
[1, 1, 1],
|
|
67
|
+
[1, 0, 1],
|
|
68
|
+
[1, 1, 1],
|
|
69
|
+
],
|
|
70
|
+
"9": [
|
|
71
|
+
[1, 1, 1],
|
|
72
|
+
[1, 0, 1],
|
|
73
|
+
[1, 1, 1],
|
|
74
|
+
[0, 0, 1],
|
|
75
|
+
[1, 1, 1],
|
|
76
|
+
],
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
__all__ = [
|
|
81
|
+
'DIGITS'
|
|
82
|
+
]
|
|
@@ -86,16 +86,44 @@ class PixelGrid:
|
|
|
86
86
|
|
|
87
87
|
@current_frame.setter
|
|
88
88
|
def current_frame(self, value: Any) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Setter indicating which frame index to display..
|
|
91
|
+
|
|
92
|
+
Note:
|
|
93
|
+
Must be an integer between 0 and len(frames) - 1.
|
|
94
|
+
"""
|
|
89
95
|
if not isinstance(value, int) or not (0 <= value < len(self.frames)):
|
|
90
96
|
raise ValueError("current_frame must be an index into frames list")
|
|
91
97
|
self.__current_frame = value
|
|
92
98
|
|
|
93
99
|
@property
|
|
94
100
|
def grid(self) -> List[List[int]]:
|
|
101
|
+
"""
|
|
102
|
+
A column-major 0/1 list representing the pixel grid.
|
|
103
|
+
"""
|
|
95
104
|
return self.__grid
|
|
96
105
|
|
|
97
106
|
@grid.setter
|
|
98
107
|
def grid(self, value: Any) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Setter for the grid data.
|
|
110
|
+
|
|
111
|
+
Note:
|
|
112
|
+
A valid grid is structured as follows:
|
|
113
|
+
- Must be a list
|
|
114
|
+
- Containing 9 lists
|
|
115
|
+
- Each list:
|
|
116
|
+
- Must be the same length
|
|
117
|
+
- Each item:
|
|
118
|
+
- Integer
|
|
119
|
+
- 0-1
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
Framework Laptop 9x34:
|
|
123
|
+
- 9 lists
|
|
124
|
+
- each with 34 items
|
|
125
|
+
- 0-1
|
|
126
|
+
"""
|
|
99
127
|
if not is_valid_grid(value, self.width, self.height):
|
|
100
128
|
raise ValueError("Grid must match width/height and be 0/1 values")
|
|
101
129
|
self.__grid = value
|
|
@@ -105,6 +133,9 @@ class PixelGrid:
|
|
|
105
133
|
|
|
106
134
|
@property
|
|
107
135
|
def preferred_device(self):
|
|
136
|
+
"""
|
|
137
|
+
The device we want to send the grid to.
|
|
138
|
+
"""
|
|
108
139
|
return self.__preferred_device
|
|
109
140
|
|
|
110
141
|
@preferred_device.setter
|
|
@@ -128,6 +159,9 @@ class PixelGrid:
|
|
|
128
159
|
)
|
|
129
160
|
|
|
130
161
|
def run(self) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Run the UI.
|
|
164
|
+
"""
|
|
131
165
|
while True:
|
|
132
166
|
event, _ = self.window.read()
|
|
133
167
|
if event in (sg.WINDOW_CLOSED, 'Exit'):
|
|
@@ -152,6 +186,9 @@ class PixelGrid:
|
|
|
152
186
|
|
|
153
187
|
# Frame management
|
|
154
188
|
def add_frame(self) -> None:
|
|
189
|
+
"""
|
|
190
|
+
Add a new frame to the grid.
|
|
191
|
+
"""
|
|
155
192
|
new_grid = copy.deepcopy(self.grid)
|
|
156
193
|
frm = Frame(new_grid)
|
|
157
194
|
self.frames = self.frames + [frm]
|
|
@@ -161,6 +198,11 @@ class PixelGrid:
|
|
|
161
198
|
self._update_frame_indicator()
|
|
162
199
|
|
|
163
200
|
def prev_frame(self) -> None:
|
|
201
|
+
"""
|
|
202
|
+
Go back one frame.
|
|
203
|
+
|
|
204
|
+
Show the frame that's before the current one.
|
|
205
|
+
"""
|
|
164
206
|
if self.current_frame > 0:
|
|
165
207
|
self.current_frame -= 1
|
|
166
208
|
self._load_frame()
|
|
@@ -256,5 +298,9 @@ class PixelGrid:
|
|
|
256
298
|
return is_valid_grid(grid, self.width, self.height)
|
|
257
299
|
|
|
258
300
|
|
|
259
|
-
|
|
301
|
+
def main():
|
|
260
302
|
PixelGrid().run()
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
if __name__ == '__main__':
|
|
306
|
+
main()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import PySimpleGUI as sg
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
MENU_DEF = [
|
|
5
|
+
['File', [
|
|
6
|
+
'Export',
|
|
7
|
+
'Export to File',
|
|
8
|
+
'Load from File',
|
|
9
|
+
'---',
|
|
10
|
+
'Exit'
|
|
11
|
+
]],
|
|
12
|
+
['Matrix', [
|
|
13
|
+
'Send to Matrix'
|
|
14
|
+
]],
|
|
15
|
+
['Frame', [
|
|
16
|
+
'Add Frame'
|
|
17
|
+
]]
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MenuBar:
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self.__layout = None
|
|
24
|
+
self.__menu_bar = None
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def is_built(self):
|
|
28
|
+
return self.__layout is not None and self.__menu_bar is not None
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def layout(self):
|
|
32
|
+
return self.__layout
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def menu_bar(self):
|
|
36
|
+
return self.__menu_bar
|
|
37
|
+
|
|
38
|
+
def build(self):
|
|
39
|
+
if self.__layout is not None and self.menu_bar is not None:
|
|
40
|
+
return [self.menu_bar, [sg.VPush()], *self.__layout]
|
|
41
|
+
raise RuntimeError("Menu bar or layout is not defined.")
|
|
42
|
+
|
|
43
|
+
|
|
@@ -21,17 +21,14 @@ import threading
|
|
|
21
21
|
from time import sleep
|
|
22
22
|
from typing import Optional, Dict, Any, Union, List
|
|
23
23
|
|
|
24
|
-
from is_matrix_forge.log_engine import ROOT_LOGGER, Loggable
|
|
25
|
-
|
|
26
24
|
from inspyre_toolbox.syntactic_sweets.classes import validate_type
|
|
27
|
-
from inspyre_toolbox.syntactic_sweets.classes.decorators.aliases import
|
|
25
|
+
from inspyre_toolbox.syntactic_sweets.classes.decorators.aliases import method_alias
|
|
28
26
|
from serial.tools.list_ports_common import ListPortInfo
|
|
29
27
|
|
|
30
28
|
from is_matrix_forge.common.helpers import coerce_to_int
|
|
31
29
|
from aliaser.metaclass import AliasMeta
|
|
32
30
|
from is_matrix_forge.led_matrix.constants import SLOT_MAP
|
|
33
31
|
from is_matrix_forge.led_matrix.display.text import show_string as _show_string_raw
|
|
34
|
-
from is_matrix_forge.led_matrix.controller import MultitonMeta
|
|
35
32
|
from is_matrix_forge.led_matrix.controller.helpers.threading import synchronized
|
|
36
33
|
from is_matrix_forge.led_matrix.commands.map import CommandVals
|
|
37
34
|
from is_matrix_forge.led_matrix.hardware import send_command
|
|
@@ -40,10 +37,32 @@ from is_matrix_forge.led_matrix.display.text import show_string
|
|
|
40
37
|
|
|
41
38
|
COMMANDS = CommandVals
|
|
42
39
|
|
|
43
|
-
MOD_LOGGER = ROOT_LOGGER.get_child('led_matrix.controller.controller')
|
|
44
40
|
|
|
41
|
+
class _BreatherPauseCtx:
|
|
42
|
+
def __init__(self, controller: LEDMatrixController):
|
|
43
|
+
self.controller = controller
|
|
44
|
+
self._was_breathing = False
|
|
45
|
+
|
|
46
|
+
def __enter__(self):
|
|
47
|
+
"""
|
|
48
|
+
Pause the breathing animation. This context manager ensures that the
|
|
49
|
+
device is not breathing when the context is exited. **A sleep of .05
|
|
50
|
+
seconds is carried out to ensure the hardware settles before the
|
|
51
|
+
context is exited. This is to ensure that the device has time to
|
|
52
|
+
leave the breathing state and restore the previous state.**
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
if getattr(self.controller, "breathing", False):
|
|
56
|
+
self._was_breathing = True
|
|
57
|
+
self.controller.breathing = False
|
|
58
|
+
sleep(0.05) # Required short delay to ensure hardware settles
|
|
45
59
|
|
|
46
|
-
|
|
60
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
61
|
+
if self._was_breathing:
|
|
62
|
+
self.controller.breathing = True
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class LEDMatrixController(metaclass=AliasMeta):
|
|
47
66
|
"""
|
|
48
67
|
Controller class for LED Matrix devices.
|
|
49
68
|
|
|
@@ -89,7 +108,10 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
89
108
|
do_not_show_grid_on_init: Optional[bool] = False,
|
|
90
109
|
thread_safe: Optional[bool] = False,
|
|
91
110
|
do_not_warn_on_thread_misuse: Optional[bool] = False,
|
|
92
|
-
hold_all: Optional[bool] = False
|
|
111
|
+
hold_all: Optional[bool] = False,
|
|
112
|
+
skip_greeting: Optional[bool] = False,
|
|
113
|
+
skip_identify: Optional[bool] = False,
|
|
114
|
+
skip_all_init_animations: Optional[bool] = False
|
|
93
115
|
):
|
|
94
116
|
"""
|
|
95
117
|
Initialize a new LED Matrix Controller.
|
|
@@ -150,7 +172,16 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
150
172
|
|
|
151
173
|
self.__set_up_thread_safety__(thread_safe)
|
|
152
174
|
|
|
153
|
-
self.__post_init__(
|
|
175
|
+
self.__post_init__(
|
|
176
|
+
skip_identify=skip_identify or skip_all_init_animations,
|
|
177
|
+
skip_greeting=skip_greeting or skip_all_init_animations
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def __breather_paused(self):
|
|
181
|
+
return _BreatherPauseCtx(self)
|
|
182
|
+
|
|
183
|
+
def __greet(self):
|
|
184
|
+
self.scroll_text('Hello')
|
|
154
185
|
|
|
155
186
|
# ---------------------------------------------------------------------
|
|
156
187
|
# Keep‑alive internal worker
|
|
@@ -172,7 +203,7 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
172
203
|
# Wait with timeout so the thread can exit early when stop_evt is set
|
|
173
204
|
stop_evt.wait(self._KEEP_ALIVE_INTERVAL)
|
|
174
205
|
|
|
175
|
-
#
|
|
206
|
+
# ----------------------------------f-----------------------------------
|
|
176
207
|
# Property controlling the keep‑alive thread
|
|
177
208
|
# ---------------------------------------------------------------------
|
|
178
209
|
|
|
@@ -214,7 +245,7 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
214
245
|
self._keep_alive = False
|
|
215
246
|
self.__post_init__()
|
|
216
247
|
|
|
217
|
-
def __post_init__(self):
|
|
248
|
+
def __post_init__(self, skip_greeting= False, skip_identify=False):
|
|
218
249
|
from is_matrix_forge.led_matrix.display.effects.breather import Breather
|
|
219
250
|
|
|
220
251
|
self.__breather = Breather(self)
|
|
@@ -235,19 +266,22 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
235
266
|
if self.breather is None:
|
|
236
267
|
self.__breather = Breather(self)
|
|
237
268
|
|
|
269
|
+
if not skip_greeting:
|
|
270
|
+
self.__greet()
|
|
271
|
+
|
|
272
|
+
if not skip_identify:
|
|
273
|
+
self.identify()
|
|
274
|
+
|
|
238
275
|
def __set_up_thread_safety__(self, thread_safe_opt: bool):
|
|
239
276
|
if thread_safe_opt is None or not thread_safe_opt:
|
|
240
277
|
self._thread_safe = False
|
|
241
278
|
return
|
|
242
279
|
else:
|
|
243
280
|
self._thread_safe = True
|
|
244
|
-
self._cmd_lock = threading.RLock()
|
|
245
281
|
|
|
246
282
|
return
|
|
247
283
|
|
|
248
|
-
|
|
249
284
|
@property
|
|
250
|
-
@alias('is_animating')
|
|
251
285
|
def animating(self) -> bool:
|
|
252
286
|
"""
|
|
253
287
|
Check if the device is currently animating.
|
|
@@ -255,12 +289,9 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
255
289
|
Returns:
|
|
256
290
|
bool: True if the device is animating, False otherwise.
|
|
257
291
|
"""
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
except TypeError as e:
|
|
262
|
-
log.error('Received null response')
|
|
263
|
-
|
|
292
|
+
# Guard against `None`, and empty lists
|
|
293
|
+
res = send_command(dev= self.device, command= COMMANDS.Animate, with_response= True)
|
|
294
|
+
return bool(res and res[0])
|
|
264
295
|
|
|
265
296
|
@property
|
|
266
297
|
def breather(self) -> Breather:
|
|
@@ -368,8 +399,8 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
368
399
|
"""
|
|
369
400
|
return self.__keep_image
|
|
370
401
|
|
|
371
|
-
@keep_image.setter
|
|
372
402
|
@validate_type(bool)
|
|
403
|
+
@keep_image.setter
|
|
373
404
|
def keep_image(self, new):
|
|
374
405
|
self.__keep_image = new
|
|
375
406
|
|
|
@@ -416,7 +447,7 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
416
447
|
def scroll_text(self, text: str, skip_end_space: bool = False, loop: bool = False):
|
|
417
448
|
from is_matrix_forge.led_matrix.display.text.scroller import TextScroller
|
|
418
449
|
from is_matrix_forge.led_matrix.display.animations.animation import Animation
|
|
419
|
-
from is_matrix_forge.led_matrix import fonts as font
|
|
450
|
+
from is_matrix_forge.led_matrix.display.assets import fonts as font
|
|
420
451
|
|
|
421
452
|
text_animation = Animation(TextScroller(font).scroll(text, skip_end_space))
|
|
422
453
|
text_animation.set_all_frame_durations(.03)
|
|
@@ -424,6 +455,17 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
424
455
|
text_animation.loop = True
|
|
425
456
|
return self.play_animation(text_animation)
|
|
426
457
|
|
|
458
|
+
@property
|
|
459
|
+
def serial_number(self) -> str:
|
|
460
|
+
"""
|
|
461
|
+
Get the USB serial number of the device, if available.
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
str:
|
|
465
|
+
The USB serial number of the device, or an empty string if not available.
|
|
466
|
+
"""
|
|
467
|
+
return self.device.serial_number
|
|
468
|
+
|
|
427
469
|
@property
|
|
428
470
|
def set_brightness_on_init(self):
|
|
429
471
|
"""
|
|
@@ -631,6 +673,9 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
631
673
|
if not skip_clear:
|
|
632
674
|
self.clear()
|
|
633
675
|
|
|
676
|
+
@property
|
|
677
|
+
def is_animating(self):
|
|
678
|
+
return self.animating
|
|
634
679
|
|
|
635
680
|
def jump_to_bootloader(self):
|
|
636
681
|
"""
|
|
@@ -642,8 +687,21 @@ class LEDMatrixController(Loggable, metaclass=AliasMeta):
|
|
|
642
687
|
from is_matrix_forge.led_matrix.hardware import bootloader_jump
|
|
643
688
|
bootloader_jump(self.device)
|
|
644
689
|
|
|
645
|
-
def list_patterns(self):
|
|
646
|
-
|
|
690
|
+
def list_patterns(self, skip_print=False):
|
|
691
|
+
"""
|
|
692
|
+
List the built-in patterns available on the device.
|
|
693
|
+
|
|
694
|
+
The primary purpose for this method is to return the pattern names,
|
|
695
|
+
but also prints them if `skip_print` is set to `False`.
|
|
696
|
+
|
|
697
|
+
Parameters:
|
|
698
|
+
skip_print (bool, optional):
|
|
699
|
+
If True, don't print the pattern names. (optional, defaults to False).
|
|
700
|
+
"""
|
|
701
|
+
if not skip_print:
|
|
702
|
+
print(', '.join(self.built_in_pattern_names))
|
|
703
|
+
|
|
704
|
+
return self.built_in_pattern_names
|
|
647
705
|
|
|
648
706
|
@synchronized
|
|
649
707
|
def set_brightness(self, brightness: Union[int, float], __from_setter=False) -> None:
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
3
|
+
from typing import List, Type, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_controllers(
|
|
7
|
+
threaded: bool = False,
|
|
8
|
+
max_workers: Optional[int] = None,
|
|
9
|
+
controller_cls: Optional[Type] = None,
|
|
10
|
+
devices: Optional[list] = None,
|
|
11
|
+
**controller_kwargs
|
|
12
|
+
) -> List['LEDMatrixController']:
|
|
13
|
+
"""
|
|
14
|
+
Create LEDMatrixController objects, optionally in parallel threads.
|
|
15
|
+
|
|
16
|
+
Parameters:
|
|
17
|
+
threaded (bool): If True, create controllers in threads.
|
|
18
|
+
max_workers (int, optional): Maximum threads to use (default: len(devices)).
|
|
19
|
+
controller_cls (Type, optional): Alternate controller class (default: LEDMatrixController).
|
|
20
|
+
devices (list, optional): Device list (default: imported DEVICES).
|
|
21
|
+
**controller_kwargs: Additional kwargs for controller class.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
List[LEDMatrixController]: List of controller instances.
|
|
25
|
+
"""
|
|
26
|
+
from is_matrix_forge.led_matrix.constants import DEVICES
|
|
27
|
+
from is_matrix_forge.led_matrix.controller.controller import LEDMatrixController
|
|
28
|
+
|
|
29
|
+
_devices = devices or DEVICES
|
|
30
|
+
_controller_cls = controller_cls or LEDMatrixController
|
|
31
|
+
_max_workers = max_workers or len(_devices)
|
|
32
|
+
|
|
33
|
+
def create_controller(device):
|
|
34
|
+
# Here’s where you could add try/except if your hardware is flaky!
|
|
35
|
+
return _controller_cls(device, 100, thread_safe=True, **controller_kwargs)
|
|
36
|
+
|
|
37
|
+
if not threaded:
|
|
38
|
+
return [create_controller(dev) for dev in _devices]
|
|
39
|
+
|
|
40
|
+
with ThreadPoolExecutor(max_workers=_max_workers) as executor:
|
|
41
|
+
futures = [executor.submit(create_controller, dev) for dev in _devices]
|
|
42
|
+
controllers = []
|
|
43
|
+
for fut in as_completed(futures):
|
|
44
|
+
controllers.append(fut.result())
|
|
45
|
+
return controllers
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import threading
|
|
3
|
+
|
|
4
|
+
from is_matrix_forge.log_engine import ROOT_LOGGER as PARENT_LOGGER
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def synchronized(method=None, *, pause_breather=True):
|
|
8
|
+
"""
|
|
9
|
+
Lock & warn if you’re calling off-thread with thread_safe=False.
|
|
10
|
+
Optionally pause/resume the breather around the method.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
@synchronized
|
|
14
|
+
def foo(...): ...
|
|
15
|
+
|
|
16
|
+
@synchronized(pause_breather=True)
|
|
17
|
+
def draw_grid(...): ...
|
|
18
|
+
"""
|
|
19
|
+
def decorator(method):
|
|
20
|
+
@functools.wraps(method)
|
|
21
|
+
def wrapper(self, *args, **kwargs):
|
|
22
|
+
cur_thread_id = threading.get_ident()
|
|
23
|
+
|
|
24
|
+
# ⚠️ misuse detection: warn if another thread calls while thread_safe is False
|
|
25
|
+
if (
|
|
26
|
+
not getattr(self, "_thread_safe", False)
|
|
27
|
+
and getattr(self, "_warn_on_thread_misuse", True)
|
|
28
|
+
and cur_thread_id != getattr(self, "_owner_thread_id", None)
|
|
29
|
+
):
|
|
30
|
+
PARENT_LOGGER.warning(
|
|
31
|
+
"%r called from thread %r but thread_safe=False",
|
|
32
|
+
self,
|
|
33
|
+
threading.current_thread().name
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
ctx = None
|
|
37
|
+
if pause_breather:
|
|
38
|
+
# Try to get the Breather context manager in a robust way
|
|
39
|
+
breather = getattr(self, "breather", None)
|
|
40
|
+
if breather is not None and hasattr(breather, "paused"):
|
|
41
|
+
ctx = breather.paused
|
|
42
|
+
else:
|
|
43
|
+
# fallback to legacy or alternate pause context
|
|
44
|
+
ctx = getattr(self, "breather_paused", None)
|
|
45
|
+
if ctx is None:
|
|
46
|
+
raise RuntimeError(
|
|
47
|
+
"Could not find a valid breather pause context. "
|
|
48
|
+
"Neither 'breather.paused' nor 'breather_paused' are present on this object."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if ctx is not None:
|
|
52
|
+
with ctx():
|
|
53
|
+
return _run_locked(self, method, *args, **kwargs)
|
|
54
|
+
else:
|
|
55
|
+
return _run_locked(self, method, *args, **kwargs)
|
|
56
|
+
|
|
57
|
+
return wrapper
|
|
58
|
+
|
|
59
|
+
def _run_locked(self, method, *args, **kwargs):
|
|
60
|
+
# 🔐 actual lock if thread_safe=True
|
|
61
|
+
if getattr(self, "_thread_safe", False) and getattr(self, "_cmd_lock", None):
|
|
62
|
+
with self._cmd_lock:
|
|
63
|
+
return method(self, *args, **kwargs)
|
|
64
|
+
# fallback: call method directly
|
|
65
|
+
return method(self, *args, **kwargs)
|
|
66
|
+
|
|
67
|
+
return decorator if method is None else decorator(method)
|