IS-Matrix-Forge 1.0.0.dev7__tar.gz → 1.0.0.dev9__tar.gz

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