26
26
CircuitPython helper library for displaying a slideshow of images on a board with a built-in
27
27
display.
28
28
29
- * Author(s): Kattni Rembor, Carter Nelson
29
+ * Author(s): Kattni Rembor, Carter Nelson, Roy Hooper
30
30
31
31
Implementation Notes
32
32
--------------------
33
33
34
34
**Hardware:**
35
35
36
- * `Adafruit Hollowing M0 Express <https://www.adafruit.com/product/3900>`_"
36
+ * `Adafruit Hollowing M0 Express <https://www.adafruit.com/product/3900>`_
37
37
38
38
**Software and Dependencies:**
39
39
52
52
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Slideshow.git"
53
53
54
54
55
- class SlideShow ():
56
- """Class for displaying a slideshow of .bmp images on boards with built-in
57
- displays.
55
+ _FADE_IN = 0
56
+ _SHOW_IMG = 1
57
+ _FADE_OUT = 2
58
+ _LOAD_IMG = 3
59
+ _WAIT = 4
60
+
61
+
62
+ class PlayBackMode :
63
+ """Helper class for handling playback mode."""
64
+ # pylint: disable=too-few-public-methods
65
+ ALPHA = 0
66
+ RANDOM = 1
67
+ # pylint: enable=too-few-public-methods
68
+
69
+
70
+ class PlayBackDirection :
71
+ """Helper class for handling playback direction."""
72
+ # pylint: disable=too-few-public-methods
73
+ BACKWARD = - 1
74
+ FORWARD = 1
75
+ # pylint: enable=too-few-public-methods
76
+
77
+
78
+ class SlideShow :
79
+ # pylint: disable=too-many-instance-attributes
80
+ """
81
+ Class for displaying a slideshow of .bmp images on boards with built-in displays.
58
82
59
83
:param str folder: Specify the folder containing the image files, in quotes. Default is
60
84
the root directory, ``"/"``.
@@ -68,6 +92,15 @@ class SlideShow():
68
92
69
93
:param int dwell: The number of seconds each image displays, in seconds. Default is 3.
70
94
95
+ :param fade_effect: Specify whether to include the fade effect between images. ``True``
96
+ tells the code to fade the backlight up and down between image display
97
+ transitions. ``False`` maintains max brightness on the backlight between
98
+ image transitions. Default is ``True``.
99
+
100
+ :param auto_advance: Specify whether to automatically advance after dwell seconds. ``True``
101
+ if slideshow should auto play, ``False`` if you want to control advancement
102
+ manually. Default is ``True``.
103
+
71
104
Example code for Hollowing Express. With this example, the slideshow will play through once
72
105
in alphabetical order:
73
106
@@ -77,44 +110,76 @@ class SlideShow():
77
110
78
111
slideshow = adafruit_slideshow.SlideShow()
79
112
slideshow.loop = False
80
- slideshow.order = slideshow .ALPHA
113
+ slideshow.order = PlayBackMode .ALPHA
81
114
82
115
while slideshow.update():
83
116
pass
84
- """
85
- MAX_BRIGHTNESS = 2 ** 15
86
117
87
- ALPHA = 0
88
- RANDOM = 1
118
+ Example code for Hollowing Express. Sets ``dwell`` to 0 seconds, turns ``auto_advance`` off,
119
+ and uses capacitive touch to advance backwards and forwards through the images and to control
120
+ the brightness level of the backlight:
121
+
122
+ .. code-block:: python
123
+
124
+ from adafruit_slideshow import PlayBackMode, SlideShow, PlayBackDirection
125
+ import touchio
126
+ import board
127
+
128
+ forward_button = touchio.TouchIn(board.TOUCH4)
129
+ back_button = touchio.TouchIn(board.TOUCH1)
130
+
131
+ brightness_up = touchio.TouchIn(board.TOUCH3)
132
+ brightness_down = touchio.TouchIn(board.TOUCH2)
89
133
90
- FADE_IN = 0
91
- SHOW_IMG = 1
92
- FADE_OUT = 2
93
- LOAD_IMG = 3
134
+ slideshow = SlideShow()
135
+ slideshow.auto_advance = False
136
+ slideshow.dwell = 0
94
137
95
- def __init__ (self , folder = "/" , order = RANDOM , loop = True , dwell = 3 ):
138
+ while True:
139
+ if forward_button.value:
140
+ slideshow.advance()
141
+ if back_button.value:
142
+ slideshow.advance(direction=PlayBackDirection.BACKWARD)
143
+
144
+ if brightness_up.value:
145
+ slideshow.backlight_level_up()
146
+ elif brightness_down.value:
147
+ slideshow.backlight_level_down()
148
+ slideshow.update()
149
+ """
150
+
151
+ _max_brightness = 2 ** 15
152
+
153
+ # pylint: disable=too-many-arguments
154
+ def __init__ (self , folder = "/" , order = PlayBackMode .RANDOM , loop = True , dwell = 3 , fade_effect = True ,
155
+ auto_advance = True ):
96
156
self ._group = displayio .Group ()
97
157
board .DISPLAY .show (self ._group )
98
158
self ._backlight = pulseio .PWMOut (board .TFT_BACKLIGHT )
99
- self ._folder = folder
100
- self ._order = order
101
- self ._loop = loop
102
- self ._dwell = dwell
103
- self ._current_state = self .LOAD_IMG
159
+ self .folder = folder
160
+ """Specifies the folder containing the image files. Default is the root directory, ``"/"``.
161
+ """
162
+ self .loop = loop
163
+ """Specifies whether to loop through the images continuously or play through the list once.
164
+ ``True`` will continue to loop, ``False`` will play only once. Default is `True`."""
165
+ self .dwell = dwell
166
+ """The number of seconds each image displays, in seconds. Default is 3."""
167
+ self .fade_effect = fade_effect
168
+ self ._current_state = _LOAD_IMG
104
169
self ._img_start = None
105
170
self ._file_list = None
106
171
self ._images = None
107
172
self ._load_images ()
108
-
109
- @ property
110
- def folder ( self ):
111
- """Specifies the folder containing the image files. Default is the root directory, ``"/" ``.
112
- """
113
- return self ._folder
114
-
115
- @ folder . setter
116
- def folder ( self , folder ):
117
- self . _folder = folder
173
+ self . _order = None
174
+ self . order = order
175
+ self . direction = PlayBackDirection . FORWARD
176
+ """Specify the playback direction. Default is ``PlayBackDirection.FORWARD ``. Can also be
177
+ ``PlayBackDirection.BACKWARD``. """
178
+ self .auto_advance = auto_advance
179
+ """Enable auto-advance based on dwell time. Set to ``False`` to manually control."""
180
+ self . _update_order ()
181
+ self . _current_backlight_level = self . _max_brightness
182
+ # pylint: enable=too-many-arguments
118
183
119
184
@property
120
185
def order (self ):
@@ -124,63 +189,107 @@ def order(self):
124
189
125
190
@order .setter
126
191
def order (self , order ):
192
+ if order not in [PlayBackMode .ALPHA , PlayBackMode .RANDOM ]:
193
+ raise ValueError ("Order must be either 'RANDOM' or 'ALPHA'" )
194
+ if order == self ._order :
195
+ return
127
196
self ._order = order
197
+ self ._update_order ()
128
198
129
- @ property
130
- def loop ( self ) :
131
- """Specifies whether to loop through the images continuously or play through the list once.
132
- ``True`` will continue to loop, ``False`` will play only once. Default is `True`."""
133
- return self ._loop
199
+ def _update_order ( self ):
200
+ if self . order == PlayBackMode . ALPHA :
201
+ self . _file_list = sorted ( self . _file_list )
202
+ if self . order == PlayBackMode . RANDOM :
203
+ self ._file_list = sorted ( self . _file_list , key = lambda x : random . random ())
134
204
135
- @loop .setter
136
- def loop (self , loop ):
137
- self ._loop = loop
205
+ def backlight_level_up (self , step = 16 ):
206
+ """Increases the backlight brightness level.
138
207
139
- @property
140
- def dwell (self ):
141
- """The number of seconds each image displays, in seconds. Default is 3."""
142
- return self ._dwell
208
+ :param step: Specify the number of steps by which current backlight level will be increased.
209
+ Default is 16.
210
+ """
211
+ self ._max_brightness += step
212
+ if self ._max_brightness >= 2 ** 16 :
213
+ self ._max_brightness = 2 ** 16 - 1
214
+ self ._current_backlight_level = self ._max_brightness
215
+ return self ._current_backlight_level
216
+
217
+ def backlight_level_down (self , step = 16 ):
218
+ """Decreases the backlight brightness level.
143
219
144
- @dwell .setter
145
- def dwell (self , dwell ):
146
- self ._dwell = dwell
220
+ :param step: Specify the number of steps by which current backlight level will be decreased.
221
+ Default is 16.
222
+ """
223
+ self ._max_brightness -= step
224
+ if self ._max_brightness < 0 :
225
+ self ._max_brightness = 0
226
+ self ._current_backlight_level = self ._max_brightness
227
+ return self ._current_backlight_level
228
+
229
+ def _fade_up (self ):
230
+ steps = 100
231
+ for b in range (steps ):
232
+ self ._backlight .duty_cycle = b * self ._current_backlight_level // steps
233
+ time .sleep (0.01 )
234
+
235
+ def _fade_down (self ):
236
+ steps = 100
237
+ for b in range (steps , - 1 , - 1 ):
238
+ self ._backlight .duty_cycle = b * self ._current_backlight_level // steps
239
+ time .sleep (0.01 )
147
240
148
241
def update (self ):
149
242
"""Updates the slideshow to the next image."""
150
243
now = time .monotonic ()
151
- if self ._current_state == self . FADE_IN :
152
- steps = 100
153
- for b in range ( steps ):
154
- self . _backlight . duty_cycle = b * SlideShow . MAX_BRIGHTNESS // steps
155
- time . sleep ( 0.01 )
156
- self ._current_state = self . SHOW_IMG
244
+ if self ._current_state == _FADE_IN :
245
+ if self . fade_effect :
246
+ self . _fade_up ()
247
+ else :
248
+ self . _backlight . duty_cycle = self . _current_backlight_level
249
+ self ._current_state = _SHOW_IMG
157
250
self ._img_start = time .monotonic ()
158
251
159
- if self ._current_state == self .SHOW_IMG :
160
- if now - self ._img_start > self ._dwell :
161
- self ._current_state = self .FADE_OUT
252
+ if self ._current_state == _SHOW_IMG :
253
+ self ._backlight .duty_cycle = self ._current_backlight_level
254
+ if now - self ._img_start > self .dwell :
255
+ self ._current_state = _FADE_OUT if self .auto_advance else _WAIT
162
256
163
- if self ._current_state == self .FADE_OUT :
164
- steps = 100
165
- for b in range (steps , - 1 , - 1 ):
166
- self ._backlight .duty_cycle = b * SlideShow .MAX_BRIGHTNESS // steps
167
- time .sleep (0.01 )
257
+ if self ._current_state == _WAIT :
258
+ self ._backlight .duty_cycle = self ._current_backlight_level
259
+
260
+ if self ._current_state == _FADE_OUT :
261
+ if self .fade_effect :
262
+ self ._fade_down ()
263
+ else :
264
+ self ._backlight .duty_cycle = self ._current_backlight_level
168
265
self ._group .pop ()
169
- self ._current_state = self . LOAD_IMG
266
+ self ._current_state = _LOAD_IMG
170
267
171
- if self ._current_state == self . LOAD_IMG :
268
+ if self ._current_state == _LOAD_IMG :
172
269
try :
173
270
imagename = next (self ._images )
174
271
except StopIteration :
175
272
return False
176
273
try :
177
274
self ._show_bmp (imagename )
178
- self ._current_state = self . FADE_IN
275
+ self ._current_state = _FADE_IN
179
276
except ValueError as error :
180
277
print ("Incompatible image:" , imagename , str (error ))
181
278
182
279
return True
183
280
281
+ def advance (self , direction = None ):
282
+ """Displays the next image when `auto_advance` is False.
283
+
284
+ Does not advance the image until the current image change is over.
285
+
286
+ :param int direction: Change the playback direction when advancing to the next image.
287
+ """
288
+ if direction :
289
+ self .direction = direction
290
+ if self ._current_state == _WAIT :
291
+ self ._current_state = _FADE_OUT
292
+
184
293
def _show_bmp (self , imagename ):
185
294
"""Opens and loads the image onto the display."""
186
295
with open (imagename , "rb" ) as image :
@@ -191,19 +300,25 @@ def _show_bmp(self, imagename):
191
300
192
301
def _get_next_image (self ):
193
302
"""Cycles through the list of images."""
303
+ index = - 1 if self .direction == PlayBackDirection .FORWARD else len (self ._file_list )
194
304
while True :
195
- for image in self ._file_list :
196
- yield image
197
- if not self ._loop :
305
+ wrapped = False
306
+ index += self .direction
307
+ if index < 0 :
308
+ index = len (self ._file_list ) - 1
309
+ wrapped = True
310
+ elif index >= len (self ._file_list ):
311
+ index = 0
312
+ wrapped = True
313
+ yield self ._file_list [index ]
314
+ if wrapped and not self .loop :
198
315
return
199
316
200
317
def _load_images (self ):
201
- """Loads the list of images to be displayed in alphabetical or random order ."""
318
+ """Loads the list of images to be displayed."""
202
319
self ._file_list = self ._get_filenames ()
203
- if self ._order == SlideShow .RANDOM :
204
- self ._file_list = sorted (self ._file_list , key = lambda x : random .random ())
205
- self ._images = self ._get_next_image ()
320
+ self ._images = self ._get_next_image ()
206
321
207
322
def _get_filenames (self , extension = "bmp" ):
208
323
"""Creates a list of available image files ending with .bmp in the specified folder."""
209
- return list (filter (lambda x : x .endswith (extension ), os .listdir (self ._folder )))
324
+ return list (filter (lambda x : x .endswith (extension ), os .listdir (self .folder )))
0 commit comments