scrollkit 0.8.3__py3-none-any.whl

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 (111) hide show
  1. scrollkit/__init__.py +16 -0
  2. scrollkit/app/__init__.py +6 -0
  3. scrollkit/app/base.py +918 -0
  4. scrollkit/app/memory.py +70 -0
  5. scrollkit/config/__init__.py +1 -0
  6. scrollkit/config/settings_manager.py +215 -0
  7. scrollkit/config/transition_names.py +31 -0
  8. scrollkit/dev/__init__.py +41 -0
  9. scrollkit/dev/capabilities.py +374 -0
  10. scrollkit/dev/harness.py +383 -0
  11. scrollkit/dev/metrics.py +91 -0
  12. scrollkit/dev/performance.py +174 -0
  13. scrollkit/dev/validation.py +245 -0
  14. scrollkit/display/__init__.py +14 -0
  15. scrollkit/display/_graphics.py +299 -0
  16. scrollkit/display/_recording.py +165 -0
  17. scrollkit/display/_sim_backend.py +99 -0
  18. scrollkit/display/bitmap_text.py +408 -0
  19. scrollkit/display/boards.py +186 -0
  20. scrollkit/display/colors.py +176 -0
  21. scrollkit/display/content.py +604 -0
  22. scrollkit/display/gradient_text.py +167 -0
  23. scrollkit/display/interface.py +126 -0
  24. scrollkit/display/simulator.py +80 -0
  25. scrollkit/display/text_fill.py +59 -0
  26. scrollkit/display/text_pixels.py +250 -0
  27. scrollkit/display/unified.py +595 -0
  28. scrollkit/effects/__init__.py +28 -0
  29. scrollkit/effects/drip_splash.py +253 -0
  30. scrollkit/effects/easing.py +131 -0
  31. scrollkit/effects/overlay.py +92 -0
  32. scrollkit/effects/particles.py +355 -0
  33. scrollkit/effects/reveal_splash.py +132 -0
  34. scrollkit/effects/scrolling.py +363 -0
  35. scrollkit/effects/swarm_reveal.py +512 -0
  36. scrollkit/effects/text_render.py +25 -0
  37. scrollkit/effects/transitions.py +873 -0
  38. scrollkit/exceptions.py +55 -0
  39. scrollkit/network/__init__.py +1 -0
  40. scrollkit/network/http_client.py +505 -0
  41. scrollkit/network/mdns.py +44 -0
  42. scrollkit/network/wifi_manager.py +382 -0
  43. scrollkit/ota/__init__.py +13 -0
  44. scrollkit/ota/client.py +528 -0
  45. scrollkit/ota/display_progress.py +125 -0
  46. scrollkit/ota/manifest.py +206 -0
  47. scrollkit/ota/publish.py +379 -0
  48. scrollkit/simulator/ATTRIBUTION.md +20 -0
  49. scrollkit/simulator/CIRCUITPYTHON_COMPATIBILITY.md +364 -0
  50. scrollkit/simulator/LICENSE +176 -0
  51. scrollkit/simulator/README.md +87 -0
  52. scrollkit/simulator/__init__.py +11 -0
  53. scrollkit/simulator/adafruit_bitmap_font/__init__.py +5 -0
  54. scrollkit/simulator/adafruit_bitmap_font/bitmap_font.py +273 -0
  55. scrollkit/simulator/adafruit_bitmap_font/glyph_cache.py +70 -0
  56. scrollkit/simulator/adafruit_display_text/__init__.py +5 -0
  57. scrollkit/simulator/adafruit_display_text/label.py +336 -0
  58. scrollkit/simulator/bitmaptools.py +50 -0
  59. scrollkit/simulator/core/__init__.py +8 -0
  60. scrollkit/simulator/core/color_utils.py +62 -0
  61. scrollkit/simulator/core/device_benchmarks.json +254 -0
  62. scrollkit/simulator/core/feasibility.py +160 -0
  63. scrollkit/simulator/core/hardware_profile.py +191 -0
  64. scrollkit/simulator/core/led_matrix.py +307 -0
  65. scrollkit/simulator/core/matrixportal_s3_baseline.json +12 -0
  66. scrollkit/simulator/core/performance_manager.py +253 -0
  67. scrollkit/simulator/core/pixel_buffer.py +188 -0
  68. scrollkit/simulator/devices/__init__.py +7 -0
  69. scrollkit/simulator/devices/base_device.py +79 -0
  70. scrollkit/simulator/devices/matrixportal_s3.py +87 -0
  71. scrollkit/simulator/displayio/__init__.py +12 -0
  72. scrollkit/simulator/displayio/bitmap.py +158 -0
  73. scrollkit/simulator/displayio/display.py +195 -0
  74. scrollkit/simulator/displayio/fourwire.py +54 -0
  75. scrollkit/simulator/displayio/group.py +125 -0
  76. scrollkit/simulator/displayio/ondiskbitmap.py +109 -0
  77. scrollkit/simulator/displayio/palette.py +115 -0
  78. scrollkit/simulator/displayio/tilegrid.py +155 -0
  79. scrollkit/simulator/fonts/3x5.bdf +2474 -0
  80. scrollkit/simulator/fonts/Arial_16.bdf +7366 -0
  81. scrollkit/simulator/fonts/Arial_16.bdf.license +3 -0
  82. scrollkit/simulator/fonts/Arial_Bold_12.bdf +6131 -0
  83. scrollkit/simulator/fonts/Arial_Bold_12.bdf.license +2 -0
  84. scrollkit/simulator/fonts/Arial_Bold_18.bdf +32653 -0
  85. scrollkit/simulator/fonts/Arial_Bold_18.bdf.license +2 -0
  86. scrollkit/simulator/fonts/Junction_regular_24.bdf +8676 -0
  87. scrollkit/simulator/fonts/Junction_regular_24.bdf.license +3 -0
  88. scrollkit/simulator/fonts/LeagueSpartan-Bold-16.bdf +12458 -0
  89. scrollkit/simulator/fonts/LeagueSpartan-Bold-16.bdf.license +1921 -0
  90. scrollkit/simulator/fonts/LeagueSpartan_Bold_16.bdf +12458 -0
  91. scrollkit/simulator/fonts/LeagueSpartan_Bold_16.bdf.license +4 -0
  92. scrollkit/simulator/fonts/LibreBodoniv2002-Bold-27.bdf +16818 -0
  93. scrollkit/simulator/fonts/LibreBodoniv2002-Bold-27.bdf.license +1921 -0
  94. scrollkit/simulator/fonts/tom-thumb.bdf +2353 -0
  95. scrollkit/simulator/fonts/viii-bold.bdf +2673 -0
  96. scrollkit/simulator/fonts/viii.bdf +2659 -0
  97. scrollkit/simulator/terminalio/__init__.py +20 -0
  98. scrollkit/utils/__init__.py +1 -0
  99. scrollkit/utils/color_utils.py +54 -0
  100. scrollkit/utils/diagnostics.py +227 -0
  101. scrollkit/utils/error_handler.py +347 -0
  102. scrollkit/utils/system_utils.py +245 -0
  103. scrollkit/utils/url_utils.py +46 -0
  104. scrollkit/web/__init__.py +6 -0
  105. scrollkit/web/settings_server.py +328 -0
  106. scrollkit/web/wifi_setup.py +331 -0
  107. scrollkit-0.8.3.dist-info/METADATA +248 -0
  108. scrollkit-0.8.3.dist-info/RECORD +111 -0
  109. scrollkit-0.8.3.dist-info/WHEEL +5 -0
  110. scrollkit-0.8.3.dist-info/licenses/LICENSE +31 -0
  111. scrollkit-0.8.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,115 @@
