Package tdl
[frames] | no frames]

Source Code for Package tdl

   1  """ 
   2      The documentation for python-tdl.  A Pythonic port of 
   3      U{libtcod<http://doryen.eptalys.net/libtcod/>}. 
   4   
   5      Getting Started 
   6      =============== 
   7        Once the library is imported you can load the font you want to use with 
   8        L{tdl.setFont}. 
   9        This is optional and when skipped will use a decent default font. 
  10         
  11        After that you call L{tdl.init} to set the size of the window and get the 
  12        root console in return. 
  13        This console is the canvas to what will appear on the screen. 
  14   
  15      Indexing Consoles 
  16      ================= 
  17        For most methods taking a position you can use Python-style negative 
  18        indexes to refer to the opposite side of a console with (-1, -1) 
  19        starting at the bottom right. 
  20        You can also check if a point is part of a console using containment 
  21        logic i.e. ((x, y) in console). 
  22         
  23      Drawing 
  24      ======= 
  25        Once you have the root console from L{tdl.init} you can start drawing on 
  26        it using a method such as L{Console.drawChar}. 
  27        When using this method you can have the char parameter be an integer or a 
  28        single character string. 
  29        The fgcolor and bgcolor parameters expect a three item list 
  30        [red, green, blue] with integers in the 0-255 range with [0, 0, 0] being 
  31        black and [255, 255, 255] being white. 
  32        Or instead you can use None for any of the three parameters to tell the 
  33        library to keep what is at that spot instead of overwriting it. 
  34        After the drawing functions are called a call to L{tdl.flush} will update 
  35        the screen. 
  36  """ 
  37   
  38  import sys 
  39  import os 
  40  import ctypes 
  41  import weakref 
  42  import array 
  43  import itertools 
  44  import textwrap 
  45   
  46  from . import event, map 
  47  from .__tcod import _lib, _Color, _unpackfile 
  48   
  49  _IS_PYTHON3 = (sys.version_info[0] == 3) 
  50   
  51  if _IS_PYTHON3: # some type lists to use with isinstance 
  52      _INTTYPES = (int,) 
  53      _NUMTYPES = (int, float) 
  54      _STRTYPES = (str, bytes) 
  55  else: 
  56      _INTTYPES = (int, long) 
  57      _NUMTYPES = (int, long, float) 
  58      _STRTYPES = (str,) 
