IS-Matrix-Forge 1.0.0.dev9__tar.gz → 1.0.0.dev27__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.dev9 → is_matrix_forge-1.0.0.dev27}/PKG-INFO +35 -2
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/README.md +40 -7
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/__init__.py +1 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/__init__.py +1 -1
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/base.py +289 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/builders.py +132 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/helpers.py +35 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/models/__init__.py +17 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/models/glyph.py +4 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/helpers/__init__.py +9 -2
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/common/helpers/errors.py +23 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/common/logging/__init__.py +17 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/common/logging/log_exceptions.py +79 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/__init__.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/main_window/__init__.py +9 -10
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/dev_tools/__init__.py +1 -1
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/__init__.py +4 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/Scripts/battery_monitor.py +4 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/identify_matrices.py +102 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/Scripts/install_presets/main.py +68 -16
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/__init__.py +210 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/__init__.py +108 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/commands/__init__.py +17 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/commands/display_text.py +37 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/commands/identify_matrices.py +42 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/commands/scroll_text.py +35 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/guards.py +115 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/__init__.py +96 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/commands/__init__.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/commands/map.py +1 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/constants.py +4 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/controller/__init__.py +4 -15
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/base.py +90 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/__init__.py +17 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/animation.py +200 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/breather.py +67 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/__init__.py +370 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/__init__.py +12 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/breather_killer.py +25 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/easing.py +15 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/fade_planner.py +25 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/fade_spec.py +10 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/levels.py +13 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/percent.py +25 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/safe_ops.py +31 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/manager.py +425 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/drawing.py +98 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/example_template.py +45 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/identify.py +46 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/keep_alive.py +81 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/controller.py +76 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/controller/helpers/__init__.py +28 -6
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/helpers/threading.py +45 -0
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/controller/controller.py → is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/legacy.py +113 -36
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/__init__.py +1 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/__init__.py +19 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/animation.py +93 -64
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/audio_visualizer.py +3 -3
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/effects/__init__.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/font/__init__.py +1 -1
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations/frame/base.py +452 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/frame/helpers.py +15 -1
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations/text/__init__.py +5 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations/text/glyph_normalizer.py +110 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations/text_scroller.py +222 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations.zip +0 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/assets/__init__.py +2 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/assets/fonts.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/effects/__init__.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/effects/breather.py +2 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/grid/__init__.py +1 -1
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/grid/grid.py → is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/grid/base.py +179 -24
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/grid/composite/background.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/grid/composite/composite.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/grid/helpers.py +4 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/grid/presets/__init__.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/helpers/__init__.py +16 -4
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/patterns/built_in/constants.py +3 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/patterns/built_in/stencils/__init__.py +9 -6
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/scene/scene.py +3 -3
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/text/__init__.py +2 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/text/text.py +303 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/base.py +5 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/matrix.py +5 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/hardware.py +68 -7
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/helpers/__init__.py +18 -2
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/is_matrix_forge.led_matrix.display.zip +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/tools/__init__.py +1 -1
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/utils/__init__.py +1 -1
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/log_engine.py +44 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/layout/__init__.py +1 -1
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/progress.py +449 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/scripts/__init__.py +0 -0
- is_matrix_forge-1.0.0.dev27/is_matrix_forge/scripts/preset_installer.py +18 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/pyproject.toml +9 -2
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/__init__.py +0 -8
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/assets/font_map.py +0 -119
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/Scripts/__init__.py +0 -4
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/Scripts/identify_matrices.py +0 -228
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/__init__.py +0 -58
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/controller/helpers/threading.py +0 -67
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/__init__.py +0 -13
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/animations/frame/base.py +0 -245
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/animations/text_scroller.py +0 -104
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/assets/__init__.py +0 -2
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/led_matrix.py +0 -25
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/log_engine.py +0 -25
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/progress.py +0 -99
- is_matrix_forge-1.0.0.dev9/is_matrix_forge/scripts/preset_installer.py +0 -184
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/LICENSE.md +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/Untitled.ipynb +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/audio/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/audio/files/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/audio/files/plugged.wav +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/audio/files/unplugged.wav +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/char_map.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/digit_map.py +0 -0
- /is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/patterns/built_in/constants.py → /is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/decorators/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/decorators/freeze_setter.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/dirs.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/helpers/github_api.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/main_window/layout/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/main_window/layout/button_grid/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/main_window/layout/button_grid/column.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/main_window/layout/button_grid/grid.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/main_window/layout/menu_bar.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/main_window/streamlit_app.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/dev_tools/debug.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/dev_tools/presets.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/b1display.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/c1minimal.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/font.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/helpers/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/ledmatrix.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/Scripts/install_presets/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/Scripts/install_presets/arguments.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/Scripts/pixel_grid.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/controller/multiton.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/controller/threaded.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/font/font.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/font/map_builder.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/frame/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/keep_awake.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/animations/plugged.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/effects/fade.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/effects/scroller/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/effects/scroller/base.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/effects/scroller/helpers.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/grid/composite/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/grid/composite/foreground.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/grid/presets/manifest.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/helpers/columns.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/media.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/patterns/built_in/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/patterns/built_in/commands.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/patterns/built_in/stencils/res.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/scene/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/scene/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/text/scroller.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/composite_grid.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/grid.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/misc.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/helpers/device/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/helpers/device/bootloader/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/helpers/device/bootloader/constants.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/helpers/device/bootloader/unix.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/helpers/device/bootloader/win.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/helpers/status_handler.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/status.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/tools/identify.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/events.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/event/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/event/collection.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/event/errors.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/event/event.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/layout/base.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/metaclasses/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/windows/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/windows/base.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/windows/main/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/windows/main/layout.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/helpers.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/monitor.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/notify/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/notify/sounds/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/notify/sounds/base.py +0 -0
- /is_matrix_forge-1.0.0.dev9/is_matrix_forge/scripts/__init__.py → /is_matrix_forge-1.0.0.dev27/is_matrix_forge/scripts/led-matrix.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/base/__init__.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/base/answer.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/base/message.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/base/request.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/exchange.py +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/blank.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/manifest.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/shock_face.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test1.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test2.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test3.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test4.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test5.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test6.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test7.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/x_animation II.json +0 -0
- {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/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.dev27
|
|
4
4
|
Summary:
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: led,matrix,framework,hardware
|
|
@@ -28,7 +28,7 @@ Requires-Dist: pyserial (>=3.5,<4.0)
|
|
|
28
28
|
Requires-Dist: pysimplegui-4-foss (==4.60.4.1)
|
|
29
29
|
Requires-Dist: pysimpleguiweb (==0.39.0)
|
|
30
30
|
Requires-Dist: requests (>=2.32.4,<3.0.0)
|
|
31
|
-
Requires-Dist: wmi (>=1.5.1,<2.0.0)
|
|
31
|
+
Requires-Dist: wmi (>=1.5.1,<2.0.0) ; sys_platform == "win32"
|
|
32
32
|
Project-URL: Homepage, https://github.com/Inspyre-Softworks/IS-Matrix-Forge
|
|
33
33
|
Project-URL: Repository, https://github.com/Inspyre-Softworks/IS-Matrix-Forge
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
@@ -136,6 +136,26 @@ ctrl = LEDMatrixController(DEVICES[0])
|
|
|
136
136
|
ctrl.scroll_text("Hello World!", loop=False)
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
## Controller Architecture
|
|
140
|
+
|
|
141
|
+
- Composition is implemented via multiple mixins that all use cooperative
|
|
142
|
+
initialization with `super()`.
|
|
143
|
+
- MRO ordering ensures safe access patterns during `__init__`:
|
|
144
|
+
- `DeviceBase` initializes early so mixins can use `self.device`.
|
|
145
|
+
- `BrightnessManager` precedes `BreatherManager` because the breather inspects
|
|
146
|
+
brightness at init time.
|
|
147
|
+
- `BreatherManager` precedes `IdentifyManager` because `IdentifyManager` may
|
|
148
|
+
call `@synchronized` methods in `__init__` that rely on a breather pause context.
|
|
149
|
+
- `Loggable` is placed last so the cooperative chain can pass `logger=` safely.
|
|
150
|
+
- Thread safety: pass `thread_safe=True` to enable an internal `RLock` used by
|
|
151
|
+
the `@synchronized` decorator for device operations.
|
|
152
|
+
- Logging: integrates with InspyLogger when available and falls back to a simple
|
|
153
|
+
logger; the controller passes `parent_log_device` through the cooperative chain
|
|
154
|
+
and the fallback stub supports it.
|
|
155
|
+
|
|
156
|
+
MRO diagram (left → right, init order)
|
|
157
|
+
`DeviceBase → KeepAliveManager → AnimationManager → DrawingManager → BrightnessManager → BreatherManager → IdentifyManager → Loggable`
|
|
158
|
+
|
|
139
159
|
### Progress Bars
|
|
140
160
|
|
|
141
161
|
```python
|
|
@@ -188,6 +208,19 @@ run_power_monitor(device)
|
|
|
188
208
|
Contributions are welcome! If you have ideas or improvements for Matrix Forge,
|
|
189
209
|
feel free to open an issue or submit a pull request.
|
|
190
210
|
|
|
211
|
+
- See docs/contributing-mixins.md for guidance on authoring new controller mixins
|
|
212
|
+
using cooperative initialization and the `@synchronized` decorator.
|
|
213
|
+
A minimal reference mixin is available at
|
|
214
|
+
`is_matrix_forge/led_matrix/controller/components/example_template.py`.
|
|
215
|
+
|
|
216
|
+
Quick checklist for a new controller mixin
|
|
217
|
+
- Create `is_matrix_forge/led_matrix/controller/components/<name>.py` with `ClassNameManager`.
|
|
218
|
+
- In `__init__(...)`, accept keyword-only args and call `super().__init__(**kwargs)`.
|
|
219
|
+
- Avoid IO-heavy work in `__init__`; use `@synchronized` for device methods.
|
|
220
|
+
- If calling synchronized methods in `__init__`, ensure MRO places you after `BreatherManager`.
|
|
221
|
+
- If adding to the main controller, place after `DeviceBase` and before `IdentifyManager`; consider dependencies.
|
|
222
|
+
- Provide docstrings, type hints, and a small usage example or test.
|
|
223
|
+
|
|
191
224
|
## License
|
|
192
225
|
|
|
193
226
|
This project is licensed under the MIT License - see the LICENSE.md file for details.
|
|
@@ -80,7 +80,7 @@ This project requires Python 3.12 or newer and the following dependencies:
|
|
|
80
80
|
pip install .
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
## Usage Examples
|
|
83
|
+
## Usage Examples
|
|
84
84
|
|
|
85
85
|
### Identify Connected Devices
|
|
86
86
|
|
|
@@ -98,8 +98,28 @@ from is_matrix_forge.led_matrix.controller.controller import LEDMatrixController
|
|
|
98
98
|
from is_matrix_forge.led_matrix.helpers.device import DEVICES
|
|
99
99
|
|
|
100
100
|
ctrl = LEDMatrixController(DEVICES[0])
|
|
101
|
-
ctrl.scroll_text("Hello World!", loop=False)
|
|
102
|
-
```
|
|
101
|
+
ctrl.scroll_text("Hello World!", loop=False)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Controller Architecture
|
|
105
|
+
|
|
106
|
+
- Composition is implemented via multiple mixins that all use cooperative
|
|
107
|
+
initialization with `super()`.
|
|
108
|
+
- MRO ordering ensures safe access patterns during `__init__`:
|
|
109
|
+
- `DeviceBase` initializes early so mixins can use `self.device`.
|
|
110
|
+
- `BrightnessManager` precedes `BreatherManager` because the breather inspects
|
|
111
|
+
brightness at init time.
|
|
112
|
+
- `BreatherManager` precedes `IdentifyManager` because `IdentifyManager` may
|
|
113
|
+
call `@synchronized` methods in `__init__` that rely on a breather pause context.
|
|
114
|
+
- `Loggable` is placed last so the cooperative chain can pass `logger=` safely.
|
|
115
|
+
- Thread safety: pass `thread_safe=True` to enable an internal `RLock` used by
|
|
116
|
+
the `@synchronized` decorator for device operations.
|
|
117
|
+
- Logging: integrates with InspyLogger when available and falls back to a simple
|
|
118
|
+
logger; the controller passes `parent_log_device` through the cooperative chain
|
|
119
|
+
and the fallback stub supports it.
|
|
120
|
+
|
|
121
|
+
MRO diagram (left → right, init order)
|
|
122
|
+
`DeviceBase → KeepAliveManager → AnimationManager → DrawingManager → BrightnessManager → BreatherManager → IdentifyManager → Loggable`
|
|
103
123
|
|
|
104
124
|
### Progress Bars
|
|
105
125
|
|
|
@@ -148,10 +168,23 @@ run_power_monitor(device)
|
|
|
148
168
|
2. Check that the matrix dimensions match the expected 9x34 size.
|
|
149
169
|
3. Try clearing the matrix and restarting the application.
|
|
150
170
|
|
|
151
|
-
## Contributing
|
|
152
|
-
|
|
153
|
-
Contributions are welcome! If you have ideas or improvements for Matrix Forge,
|
|
154
|
-
feel free to open an issue or submit a pull request.
|
|
171
|
+
## Contributing
|
|
172
|
+
|
|
173
|
+
Contributions are welcome! If you have ideas or improvements for Matrix Forge,
|
|
174
|
+
feel free to open an issue or submit a pull request.
|
|
175
|
+
|
|
176
|
+
- See docs/contributing-mixins.md for guidance on authoring new controller mixins
|
|
177
|
+
using cooperative initialization and the `@synchronized` decorator.
|
|
178
|
+
A minimal reference mixin is available at
|
|
179
|
+
`is_matrix_forge/led_matrix/controller/components/example_template.py`.
|
|
180
|
+
|
|
181
|
+
Quick checklist for a new controller mixin
|
|
182
|
+
- Create `is_matrix_forge/led_matrix/controller/components/<name>.py` with `ClassNameManager`.
|
|
183
|
+
- In `__init__(...)`, accept keyword-only args and call `super().__init__(**kwargs)`.
|
|
184
|
+
- Avoid IO-heavy work in `__init__`; use `@synchronized` for device methods.
|
|
185
|
+
- If calling synchronized methods in `__init__`, ensure MRO places you after `BreatherManager`.
|
|
186
|
+
- If adding to the main controller, place after `DeviceBase` and before `IdentifyManager`; consider dependencies.
|
|
187
|
+
- Provide docstrings, type hints, and a small usage example or test.
|
|
155
188
|
|
|
156
189
|
## License
|
|
157
190
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from is_matrix_forge.led_matrix.controller import get_controllers
|
{is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/__init__.py
RENAMED
|
@@ -3,6 +3,6 @@ Author: Taylor B. tayjaybabee@gmail.com
|
|
|
3
3
|
Date: 2025-04-15 09:15:37
|
|
4
4
|
LastEditors: Taylor B. tayjaybabee@gmail.com
|
|
5
5
|
LastEditTime: 2025-04-15 09:24:40
|
|
6
|
-
FilePath: is_matrix_forge/assets/
|
|
6
|
+
FilePath: is_matrix_forge/assets/base.py
|
|
7
7
|
Description: 这是默认设置,可以在设置》工具》File Description中进行配置
|
|
8
8
|
"""
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author:
|
|
3
|
+
Inspyre Softworks
|
|
4
|
+
|
|
5
|
+
Project:
|
|
6
|
+
IS-Matrix-Forge
|
|
7
|
+
|
|
8
|
+
File:
|
|
9
|
+
${DIR_PATH}/${FILE_NAME}
|
|
10
|
+
|
|
11
|
+
Description:
|
|
12
|
+
FontMap: a dict-like facade over character/symbol glyph data with optional
|
|
13
|
+
case-insensitive lookups and a configurable fallback character.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections.abc import Mapping, Iterator
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from typing import Any, Dict, Iterable, Optional, Tuple, Callable
|
|
21
|
+
|
|
22
|
+
from inspyre_toolbox.syntactic_sweets.classes import validate_type
|
|
23
|
+
from .models.glyph import Glyph
|
|
24
|
+
from .helpers import load_builtin_char_map, get_ignore_case, has_key_ignore_case
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(init=False, slots=True)
|
|
28
|
+
class FontMap(Mapping[str, Glyph]):
|
|
29
|
+
"""
|
|
30
|
+
FontMap allows lookup of character and symbol patterns as a dict-like mapping.
|
|
31
|
+
|
|
32
|
+
Parameters:
|
|
33
|
+
font_map:
|
|
34
|
+
Either:
|
|
35
|
+
- a *combined* map: { 'A': Glyph, '?': Glyph, ... }, or
|
|
36
|
+
- a *raw* map with top-level keys for characters/symbols:
|
|
37
|
+
{ 'characters': {...}, 'symbols': {...} }.
|
|
38
|
+
If None, falls back to `load_builtin_char_map()`.
|
|
39
|
+
characters_key:
|
|
40
|
+
Key name used for character definitions when a *raw* map is supplied.
|
|
41
|
+
symbols_key:
|
|
42
|
+
Key name used for symbol definitions when a *raw* map is supplied.
|
|
43
|
+
case_sensitive:
|
|
44
|
+
When False (default), keys are normalized to uppercase.
|
|
45
|
+
fallback_char:
|
|
46
|
+
Glyph to use when a key is missing. Defaults to '?'. If the glyph for '?' is
|
|
47
|
+
unavailable the implementation falls back to ' ' when present, otherwise a
|
|
48
|
+
:class:`ValueError` is raised requesting an explicit fallback.
|
|
49
|
+
|
|
50
|
+
Properties:
|
|
51
|
+
is_case_sensitive (bool): Current case-sensitivity flag.
|
|
52
|
+
fallback_char (str): Current fallback character.
|
|
53
|
+
characters (list[str]): Keys in the character sub-map (if available).
|
|
54
|
+
symbols (list[str]): Keys in the symbol sub-map (if available).
|
|
55
|
+
character_map (dict[str, Glyph]): Raw character map (best-effort).
|
|
56
|
+
symbol_map (dict[str, Glyph]): Raw symbol map (best-effort).
|
|
57
|
+
|
|
58
|
+
Methods:
|
|
59
|
+
lookup(key: str, kind: Optional[str] = None) -> Glyph
|
|
60
|
+
Look up a glyph, optionally forcing 'character' or 'symbol'.
|
|
61
|
+
reload(font_map: dict) -> None
|
|
62
|
+
Replace the underlying map and rebuild indexes.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
ValueError:
|
|
66
|
+
- If `fallback_char` is not a single character.
|
|
67
|
+
Example Usage:
|
|
68
|
+
fm = FontMap() # load built-ins
|
|
69
|
+
g_q = fm.get('?') # dict-like get
|
|
70
|
+
g_a = fm['A'] # Mapping access (uses fallback if missing)
|
|
71
|
+
fm.is_case_sensitive = True # switch mode and reindex
|
|
72
|
+
fm.fallback_char = ' ' # change fallback
|
|
73
|
+
g = fm.lookup('→', kind='symbol') # force symbol map
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
_glyphs: Dict[str, Glyph]
|
|
77
|
+
_raw_map: Optional[Dict[str, Dict[str, Glyph]]]
|
|
78
|
+
_characters_key: str
|
|
79
|
+
_symbols_key: str
|
|
80
|
+
_case_sensitive: bool
|
|
81
|
+
_fallback_char: str
|
|
82
|
+
|
|
83
|
+
DEFAULT_FALLBACK_CHAR: str = '?'
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
font_map: Optional[Dict[str, Any]] = None,
|
|
88
|
+
*,
|
|
89
|
+
characters_key: str = 'characters',
|
|
90
|
+
symbols_key: str = 'symbols',
|
|
91
|
+
case_sensitive: bool = False,
|
|
92
|
+
fallback_char: Optional[str] = None
|
|
93
|
+
) -> None:
|
|
94
|
+
self._characters_key = characters_key
|
|
95
|
+
self._symbols_key = symbols_key
|
|
96
|
+
self._case_sensitive = case_sensitive
|
|
97
|
+
self._fallback_char = fallback_char or '?'
|
|
98
|
+
self._raw_map = None
|
|
99
|
+
self._glyphs = {}
|
|
100
|
+
|
|
101
|
+
# Load data
|
|
102
|
+
if font_map is None:
|
|
103
|
+
# Prefer helper: expected to return a combined {str: Glyph} mapping.
|
|
104
|
+
data = load_builtin_char_map()
|
|
105
|
+
self._ingest_unknown_map(data)
|
|
106
|
+
else:
|
|
107
|
+
self._ingest_unknown_map(font_map)
|
|
108
|
+
|
|
109
|
+
self._validate_fallback()
|
|
110
|
+
|
|
111
|
+
# ---------- ingestion / normalization ----------
|
|
112
|
+
|
|
113
|
+
def _looks_like_raw_map(self, data: Dict[str, Any]) -> bool:
|
|
114
|
+
return (
|
|
115
|
+
isinstance(data, dict)
|
|
116
|
+
and (has_key_ignore_case(data, self._characters_key) or has_key_ignore_case(data, self._symbols_key))
|
|
117
|
+
and isinstance(get_ignore_case(data, self._characters_key, {}), dict)
|
|
118
|
+
and isinstance(get_ignore_case(data, self._symbols_key, {}), dict)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def _ingest_unknown_map(self, data: Dict[str, Any]) -> None:
|
|
122
|
+
if self._looks_like_raw_map(data):
|
|
123
|
+
chars = dict(get_ignore_case(data, self._characters_key, {}) or {})
|
|
124
|
+
syms = dict(get_ignore_case(data, self._symbols_key, {}) or {})
|
|
125
|
+
self._raw_map = {'characters': chars, 'symbols': syms}
|
|
126
|
+
self._glyphs = {}
|
|
127
|
+
self._merge_into_glyphs(chars)
|
|
128
|
+
self._merge_into_glyphs(syms)
|
|
129
|
+
else:
|
|
130
|
+
self._raw_map = None
|
|
131
|
+
self._ingest_combined_map(data) # treat as already-flat mapping
|
|
132
|
+
|
|
133
|
+
def _ingest_combined_map(self, combined: Dict[str, Glyph]) -> None:
|
|
134
|
+
self._raw_map = None
|
|
135
|
+
self._glyphs = {}
|
|
136
|
+
self._merge_into_glyphs(combined)
|
|
137
|
+
|
|
138
|
+
def _merge_into_glyphs(self, mapping: Dict[str, Glyph]) -> None:
|
|
139
|
+
if not mapping:
|
|
140
|
+
return
|
|
141
|
+
normalise: Callable[[str], str] = str if self._case_sensitive else lambda key: str(key).upper()
|
|
142
|
+
for k, v in mapping.items():
|
|
143
|
+
self._glyphs[normalise(k)] = v
|
|
144
|
+
|
|
145
|
+
def _normalize_key(self, key: str) -> str:
|
|
146
|
+
return key if self._case_sensitive else key.upper()
|
|
147
|
+
|
|
148
|
+
def _validate_fallback(self) -> None:
|
|
149
|
+
# Ensure fallback exists; prefer '?' then space, otherwise require an explicit fallback.
|
|
150
|
+
normalized = self._normalize_key(self._fallback_char)
|
|
151
|
+
if normalized in self._glyphs:
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
space_key = self._normalize_key(' ')
|
|
155
|
+
if space_key in self._glyphs:
|
|
156
|
+
self._fallback_char = ' '
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
if not self._glyphs:
|
|
160
|
+
raise ValueError('font_map must contain at least one glyph definition')
|
|
161
|
+
|
|
162
|
+
raise ValueError(
|
|
163
|
+
"Fallback character is missing from the font_map and no space fallback is available. "
|
|
164
|
+
"Provide a valid fallback_char or include an appropriate glyph."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# ---------- Mapping protocol ----------
|
|
168
|
+
|
|
169
|
+
def __getitem__(self, key: str) -> Glyph:
|
|
170
|
+
try:
|
|
171
|
+
return self._glyphs[self._normalize_key(key)]
|
|
172
|
+
except KeyError:
|
|
173
|
+
return self._glyphs[self._normalize_key(self._fallback_char)]
|
|
174
|
+
|
|
175
|
+
def __iter__(self) -> Iterator[str]:
|
|
176
|
+
return iter(self._glyphs)
|
|
177
|
+
|
|
178
|
+
def __len__(self) -> int:
|
|
179
|
+
return len(self._glyphs)
|
|
180
|
+
|
|
181
|
+
# dict-like conveniences
|
|
182
|
+
def get(self, key: str, default: Optional[Glyph] = None) -> Optional[Glyph]: # type: ignore[override]
|
|
183
|
+
return self._glyphs.get(self._normalize_key(key), default)
|
|
184
|
+
|
|
185
|
+
def keys(self) -> Iterable[str]: # type: ignore[override]
|
|
186
|
+
return self._glyphs.keys()
|
|
187
|
+
|
|
188
|
+
def values(self) -> Iterable[Glyph]: # type: ignore[override]
|
|
189
|
+
return self._glyphs.values()
|
|
190
|
+
|
|
191
|
+
def items(self) -> Iterable[tuple[str, Glyph]]: # type: ignore[override]
|
|
192
|
+
return self._glyphs.items()
|
|
193
|
+
|
|
194
|
+
def __contains__(self, key: object) -> bool:
|
|
195
|
+
return isinstance(key, str) and self._normalize_key(key) in self._glyphs
|
|
196
|
+
|
|
197
|
+
# ---------- Higher-level API ----------
|
|
198
|
+
|
|
199
|
+
def lookup(self, key: str, kind: Optional[str] = None) -> Glyph:
|
|
200
|
+
"""
|
|
201
|
+
Lookup a character or symbol by key. If `kind` is provided, force that sub-map.
|
|
202
|
+
|
|
203
|
+
Parameters:
|
|
204
|
+
key:
|
|
205
|
+
The character/symbol to look up.
|
|
206
|
+
kind:
|
|
207
|
+
'character'/'char' or 'symbol' to force the specific sub-map. If not provided
|
|
208
|
+
or the raw segmentation is unavailable, falls back to the combined map.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Glyph for `key`, or the configured fallback glyph if not present.
|
|
212
|
+
"""
|
|
213
|
+
if kind is None or self._raw_map is None:
|
|
214
|
+
return self[key]
|
|
215
|
+
|
|
216
|
+
k = self._normalize_key(key)
|
|
217
|
+
if kind.lower().startswith('sym'):
|
|
218
|
+
return self._lookup_in(self._raw_map['symbols'], k)
|
|
219
|
+
if kind.lower().startswith('char'):
|
|
220
|
+
return self._lookup_in(self._raw_map['characters'], k)
|
|
221
|
+
return self[key]
|
|
222
|
+
|
|
223
|
+
def _lookup_in(self, mapping: Dict[str, Glyph], key: str) -> Glyph:
|
|
224
|
+
if not self._case_sensitive:
|
|
225
|
+
mapping = {str(k).upper(): v for k, v in mapping.items()}
|
|
226
|
+
return mapping.get(key, self._glyphs[self._normalize_key(self._fallback_char)])
|
|
227
|
+
|
|
228
|
+
def reload(self, font_map: Dict[str, Any]) -> None:
|
|
229
|
+
"""
|
|
230
|
+
Reload the font map (raw or combined) and rebuild indexes.
|
|
231
|
+
"""
|
|
232
|
+
self._ingest_unknown_map(font_map)
|
|
233
|
+
self._validate_fallback()
|
|
234
|
+
|
|
235
|
+
# ---------- Properties ----------
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def character_map(self) -> Dict[str, Glyph]:
|
|
239
|
+
if self._raw_map is None:
|
|
240
|
+
return dict(self._glyphs) # best-effort: treat all as characters
|
|
241
|
+
return dict(self._raw_map['characters'])
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def symbol_map(self) -> Dict[str, Glyph]:
|
|
245
|
+
return {} if self._raw_map is None else dict(self._raw_map['symbols'])
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def characters(self) -> list[str]:
|
|
249
|
+
return list(self.character_map.keys())
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def symbols(self) -> list[str]:
|
|
253
|
+
return list(self.symbol_map.keys())
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def fallback_char(self) -> str:
|
|
257
|
+
return self._fallback_char
|
|
258
|
+
|
|
259
|
+
@fallback_char.setter
|
|
260
|
+
def fallback_char(self, new: str) -> None:
|
|
261
|
+
if not isinstance(new, str) or len(new) != 1:
|
|
262
|
+
raise ValueError('fallback_char must be a single character string')
|
|
263
|
+
self._fallback_char = new
|
|
264
|
+
self._validate_fallback()
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def is_case_sensitive(self) -> bool:
|
|
268
|
+
return self._case_sensitive
|
|
269
|
+
|
|
270
|
+
@validate_type(bool)
|
|
271
|
+
@is_case_sensitive.setter
|
|
272
|
+
def is_case_sensitive(self, new: bool) -> None:
|
|
273
|
+
if new is self._case_sensitive:
|
|
274
|
+
return
|
|
275
|
+
# Rebuild normalized key index under the new rule.
|
|
276
|
+
self._case_sensitive = new
|
|
277
|
+
existing = dict(self._glyphs)
|
|
278
|
+
self._glyphs.clear()
|
|
279
|
+
self._merge_into_glyphs(existing)
|
|
280
|
+
self._validate_fallback()
|
|
281
|
+
|
|
282
|
+
def __repr__(self) -> str:
|
|
283
|
+
return (
|
|
284
|
+
f'{self.__class__.__name__}('
|
|
285
|
+
f'case_sensitive={self._case_sensitive}, '
|
|
286
|
+
f'fallback_char={self._fallback_char!r}, '
|
|
287
|
+
f'count={len(self)}'
|
|
288
|
+
f')'
|
|
289
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Author : Taylor-Jayde Blackstone / Inspyre Softworks
|
|
3
|
+
# Project : IS-Matrix-Forge
|
|
4
|
+
# File : is_matrix_forge/assets/font_map/builders.py
|
|
5
|
+
#
|
|
6
|
+
# Description :
|
|
7
|
+
# Adapter/builder for turning flat 5x6 (length-30) 0/1 glyph lists into
|
|
8
|
+
# a raw map suitable for FontMap: {'characters': {...}, 'symbols': {...}}.
|
|
9
|
+
# Handles validation, reshaping to row-major 2D lists, and optional aliases.
|
|
10
|
+
#
|
|
11
|
+
# Dependencies:
|
|
12
|
+
# - is_matrix_forge/assets/font_map/models/glyph.py (Glyph type)
|
|
13
|
+
# - is_matrix_forge/assets/font_map/base.py (FontMap)
|
|
14
|
+
# =============================================================================
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from typing import Dict, Iterable, Mapping, MutableMapping, Sequence, Tuple
|
|
20
|
+
|
|
21
|
+
from .models.glyph import Glyph
|
|
22
|
+
from .base import FontMap
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _to_rows(flat: Sequence[int], width: int, height: int) -> Glyph:
|
|
26
|
+
"""
|
|
27
|
+
Convert a flat 0/1 list into a row-major 2D structure that matches the Glyph type.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
flat :
|
|
32
|
+
Flat sequence of ints (0/1). Expected length == width * height.
|
|
33
|
+
width :
|
|
34
|
+
Glyph width (columns).
|
|
35
|
+
height :
|
|
36
|
+
Glyph height (rows).
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
Glyph
|
|
41
|
+
Project Glyph value (list of lists of ints).
|
|
42
|
+
"""
|
|
43
|
+
if len(flat) != width * height:
|
|
44
|
+
raise ValueError(f'Glyph length {len(flat)} != {width*height} (expected).')
|
|
45
|
+
if any(px not in (0, 1) for px in flat):
|
|
46
|
+
raise ValueError('Glyph data must be 0/1.')
|
|
47
|
+
return [list(flat[r * width:(r + 1) * width]) for r in range(height)]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(slots=True)
|
|
51
|
+
class FontMapBuilder5x6:
|
|
52
|
+
"""
|
|
53
|
+
Build a FontMap from flat 5x6 glyph dictionaries (characters + symbols).
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
width :
|
|
58
|
+
Glyph width in pixels. Default 5.
|
|
59
|
+
height :
|
|
60
|
+
Glyph height in pixels. Default 6.
|
|
61
|
+
case_sensitive :
|
|
62
|
+
Whether keys should remain case-sensitive in the produced map.
|
|
63
|
+
fallback_char :
|
|
64
|
+
Fallback character key to ensure is present in the output (e.g., '?').
|
|
65
|
+
|
|
66
|
+
Notes
|
|
67
|
+
-----
|
|
68
|
+
- Input dicts should be {str: List[int]} with length-30 lists.
|
|
69
|
+
- Aliases are additional keys pointing at an existing glyph key.
|
|
70
|
+
"""
|
|
71
|
+
width: int = 5
|
|
72
|
+
height: int = 6
|
|
73
|
+
case_sensitive: bool = False
|
|
74
|
+
fallback_char: str = '?'
|
|
75
|
+
_chars: MutableMapping[str, Glyph] = field(default_factory=dict, init=False)
|
|
76
|
+
_syms: MutableMapping[str, Glyph] = field(default_factory=dict, init=False)
|
|
77
|
+
|
|
78
|
+
# --- Registration --------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
def add_char_map(self, mapping: Mapping[str, Sequence[int]]) -> 'FontMapBuilder5x6':
|
|
81
|
+
"""Register many character glyphs from a flat 0/1 list mapping."""
|
|
82
|
+
for k, v in mapping.items():
|
|
83
|
+
key = k if self.case_sensitive else str(k).upper()
|
|
84
|
+
self._chars[key] = _to_rows(v, self.width, self.height)
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
def add_symbol_map(self, mapping: Mapping[str, Sequence[int]]) -> 'FontMapBuilder5x6':
|
|
88
|
+
"""Register many symbol glyphs from a flat 0/1 list mapping."""
|
|
89
|
+
for k, v in mapping.items():
|
|
90
|
+
key = k if self.case_sensitive else (k if len(k) != 1 else k.upper())
|
|
91
|
+
self._syms[key] = _to_rows(v, self.width, self.height)
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def add_aliases(self, aliases: Mapping[str, str]) -> 'FontMapBuilder5x6':
|
|
95
|
+
"""
|
|
96
|
+
Add alias keys that point to existing glyphs.
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
builder.add_aliases({'°C': 'degC', '°F': 'degF'})
|
|
100
|
+
"""
|
|
101
|
+
for alias, target in aliases.items():
|
|
102
|
+
# Try symbols first, then characters
|
|
103
|
+
if target in self._syms:
|
|
104
|
+
self._syms[alias] = self._syms[target]
|
|
105
|
+
elif target in self._chars:
|
|
106
|
+
self._syms[alias] = self._chars[target]
|
|
107
|
+
else:
|
|
108
|
+
tnorm = target if self.case_sensitive else target.upper()
|
|
109
|
+
if tnorm in self._chars:
|
|
110
|
+
self._syms[alias] = self._chars[tnorm]
|
|
111
|
+
elif tnorm in self._syms:
|
|
112
|
+
self._syms[alias] = self._syms[tnorm]
|
|
113
|
+
else:
|
|
114
|
+
raise KeyError(f'Alias target {target!r} not found in current maps.')
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
# --- Output --------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
def build_raw(self) -> Dict[str, Dict[str, Glyph]]:
|
|
120
|
+
"""Produce a raw map suitable for FontMap(font_map=RAW)."""
|
|
121
|
+
return {'characters': dict(self._chars), 'symbols': dict(self._syms)}
|
|
122
|
+
|
|
123
|
+
def build_font_map(self) -> FontMap:
|
|
124
|
+
"""Construct and return a FontMap from the accumulated maps."""
|
|
125
|
+
raw = self.build_raw()
|
|
126
|
+
return FontMap(
|
|
127
|
+
font_map=raw,
|
|
128
|
+
characters_key='characters',
|
|
129
|
+
symbols_key='symbols',
|
|
130
|
+
case_sensitive=self.case_sensitive,
|
|
131
|
+
fallback_char=self.fallback_char,
|
|
132
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
from .models.glyph import Glyph
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def load_builtin_char_map() -> Dict[str, Glyph]:
|
|
10
|
+
resource = files('is_matrix_forge.assets').joinpath('char_map.json')
|
|
11
|
+
if not resource.is_file():
|
|
12
|
+
raise FileNotFoundError('char_map.json missing from is_matrix_forge.assets package data')
|
|
13
|
+
data = json.loads(resource.read_text())
|
|
14
|
+
|
|
15
|
+
return dict(data)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def has_key_ignore_case(
|
|
19
|
+
d: Dict[str, Any],
|
|
20
|
+
key: str
|
|
21
|
+
) -> bool:
|
|
22
|
+
kf = key.casefold()
|
|
23
|
+
|
|
24
|
+
return any(k.casefold() == kf for k in d)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_ignore_case(
|
|
28
|
+
d: Dict[str, Any],
|
|
29
|
+
key: str,
|
|
30
|
+
default=None
|
|
31
|
+
):
|
|
32
|
+
kf = key.casefold()
|
|
33
|
+
|
|
34
|
+
return next((v for k, v in d.items() if k.casefold() == kf), default)
|
|
35
|
+
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Optional
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
try:
|
|
5
|
+
import requests
|
|
6
|
+
except ModuleNotFoundError: # pragma: no cover - optional dependency
|
|
7
|
+
requests = None
|
|
8
|
+
try:
|
|
9
|
+
from inspyre_toolbox.path_man import provision_path
|
|
10
|
+
except ModuleNotFoundError: # pragma: no cover - fallback
|
|
11
|
+
def provision_path(path):
|
|
12
|
+
return Path(path)
|
|
6
13
|
from is_matrix_forge.log_engine import ROOT_LOGGER
|
|
7
14
|
|
|
8
15
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from is_matrix_forge.log_engine import ROOT_LOGGER
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
MOD_LOGGER = ROOT_LOGGER.get_child('common.helpers.errors')
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def catch_and_notify(
|
|
9
|
+
exception: Exception,
|
|
10
|
+
operation: str,
|
|
11
|
+
op_args: tuple = (),
|
|
12
|
+
op_kwargs: dict = {},
|
|
13
|
+
logger = MOD_LOGGER,
|
|
14
|
+
log_level = 'error'
|
|
15
|
+
) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Catch and notify an exception.
|
|
18
|
+
"""
|
|
19
|
+
logger.log(
|
|
20
|
+
log_level,
|
|
21
|
+
f'Operation "{operation}" failed with exception: {exception}'
|
|
22
|
+
)
|
|
23
|
+
)
|