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.
Files changed (214) hide show
  1. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/PKG-INFO +35 -2
  2. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/README.md +40 -7
  3. is_matrix_forge-1.0.0.dev27/is_matrix_forge/__init__.py +1 -0
  4. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/__init__.py +1 -1
  5. is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/base.py +289 -0
  6. is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/builders.py +132 -0
  7. is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/helpers.py +35 -0
  8. is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/models/__init__.py +17 -0
  9. is_matrix_forge-1.0.0.dev27/is_matrix_forge/assets/font_map/models/glyph.py +4 -0
  10. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/helpers/__init__.py +9 -2
  11. is_matrix_forge-1.0.0.dev27/is_matrix_forge/common/helpers/errors.py +23 -0
  12. is_matrix_forge-1.0.0.dev27/is_matrix_forge/common/logging/__init__.py +17 -0
  13. is_matrix_forge-1.0.0.dev27/is_matrix_forge/common/logging/log_exceptions.py +79 -0
  14. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/designer_gui/__init__.py +1 -1
  15. {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
  16. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/dev_tools/__init__.py +1 -1
  17. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/__init__.py +4 -0
  18. {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
  19. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/identify_matrices.py +102 -0
  20. {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
  21. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/__init__.py +210 -0
  22. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/__init__.py +108 -0
  23. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/commands/__init__.py +17 -0
  24. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/commands/display_text.py +37 -0
  25. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/commands/identify_matrices.py +42 -0
  26. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/arguments/commands/scroll_text.py +35 -0
  27. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/Scripts/led_matrix/guards.py +115 -0
  28. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/__init__.py +96 -0
  29. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/commands/__init__.py +1 -1
  30. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/commands/map.py +1 -0
  31. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/constants.py +4 -1
  32. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/controller/__init__.py +4 -15
  33. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/base.py +90 -0
  34. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/__init__.py +17 -0
  35. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/animation.py +200 -0
  36. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/breather.py +67 -0
  37. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/__init__.py +370 -0
  38. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/__init__.py +12 -0
  39. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/breather_killer.py +25 -0
  40. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/easing.py +15 -0
  41. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/fade_planner.py +25 -0
  42. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/fade_spec.py +10 -0
  43. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/levels.py +13 -0
  44. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/percent.py +25 -0
  45. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/helpers/safe_ops.py +31 -0
  46. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/brightness/manager.py +425 -0
  47. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/drawing.py +98 -0
  48. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/example_template.py +45 -0
  49. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/identify.py +46 -0
  50. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/components/keep_alive.py +81 -0
  51. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/controller.py +76 -0
  52. {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
  53. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/controller/helpers/threading.py +45 -0
  54. 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
  55. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/__init__.py +1 -0
  56. {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
  57. {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
  58. {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
  59. {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
  60. {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
  61. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations/frame/base.py +452 -0
  62. {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
  63. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations/text/__init__.py +5 -0
  64. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations/text/glyph_normalizer.py +110 -0
  65. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations/text_scroller.py +222 -0
  66. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/animations.zip +0 -0
  67. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/assets/__init__.py +2 -0
  68. {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
  69. {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
  70. {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
  71. {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
  72. 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
  73. {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
  74. {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
  75. {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
  76. {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
  77. {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
  78. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/patterns/built_in/constants.py +3 -0
  79. {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
  80. {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
  81. {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
  82. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/display/text/text.py +303 -0
  83. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/base.py +5 -1
  84. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/matrix.py +5 -1
  85. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/hardware.py +68 -7
  86. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/helpers/__init__.py +18 -2
  87. is_matrix_forge-1.0.0.dev27/is_matrix_forge/led_matrix/is_matrix_forge.led_matrix.display.zip +0 -0
  88. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/tools/__init__.py +1 -1
  89. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/utils/__init__.py +1 -1
  90. is_matrix_forge-1.0.0.dev27/is_matrix_forge/log_engine.py +44 -0
  91. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/layout/__init__.py +1 -1
  92. is_matrix_forge-1.0.0.dev27/is_matrix_forge/progress.py +449 -0
  93. is_matrix_forge-1.0.0.dev27/is_matrix_forge/scripts/__init__.py +0 -0
  94. is_matrix_forge-1.0.0.dev27/is_matrix_forge/scripts/preset_installer.py +18 -0
  95. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/pyproject.toml +9 -2
  96. is_matrix_forge-1.0.0.dev9/is_matrix_forge/__init__.py +0 -8
  97. is_matrix_forge-1.0.0.dev9/is_matrix_forge/assets/font_map.py +0 -119
  98. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/Scripts/__init__.py +0 -4
  99. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/Scripts/identify_matrices.py +0 -228
  100. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/__init__.py +0 -58
  101. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/controller/helpers/threading.py +0 -67
  102. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/__init__.py +0 -13
  103. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/animations/frame/base.py +0 -245
  104. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/animations/text_scroller.py +0 -104
  105. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/display/assets/__init__.py +0 -2
  106. is_matrix_forge-1.0.0.dev9/is_matrix_forge/led_matrix/led_matrix.py +0 -25
  107. is_matrix_forge-1.0.0.dev9/is_matrix_forge/log_engine.py +0 -25
  108. is_matrix_forge-1.0.0.dev9/is_matrix_forge/progress.py +0 -99
  109. is_matrix_forge-1.0.0.dev9/is_matrix_forge/scripts/preset_installer.py +0 -184
  110. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/LICENSE.md +0 -0
  111. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/Untitled.ipynb +0 -0
  112. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/audio/__init__.py +0 -0
  113. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/audio/files/__init__.py +0 -0
  114. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/audio/files/plugged.wav +0 -0
  115. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/audio/files/unplugged.wav +0 -0
  116. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/char_map.json +0 -0
  117. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/assets/digit_map.py +0 -0
  118. /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
  119. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/__init__.py +0 -0
  120. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/decorators/__init__.py +0 -0
  121. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/decorators/freeze_setter.py +0 -0
  122. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/dirs.py +0 -0
  123. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/common/helpers/github_api.py +0 -0
  124. {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
  125. {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
  126. {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
  127. {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
  128. {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
  129. {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
  130. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/dev_tools/debug.py +0 -0
  131. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/dev_tools/presets.py +0 -0
  132. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/__init__.py +0 -0
  133. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/b1display.py +0 -0
  134. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/c1minimal.py +0 -0
  135. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/font.py +0 -0
  136. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/helpers/__init__.py +0 -0
  137. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/inputmodule/ledmatrix.py +0 -0
  138. {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
  139. {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
  140. {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
  141. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/controller/multiton.py +0 -0
  142. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/controller/threaded.py +0 -0
  143. {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
  144. {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
  145. {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
  146. {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
  147. {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
  148. {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
  149. {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
  150. {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
  151. {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
  152. {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
  153. {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
  154. {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
  155. {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
  156. {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
  157. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/display/media.py +0 -0
  158. {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
  159. {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
  160. {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
  161. {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
  162. {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
  163. {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
  164. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/__init__.py +0 -0
  165. {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
  166. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/grid.py +0 -0
  167. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/errors/misc.py +0 -0
  168. {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
  169. {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
  170. {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
  171. {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
  172. {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
  173. {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
  174. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/status.py +0 -0
  175. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/led_matrix/tools/identify.py +0 -0
  176. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/__init__.py +0 -0
  177. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/errors.py +0 -0
  178. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/events.py +0 -0
  179. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/__init__.py +0 -0
  180. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/errors.py +0 -0
  181. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/event/__init__.py +0 -0
  182. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/event/collection.py +0 -0
  183. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/event/errors.py +0 -0
  184. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/event/event.py +0 -0
  185. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/layout/base.py +0 -0
  186. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/metaclasses/__init__.py +0 -0
  187. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/windows/__init__.py +0 -0
  188. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/gui/windows/base.py +0 -0
  189. {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
  190. {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
  191. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/helpers.py +0 -0
  192. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/monitor/monitor.py +0 -0
  193. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/notify/__init__.py +0 -0
  194. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/notify/sounds/__init__.py +0 -0
  195. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/notify/sounds/base.py +0 -0
  196. /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
  197. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/__init__.py +0 -0
  198. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/base/__init__.py +0 -0
  199. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/base/answer.py +0 -0
  200. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/base/message.py +0 -0
  201. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/base/request.py +0 -0
  202. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/is_matrix_forge/serial_com/exchange.py +0 -0
  203. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/blank.json +0 -0
  204. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/manifest.json +0 -0
  205. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/shock_face.json +0 -0
  206. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test1.json +0 -0
  207. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test2.json +0 -0
  208. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test3.json +0 -0
  209. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test4.json +0 -0
  210. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test5.json +0 -0
  211. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test6.json +0 -0
  212. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/test7.json +0 -0
  213. {is_matrix_forge-1.0.0.dev9 → is_matrix_forge-1.0.0.dev27}/presets/x_animation II.json +0 -0
  214. {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.dev9
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
@@ -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/grid.py
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
+
@@ -0,0 +1,17 @@
1
+ """
2
+
3
+
4
+ Author:
5
+ Inspyre Softworks
6
+
7
+ Project:
8
+ IS-Matrix-Forge
9
+
10
+ File:
11
+ is_matrix_forge/assets/font_map/models/__init__.py
12
+
13
+
14
+ Description:
15
+
16
+
17
+ """
@@ -0,0 +1,4 @@
1
+ from typing import List
2
+
3
+
4
+ Glyph = List[List[int]]
@@ -1,8 +1,15 @@
1
1
  import hashlib
2
2
  from pathlib import Path
3
3
  from typing import Optional
4
- import requests
5
- from inspyre_toolbox.path_man import provision_path
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
+ )
@@ -0,0 +1,17 @@
1
+ """
2
+
3
+
4
+ Author:
5
+ Inspyre Softworks
6
+
7
+ Project:
8
+ IS-Matrix-Forge
9
+
10
+ File:
11
+ is_matrix_forge/common/logging/__init__.py
12
+
13
+
14
+ Description:
15
+
16
+
17
+ """