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

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