#!/usr/bin/env python3 import threading import os import os.path import random import time import sys import getopt import blup.output import blup.animation import blup.frame class AnimationLoaderThread(threading.Thread): """ This class is used by the player to pre-load an animation while another one is being played. """ def __init__(self, filename): threading.Thread.__init__(self) self.__filename = filename self.__anim = None self.__error = False def run(self): try: self.__anim = blup.animation.load(self.__filename) except Exception as e: print('%s: %s while loading %s' % (self.__class__.__name__, repr(e), self.__filename)) self.__error = True self.__anim = None def getAnim(self): self.join() return self.__anim class MiniPlayer(object): """ Minimal animation player that does nothing but randomly selecting animations out of a directory and playing them. """ def __init__(self, animDir, animExtensions, output): self.__animDir = animDir self.__animExtensions = animExtensions self.__output = output self.loopTime = 10000 self.gap = 800 self.maxPlayNext = 3 self.__playNext = [] self.__running = False self.__anims = {} self.__anims_remaining = {} def terminate(self): self.__running = False def playNext(self, filename): """ Add an animation to the queue. """ if filename.find('/') >= 0: raise ValueError('filename must not contain slashes') if ( os.path.isfile(os.path.join(self.__animDir, filename)) and len(self.__playNext) < self.maxPlayNext ): self.__playNext.append(filename) else: return False def getNextFilename(self): """ Return the next animation filename to be played, either from the queue, or randomly chosen. """ if len(self.__playNext) > 0 and os.path.isfile(self.__playNext[0]): anim = self.__playNext.pop(0) else: anims = set() for dp, dns, fns in os.walk(self.__animDir, followlinks=True): for fn in fns: root, ext = os.path.splitext(fn) if ext[1:] in self.__animExtensions: anims.add(os.path.join(dp, fn)) if anims != self.__anims or len(self.__anims_remaining) == 0: self.__anims = anims self.__anims_remaining = set(anims) anim = random.sample(self.__anims_remaining, 1)[0] self.__anims_remaining.remove(anim) print(anim) return anim def run(self): """ Runs the player until terminate() gets called (e.g. by another thread). """ self.__running = True currentAnim = None player = blup.animation.AnimationPlayer() while self.__running: # begin to load the next animation loader = AnimationLoaderThread(self.getNextFilename()) loader.start() # play the animation in case it had been successfully loaded before if currentAnim is not None: if currentAnim.duration < self.loopTime: count = self.loopTime // currentAnim.duration else: count = 1 player.play(currentAnim, self.__output, count=count) # show a blank frame for some thime if self.gap > 0 and self.__running: # TODO: use correct frame size #dim = blup.frame.FrameDimension(18,8,8,3) dim = blup.frame.FrameDimension(22, 16, 256, 3) self.__output.sendFrame(blup.frame.Frame(dim)) time.sleep(self.gap / 1000.0) # get the next animation from the loader currentAnim = loader.getAnim() def printUsage(errMsg=None): if errMsg is not None: print('error: %s\n' % (errMsg)) print('usage: %s [OPTIONS] PATH' % (sys.argv[0])) print('where PATH is the directory containing the animations to play') print('supported options:') print(' -o OUTPUT where to output the frames (default: shell)') print(' --output OUTPUT\n') print(' -h print(this text') print(' --help') def main(): try: (opts, args) = getopt.gnu_getopt(sys.argv, 'ho:', ['help', 'output=']) opts = dict(opts) except getopt.GetoptError as e: printUsage(e.msg) sys.exit(1) if '--help' in opts: printUsage() sys.exit(0) if '-o' in opts: output = opts['-o'] elif '--output' in opts: output = opts['--output'] else: output = 'shell' try: out = blup.output.getOutput(output) except blup.output.IllegalOutputSpecificationError: print('illegal output specification') print('available outputs:') print(blup.output.getOutputDescriptions()) sys.exit(1) except Exception as e: print('could not initialize output: %s' % (str(e))) sys.exit(1) if len(args) != 2: printUsage() sys.exit(1) else: animDir = args[1] if not os.path.isdir(animDir): print('%s is not a directory' % (animDir)) sys.exit(1) p = MiniPlayer(animDir, ['bml'], out) try: p.run() except KeyboardInterrupt: sys.exit(0) if __name__ == '__main__': main()