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