#!/usr/bin/env python3 # # This was (poorly) derived from the Asciimatics "credits.py" sample source. # The only thing that might be of value is the `FigletBlockText` class which # will convert '#' in a Figlet font in to the Unicode FULL BLOCK. # # The Asciimatics source has an Apache 2.0 license, since the sample source # this was derived from ("credits.py") did not have a different license # preamble, I am under the impression that it has the same license. # # This has been abandoned by S.W. Black of yam655.com. It was a poorly # written proof-of-concept. It served its purpose. This is provided as an # example only. I'm not cleaning it up or maintaining it. # # Again, treat it as a derivative of the sample 'credits.py' in Asciimatics # and that it has an Apache 2.0 license. from __future__ import division from pyfiglet import Figlet import pygame from asciimatics.widgets import Label from asciimatics.effects import Scroll, Mirage, Wipe, Cycle, Matrix, \ BannerText, Stars, Print, Effect from asciimatics.particles import DropScreen, ShootScreen from asciimatics.renderers import FigletText, SpeechBubble, Rainbow, Fire, Plasma, StaticRenderer from asciimatics.scene import Scene from asciimatics.screen import Screen from asciimatics.exceptions import ResizeScreenError import sys font_small='3x5' font_medium='clr6x8' font_large='clb8x10' font_bold_6x10='clb6x10' font_bold_8x10='clb8x10' font_bold_8x8='clb8x8' font_italic_8x8='cli8x8' font_4x6='clr4x6' font_5x6='clr5x6' font_5x8='clr5x8' font_5x10='clr5x10' font_6x6='clr6x6' font_6x8='clr6x8' font_6x10='clr6x10' font_7x8='clr7x8' font_7x10='clr7x10' font_8x8='clr8x8' font_8x10='clr8x10' # font_small=font_4x6 # font_medium=font_6x8 # font_large=font_8x10 class FigletBlockText(StaticRenderer): """ This class renders the supplied text using the specified Figlet font. See http://www.figlet.org/ for details of available fonts. """ def __init__(self, text, font='term', width=200): """ :param text: The text string to convert with Figlet. :param font: The Figlet font to use (optional). :param width: The maximum width for this text in characters. """ super(FigletBlockText, self).__init__() self._images = [Figlet(font=font, width=width).renderText(text.upper()).replace('#','\N{FULL BLOCK}')] class PyGamePlayer(Effect): def __init__(self, screen, **kwargs): self.started = False super(PyGamePlayer, self).__init__(screen, **kwargs) def reset(self): pass # Nothing required def _update(self, frame_no): if not self.started: self.started = True pygame.mixer.music.play() @property def stop_frame(self): return self._stop_frame @property def frame_update_count(self): return 1 class PrintMap(Effect): """ Special effect that simply prints the specified text (from a Renderer) at the required location. """ def __init__(self, screen, renderer, y, x=None, attr=0, bg=0, clear=False, transparent=True, speed=4, colour_map={0: 7}, **kwargs): """ :param screen: The Screen being used for the Scene. :param renderer: The renderer to be printed. :param x: The column (x coordinate) for the start of the text. If not specified, defaults to centring the text on screen. :param y: The line (y coordinate) for the start of the text. :param colour_map: The foreground colour_map {frame: colour} to use for the text. :param attr: The colour attribute to use for the text. :param bg: The background colour to use for the text. :param clear: Whether to clear the text before stopping. :param transparent: Whether to print spaces (and so be able to overlay other Effects). If False, this will redraw all characters and so replace any Effect underneath it. :param speed: The refresh rate in frames between refreshes. Note that a spped of 1 will force the Screen to redraw the Effect every frame update, while a value of 0 will redraw on demand - i.e. will redraw every time that an update is required by another Effect. Also see the common keyword arguments in :py:obj:`.Effect`. """ super(PrintMap, self).__init__(screen, **kwargs) self._renderer = renderer self._transparent = transparent self._y = y self._x = ((self._screen.width - renderer.max_width) // 2 if x is None else x) self._colour_map = colour_map self._colour = colour_map.get(0, 7) self._attr = attr self._bg = bg self._clear = clear self._speed = speed self._frame_no = 0 def reset(self): pass # Nothing required def _update(self, frame_no): while self._frame_no < frame_no: self._colour = self._colour_map.get(self._frame_no, self._colour) self._frame_no += 1 if self._clear and \ (frame_no == self._stop_frame - 1) or (self._delete_count == 1): for i in range(0, self._renderer.max_height): self._screen.print_at(" " * self._renderer.max_width, self._x, self._y + i, bg=self._bg) elif self._speed == 0 or frame_no % self._speed == 0: image, colours = self._renderer.rendered_text for (i, line) in enumerate(image): self._screen.paint(line, self._x, self._y + i, self._colour, attr=self._attr, bg=self._bg, transparent=self._transparent, colour_map=colours[i]) @property def stop_frame(self): return self._stop_frame @property def frame_update_count(self): # Only demand update for next update frame. return self._speed - (self._frame_no % self._speed) if self._speed > 0 else 1000000 def from_human_duration(code): if code is None or isinstance(code, int) or isinstance(code,float) or code == "": return code frac = 0 if '.' in code: splits = code.split('.') if len(splits) > 1: frac = float('0.{}'.format(splits[-1])) splits = splits[0].split(":") else: splits = code.split(":") ret = 0 if len(splits) > 3: ret += float(splits[0]) del splits[0] ret *= 24 if len(splits) > 2: ret += float(splits[0]) del splits[0] ret *= 60 if len(splits) > 1: ret += float(splits[0]) del splits[0] ret *= 60 if len(splits) > 0: ret += float(splits[0]) return ret + frac def best_font(max_width, string, *, recommend=None): font_list = [ font_large, font_medium, font_small ] if recommend: if recommend == 's': recommend = font_small elif recommend == 'm': recommend = font_medium elif recommend == 'l': recommend = font_large font_list.insert(0, recommend) height = -1 for font in font_list: text = Figlet(font=font, width=200).renderText(string) lines = text.split("\n") width = max([len(x) for x in lines]) if width < max_width: height = len(lines) break else: font = None if font is None: font = 'term' height = 1 return (font, height) def music_video(screen): scenes = [] vid_colors = [ 198, 196, 51, 208, 39, 220, 27, 154, 57, 82, 129, 47, 200, 49, ] pygame.mixer.init() pygame.mixer.music.load('Do_You_Like_Beards.mp3') # pygame.mixer.music.load('This_Is_A_Test.mp3') text = Figlet(font=font_bold_8x10, width=200).renderText("Do you like\n beards?") width = max([len(x) for x in text.split("\n")]) effects = [ Print(screen, FigletBlockText("Do you like\n beards?", font_bold_8x10), 1, x=(screen.width - width) // 2, colour=vid_colors[7]), Print( screen, StaticRenderer(["Do you like beards?\n" "Mr. Beany's Bitty Band\n" "https://mrbreany.bandcamp.com\n" "https://yam655.com/\n" "Creative Commons Attribution 4.0" ]), screen.height - 5, x = 0, colour = Screen.COLOUR_WHITE, ), ] scenes.append(Scene(effects, -1, clear=False)) effects = [ PyGamePlayer(screen, start_frame=0), ShootScreen(screen, screen.width // 2, screen.height // 2, 20), ] scenes.append(Scene(effects, 20, clear=False)) vidterm = 'beard' vid_cnt = 0 vid_state = {'vid_cnt': 0} def vid_scene(scene_len, obj, vid_state=vid_state, vid_colors=vid_colors, screen=screen): vid_cnt = vid_state['vid_cnt'] % len(vid_colors) orig_vid_cnt = vid_cnt effects = [] if isinstance(obj, str): obj = [obj, ] offset = scene_len // len(obj) offset1 = 0 idxr = obj total_height = 0 if isinstance(obj, dict): idxr = sorted(obj) box = [] for obj1 in idxr: if isinstance(obj, dict): offset1 = obj1 obj1 = obj[obj1] else: offset1 += offset if isinstance(obj1, str): font, height = best_font(screen.width, obj1) else: font, height = best_font(screen.width, obj1[0], recommend=obj1[1]) obj1 = obj1[0] rainbow = vidterm in obj1 box.append({'text': obj1, 'y': total_height, 'rainbow': rainbow, 'font': font, 'frame':offset1}) total_height += height if len(obj) == 1: remainder = 0 margin = (screen.height - total_height) // 2 elif len(obj) == 2: remainder = (screen.height - total_height) // 2 margin = remainder // 2 else: margin = 0 remainder = (screen.height - total_height) // (len(obj) - 1) if remainder or margin: offset = margin for idx in range(len(box)): box[idx]['y'] += offset offset += remainder for entry in box: if entry['rainbow']: effects.append(Print(screen, Rainbow(screen, FigletBlockText(entry['text'], entry['font'])), entry['y'], start_frame=entry['frame'])) else: effects.append(Print(screen, FigletBlockText(entry['text'], entry['font']), entry['y'], colour = vid_colors[vid_cnt % len(vid_colors)], start_frame=entry['frame'])) vid_cnt += 1 vid_cnt = vid_cnt % len(vid_colors) if vid_cnt == orig_vid_cnt: vid_cnt += 1 vid_state['vid_cnt'] = vid_cnt scenes.append(Scene(effects, scene_len)) return None vid_scene(100, {0:'Do you like', 15:'beards?', 50:("There's only one answer.", 's')}) vid_scene(75, {0:'Do you like', 15:'beards?', 40:("The answer is yes!", 's')}) vid_scene(40, {0:('Yes!', 'm'), 15:("I like", 's'), 20:"beards!"}) vid_scene(35, {0:('Yes!', 'm'), 15:("You do,", 's'), 20:"too!"}) vid_scene(58, {0:"And if you don't,", 25:"why, we can fix that."}) vid_scene(61, {0:"If you don't like", 25:'beards,', 40:"why, we can fix that."}) scenes.append(Scene([ShootScreen(screen, screen.width // 2, screen.height // 2, 20),], 20, clear=False)) vid_scene(75, {0:("No, I won't kill you.", 's'), 35:"No, I won't."}) vid_scene(80, {0:('I might force', 'm'), 35:("you to have a", 's'), 55:"beard."}) vid_scene(60, {0:("No, I won't kill you.", 's'), 30:"No, I won't."}) vid_scene(85, {0:('But, I might', 'm'), 35:("force you to have a", 's'), 65:"beard."}) vid_scene(80, {0:('How would I', 'm'), 20:("force you to have a", 's'), 65:"beard"}) vid_scene(77, {0:("when you can't", 'm'), 20:("even grow a", 's'), 45:"beard?"}) vid_scene(70, {0:('How would I', 'm'), 20:("force you to grow a", 's'), 45:"beard"}) vid_scene(80, {0:"when you don't", 20:("have the", 's'), 40:"capacity?"}) vid_scene(71, {0:("I've got some glue here", 's'), 30:"and you've got a face."}) vid_scene(71, {0:("And there's some extra hair", 's'), 30:"from all over the place."}) vid_scene(71, {0:("I've got some glue here", 's'), 30:"and you've got a face."}) vid_scene(80, {0:("and I can put", 's'), 30:"the two together."}) vid_scene(70, {0:("Give you a", 'm'), 25:'beard', 40:("that won't come off.", 's')}) vid_scene(77, {0:("A lovely", 'm'), 35:'beard', 50:("with lovely hair.", 's')}) vid_scene(60, {0:"And maybe the hair", 30:"will get everywhere."}) scenes.append(Scene([ShootScreen(screen, screen.width // 2, screen.height // 2, 30),], 30, clear=False)) vid_scene(95, {0:("Now you won't", 'm'), 45:("just have a", 's'), 65:"beard,"}) vid_scene(90, {0:"you might also have", 45:"hair over there."}) vid_scene(140, {0:"Yes, I mean your elbows!", 70:"Yes, I mean your knees!"}) vid_scene(75, {0:"Lovely hair", 30:"for all to see!"}) vid_scene(140, {0:"Maybe hair on your nose!", 70:"Maybe hair on your toes!"}) vid_scene(140, {0:"Lots of lovely hair for", 65:"you and me and all to see!"}) scenes.append(Scene([ShootScreen(screen, screen.width // 2, screen.height // 2, 20),], 20, clear=False)) vid_scene(140, {0:"All you need to do", 60:("to prevent this is change", 's'), 105:"your mind."}) vid_scene(140, {0:"All you need to do", 60:("is declare your love of", 's'), 105:"beards!"}) vid_scene(140, {0:"Then I can give you", 65:("this lovely yarn", 's'), 105:"beard."}) vid_scene(130, {0:"Then I can give you", 60:("this lovely yarn", 's'), 100:"beard."}) vid_scene(120, {0:"Then you can wear it", 60:"and all will see"}) vid_scene(125, {0:("lovely", 's'), 25:'beards', 45:"from sea to sea."}) vid_scene(75, {0:("Lovely", 's'), 35:'beard', 55:"here and there."}) vid_scene(85, {0:("A lovely", 's'), 25:'beard,', 35:"yes, everywhere."}) effects = [ Print(screen, SpeechBubble('The End.'), screen.height // 2 - 1, attr=Screen.A_BOLD), Print( screen, StaticRenderer(["Do you like beards?\n" "Mr. Beany's Bitty Band\n" "https://mrbreany.bandcamp.com\n" "https://yam655.com/\n" "Creative Commons Attribution 4.0" ]), screen.height - 5, x = 0, colour = Screen.COLOUR_WHITE, ), ] scenes.append(Scene(effects, -1)) screen.play(scenes, stop_on_resize=True, repeat=False) if __name__ == "__main__": # while True: try: Screen.wrapper(music_video) sys.exit(0) except ResizeScreenError: pass