1#!/usr/bin/env python 2""" pygame.examples.font_viewer 3Scroll through your system fonts from a list of surfaces or one huge buffer. 4 5This example exhibits: 6* iterate over available fonts using font.get_fonts and font.SysFont() 7* click and drag using mouse input 8* scrolling with the scroll wheel 9* save a surface to disk 10* work with a very large surface 11* simple mouse and keyboard scroll speed acceleration 12 13By default this example uses the fonts returned by pygame.font.get_fonts() 14and opens them using pygame.font.SysFont(). 15Alternatively, you may pass a path to the command line. The TTF files found 16in that directory will be used instead. 17 18Mouse Controls: 19* Use the mouse wheel or click and drag to scroll 20 21Keyboard Controls: 22* Press up or down to scroll 23* Press escape to exit 24""" 25import sys 26import os 27 28import pygame as pg 29 30use_big_surface = False # draw into large buffer and save png file 31 32 33class FontViewer: 34 """ 35 This example is encapsulated by the fontviewer class 36 It initializes the pygame window, handles input, and draws itself 37 to the screen. 38 """ 39 40 KEY_SCROLL_SPEED = 10 41 MOUSE_SCROLL_SPEED = 50 42 43 def __init__(self, **dparams): 44 pg.init() 45 self.font_dir = dparams.get("folder", None) 46 47 # create a window that uses 80 percent of the screen 48 info = pg.display.Info() 49 w = info.current_w 50 h = info.current_h 51 pg.display.set_mode((int(w * 0.8), int(h * 0.8))) 52 self.font_size = h // 20 53 54 self.clock = pg.time.Clock() 55 self.y_offset = 0 56 self.grabbed = False 57 self.render_fonts("&N abcDEF789") 58 59 if use_big_surface or "big" in sys.argv: 60 self.render_surface() 61 self.display_surface() 62 self.save_png() 63 else: 64 self.display_fonts() 65 66 def get_font_list(self): 67 """ 68 Generate a font list using font.get_fonts() for system fonts or 69 from a path from the command line. 70 """ 71 path = "" 72 if len(sys.argv) > 1 and os.path.exists(sys.argv[1]): 73 path = os.path.join(sys.argv[1], "") 74 fonts = [] 75 if os.path.exists(path): 76 # this list comprehension could replace the following loop 77 # fonts = [f in os.listdir(path) if f.endswith('.ttf')] 78 for font in os.listdir(path): 79 if font.endswith(".ttf"): 80 fonts.append(font) 81 return fonts or pg.font.get_fonts(), path 82 83 def render_fonts(self, text="A display of font &N", **dparams): 84 """ 85 Build a list that includes a surface and the running total of their 86 height for each font in the font list. Store the largest width and 87 other variables for later use. 88 """ 89 font_size = dparams.get("size", 0) or self.font_size 90 color = dparams.get("color", (255, 255, 255)) 91 self.back_color = dparams.get("back_color", (0, 0, 0)) 92 93 fonts, path = self.get_font_list() 94 font_surfaces = [] 95 total_height = 0 96 max_width = 0 97 98 load_font = pg.font.Font if path else pg.font.SysFont 99 100 # display instructions at the top of the display 101 font = pg.font.SysFont(pg.font.get_default_font(), font_size) 102 lines = ( 103 "Use the scroll wheel or click and drag", 104 "to scroll up and down", 105 "Foreign fonts might render incorrectly", 106 "Here are your {} fonts".format(len(fonts)), 107 "", 108 ) 109 for line in lines: 110 surf = font.render(line, 1, color, self.back_color) 111 font_surfaces.append((surf, total_height)) 112 total_height += surf.get_height() 113 max_width = max(max_width, surf.get_width()) 114 115 # render all the fonts and store them with the total height 116 for name in sorted(fonts): 117 try: 118 font = load_font(path + name, font_size) 119 except IOError: 120 continue 121 line = text.replace("&N", name) 122 try: 123 surf = font.render(line, 1, color, self.back_color) 124 except pg.error as e: 125 print(e) 126 break 127 128 max_width = max(max_width, surf.get_width()) 129 font_surfaces.append((surf, total_height)) 130 total_height += surf.get_height() 131 132 # store variables for later usage 133 self.total_height = total_height 134 self.max_width = max_width 135 self.font_surfaces = font_surfaces 136 self.max_y = total_height - pg.display.get_surface().get_height() 137 138 def display_fonts(self): 139 """ 140 Display the visible fonts based on the y_offset value(updated in 141 handle_events) and the height of the pygame window. 142 """ 143 display = pg.display.get_surface() 144 clock = pg.time.Clock() 145 center = display.get_width() // 2 146 147 while True: 148 # draw visible surfaces 149 display.fill(self.back_color) 150 for surface, top in self.font_surfaces: 151 bottom = top + surface.get_height() 152 if ( 153 bottom >= self.y_offset 154 and top <= self.y_offset + display.get_height() 155 ): 156 x = center - surface.get_width() // 2 157 display.blit(surface, (x, top - self.y_offset)) 158 # get input and update the screen 159 if not self.handle_events(): 160 break 161 pg.display.flip() 162 clock.tick(30) 163 164 def render_surface(self): 165 """ 166 Note: this method uses twice the memory and is only called if 167 big_surface is set to true or big is added to the command line. 168 169 Optionally generates one large buffer to draw all the font surfaces 170 into. This is necessary to save the display to a png file and may 171 be useful for testing large surfaces. 172 """ 173 174 large_surface = pg.surface.Surface( 175 (self.max_width, self.total_height) 176 ).convert() 177 large_surface.fill(self.back_color) 178 print("scrolling surface created") 179 180 # display the surface size and memory usage 181 byte_size = large_surface.get_bytesize() 182 total_size = byte_size * (self.max_width * self.total_height) 183 print( 184 "Surface Size = {}x{} @ {}bpp: {:,.3f}mb".format( 185 self.max_width, self.total_height, byte_size, total_size / 1000000.0 186 ) 187 ) 188 189 y = 0 190 center = int(self.max_width / 2) 191 for surface, top in self.font_surfaces: 192 w = surface.get_width() 193 x = center - int(w / 2) 194 large_surface.blit(surface, (x, y)) 195 y += surface.get_height() 196 self.max_y = large_surface.get_height() - pg.display.get_surface().get_height() 197 self.surface = large_surface 198 199 def display_surface(self, time=10): 200 """ 201 Display the large surface created by the render_surface method. Scrolls 202 based on the y_offset value(set in handle_events) and the height of the 203 pygame window. 204 """ 205 screen = pg.display.get_surface() 206 207 # Create a Rect equal to size of screen. Then we can just change its 208 # top attribute to draw the desired part of the rendered font surface 209 # to the display surface 210 rect = pg.rect.Rect( 211 0, 212 0, 213 self.surface.get_width(), 214 min(self.surface.get_height(), screen.get_height()), 215 ) 216 217 x = int((screen.get_width() - self.surface.get_width()) / 2) 218 going = True 219 while going: 220 if not self.handle_events(): 221 going = False 222 screen.fill(self.back_color) 223 rect.top = self.y_offset 224 screen.blit(self.surface, (x, 0), rect) 225 pg.display.flip() 226 self.clock.tick(20) 227 228 def save_png(self, name="font_viewer.png"): 229 pg.image.save(self.surface, name) 230 file_size = os.path.getsize(name) // 1024 231 print("font surface saved to {}\nsize: {:,}Kb".format(name, file_size)) 232 233 def handle_events(self): 234 """ 235 This method handles user input. It returns False when it receives 236 a pygame.QUIT event or the user presses escape. The y_offset is 237 changed based on mouse and keyboard input. display_fonts() and 238 display_surface() use the y_offset to scroll display. 239 """ 240 events = pg.event.get() 241 for e in events: 242 if e.type == pg.QUIT: 243 return False 244 elif e.type == pg.KEYDOWN: 245 if e.key == pg.K_ESCAPE: 246 return False 247 elif e.type == pg.MOUSEWHEEL: 248 self.y_offset += e.y * self.MOUSE_SCROLL_SPEED * -1 249 elif e.type == pg.MOUSEBUTTONDOWN: 250 # enter dragging mode on mouse down 251 self.grabbed = True 252 pg.event.set_grab(True) 253 elif e.type == pg.MOUSEBUTTONUP: 254 # exit drag mode on mouse up 255 self.grabbed = False 256 pg.event.set_grab(False) 257 258 # allow simple accelerated scrolling with the keyboard 259 keys = pg.key.get_pressed() 260 if keys[pg.K_UP]: 261 self.key_held += 1 262 self.y_offset -= int(self.KEY_SCROLL_SPEED * (self.key_held // 10)) 263 elif keys[pg.K_DOWN]: 264 self.key_held += 1 265 self.y_offset += int(self.KEY_SCROLL_SPEED * (self.key_held // 10)) 266 else: 267 self.key_held = 20 268 269 # set the y_offset for scrolling and keep it between 0 and max_y 270 y = pg.mouse.get_rel()[1] 271 if y and self.grabbed: 272 self.y_offset -= y 273 274 self.y_offset = min((max(self.y_offset, 0), self.max_y)) 275 return True 276 277 278viewer = FontViewer() 279pg.quit() 280