43
43
import time
44
44
import os
45
45
import random
46
+ import json
46
47
import displayio
47
48
49
+ try :
50
+ # text slides are an optional feature and require adafruit_display_text
51
+ from adafruit_display_text import bitmap_label
52
+ import terminalio
53
+
54
+ TEXT_SLIDES_ENABLED = True
55
+ except ImportError :
56
+ print ("Warning: adafruit_display_text not found. No support for text slides." )
57
+ TEXT_SLIDES_ENABLED = False
58
+
59
+ try :
60
+ # custom fonts are an optional feature and require adafruit_bitmap_font
61
+ from adafruit_bitmap_font import bitmap_font
62
+
63
+ CUSTOM_FONTS = True
64
+ except ImportError :
65
+ print ("Warning: adafruit_bitmap_font not found. No support for custom fonts." )
66
+ CUSTOM_FONTS = False
67
+
48
68
__version__ = "0.0.0-auto.0"
49
69
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Slideshow.git"
50
70
@@ -190,12 +210,24 @@ def __init__(
190
210
h_align = HorizontalAlignment .LEFT ,
191
211
v_align = VerticalAlignment .TOP ,
192
212
):
213
+ def _check_json_file (file ):
214
+ if TEXT_SLIDES_ENABLED :
215
+ if file .endswith (".json" ):
216
+ with open (file ) as _file_obj :
217
+ try :
218
+ json_data = json .loads (_file_obj .read ())
219
+ if "text" in json_data :
220
+ return True
221
+ except ValueError :
222
+ return False
223
+ return False
224
+
193
225
self .loop = loop
194
- """Specifies whether to loop through the images continuously or play through the list once.
226
+ """Specifies whether to loop through the slides continuously or play through the list once.
195
227
``True`` will continue to loop, ``False`` will play only once."""
196
228
197
229
self .dwell = dwell
198
- """The number of seconds each image displays, in seconds."""
230
+ """The number of seconds each slide displays, in seconds."""
199
231
200
232
self .direction = direction
201
233
"""Specify the playback direction. Default is ``PlayBackDirection.FORWARD``. Can also be
@@ -205,16 +237,19 @@ def __init__(
205
237
"""Enable auto-advance based on dwell time. Set to ``False`` to manually control."""
206
238
207
239
self .fade_effect = fade_effect
208
- """Whether to include the fade effect between images . ``True`` tells the code to fade the
209
- backlight up and down between image display transitions. ``False`` maintains max
210
- brightness on the backlight between image transitions."""
240
+ """Whether to include the fade effect between slides . ``True`` tells the code to fade the
241
+ backlight up and down between slide display transitions. ``False`` maintains max
242
+ brightness on the backlight between slide transitions."""
211
243
212
244
# Load the image names before setting order so they can be reordered.
213
245
self ._img_start = None
214
246
self ._file_list = [
215
- folder + "/" + f
247
+ folder + f
216
248
for f in os .listdir (folder )
217
- if (f .endswith (".bmp" ) and not f .startswith ("." ))
249
+ if (
250
+ not f .startswith ("." )
251
+ and (f .endswith (".bmp" ) or _check_json_file (folder + f ))
252
+ )
218
253
]
219
254
220
255
self ._order = None
@@ -226,11 +261,9 @@ def __init__(
226
261
self ._h_align = h_align
227
262
self ._v_align = v_align
228
263
229
- self ._current_image = - 1
230
- self ._image_file = None
264
+ self ._current_slide_index = - 1
265
+ self ._slide_file = None
231
266
self ._brightness = 0.5
232
- # 4.0.0 Beta 2 replaces Sprite with TileGrid so use either.
233
- self ._sprite_class = getattr (displayio , "Sprite" , displayio .TileGrid )
234
267
235
268
# Setup the display
236
269
self ._group = displayio .Group ()
@@ -249,9 +282,9 @@ def __init__(
249
282
self .advance ()
250
283
251
284
@property
252
- def current_image_name (self ):
285
+ def current_slide_name (self ):
253
286
"""Returns the current image name."""
254
- return self ._file_list [self ._current_image ]
287
+ return self ._file_list [self ._current_slide_index ]
255
288
256
289
@property
257
290
def order (self ):
@@ -265,9 +298,9 @@ def order(self, order):
265
298
raise ValueError ("Order must be either 'RANDOM' or 'ALPHABETICAL'" )
266
299
267
300
self ._order = order
268
- self ._reorder_images ()
301
+ self ._reorder_slides ()
269
302
270
- def _reorder_images (self ):
303
+ def _reorder_slides (self ):
271
304
if self .order == PlayBackOrder .ALPHABETICAL :
272
305
self ._file_list = sorted (self ._file_list )
273
306
elif self .order == PlayBackOrder .RANDOM :
@@ -315,73 +348,136 @@ def _fade_down(self):
315
348
self ._set_backlight (self .brightness * i / steps )
316
349
time .sleep (0.01 )
317
350
351
+ def _create_label (self , file ):
352
+ # pylint: disable=too-many-branches
353
+ """Creates and returns a label from a file object that contains
354
+ valid valid json describing the text to use.
355
+ See: examples/sample_text_slide.json
356
+ """
357
+ json_data = json .loads (file .read ())
358
+ _scale = 1
359
+ if "scale" in json_data :
360
+ _scale = int (json_data ["scale" ])
361
+
362
+ if CUSTOM_FONTS :
363
+ if "font" in json_data :
364
+ _font = bitmap_font .load_font (json_data ["font" ])
365
+ else :
366
+ _font = terminalio .FONT
367
+ else :
368
+ _font = terminalio .FONT
369
+
370
+ label = bitmap_label .Label (_font , text = json_data ["text" ], scale = _scale )
371
+ if "h_align" not in json_data or json_data ["h_align" ] == "LEFT" :
372
+ x_anchor_point = 0.0
373
+ x_anchored_position = 0
374
+ elif json_data ["h_align" ] == "CENTER" :
375
+ x_anchor_point = 0.5
376
+ x_anchored_position = self ._display .width // 2
377
+ elif json_data ["h_align" ] == "RIGHT" :
378
+ x_anchor_point = 1.0
379
+ x_anchored_position = self ._display .width - 1
380
+ else :
381
+ # wrong value for align
382
+ x_anchor_point = 0.0
383
+ x_anchored_position = 0
384
+
385
+ if "v_align" not in json_data or json_data ["v_align" ] == "TOP" :
386
+ y_anchor_point = 0.0
387
+ y_anchored_position = 0
388
+ elif json_data ["v_align" ] == "CENTER" :
389
+ y_anchor_point = 0.5
390
+ y_anchored_position = self ._display .height // 2
391
+ elif json_data ["v_align" ] == "BOTTOM" :
392
+ y_anchor_point = 1.0
393
+ y_anchored_position = self ._display .height - 1
394
+ else :
395
+ # wrong value for align
396
+ y_anchor_point = 0.0
397
+ y_anchored_position = 0
398
+
399
+ if "background_color" in json_data :
400
+ label .background_color = int (json_data ["background_color" ], 16 )
401
+
402
+ if "color" in json_data :
403
+ label .color = int (json_data ["color" ], 16 )
404
+
405
+ label .anchor_point = (x_anchor_point , y_anchor_point )
406
+ label .anchored_position = (x_anchored_position , y_anchored_position )
407
+ return label
408
+
318
409
def update (self ):
319
410
"""Updates the slideshow to the next image."""
320
411
now = time .monotonic ()
321
412
if not self .auto_advance or now - self ._img_start < self .dwell :
322
413
return True
323
414
return self .advance ()
324
415
325
- # pylint: disable=too-many-branches
416
+ # pylint: disable=too-many-branches, too-many-statements
326
417
def advance (self ):
327
418
"""Displays the next image. Returns True when a new image was displayed, False otherwise."""
328
- if self ._image_file :
419
+ if self ._slide_file :
329
420
self ._fade_down ()
330
421
self ._group .pop ()
331
- self ._image_file .close ()
332
- self ._image_file = None
422
+ self ._slide_file .close ()
423
+ self ._slide_file = None
333
424
334
- self ._current_image += self .direction
425
+ self ._current_slide_index += self .direction
335
426
336
- # Try and load an OnDiskBitmap until a valid file is found or we run out of options. This
427
+ # Try to load slides until a valid file is found or we run out of options. This
337
428
# loop stops because we either set odb or reduce the length of _file_list.
338
429
odb = None
339
- while not odb and self ._file_list :
340
- if 0 <= self ._current_image < len (self ._file_list ):
430
+ lbl = None
431
+ while not odb and not lbl and self ._file_list :
432
+ if 0 <= self ._current_slide_index < len (self ._file_list ):
341
433
pass
342
434
elif not self .loop :
343
435
return False
344
436
else :
345
- image_count = len (self ._file_list )
346
- if self ._current_image < 0 :
347
- self ._current_image += image_count
348
- elif self ._current_image >= image_count :
349
- self ._current_image -= image_count
350
- self ._reorder_images ()
351
-
352
- image_name = self ._file_list [self ._current_image ]
353
- self ._image_file = open (image_name , "rb" )
354
- try :
355
- odb = displayio .OnDiskBitmap (self ._image_file )
356
- except ValueError :
357
- self ._image_file .close ()
358
- self ._image_file = None
359
- del self ._file_list [self ._current_image ]
360
-
361
- if not odb :
362
- raise RuntimeError ("No valid images" )
363
-
364
- if self ._h_align == HorizontalAlignment .RIGHT :
365
- self ._group .x = self ._display .width - odb .width
366
- elif self ._h_align == HorizontalAlignment .CENTER :
367
- self ._group .x = round (self ._display .width / 2 - odb .width / 2 )
368
- else :
369
- self ._group .x = 0
437
+ slide_count = len (self ._file_list )
438
+ if self ._current_slide_index < 0 :
439
+ self ._current_slide_index += slide_count
440
+ elif self ._current_slide_index >= slide_count :
441
+ self ._current_slide_index -= slide_count
442
+ self ._reorder_slides ()
443
+
444
+ file_name = self ._file_list [self ._current_slide_index ]
445
+ self ._slide_file = open (file_name , "rb" )
446
+ if file_name .endswith (".bmp" ):
447
+ try :
448
+ odb = displayio .OnDiskBitmap (self ._slide_file )
449
+ except ValueError :
450
+ self ._slide_file .close ()
451
+ self ._slide_file = None
452
+ del self ._file_list [self ._current_slide_index ]
453
+ elif file_name .endswith (".json" ):
454
+ lbl = self ._create_label (self ._slide_file )
455
+
456
+ if not odb and not lbl :
457
+ raise RuntimeError ("No valid images or text json files" )
458
+
459
+ if odb :
460
+ if self ._h_align == HorizontalAlignment .RIGHT :
461
+ self ._group .x = self ._display .width - odb .width
462
+ elif self ._h_align == HorizontalAlignment .CENTER :
463
+ self ._group .x = round (self ._display .width / 2 - odb .width / 2 )
464
+ else :
465
+ self ._group .x = 0
370
466
371
- if self ._v_align == VerticalAlignment .BOTTOM :
372
- self ._group .y = self ._display .height - odb .height
373
- elif self ._v_align == VerticalAlignment .CENTER :
374
- self ._group .y = round (self ._display .height / 2 - odb .height / 2 )
375
- else :
376
- self ._group .y = 0
467
+ if self ._v_align == VerticalAlignment .BOTTOM :
468
+ self ._group .y = self ._display .height - odb .height
469
+ elif self ._v_align == VerticalAlignment .CENTER :
470
+ self ._group .y = round (self ._display .height / 2 - odb .height / 2 )
471
+ else :
472
+ self ._group .y = 0
377
473
378
- try :
379
- sprite = self ._sprite_class (odb , pixel_shader = displayio .ColorConverter ())
380
- except TypeError :
381
- sprite = self ._sprite_class (
382
- odb , pixel_shader = displayio .ColorConverter (), position = (0 , 0 )
474
+ image_tilegrid = displayio .TileGrid (
475
+ odb , pixel_shader = displayio .ColorConverter ()
383
476
)
384
- self ._group .append (sprite )
477
+
478
+ self ._group .append (image_tilegrid )
479
+ if lbl :
480
+ self ._group .append (lbl )
385
481
386
482
if hasattr (self ._display , "refresh" ):
387
483
self ._display .refresh ()
0 commit comments