1
+ # Copyright (c) 2024-2026 Michael Winslow Czeiszperger
2
+ """CircuitPython displayio.Palette equivalent."""
3
+
4
+ from ..core.color_utils import rgb565_to_rgb888, rgb888_to_rgb565
5
+
6
+
7
+ class Palette:
8
+ """Color palette for indexed color display.
9
+
10
+ Stores a collection of colors that can be referenced by index.
11
+ Compatible with CircuitPython's displayio.Palette API.
12
+ """
13
+
14
+ def __init__(self, color_count):
15
+ """Initialize palette with specified number of colors.
16
+
17
+ Args:
18
+ color_count: Maximum number of colors in palette
19
+ """
20
+ self._color_count = color_count
21
+ self._colors = [None] * color_count
22
+ self._transparent = [False] * color_count
23
+
24
+ def __len__(self):
25
+ """Get number of colors in palette."""
26
+ return self._color_count
27
+
28
+ def __setitem__(self, index, color):
29
+ """Set a color in the palette.
30
+
31
+ Args:
32
+ index: Color index (0 to color_count-1)
33
+ color: Color value as RGB565 integer or RGB888 tuple
34
+ """
35
+ if not 0 <= index < self._color_count:
36
+ raise IndexError(f"Palette index {index} out of range")
37
+
38
+ if isinstance(color, (list, tuple)):
39
+ # Convert RGB888 tuple to RGB565
40
+ if len(color) >= 3:
41
+ self._colors[index] = rgb888_to_rgb565(color[0], color[1], color[2])
42
+ else:
43
+ raise ValueError("Color tuple must have at least 3 values")
44
+ else:
45
+ # displayio palettes take 24-bit RGB888 integers (0xRRGGBB). Do NOT
46
+ # guess RGB565-vs-RGB888 by magnitude: colors with a zero red byte
47
+ # (e.g. green 0x00FF00, blue 0x0000FF) are <= 0xFFFF and would be
48
+ # mis-read as RGB565, rendering green as yellow, blue as purple, etc.
49
+ r = (color >> 16) & 0xFF
50
+ g = (color >> 8) & 0xFF
51
+ b = color & 0xFF
52
+ self._colors[index] = rgb888_to_rgb565(r, g, b)
53
+
54
+ def __getitem__(self, index):
55
+ """Get a color from the palette.
56
+
57
+ Args:
58
+ index: Color index
59
+
60
+ Returns:
61
+ Color value as RGB565 integer
62
+ """
63
+ if not 0 <= index < self._color_count:
64
+ raise IndexError(f"Palette index {index} out of range")
65
+
66
+ color = self._colors[index]
67
+ return 0 if color is None else color
68
+
69
+ def make_transparent(self, index):
70
+ """Make a palette entry transparent.
71
+
72
+ Args:
73
+ index: Color index to make transparent
74
+ """
75
+ if not 0 <= index < self._color_count:
76
+ raise IndexError(f"Palette index {index} out of range")
77
+
78
+ self._transparent[index] = True
79
+
80
+ def make_opaque(self, index):
81
+ """Make a palette entry opaque.
82
+
83
+ Args:
84
+ index: Color index to make opaque
85
+ """
86
+ if not 0 <= index < self._color_count:
87
+ raise IndexError(f"Palette index {index} out of range")
88
+
89
+ self._transparent[index] = False
90
+
91
+ def is_transparent(self, index):
92
+ """Check if a palette entry is transparent.
93
+
94
+ Args:
95
+ index: Color index to check
96
+
97
+ Returns:
98
+ True if transparent, False otherwise
99
+ """
100
+ if not 0 <= index < self._color_count:
101
+ return False
102
+
103
+ return self._transparent[index]
104
+
105
+ def get_rgb888(self, index):
106
+ """Get a color as RGB888 tuple.
107
+
108
+ Args:
109
+ index: Color index
110
+
111
+ Returns:
112
+ (r, g, b) tuple in 0-255 range
113
+ """
114
+ color565 = self[index]
115
+ return rgb565_to_rgb888(color565)
@@ -0,0 +1,155 @@
1
+ # Copyright (c) 2024-2026 Michael Winslow Czeiszperger
2
+ """CircuitPython displayio.TileGrid equivalent."""
3
+
4
+
5
+ class TileGrid:
6
+ """TileGrid arranges bitmap tiles on screen.
7
+
8
+ A TileGrid is a grid of tiles that are sourced from a bitmap.
9
+ Each tile can be individually positioned and colored using a palette.
10
+ Compatible with CircuitPython's displayio.TileGrid API.
11
+ """
12
+
13
+ def __init__(self, bitmap, *, pixel_shader, width=1, height=1,
14
+ tile_width=None, tile_height=None, default_tile=0, x=0, y=0):
15
+ """Initialize TileGrid.
16
+
17
+ Args:
18
+ bitmap: Source bitmap containing tile graphics
19
+ pixel_shader: Palette to use for coloring
20
+ width: Number of tiles wide (default 1)
21
+ height: Number of tiles high (default 1)
22
+ tile_width: Width of each tile in pixels (default: bitmap width)
23
+ tile_height: Height of each tile in pixels (default: bitmap height)
24
+ default_tile: Default tile index (default 0)
25
+ x: X position of grid (default 0)
26
+ y: Y position of grid (default 0)
27
+ """
28
+ self.bitmap = bitmap
29
+ self.pixel_shader = pixel_shader
30
+ self.width = width
31
+ self.height = height
32
+
33
+ # Set tile dimensions
34
+ if tile_width is None:
35
+ self.tile_width = bitmap.width
36
+ else:
37
+ self.tile_width = tile_width
38
+
39
+ if tile_height is None:
40
+ self.tile_height = bitmap.height
41
+ else:
42
+ self.tile_height = tile_height
43
+
44
+ # Calculate tiles per row in bitmap
45
+ self._tiles_per_row = bitmap.width // self.tile_width
46
+
47
+ # Initialize tile indices
48
+ self._tiles = [[default_tile for _ in range(width)] for _ in range(height)]
49
+
50
+ # Position and visibility
51
+ self.x = x
52
+ self.y = y
53
+ self.hidden = False
54
+ self._transpose_xy = False
55
+ self._flip_x = False
56
+ self._flip_y = False
57
+
58
+ def __setitem__(self, index, tile_index):
59
+ """Set tile at given position.
60
+
61
+ Args:
62
+ index: (x, y) tuple or flat index
63
+ tile_index: Index of tile in bitmap
64
+ """
65
+ if isinstance(index, tuple):
66
+ x, y = index
67
+ if not (0 <= x < self.width and 0 <= y < self.height):
68
+ raise IndexError(f"Tile index ({x}, {y}) out of bounds")
69
+ else:
70
+ # Convert flat index to x, y
71
+ y = index // self.width
72
+ x = index % self.width
73
+ if not (0 <= index < self.width * self.height):
74
+ raise IndexError(f"Tile index {index} out of bounds")
75
+
76
+ self._tiles[y][x] = tile_index
77
+
78
+ def __getitem__(self, index):
79
+ """Get tile at given position.
80
+
81
+ Args:
82
+ index: (x, y) tuple or flat index
83
+
84
+ Returns:
85
+ Tile index
86
+ """
87
+ if isinstance(index, tuple):
88
+ x, y = index
89
+ if not (0 <= x < self.width and 0 <= y < self.height):
90
+ raise IndexError(f"Tile index ({x}, {y}) out of bounds")
91
+ else:
92
+ # Convert flat index to x, y
93
+ y = index // self.width
94
+ x = index % self.width
95
+ if not (0 <= index < self.width * self.height):
96
+ raise IndexError(f"Tile index {index} out of bounds")
97
+
98
+ return self._tiles[y][x]
99
+
100
+ @property
101
+ def transpose_xy(self):
102
+ """Get transpose_xy setting."""
103
+ return self._transpose_xy
104
+
105
+ @transpose_xy.setter
106
+ def transpose_xy(self, value):
107
+ """Set whether to swap x and y coordinates."""
108
+ self._transpose_xy = bool(value)
109
+
110
+ @property
111
+ def flip_x(self):
112
+ """Get flip_x setting."""
113
+ return self._flip_x
114
+
115
+ @flip_x.setter
116
+ def flip_x(self, value):
117
+ """Set whether to flip horizontally."""
118
+ self._flip_x = bool(value)
119
+
120
+ @property
121
+ def flip_y(self):
122
+ """Get flip_y setting."""
123
+ return self._flip_y
124
+
125
+ @flip_y.setter
126
+ def flip_y(self, value):
127
+ """Set whether to flip vertically."""
128
+ self._flip_y = bool(value)
129
+
130
+ def get_tile_bitmap(self, tile_index):
131
+ """Get bitmap data for a specific tile.
132
+
133
+ Args:
134
+ tile_index: Index of tile to get
135
+
136
+ Returns:
137
+ Bitmap-like object with tile data
138
+ """
139
+ # Calculate tile position in source bitmap
140
+ tile_x = (tile_index % self._tiles_per_row) * self.tile_width
141
+ tile_y = (tile_index // self._tiles_per_row) * self.tile_height
142
+
143
+ # Create a view of the tile data
144
+ # (In actual implementation, this would return a view of the bitmap data)
145
+ return (tile_x, tile_y, self.tile_width, self.tile_height)
146
+
147
+ @property
148
+ def pixel_width(self):
149
+ """Get total width in pixels."""
150
+ return self.width * self.tile_width
151
+
152
+ @property
153
+ def pixel_height(self):
154
+ """Get total height in pixels."""
155
+ return self.height * self.tile_height