59 60 -def _encodeString(string): # still used for filepaths, and that's about it
61 "changes string into bytes if running in python 3, for sending to ctypes" 62 if _IS_PYTHON3 and isinstance(string, str): 63 return string.encode() 64 return string 65
66 #def _formatString(string): 67 # pass 68 69 -def _formatChar(char):
70 """Prepares a single character for passing to ctypes calls, needs to return 71 an integer but can also pass None which will keep the current character 72 instead of overwriting it. 73 74 This is called often and needs to be optimized whenever possible. 75 """ 76 if char is None: 77 return None 78 if isinstance(char, _INTTYPES): 79 return char 80 if isinstance(char, _STRTYPES) and len(char) == 1: 81 return ord(char) 82 raise TypeError('Expected char parameter to be a single character string, number, or None, got: %s' % repr(char))
83 84 _fontinitialized = False 85 _rootinitialized = False 86 _rootconsole = None 87 # remove dots from common functions 88 _setchar = _lib.TCOD_console_set_char 89 _setfore = _lib.TCOD_console_set_char_foreground 90 _setback = _lib.TCOD_console_set_char_background 91 _setcharEX = _lib.TCOD_console_put_char_ex
92 -def _verify_colors(*colors):
93 """Used internally. 94 Raise an assertion error if the parameters can not be converted into colors. 95 """ 96 for color in colors: 97 assert _iscolor(color), 'a color must be a 3 item tuple, web format, or None, received %s' % repr(color) 98 return True
99
100 -def _iscolor(color):
101 """Used internally. 102 A debug function to see if an object can be used as a TCOD color struct. 103 None counts as a parameter to keep the current colors instead. 104 105 This function is often part of an inner-loop and can slow a program down. 106 It has been made to work with assert and can be skipped with the -O flag. 107 Still it's called often and must be optimized. 108 """ 109 if color is None: 110 return True 111 if isinstance(color, (tuple, list, _Color)): 112 return len(color) == 3 113 if isinstance(color, _INTTYPES): 114 return True 115 return False
116
117 -def _formatColor(color):
118 """Format the color to ctypes 119 """ 120 if color is None: 121 return None 122 if isinstance(color, _Color): 123 return color 124 if isinstance(color, _INTTYPES): 125 # format a web style color with the format 0xRRGGBB 126 return _Color(color >> 16 & 0xff, color >> 8 & 0xff, color & 0xff) 127 return _Color(*color)
128
129 -class TDLError(Exception):
130 """ 131 The catch all for most TDL specific errors. 132 """
133
134 -class _MetaConsole(object):
135 """ 136 Contains methods shared by both the L{Console} and L{Window} classes. 137 """ 138 __slots__ = ('width', 'height', 'console', '__weakref__', '__dict__') 139
140 - def drawChar(self, x, y, char, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
141 """Draws a single character. 142 143 @type x: int 144 @param x: X coordinate to draw at. 145 @type y: int 146 @param y: Y coordinate to draw at. 147 148 @type char: int, string, or None 149 @param char: Should be an integer, single character string, or None. 150 151 You can set the char parameter as None if you only want to change 152 the colors of the tile. 153 154 @type fgcolor: (r, g, b) or None 155 @param fgcolor: For fgcolor and bgcolor you use a 3 item list with 156 integers ranging 0-255 or None. 157 158 None will keep the current color at this position unchanged. 159 @type bgcolor: (r, g, b) or None 160 @param bgcolor: Background color. See fgcolor 161 162 @raise AssertionError: Having x or y values that can't be placed inside 163 of the console will raise an AssertionError. 164 You can use always use ((x, y) in console) to 165 check if a tile is drawable. 166 """ 167 168 assert _verify_colors(fgcolor, bgcolor) 169 x, y = self._normalizePoint(x, y) 170 171 self._setChar(ctypes.c_int(x), ctypes.c_int(y), _formatChar(char), 172 _formatColor(fgcolor), _formatColor(bgcolor))
173
174 - def drawStr(self, x, y, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
175 """Draws a string starting at x and y. Optinally colored. 176 177 A string that goes past the right side will wrap around. A string 178 wraping to below the console will raise a L{TDLError} but will still be 179 written out. This means you can safely ignore the errors with a 180 try... except block if you're fine with partily written strings. 181 182 \\r and \\n are drawn on the console as normal character tiles. No 183 special encoding is done and any string will translate to the character 184 table as is. 185 186 For a string drawing operation that respects special characters see the 187 L{Typewriter} class. 188 189 @type x: int 190 @param x: X coordinate to draw at. 191 @type y: int 192 @param y: Y coordinate to draw at. 193 194 @type string: string or iterable 195 @param string: Can be a string or an iterable of numbers. 196 197 Special characters are ignored and rendered as any other 198 character. 199 200 @type fgcolor: (r, g, b) or None 201 @param fgcolor: For fgcolor and bgcolor you use a 3 item list with 202 integers ranging 0-255 or None. 203 204 None will keep the current color at this position unchanged. 205 @type bgcolor: (r, g, b) or None 206 @param bgcolor: Background color. See fgcolor 207 208 @raise AssertionError: Having x or y values that can't be placed inside 209 of the console will raise an AssertionError. 210 211 You can use always use ((x, y) in console) to 212 check if a tile is drawable. 213 """ 214 215 x, y = self._normalizePoint(x, y) 216 assert _verify_colors(fgcolor, bgcolor) 217 fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor) 218 width, height = self.getSize() 219 batch = [] # prepare a batch operation 220 def _drawStrGen(x=x, y=y, string=string, width=width, height=height): 221 """Generator for drawStr 222 223 Iterates over ((x, y), ch) data for _setCharBatch, raising an 224 error if the end of the console is reached. 225 """ 226 for char in string: 227 if y == height: 228 raise TDLError('End of console reached.') 229 #batch.append(((x, y), _formatChar(char))) # ((x, y), ch) 230 yield((x, y), _formatChar(char)) 231 x += 1 # advance cursor 232 if x == width: # line break 233 x = 0 234 y += 1
235 self._setCharBatch(_drawStrGen(), fgcolor, bgcolor)
236
237 - def drawRect(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
238 """Draws a rectangle starting from x and y and extending to width and height. 239 240 If width or height are None then it will extend to the edge of the console. 241 242 @type x: int 243 @param x: x coordinate to draw at. 244 @type y: int 245 @param y: y coordinate to draw at. 246 247 @type width: int or None 248 @param width: Width of the rectangle. 249 250 Can be None to extend to the bottom right of the 251 console or can be a negative number to be sized reltive 252 to the total size of the console. 253 @type height: int or None 254 @param height: Height of the rectangle. See width. 255 256 @type string: int, string, or None 257 @param string: Should be an integer, single character string, or None. 258 259 You can set the char parameter as None if you only want 260 to change the colors of an area. 261 262 @type fgcolor: (r, g, b) or None 263 @param fgcolor: For fgcolor and bgcolor you use a 3 item list with 264 integers ranging 0-255 or None. 265 266 None will keep the current color at this position unchanged. 267 @type bgcolor: (r, g, b) or None 268 @param bgcolor: Background color. See fgcolor 269 270 @raise AssertionError: Having x or y values that can't be placed inside 271 of the console will raise an AssertionError. 272 273 You can use always use ((x, y) in console) to 274 check if a tile is drawable. 275 """ 276 x, y, width, height = self._normalizeRect(x, y, width, height) 277 assert _verify_colors(fgcolor, bgcolor) 278 fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor) 279 char = _formatChar(string) 280 # use itertools to make an x,y grid 281 # using ctypes here reduces type converstions later 282 grid = itertools.product((ctypes.c_int(x) for x in range(x, x + width)), 283 (ctypes.c_int(y) for y in range(y, y + height))) 284 # zip the single character in a batch variable 285 batch = zip(grid, itertools.repeat(char, width * height)) 286 self._setCharBatch(batch, fgcolor, bgcolor, nullChar=(char is None))
287
288 - def drawFrame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
289 """Similar to L{drawRect} but only draws the outline of the rectangle. 290 291 @type x: int 292 @param x: x coordinate to draw at. 293 @type y: int 294 @param y: y coordinate to draw at. 295 296 @type width: int or None 297 @param width: Width of the rectangle. 298 299 Can be None to extend to the bottom right of the 300 console or can be a negative number to be sized reltive 301 to the total size of the console. 302 @type height: int or None 303 @param height: Height of the rectangle. See width. 304 305 @type string: int, string, or None 306 @param string: Should be an integer, single character string, or None. 307 308 You can set the char parameter as None if you only want 309 to change the colors of an area. 310 311 @type fgcolor: (r, g, b) or None 312 @param fgcolor: For fgcolor and bgcolor you use a 3 item list with 313 integers ranging 0-255 or None. 314 315 None will keep the current color at this position unchanged. 316 @type bgcolor: (r, g, b) or None 317 @param bgcolor: Background color. See fgcolor 318 319 @raise AssertionError: Having x or y values that can't be placed inside 320 of the console will raise an AssertionError. 321 322 You can use always use ((x, y) in console) to 323 check if a tile is drawable. 324 """ 325 x, y, width, height = self._normalizeRect(x, y, width, height) 326 assert _verify_colors(fgcolor, bgcolor) 327 fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor) 328 char = _formatChar(string) 329 if width == 1 or height == 1: # it's just a single width line here 330 return self.drawRect(x, y, width, height, char, fgcolor, bgcolor) 331 332 # draw sides of frame with drawRect 333 self.drawRect(x, y, 1, height, char, fgcolor, bgcolor) 334 self.drawRect(x, y, width, 1, char, fgcolor, bgcolor) 335 self.drawRect(x + width - 1, y, 1, height, char, fgcolor, bgcolor) 336 self.drawRect(x, y + height - 1, width, 1, char, fgcolor, bgcolor)
337
338 - def _normalizePoint(self, x, y):
339 """Check if a point is in bounds and make minor adjustments. 340 341 Respects Pythons negative indexes. -1 starts at the bottom right. 342 Replaces the _drawable function 343 """ 344 assert isinstance(x, _INTTYPES), 'x must be an integer, got %s' % repr(x) 345 assert isinstance(y, _INTTYPES), 'y must be an integer, got %s' % repr(y) 346 347 assert (-self.width <= x < self.width) and (-self.height <= y < self.height), \ 348 ('(%i, %i) is an invalid postition on %s' % (x, y, self)) 349 350 # handle negative indexes 351 if x < 0: 352 x += self.width 353 if y < 0: 354 y += self.height 355 return (x, y)
356
357 - def _normalizeRect(self, x, y, width, height):
358 """Check if the rectangle is in bounds and make minor adjustments. 359 raise AssertionError's for any problems 360 """ 361 x, y = self._normalizePoint(x, y) # inherit _normalizePoint logic 362 363 assert width is None or isinstance(width, _INTTYPES), 'width must be an integer or None, got %s' % repr(width) 364 assert height is None or isinstance(height, _INTTYPES), 'height must be an integer or None, got %s' % repr(height) 365 366 # if width or height are None then extend them to the edge 367 if width is None: 368 width = self.width - x 369 elif width < 0: # handle negative numbers 370 width += self.width 371 width = max(0, width) # a 'too big' negative is clamped zero 372 if height is None: 373 height = self.height - y 374 height = max(0, height) 375 elif height < 0: 376 height += self.height 377 378 # reduce rect size to bounds 379 width = min(width, self.width - x) 380 height = min(height, self.height - y) 381 382 return x, y, width, height
383
384 - def blit(self, source, x=0, y=0, width=None, height=None, srcX=0, srcY=0):
385 """Blit another console or Window onto the current console. 386 387 By default it blits the entire source to the topleft corner. 388 389 @type source: L{Console} or L{Window} 390 @param source: Source window can be a L{Console} or L{Window} instance. 391 It can even blit to itself without any problems. 392 393 @type x: int 394 @param x: X coordinate to blit to. 395 @type y: int 396 @param y: Y coordinate to blit to. 397 398 @type width: int or None 399 @param width: Width of the rectangle. 400 401 Can be None to extend as far as possible to the 402 bottom right corner of the blit area or can be a negative 403 number to be sized reltive to the total size of the 404 B{destination} console. 405 @type height: int or None 406 @param height: Height of the rectangle. See width. 407 408 @type srcX: int 409 @param srcX: The source consoles x coordinate to blit from. 410 @type srcY: int 411 @param srcY: The source consoles y coordinate to blit from. 412 """ 413 # hardcode alpha settings for now 414 fgalpha=1.0 415 bgalpha=1.0 416 417 assert isinstance(source, (Console, Window)), "source muse be a Window or Console instance" 418 419 # handle negative indexes and rects 420 # negative width and height will be set realtive to the destination 421 # and will also be clamped to the smallest Console 422 x, y, width, height = self._normalizeRect(x, y, width, height) 423 srcX, srcY, width, height = source._normalizeRect(srcX, srcY, width, height) 424 425 # translate source and self if any of them are Window instances 426 srcX, srcY = source._translate(srcX, srcY) 427 source = source.console 428 x, y = self._translate(x, y) 429 self = self.console 430 431 if self == source: 432 # if we are the same console then we need a third console to hold 433 # onto the data, otherwise it tries to copy into itself and 434 # starts destroying everything 435 tmp = Console(width, height) 436 _lib.TCOD_console_blit(source, srcX, srcY, width, height, tmp, 0, 0, fgalpha, bgalpha) 437 _lib.TCOD_console_blit(tmp, 0, 0, width, height, self, x, y, fgalpha, bgalpha) 438 else: 439 _lib.TCOD_console_blit(source, srcX, srcY, width, height, self, x, y, fgalpha, bgalpha)
440
441 - def getSize(self):
442 """Return the size of the console as (width, height) 443 444 @rtype: (int, int) 445 """ 446 return self.width, self.height
447
448 - def scroll(self, x, y):
449 """Scroll the contents of the console in the direction of x,y. 450 451 Uncovered areas will be cleared. 452 @type x: int 453 @param x: Distance to scroll along x-axis 454 @type y: int 455 @param y: Distance to scroll along y-axis 456 """ 457 assert isinstance(x, _INTTYPES), "x must be an integer, got %s" % repr(x) 458 assert isinstance(y, _INTTYPES), "y must be an integer, got %s" % repr(x) 459 def getSlide(x, length): 460 """get the parameters needed to scroll the console in the given 461 direction with x 462 returns (x, length, srcx) 463 """ 464 if x > 0: 465 srcx = 0 466 length -= x 467 elif x < 0: 468 srcx = abs(x) 469 x = 0 470 length -= srcx 471 else: 472 srcx = 0 473 return x, length, srcx
474 def getCover(x, length): 475 """return the (x, width) ranges of what is covered and uncovered""" 476 cover = (0, length) # everything covered 477 uncover = None # nothing uncovered 478 if x > 0: # left side uncovered 479 cover = (x, length - x) 480 uncover = (0, x) 481 elif x < 0: # right side uncovered 482 x = abs(x) 483 cover = (0, length - x) 484 uncover = (length - x, x) 485 return cover, uncover 486 487 width, height = self.getSize() 488 if abs(x) >= width or abs(y) >= height: 489 return self.clear() # just clear the console normally 490 491 # get the ranges of the areas that will be uncovered 492 coverX, uncoverX = getCover(x, width) 493 coverY, uncoverY = getCover(y, height) 494 # so at this point we know that coverX and coverY makes a rect that 495 # encases the area that we end up blitting to. uncoverX/Y makes a 496 # rect in the corner of the uncovered area. So we need to combine 497 # the uncoverX/Y with coverY/X to make what's left of the uncovered 498 # area. Explaining it makes it mush easier to do now. 499 500 # But first we need to blit. 501 x, width, srcx = getSlide(x, width) 502 y, height, srcy = getSlide(y, height) 503 self.blit(self, x, y, width, height, srcx, srcy) 504 505 if uncoverX: # clear sides (0x20 is space) 506 self.drawRect(uncoverX[0], coverY[0], uncoverX[1], coverY[1], 0x20) 507 if uncoverY: # clear top/bottom 508 self.drawRect(coverX[0], uncoverY[0], coverX[1], uncoverY[1], 0x20) 509 if uncoverX and uncoverY: # clear corner 510 self.drawRect(uncoverX[0], uncoverY[0], uncoverX[1], uncoverY[1], 0x20) 511
512 - def getChar(self, x, y):
513 """Return the character and colors of a tile as (ch, fg, bg) 514 515 This method runs very slowly as is not recommended to be called 516 frequently. 517 518 @rtype: (int, (r, g, b), (r, g, b)) 519 @returns: Returns a 3-item tuple. The first item is an integer of the 520 character at the position (x, y) the second and third are the 521 foreground and background colors respectfully. 522 """ 523 raise NotImplementedError('Method here only exists for the docstring')
524
525 - def __contains__(self, position):
526 """Use ((x, y) in console) to check if a position is drawable on this console. 527 """ 528 x, y = position 529 return (0 <= x < self.width) and (0 <= y < self.height)
530
531 -class Console(_MetaConsole):
532 """Contains character and color data and can be drawn to. 533 534 The console created by the L{tdl.init} function is the root console and is the 535 console that is rendered to the screen with L{flush}. 536 537 Any console created from the Console class is an off-screen console that 538 can be drawn on before being L{blit} to the root console. 539 """ 540 541 __slots__ = ('_as_parameter_', '_typewriter') 542
543 - def __init__(self, width, height):
544 """Create a new offscreen console. 545 546 @type width: int 547 @param width: Width of the console in tiles 548 @type height: int 549 @param height: Height of the console in tiles 550 """ 551 if not _rootinitialized: 552 raise TDLError('Can not create Console\'s before tdl.init') 553 self._as_parameter_ = _lib.TCOD_console_new(width, height) 554 self.console = self 555 self.width = width 556 self.height = height 557 self._typewriter = None # "typewriter lock", makes sure the colors are set to the typewriter
558 #self.clear() 559 560 @classmethod
561 - def _newConsole(cls, console):
562 """Make a Console instance, from a console ctype""" 563 self = cls.__new__(cls) 564 self._as_parameter_ = console 565 self.console = self 566 self.width = _lib.TCOD_console_get_width(self) 567 self.height = _lib.TCOD_console_get_height(self) 568 self._typewriter = None 569 #self.clear() 570 return self
571
572 - def __del__(self):
573 """ 574 If the main console is garbage collected then the window will be closed as well 575 """ 576 # If this is the root console the window will close when collected 577 try: 578 if isinstance(self._as_parameter_, ctypes.c_void_p): 579 global _rootinitialized, _rootconsole 580 _rootinitialized = False 581 _rootconsole = None 582 _lib.TCOD_console_delete(self) 583 except StandardError: 584 pass # I forget why I put this here but I'm to afraid to delete it
585
586 - def _replace(self, console):
587 """Used internally 588 589 Mostly used just to replace this Console object with the root console 590 If another Console object is used then they are swapped 591 """ 592 if isinstance(console, Console): 593 self._as_parameter_, console._as_parameter_ = \ 594 console._as_parameter_, self._as_parameter_ # swap tcod consoles 595 else: 596 self._as_parameter_ = console 597 self.width = _lib.TCOD_console_get_width(self) 598 self.height = _lib.TCOD_console_get_height(self) 599 return self
600
601 - def _translate(self, x, y):
602 """Convertion x and y to their position on the root Console for this Window 603 604 Because this is a Console instead of a Window we return the paramaters 605 untouched""" 606 return x, y
607
608 - def clear(self, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
609 """Clears the entire Console. 610 611 @type fgcolor: (r, g, b) 612 @param fgcolor: Foreground color. 613 614 Must be a 3-item list with integers that range 0-255. 615 616 Unlike most other operations you can not use None here. 617 @type bgcolor: (r, g, b) 618 @param bgcolor: Background color. See fgcolor. 619 """ 620 assert _verify_colors(fgcolor, bgcolor) 621 assert fgcolor and bgcolor, 'Can not use None with clear' 622 self._typewriter = None 623 _lib.TCOD_console_set_default_background(self, _formatColor(bgcolor)) 624 _lib.TCOD_console_set_default_foreground(self, _formatColor(fgcolor)) 625 _lib.TCOD_console_clear(self)
626
627 - def _setChar(self, x, y, char, fgcolor=None, bgcolor=None, bgblend=1):
628 """ 629 Sets a character. 630 This is called often and is designed to be as fast as possible. 631 632 Because of the need for speed this function will do NO TYPE CHECKING 633 AT ALL, it's up to the drawing functions to use the functions: 634 _formatChar and _formatColor before passing to this.""" 635 # buffer values as ctypes objects 636 console = self._as_parameter_ 637 638 if char is not None and fgcolor is not None and bgcolor is not None: 639 _setcharEX(console, x, y, char, fgcolor, bgcolor) 640 return 641 if char is not None: 642 _setchar(console, x, y, char) 643 if fgcolor is not None: 644 _setfore(console, x, y, fgcolor) 645 if bgcolor is not None: 646 _setback(console, x, y, bgcolor, bgblend)
647
648 - def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1, nullChar=False):
649 """ 650 Try to perform a batch operation otherwise fall back to _setChar. 651 If fgcolor and bgcolor are defined then this is faster but not by very 652 much. 653 654 batch is a iterable of [(x, y), ch] items 655 """ 656 if fgcolor and bgcolor and not nullChar: 657 # buffer values as ctypes objects 658 self._typewriter = None # clear the typewriter as colors will be set 659 console = self._as_parameter_ 660 bgblend = ctypes.c_int(bgblend) 661 662 _lib.TCOD_console_set_default_background(console, bgcolor) 663 _lib.TCOD_console_set_default_foreground(console, fgcolor) 664 _putChar = _lib.TCOD_console_put_char # remove dots and make local 665 for (x, y), char in batch: 666 _putChar(console, x, y, char, bgblend) 667 else: 668 for (x, y), char in batch: 669 self._setChar(x, y, char, fgcolor, bgcolor, bgblend)
670
671 - def getChar(self, x, y):
672 # inherit docstring 673 x, y = self._normalizePoint(x, y) 674 char = _lib.TCOD_console_get_char(self, x, y) 675 bgcolor = _lib.TCOD_console_get_char_background_wrapper(self, x, y) 676 fgcolor = _lib.TCOD_console_get_char_foreground_wrapper(self, x, y) 677 return char, tuple(fgcolor), tuple(bgcolor)
678
679 - def __repr__(self):
680 return "<Console (Width=%i Height=%i)>" % (self.width, self.height)
681
682 683 -class Window(_MetaConsole):
684 """A Window contains a small isolated part of a Console. 685 686 Drawing on the Window draws on the Console. 687 688 Making a Window and setting its width or height to None will extend it to 689 the edge of the console. 690 """ 691 692 __slots__ = ('parent', 'x', 'y') 693
694 - def __init__(self, console, x, y, width, height):
695 """Isolate part of a L{Console} or L{Window} instance. 696 697 @type console: L{Console} or L{Window} 698 @param console: The parent object which can be a L{Console} or another 699 L{Window} instance. 700 701 @type x: int 702 @param x: X coordinate to place the Window. 703 704 This follows the normal rules for indexing so you can use a 705 negative integer to place the Window relative to the bottom 706 right of the parent Console instance. 707 @type y: int 708 @param y: Y coordinate to place the Window. 709 710 See x. 711 712 @type width: int or None 713 @param width: Width of the Window. 714 715 Can be None to extend as far as possible to the 716 bottom right corner of the parent Console or can be a 717 negative number to be sized reltive to the Consoles total 718 size. 719 @type height: int or None 720 @param height: Height of the Window. 721 722 See width. 723 """ 724 assert isinstance(console, (Console, Window)), 'console parameter must be a Console or Window instance, got %s' % repr(console) 725 self.parent = console 726 self.x, self.y, self.width, self.height = console._normalizeRect(x, y, width, height) 727 if isinstance(console, Console): 728 self.console = console 729 else: 730 self.console = self.parent.console
731
732 - def _translate(self, x, y):
733 """Convertion x and y to their position on the root Console""" 734 # we add our position relative to our parent and then call then next parent up 735 return self.parent._translate((x + self.x), (y + self.y))
736
737 - def clear(self, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
738 """Clears the entire Window. 739 740 @type fgcolor: (r, g, b) 741 @param fgcolor: Foreground color. 742 743 Must be a 3-item list with integers that range 0-255. 744 745 Unlike most other operations you can not use None here. 746 @type bgcolor: (r, g, b) 747 @param bgcolor: Background color. See fgcolor. 748 """ 749 assert _verify_colors(fgcolor, bgcolor) 750 assert fgcolor and bgcolor, 'Can not use None with clear' 751 self.draw_rect(0, 0, None, None, 0x20, fgcolor, bgcolor)
752
753 - def _setChar(self, x, y, char=None, fgcolor=None, bgcolor=None, bgblend=1):
754 self.parent._setChar((x + self.x), (y + self.y), char, fgcolor, bgcolor, bgblend)
755
756 - def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1):
757 myX = self.x # remove dots for speed up 758 myY = self.y 759 self.parent._setCharBatch((((x + myX, y + myY), ch) for ((x, y), ch) in batch), 760 fgcolor, bgcolor, bgblend)
761 762
763 - def drawChar(self, x, y, char, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
764 # inherit docstring 765 x, y = self._normalizePoint(x, y) 766 self.parent.drawChar(x + self.x, y + self.y, char, fgcolor, bgcolor)
767
768 - def drawRect(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
769 # inherit docstring 770 x, y, width, height = self._normalizeRect(x, y, width, height) 771 self.parent.drawRect(x + self.x, y + self.y, width, height, string, fgcolor, bgcolor)
772
773 - def drawFrame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
774 # inherit docstring 775 x, y, width, height = self._normalizeRect(x, y, width, height) 776 self.parent.drawFrame(x + self.x, y + self.y, width, height, string, fgcolor, bgcolor)
777
778 - def getChar(self, x, y):
779 # inherit docstring 780 x, y = self._normalizePoint(x, y) 781 return self.console.getChar(self._translate(x, y))
782
783 - def __repr__(self):
784 return "<Window(X=%i Y=%i Width=%i Height=%i)>" % (self.x, self.y, 785 self.width, 786 self.height)
787
788 789 -class Typewriter(object):
790 """Converts a console into a scrolling text log that respects special 791 characters. 792 793 This class works best on a L{Window} or off-screen L{Console} instance. 794 In a L{Window} for example the scrolling text is limited to the L{Window}'s 795 isolated area. 796 """ 797
798 - def __init__(self, console):
799 """Add a virtual cursor to a L{Console} or L{Window} instance. 800 801 @type console: L{Console} or L{Window} 802 """ 803 assert isinstance(console, (Console, Window)), 'console parameter must be a Console or Window instance, got %s' % repr(console) 804 self.parent = console 805 if isinstance(self.parent, Console): 806 self.console = self.parent 807 else: 808 self.console = self.parent.console 809 self.cursor = (0, 0) # cursor position 810 self.scrollMode = 'scroll' #can be 'scroll', 'error' 811 self.fgcolor = _formatColor((255, 255, 255)) 812 self.bgcolor = _formatColor((0, 0, 0)) 813 self._bgblend = 1 # SET
814
815 - def _normalize(self, x, y):
816 """return the normalized the cursor position.""" 817 width, height = self.parent.getSize() 818 while x >= width: 819 x -= width 820 y += 1 821 while y >= height: 822 if self.scrollMode == 'scroll': 823 y -= 1 824 self.parent.scroll(0, -1) 825 elif self.scrollMode == 'error': 826 # reset the cursor on error 827 self.cursor = (0, 0) 828 raise TDLError('Typewriter cursor has reached the end of the console') 829 return (x, y)
830
831 - def getCursor(self):
832 """Return the virtual cursor position. 833 834 @rtype: (int, int) 835 @return: Returns (x, y) a 2-integer tuple containing where the next 836 L{addChar} or L{addStr} will start at. 837 838 This can be changed with the L{move} method.""" 839 x, y = self.cursor 840 width, height = self.parent.getSize() 841 while x >= width: 842 x -= width 843 y += 1 844 if y >= height and self.scrollMode == 'scroll': 845 y = height - 1 846 return x, y
847
848 - def move(self, x, y):
849 """Move the virtual cursor. 850 851 @type x: int 852 @param x: X position to place the cursor. 853 @type y: int 854 @param y: Y position to place the cursor. 855 """ 856 self.cursor = self.parent._normalizePoint(x, y)
857
858 - def setFG(self, color):
859 """Change the foreground color""" 860 assert _iscolor(color) 861 self.fgcolor = _formatColor(color) 862 if self.console._typewriter is self: 863 _lib.TCOD_console_set_default_foreground(self.console, self.fgcolor)
864
865 - def setBG(self, color):
866 """Change the background color""" 867 assert _iscolor(color) 868 self.bgcolor = _formatColor(color) 869 if self.console._typewriter is self: 870 _lib.TCOD_console_set_default_background(self.console, self.bgcolor)
871
872 - def _updateConsole(self):
873 """Make sure the colors on a console match the Typewriter instance""" 874 if self.console._typewriter is not self: 875 self.console._typewriter = self 876 877 _lib.TCOD_console_set_default_background(self.console, self.bgcolor) 878 _lib.TCOD_console_set_default_foreground(self.console, self.fgcolor)
879 880
881 - def addChar(self, char):
882 """Draw a single character at the cursor.""" 883 if char == '\n': # line break 884 x = 0 885 y += 1 886 return 887 if char == '\r': # return 888 x = 0 889 return 890 x, y = self._normalize(*self.cursor) 891 self.cursor = [x + 1, y] # advance cursor on next draw 892 self._updateConsole() 893 x, y = self.parent._translate(x, y) 894 _lib.TCOD_console_put_char(self.console._as_parameter_, x, y, _formatChar(char), self._bgblend)
895 896
897 - def addStr(self, string):
898 """Write a string at the cursor. Handles special characters such as newlines. 899 900 @type string: string 901 @param string: 902 """ 903 x, y = self.cursor 904 for char in string: 905 if char == '\n': # line break 906 x = 0 907 y += 1 908 continue 909 if char == '\r': # return 910 x = 0 911 continue 912 x, y = self._normalize(x, y) 913 self.parent.drawChar(x, y, char, self.fgcolor, self.bgcolor) 914 x += 1 915 self.cursor = (x, y)
916
917 - def write(self, string):
918 """This method mimics basic file-like behaviour. 919 920 Because of this method you can replace sys.stdout or sys.stderr with 921 a L{Typewriter} instance. 922 923 @type string: string 924 """ 925 # some 'basic' line buffer stuff. 926 # there must be an easier way to do this. The textwrap module didn't 927 # help much. 928 x, y = self._normalize(*self.cursor) 929 width, height = self.parent.getSize() 930 wrapper = textwrap.TextWrapper(initial_indent=(' '*x), width=width) 931 writeLines = [] 932 for line in string.split('\n'): 933 if line: 934 writeLines += wrapper.wrap(line) 935 wrapper.initial_indent = '' 936 else: 937 writeLines.append([]) 938 939 for line in writeLines: 940 x, y = self._normalize(x, y) 941 self.parent.drawStr(x, y, line[x:], self.fgcolor, self.bgcolor) 942 y += 1 943 x = 0 944 y -= 1 945 self.cursor = (x, y)
946
947 948 -def init(width, height, title='python-tdl', fullscreen=False, renderer='OPENGL'):
949 """Start the main console with the given width and height and return the 950 root console. 951 952 Call the consoles drawing functions. Then remember to use L{tdl.flush} to 953 make what's drawn visible on the console. 954 955 @type width: int 956 @param width: width of the root console (in tiles) 957 958 @type height: int 959 @param height: height of the root console (in tiles) 960 961 @type title: string 962 @param title: Text to display as the window title. 963 964 @type fullscreen: boolean 965 @param fullscreen: Can be set to True to start in fullscreen mode. 966 967 @type renderer: string 968 @param renderer: Can be one of 'GLSL', 'OPENGL', or 'SDL'. 969 970 Due to way Python works you're unlikely to see much of an 971 improvement by using 'GLSL' or 'OPENGL' as most of the 972 time Python is slow interacting with the console and the 973 rendering itself is pretty fast even on 'SDL'. 974 975 This should be left at default or switched to 'SDL' for 976 better reliability and an instantaneous start up time. 977 978 @rtype: L{Console} 979 @return: The root console. Only what is drawn on the root console is 980 what's visible after a call to L{tdl.flush}. 981 After the root console is garbage collected, the window made by 982 this function will close. 983 """ 984 RENDERERS = {'GLSL': 0, 'OPENGL': 1, 'SDL': 2} 985 global _rootinitialized, _rootconsole 986 if not _fontinitialized: # set the default font to the one that comes with tdl 987 setFont(_unpackfile('terminal.png'), 16, 16, colomn=True) 988 989 if renderer.upper() not in RENDERERS: 990 raise TDLError('No such render type "%s", expected one of "%s"' % (renderer, '", "'.join(RENDERERS))) 991 renderer = RENDERERS[renderer.upper()] 992 993 # If a console already exists then make a clone to replace it 994 if _rootconsole is not None: 995 oldroot = _rootconsole() 996 rootreplacement = Console(oldroot.width, oldroot.height) 997 rootreplacement.blit(oldroot) 998 oldroot._replace(rootreplacement) 999 del rootreplacement 1000 1001 _lib.TCOD_console_init_root(width, height, _encodeString(title), fullscreen, renderer) 1002 1003 #event.get() # flush the libtcod event queue to fix some issues 1004 # issues may be fixed already 1005 1006 event._eventsflushed = False 1007 _rootinitialized = True 1008 rootconsole = Console._newConsole(ctypes.c_void_p()) 1009 _rootconsole = weakref.ref(rootconsole) 1010 1011 return rootconsole
1012
1013 -def flush():
1014 """Make all changes visible and update the screen. 1015 1016 Remember to call this function after drawing operations. 1017 Calls to flush will enfore the frame rate limit set by L{tdl.setFPS}. 1018 1019 This function can only be called after L{tdl.init} 1020 """ 1021 if not _rootinitialized: 1022 raise TDLError('Cannot flush without first initializing with tdl.init') 1023 1024 _lib.TCOD_console_flush()
1025
1026 -def setFont(path, tileWidth, tileHeight, colomn=False, 1027 greyscale=False, altLayout=False):
1028 """Changes the font to be used for this session. 1029 This should be called before L{tdl.init} 1030 1031 While it's possible you can change the font mid program it can sometimes 1032 break in rare circumstances. So use caution when doing this. 1033 1034 @type path: string 1035 @param path: Must be a string filepath where a bmp or png file is found. 1036 1037 @type tileWidth: int 1038 @param tileWidth: The width of an individual tile. 1039 1040 @type tileHeight: int 1041 @param tileHeight: The height of an individual tile. 1042 1043 @type colomn: boolean 1044 @param colomn: Defines if the characer order goes along the rows or 1045 colomns. 1046 It should be True if the charater codes 0-15 are in the 1047 first column. And should be False if the characters 0-15 1048 are in the first row. 1049 1050 @type greyscale: boolean 1051 @param greyscale: Creates an anti-aliased font from a greyscale bitmap. 1052 Otherwise it uses the alpha channel for anti-aliasing. 1053 1054 @type altLayout: boolean 1055 @param altLayout: An alternative layout with space in the upper left 1056 corner. The colomn parameter is ignored if this is 1057 True, find examples of this layout in the font/ 1058 directory included with the python-tdl source. 1059 1060 @raise TDLError: Will be raised if no file is found at path. 1061 1062 @note: A png file that's been optimized can fail to load correctly on 1063 MAC OS X creating a garbled mess when rendering. 1064 Don't use a program like optipng or just use bmp files instead if 1065 you want your program to work on macs. 1066 """ 1067 # put up some constants that are only used here 1068 FONT_LAYOUT_ASCII_INCOL = 1 1069 FONT_LAYOUT_ASCII_INROW = 2 1070 FONT_TYPE_GREYSCALE = 4 1071 FONT_LAYOUT_TCOD = 8 1072 global _fontinitialized 1073 _fontinitialized = True 1074 flags = 0 1075 if altLayout: 1076 flags |= FONT_LAYOUT_TCOD 1077 elif colomn: 1078 flags |= FONT_LAYOUT_ASCII_INCOL 1079 else: 1080 flags |= FONT_LAYOUT_ASCII_INROW 1081 if greyscale: 1082 flags |= FONT_TYPE_GREYSCALE 1083 if not os.path.exists(path): 1084 raise TDLError('no file exists at: "%s"' % path) 1085 _lib.TCOD_console_set_custom_font(_encodeString(path), flags, tileWidth, tileHeight)
1086
1087 -def getFullscreen():
1088 """Returns True if program is fullscreen. 1089 1090 @rtype: boolean 1091 @return: Returns True if the window is in fullscreen mode. 1092 Otherwise returns False. 1093 """ 1094 if not _rootinitialized: 1095 raise TDLError('Initialize first with tdl.init') 1096 return _lib.TCOD_console_is_fullscreen()
1097
1098 -def setFullscreen(fullscreen):
1099 """Changes the fullscreen state. 1100 1101 @type fullscreen: boolean 1102 """ 1103 if not _rootinitialized: 1104 raise TDLError('Initialize first with tdl.init') 1105 _lib.TCOD_console_set_fullscreen(fullscreen)
1106
1107 -def setTitle(title):
1108 """Change the window title. 1109 1110 @type title: string 1111 """ 1112 if not _rootinitialized: 1113 raise TDLError('Not initilized. Set title with tdl.init') 1114 _lib.TCOD_console_set_window_title(_encodeString(title))
1115
1116 -def screenshot(path=None):
1117 """Capture the screen and save it as a png file 1118 1119 @type path: string 1120 @param path: The filepath to save the screenshot. 1121 1122 If path is None then the image will be placed in the current 1123 folder with the names: 1124 screenshot001.png, screenshot002.png, ... 1125 """ 1126 if not _rootinitialized: 1127 raise TDLError('Initialize first with tdl.init') 1128 if isinstance(fileobj, str): 1129 _lib.TCOD_sys_save_screenshot(_encodeString(fileobj)) 1130 elif isinstance(fileobj, file): # save to temp file and copy to file-like obj 1131 tmpname = os.tempnam() 1132 _lib.TCOD_sys_save_screenshot(_encodeString(tmpname)) 1133 with tmpname as tmpfile: 1134 fileobj.write(tmpfile.read()) 1135 os.remove(tmpname) 1136 elif fileobj is None: # save to screenshot001.png, screenshot002.png, ... 1137 filelist = os.listdir('.') 1138 n = 1 1139 filename = 'screenshot%.3i.png' % n 1140 while filename in filelist: 1141 n += 1 1142 filename = 'screenshot%.4i.png' % n 1143 _lib.TCOD_sys_save_screenshot(_encodeString(filename)) 1144 else: 1145 raise TypeError('fileobj is an invalid type: %s' % type(fileobj))
1146
1147 -def setFPS(frameRate):
1148 """Set the maximum frame rate. 1149 1150 @type frameRate: int 1151 @param frameRate: Further calls to L{tdl.flush} will limit the speed of 1152 the program to run at <frameRate> frames per second. Can 1153 also be set to 0 to run without a limit. 1154 1155 Defaults to None. 1156 """ 1157 if frameRate is None: 1158 frameRate = 0 1159 assert isinstance(frameRate, _INTTYPES), 'frameRate must be an integer or None, got: %s' % repr(frameRate) 1160 _lib.TCOD_sys_set_fps(frameRate)
1161
1162 -def getFPS():
1163 """Return the current frames per second of the running program set by 1164 L{setFPS} 1165 1166 @rtype: int 1167 @return: Returns the frameRate set by setFPS. 1168 If set to no limit, this will return 0. 1169 """ 1170 return _lib.TCOD_sys_get_fps()
1171
1172 -def forceResolution(width, height):
1173 """Change the fullscreen resoulution 1174 1175 @type width: int 1176 @type height: int 1177 """ 1178 _lib.TCOD_sys_force_fullscreen_resolution(width, height)
1179 1180 __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in ['sys', 'os', 'ctypes', 'array', 'weakref', 'itertools', 'textwrap']] 1181 __all__ += ['_MetaConsole'] # keep this object public to show the documentation in epydoc 1182