| Home | Trees | Indices | Help |
|
|---|
|
|
1 """
2 The documentation for python-untdl. A fork of tdl (A Pythonic port of
3 U{libtcod<http://doryen.eptalys.net/libtcod/>}).
4
5
6 Getting Started
7 ===============
8 Once the library is imported you can load the font you want to use with
9 L{untdl.setFont}.
10 This is optional and when skipped will use a decent default font.
11
12 After that you call L{untdl.init} to set the size of the window and get the
13 root console in return.
14 This console is the canvas to what will appear on the screen.
15
16 Indexing Consoles
17 =================
18 For most methods taking a position you can use Python-style negative
19 indexes to refer to the opposite side of a console with (-1, -1)
20 starting at the bottom right.
21 You can also check if a point is part of a console using containment
22 logic i.e. ((x, y) in console).
23
24 You may also iterate over a console using a for statement. This returns
25 every x,y coordinate available to draw on but it will be extremely slow
26 to actually operate on every coordinate individually.
27 Try to minimize draws by using an offscreen L{Console}, only drawing
28 what needs to be updated, and using L{Console.blit}.
29
30 Drawing
31 =======
32 Once you have the root console from L{untdl.init} you can start drawing on
33 it using a method such as L{Console.drawChar}.
34 When using this method you can have the char parameter be an integer or a
35 single character string.
36
37 The fgcolor and bgcolor parameters expect a three item list
38 [red, green, blue] with integers in the 0-255 range with [0, 0, 0] being
39 black and [255, 255, 255] being white.
40 Or instead you can use None in the place of any of the three parameters
41 to tell the library to not overwrite colors.
42 After the drawing functions are called a call to L{untdl.flush} will update
43 the screen.
44 """
45
46 import sys
47 import os
48
49 import ctypes
50 import weakref
51 import array
52 import itertools
53 import textwrap
54 import struct
55 import re
56 import warnings
57
58 from . import event, map, noise
59 # noinspection PyProtectedMember
60 from .__tcod import _lib, _Color, _unpackfile
61
62 _IS_PYTHON3 = (sys.version_info[0] == 3)
63
64 if _IS_PYTHON3: # some type lists to use with isinstance
65 _INTTYPES = (int,)
66 _NUMTYPES = (int, float)
67 _STRTYPES = (str, bytes)
68 else:
69 _INTTYPES = (int, long)
70 _NUMTYPES = (int, long, float)
71 _STRTYPES = (str,)
75 """changes string into bytes if running in python 3, for sending to ctypes"""
76 if _IS_PYTHON3 and isinstance(string, str):
77 return string.encode()
78 return string
79
86 """Prepares a single character for passing to ctypes calls, needs to return
87 an integer but can also pass None which will keep the current character
88 instead of overwriting it.
89
90 This is called often and needs to be optimized whenever possible.
91 """
92 if char is None:
93 return None
94 # if isinstance(char, _INTTYPES):
95 # return char
96 if isinstance(char, _STRTYPES) and len(char) == 1:
97 return ord(char)
98 return int(char) # conversion faster than type check
99 # raise TypeError('Expected char parameter to be a single character string, number, or None, got: %s' % repr(char))
100
101
102 _font_initialized = False
103 _root_initialized = False
104 _rootConsoleRef = None
105 # remove dots from common functions
106 _set_char = _lib.TCOD_console_set_char
107 _set_fore = _lib.TCOD_console_set_char_foreground
108 _set_back = _lib.TCOD_console_set_char_background
109 _set_char_ex = _lib.TCOD_console_put_char_ex
113 """Used internally.
114 Raise an assertion error if the parameters can not be converted into colors.
115 """
116 for color in colors:
117 assert _is_color(color), 'a color must be a 3 item tuple, web format, or None, received %s' % repr(color)
118 return True
119
122 """Used internally.
123 A debug function to see if an object can be used as a TCOD color struct.
124 None counts as a parameter to keep the current colors instead.
125
126 This function is often part of an inner-loop and can slow a program down.
127 It has been made to work with assert and can be skipped with the -O flag.
128 Still it's called often and must be optimized.
129 """
130 if color is None:
131 return True
132 if isinstance(color, (tuple, list, _Color)):
133 return len(color) == 3
134 if isinstance(color, _INTTYPES):
135 return True
136 return False
137
138 # # not using this for now
139 # class Color(object):
140 #
141 # def __init__(self, r, g, b):
142 # self._color = (r, g, b)
143 # self._ctype = None
144 #
145 # def _getCType(self):
146 # if not self._ctype:
147 # self._ctype = _Color(*self._color)
148 # return self._ctype
149 #
150 # def __len__(self):
151 # return 3
152
153 # Format the color to ctypes, will preserve None and False
154 _formatColor = _Color.new
158 """Try to get the width and height of a bmp of png image file"""
159 image_file = open(filename, 'rb')
160 if image_file.read(8) == b'\x89PNG\r\n\x1a\n': # PNG
161 while 1:
162 length, = struct.unpack('>i', image_file.read(4))
163 chunk_id = image_file.read(4)
164 if chunk_id == '': # EOF
165 return None
166 if chunk_id == b'IHDR':
167 # return width, height
168 return struct.unpack('>ii', image_file.read(8))
169 image_file.seek(4 + length, 1)
170 image_file.seek(0)
171 if image_file.read(8) == b'BM': # Bitmap
172 image_file.seek(18, 0) # skip to size data
173 # return width, height
174 return struct.unpack('<ii', image_file.read(8))
175 # return None on error, unknown file
182
185 """
186 Contains methods shared by both the L{Console} and L{Window} classes.
187 """
188 __slots__ = ('width', 'height', 'console', '_cursor', '_fgcolor',
189 '_bgcolor', '_bgblend', '_colorLock', '__weakref__', '__dict__')
190
192 self._cursor = (0, 0)
193 self._scrollMode = 'error'
194 self._fgcolor = _formatColor((255, 255, 255))
195 self._bgcolor = _formatColor((0, 0, 0))
196 self._bgblend = 1 # SET
197 self._colorLock = None # which object sets the ctype color options
198
200 """Check if a point is in bounds and make minor adjustments.
201
202 Respects Pythons negative indexes. -1 starts at the bottom right.
203 Replaces the _drawable function
204 """
205 #assert isinstance(x, _INTTYPES), 'x must be an integer, got %s' % repr(x)
206 #assert isinstance(y, _INTTYPES), 'y must be an integer, got %s' % repr(y)
207 # force int, always faster than type checking
208 x = int(x)
209 y = int(y)
210
211 assert (-self.width <= x < self.width) and (-self.height <= y < self.height), \
212 ('(%i, %i) is an invalid position on %s' % (x, y, self))
213
214 # handle negative indexes
215 if x < 0:
216 x += self.width
217 if y < 0:
218 y += self.height
219 return x, y
220
222 """Check if the rectangle is in bounds and make minor adjustments.
223 raise AssertionError's for any problems
224 """
225 x, y = self._normalize_point(x, y) # inherit _normalize_point logic
226
227 assert width is None or isinstance(width, _INTTYPES), 'width must be an integer or None, got %s' % repr(width)
228 assert height is None or isinstance(height, _INTTYPES), 'height must be an integer or None, got %s' % repr(
229 height)
230
231 # if width or height are None then extend them to the edge
232 if width is None:
233 width = self.width - x
234 elif width < 0: # handle negative numbers
235 width += self.width
236 width = max(0, width) # a 'too big' negative is clamped zero
237 if height is None:
238 height = self.height - y
239 height = max(0, height)
240 elif height < 0:
241 height += self.height
242
243 # reduce rect size to bounds
244 width = min(width, self.width - x)
245 height = min(height, self.height - y)
246
247 return x, y, width, height
248
250 """return the normalized the cursor position."""
251 width, height = self.get_size()
252 assert width != 0 and height != 0, 'can not print on a console with a width or height of zero'
253 while x >= width:
254 x -= width
255 y += 1
256 while y >= height:
257 if self._scrollMode == 'scroll':
258 y -= 1
259 self.scroll(0, -1)
260 elif self._scrollMode == 'error':
261 # reset the cursor on error
262 self._cursor = (0, 0)
263 raise TDLError('Cursor has reached the end of the console')
264 return x, y
265
267 """Make sure the color options on the root console match ths instance"""
268 if self.console._lock_colors is not self or force_update:
269 self.console._lock_colors = self
270 _lib.TCOD_console_set_default_background(self.console, self.bgcolor)
271 _lib.TCOD_console_set_default_foreground(self.console, self.fgcolor)
272 #
273
275 """Configure how this console will react to the cursor writing past the
276 end if the console.
277
278 This is for methods that use the virtual cursor, such as L{print_str}.
279
280 @type mode: string
281 @param mode: Possible settings are:
282
283 - 'error' - A TDLError will be raised once the cursor
284 reaches the end of the console. Everything up until
285 the error will still be drawn.
286
287 This is the default setting.
288
289 - 'scroll' - The console will scroll up as stuff is
290 written to the end.
291
292 You can restrict the region with L{untdl.Window} when
293 doing this.
294 """
295 modes = ['error', 'scroll']
296 if mode.lower() not in modes:
297 raise TDLError('mode must be one of %s, got %s' % (modes, repr(mode)))
298 self._scrollMode = mode.lower()
299
301 """Sets the colors to be used with the L{print_str} function.
302
303 Values of None will only leave the current values unchanged.
304 """
305 if self.console._lock_colors is self:
306 self.console._lock_colors = None
307 if fg is not None:
308 self._fgcolor = _formatColor(fg)
309 if bg is not None:
310 self._bgcolor = _formatColor(bg)
311
313 """Print a string at the virtual cursor.
314
315 Handles special characters such as '\\n' and '\\r'.
316 Printing past the bottom of the console will scroll everything upwards.
317
318 Colors can be set with L{set_colors} and the virtual cursor can be moved
319 with L{move}.
320
321 @type string: string
322 @param string:
323 """
324 x, y = self._cursor
325 for char in string:
326 if char == '\n': # line break
327 x = 0
328 y += 1
329 continue
330 if char == '\r': # return
331 x = 0
332 continue
333 x, y = self._normalize_cursor(x, y)
334 self.draw_char(x, y, char, self._fgcolor, self._bgcolor)
335 x += 1
336 self._cursor = (x, y)
337
339 """This method mimics basic file-like behaviour.
340
341 Because of this method you can replace sys.stdout or sys.stderr with
342 a L{Typewriter} instance.
343
344 This is a convoluted process and behaviour seen now can be excepted to
345 change on later versions.
346
347 @type string: string
348 """
349 # some 'basic' line buffer stuff.
350 # there must be an easier way to do this. The textwrap module didn't
351 # help much.
352 x, y = self._normalize_cursor(*self._cursor)
353 width, height = self.get_size()
354 wrapper = textwrap.TextWrapper(initial_indent=(' ' * x), width=width)
355 write_lines = []
356 for line in string.split('\n'):
357 if line:
358 write_lines += wrapper.wrap(line)
359 wrapper.initial_indent = ''
360 else:
361 write_lines.append([])
362
363 for line in write_lines:
364 x, y = self._normalize_cursor(x, y)
365 self.draw_str(x, y, line[x:], self._fgcolor, self._bgcolor)
366 y += 1
367 x = 0
368 y -= 1
369 self._cursor = (x, y)
370
372 """Draws a single character.
373
374 @type x: int
375 @param x: X coordinate to draw at.
376 @type y: int
377 @param y: Y coordinate to draw at.
378
379 @type char: int, string, or None
380 @param char: Should be an integer, single character string, or None.
381
382 You can set the char parameter as None if you only want to change
383 the colors of the tile.
384
385 @type fgcolor: (r, g, b) or None
386 @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
387 integers ranging 0-255 or None.
388
389 None will keep the current color at this position unchanged.
390 @type bgcolor: (r, g, b) or None
391 @param bgcolor: Background color. See fgcolor
392
393 @raise AssertionError: Having x or y values that can't be placed inside
394 of the console will raise an AssertionError.
395 You can use always use ((x, y) in console) to
396 check if a tile is drawable.
397 """
398
399 assert _verify_colors(fgcolor, bgcolor)
400 x, y = self._normalize_point(x, y)
401 x, y = ctypes.c_int(x), ctypes.c_int(y)
402 self._setChar(x, y, _format_char(char),
403 _formatColor(fgcolor), _formatColor(bgcolor))
404
406 """Draws a string starting at x and y. Optionally colored.
407
408 A string that goes past the right side will wrap around. A string
409 wrapping to below the console will raise a L{TDLError} but will still be
410 written out. This means you can safely ignore the errors with a
411 try... except block if you're fine with partially written strings.
412
413 \\r and \\n are drawn on the console as normal character tiles. No
414 special encoding is done and any string will translate to the character
415 table as is.
416
417 For a string drawing operation that respects special characters see the
418 L{Typewriter} class.
419
420 @type x: int
421 @param x: X coordinate to draw at.
422 @type y: int
423 @param y: Y coordinate to draw at.
424
425 @type string: string or iterable
426 @param string: Can be a string or an iterable of numbers.
427
428 Special characters are ignored and rendered as any other
429 character.
430
431 @type fgcolor: (r, g, b) or None
432 @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
433 integers ranging 0-255 or None.
434
435 None will keep the current color at this position unchanged.
436 @type bgcolor: (r, g, b) or None
437 @param bgcolor: Background color. See fgcolor
438
439 @raise AssertionError: Having x or y values that can't be placed inside
440 of the console will raise an AssertionError.
441
442 You can use always use ((x, y) in console) to
443 check if a tile is drawable.
444 """
445
446 x, y = self._normalize_point(x, y)
447 assert _verify_colors(fgcolor, bgcolor)
448 fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
449 width, height = self.get_size()
450 #batch = [] # prepare a batch operation
451
452 # noinspection PyShadowingNames
453 def _draw_str_gen(x=x, y=y, string=string, width=width, height=height):
454 """Generator for draw_str
455
456 Iterates over ((x, y), ch) data for _set_char_batch, raising an
457 error if the end of the console is reached.
458 """
459 for char in string:
460 if y == height:
461 raise TDLError('End of console reached.')
462 #batch.append(((x, y), _format_char(char))) # ((x, y), ch)
463 yield ((x, y), _format_char(char))
464 x += 1 # advance cursor
465 if x == width: # line break
466 x = 0
467 y += 1
468
469 self._setCharBatch(_draw_str_gen(), fgcolor, bgcolor)
470
472 """Draws a rectangle starting from x and y and extending to width and height.
473
474 If width or height are None then it will extend to the edge of the console.
475
476 @type x: int
477 @param x: x coordinate to draw at.
478 @type y: int
479 @param y: y coordinate to draw at.
480
481 @type width: int or None
482 @param width: Width of the rectangle.
483
484 Can be None to extend to the bottom right of the
485 console or can be a negative number to be sized relative
486 to the total size of the console.
487 @type height: int or None
488 @param height: Height of the rectangle. See width.
489
490 @type string: int, string, or None
491 @param string: Should be an integer, single character string, or None.
492
493 You can set the char parameter as None if you only want
494 to change the colors of an area.
495
496 @type fgcolor: (r, g, b) or None
497 @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
498 integers ranging 0-255 or None.
499
500 None will keep the current color at this position unchanged.
501 @type bgcolor: (r, g, b) or None
502 @param bgcolor: Background color. See fgcolor
503
504 @raise AssertionError: Having x or y values that can't be placed inside
505 of the console will raise an AssertionError.
506
507 You can use always use ((x, y) in console) to
508 check if a tile is drawable.
509 """
510 x, y, width, height = self._normalize_rect(x, y, width, height)
511 assert _verify_colors(fgcolor, bgcolor)
512 fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
513 char = _format_char(string)
514 # use itertools to make an x,y grid
515 # using ctypes here reduces type converstions later
516 grid = itertools.product((ctypes.c_int(x) for x in range(x, x + width)),
517 (ctypes.c_int(y) for y in range(y, y + height)))
518 # zip the single character in a batch variable
519 batch = zip(grid, itertools.repeat(char, width * height))
520 self._setCharBatch(batch, fgcolor, bgcolor, nullChar=(char is None))
521
522 - def draw_frame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
523 """Similar to L{draw_rect} but only draws the outline of the rectangle.
524
525 @type x: int
526 @param x: x coordinate to draw at.
527 @type y: int
528 @param y: y coordinate to draw at.
529
530 @type width: int or None
531 @param width: Width of the rectangle.
532
533 Can be None to extend to the bottom right of the
534 console or can be a negative number to be sized relative
535 to the total size of the console.
536 @type height: int or None
537 @param height: Height of the rectangle. See width.
538
539 @type string: int, string, or None
540 @param string: Should be an integer, single character string, or None.
541
542 You can set the char parameter as None if you only want
543 to change the colors of an area.
544
545 @type fgcolor: (r, g, b) or None
546 @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
547 integers ranging 0-255 or None.
548
549 None will keep the current color at this position unchanged.
550 @type bgcolor: (r, g, b) or None
551 @param bgcolor: Background color. See fgcolor
552
553 @raise AssertionError: Having x or y values that can't be placed inside
554 of the console will raise an AssertionError.
555
556 You can use always use ((x, y) in console) to
557 check if a tile is drawable.
558 """
559 x, y, width, height = self._normalize_rect(x, y, width, height)
560 assert _verify_colors(fgcolor, bgcolor)
561 fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
562 char = _format_char(string)
563 if width == 1 or height == 1: # it's just a single width line here
564 return self.draw_rect(x, y, width, height, char, fgcolor, bgcolor)
565
566 # draw sides of frame with draw_rect
567 self.draw_rect(x, y, 1, height, char, fgcolor, bgcolor)
568 self.draw_rect(x, y, width, 1, char, fgcolor, bgcolor)
569 self.draw_rect(x + width - 1, y, 1, height, char, fgcolor, bgcolor)
570 self.draw_rect(x, y + height - 1, width, 1, char, fgcolor, bgcolor)
571
572 # noinspection PyProtectedMember
574 """Blit another console or Window onto the current console.
575
576 By default it blits the entire source to the top left corner.
577
578 @type source: L{Console} or L{Window}
579 @param source: Source window can be a L{Console} or L{Window} instance.
580 It can even blit to itself without any problems.
581
582 @type x: int
583 @param x: X coordinate to blit to.
584 @type y: int
585 @param y: Y coordinate to blit to.
586
587 @type width: int or None
588 @param width: Width of the rectangle.
589
590 Can be None to extend as far as possible to the
591 bottom right corner of the blit area or can be a negative
592 number to be sized relative to the total size of the
593 B{destination} console.
594 @type height: int or None
595 @param height: Height of the rectangle. See width.
596
597 @type src_x: int
598 @param src_x: The source consoles x coordinate to blit from.
599 @type src_y: int
600 @param src_y: The source consoles y coordinate to blit from.
601 """
602 # hard code alpha settings for now
603 fg_alpha = 1.0
604 bg_alpha = 1.0
605
606 assert isinstance(source, (Console, Window)), "source muse be a Window or Console instance"
607
608 # handle negative indexes and rects
609 # negative width and height will be set relative to the destination
610 # and will also be clamped to the smallest Console
611 x, y, width, height = self._normalize_rect(x, y, width, height)
612 src_x, src_y, width, height = source._normalize_rect(src_x, src_y, width, height)
613
614 # translate source and self if any of them are Window instances
615 src_x, src_y = source._translate(src_x, src_y)
616 source = source.console
617 x, y = self._translate(x, y)
618 # noinspection PyMethodFirstArgAssignment
619 self = self.console
620
621 if self == source:
622 # if we are the same console then we need a third console to hold
623 # onto the data, otherwise it tries to copy into itself and
624 # starts destroying everything
625 tmp = Console(width, height)
626 _lib.TCOD_console_blit(source, src_x, src_y, width, height, tmp, 0, 0, fg_alpha, bg_alpha)
627 _lib.TCOD_console_blit(tmp, 0, 0, width, height, self, x, y, fg_alpha, bg_alpha)
628 else:
629 _lib.TCOD_console_blit(source, src_x, src_y, width, height, self, x, y, fg_alpha, bg_alpha)
630
632 """Return the virtual cursor position.
633
634 @rtype: (x, y)
635 @return: Returns (x, y) a 2-integer tuple containing where the next
636 L{addChar} or L{addStr} will start at.
637
638 This can be changed with the L{move} method."""
639 x, y = self._cursor
640 width, height = self.parent.get_size()
641 while x >= width:
642 x -= width
643 y += 1
644 if y >= height and self.scrollMode == 'scroll':
645 y = height - 1
646 return x, y
647
649 """Return the size of the console as (width, height)
650
651 @rtype: (width, height)
652 """
653 return self.width, self.height
654
656 """Return an iterator with every possible (x, y) value for this console.
657
658 It goes without saying that working on the console this way is a
659 slow process, especially for Python, and should be minimized.
660 @rtype: iter((x, y), ...)
661 """
662 return itertools.product(range(self.width), range(self.height))
663
665 """Move the virtual cursor.
666
667 @type x: int
668 @param x: X position to place the cursor.
669 @type y: int
670 @param y: Y position to place the cursor.
671 """
672 self._cursor = self._normalize_point(x, y)
673
674 # noinspection PyTypeChecker
676 """Scroll the contents of the console in the direction of x,y.
677
678 Uncovered areas will be cleared.
679 Does not move the virtual cursor.
680 @type x: int
681 @param x: Distance to scroll along x-axis
682 @type y: int
683 @param y: Distance to scroll along y-axis
684 @rtype: iter((x, y), ...)
685 @return: Iterates over the (x, y) of any tile uncovered after scrolling.
686 """
687 assert isinstance(x, _INTTYPES), "x must be an integer, got %s" % repr(x)
688 assert isinstance(y, _INTTYPES), "y must be an integer, got %s" % repr(x)
689
690 # noinspection PyShadowingNames
691 def get_slide(x, length):
692 """get the parameters needed to scroll the console in the given
693 direction with x
694 returns (x, length, src_x)
695 """
696 if x > 0:
697 src_x = 0
698 length -= x
699 elif x < 0:
700 src_x = abs(x)
701 x = 0
702 length -= src_x
703 else:
704 src_x = 0
705 return x, length, src_x
706
707 # noinspection PyShadowingNames
708 def get_cover(x, length):
709 """return the (x, width) ranges of what is covered and uncovered"""
710 cover = (0, length) # everything covered
711 uncover = None # nothing uncovered
712 if x > 0: # left side uncovered
713 cover = (x, length - x)
714 uncover = (0, x)
715 elif x < 0: # right side uncovered
716 x = abs(x)
717 cover = (0, length - x)
718 uncover = (length - x, x)
719 return cover, uncover
720
721 width, height = self.get_size()
722 if abs(x) >= width or abs(y) >= height:
723 return self.clear() # just clear the console normally
724
725 # get the ranges of the areas that will be uncovered
726 cover_x, uncover_x = get_cover(x, width)
727 cover_y, uncover_y = get_cover(y, height)
728 # so at this point we know that cover_x and cover_y makes a rect that
729 # encases the area that we end up blitting to. uncover_x/Y makes a
730 # rect in the corner of the uncovered area. So we need to combine
731 # the uncover_x/Y with cover_y/X to make what's left of the uncovered
732 # area. Explaining it makes it mush easier to do now.
733
734 # But first we need to blit.
735 x, width, src_x = get_slide(x, width)
736 y, height, src_y = get_slide(y, height)
737 self.blit(self, x, y, width, height, src_x, src_y)
738
739 if uncover_x: # clear sides (0x20 is space)
740 self.draw_rect(uncover_x[0], cover_y[0], uncover_x[1], cover_y[1], 0x20, 0x000000, 0x000000)
741 if uncover_y: # clear top/bottom
742 self.draw_rect(cover_x[0], uncover_y[0], cover_x[1], uncover_y[1], 0x20, 0x000000, 0x000000)
743 if uncover_x and uncover_y: # clear corner
744 self.draw_rect(uncover_x[0], uncover_y[0], uncover_x[1], uncover_y[1], 0x20, 0x000000, 0x000000)
745
747 """Return the character and colors of a tile as (ch, fg, bg)
748
749 This method runs very slowly as is not recommended to be called
750 frequently.
751
752 @rtype: (int, (r, g, b), (r, g, b))
753 @returns: Returns a 3-item tuple. The first item is an integer of the
754 character at the position (x, y) the second and third are the
755 foreground and background colors respectfully.
756 """
757 raise NotImplementedError('Method here only exists for the docstring')
758
760 """Use ((x, y) in console) to check if a position is drawable on this console.
761 """
762 x, y = position
763 return (0 <= x < self.width) and (0 <= y < self.height)
764
767 """Contains character and color data and can be drawn to.
768
769 The console created by the L{untdl.init} function is the root console and is the
770 console that is rendered to the screen with L{flush}.
771
772 Any console created from the Console class is an off-screen console that
773 can be drawn on before being L{blit} to the root console.
774 """
775
776 __slots__ = ('_as_parameter_', '_typewriter')
777
779 """Create a new offscreen console.
780
781 @type width: int
782 @param width: Width of the console in tiles
783 @type height: int
784 @param height: Height of the console in tiles
785 """
786 _MetaConsole.__init__(self)
787 if not _root_initialized:
788 raise TDLError('Can not create Console\'s before untdl.init')
789 self._as_parameter_ = _lib.TCOD_console_new(width, height)
790 self.console = self
791 self.width = width
792 self.height = height
793 self._typewriter = None # "typewriter lock", makes sure the colors are set to the typewriter
794 # will be phased out with the Typewriter class
795
796 # noinspection PyCallByClass
797 @classmethod
799 """Make a Console instance, from a console ctype"""
800 self = cls.__new__(cls)
801 _MetaConsole.__init__(self)
802 self._as_parameter_ = console
803 self.console = self
804 self.width = _lib.TCOD_console_get_width(self)
805 self.height = _lib.TCOD_console_get_height(self)
806 self._typewriter = None
807 return self
808
810 """
811 If the main console is garbage collected then the window will be closed as well
812 """
813 # If this is the root console the window will close when collected
814 try:
815 if isinstance(self._as_parameter_, ctypes.c_void_p):
816 global _root_initialized, _rootConsoleRef
817 _root_initialized = False
818 _rootConsoleRef = None
819 _lib.TCOD_console_delete(self)
820 except StandardError:
821 pass # I forget why I put this here but I'm to afraid to delete it
822
824 # make a new class and blit
825 clone = self.__class__(self.width, self.height)
826 clone.blit(self)
827 return clone
828
830 # save data from get_char
831 data = [self.get_char(x, y) for x, y in
832 itertools.product(range(self.width), range(self.height))]
833 return self.width, self.height, data
834
836 # make console from __init__ and unpack a get_char array
837 width, height, data = state
838 self.__init__(width, height)
839 for (x, y), graphic in zip(itertools.product(range(width),
840 range(height)), data):
841 self.draw_char(x, y, *graphic)
842
844 """Used internally
845
846 Mostly used just to replace this Console object with the root console
847 If another Console object is used then they are swapped
848 """
849 if isinstance(console, Console):
850 self._as_parameter_, console._as_parameter_ = \
851 console._as_parameter_, self._as_parameter_ # swap tcod consoles
852 else:
853 self._as_parameter_ = console
854 self.width = _lib.TCOD_console_get_width(self)
855 self.height = _lib.TCOD_console_get_height(self)
856 return self
857
858 @staticmethod
860 """Conversion of x and y to their position on the root Console for this Window
861
862 Because this is a Console instead of a Window we return the parameters
863 untouched"""
864 return x, y
865
867 """Clears the entire Console.
868
869 @type fgcolor: (r, g, b)
870 @param fgcolor: Foreground color.
871
872 Must be a 3-item list with integers that range 0-255.
873
874 Unlike most other operations you cannot use None here.
875 @type bgcolor: (r, g, b)
876 @param bgcolor: Background color. See fgcolor.
877 """
878 assert _verify_colors(fgcolor, bgcolor)
879 assert fgcolor and bgcolor, 'Can not use None with clear'
880 self._typewriter = None
881 _lib.TCOD_console_set_default_background(self, _formatColor(bgcolor))
882 _lib.TCOD_console_set_default_foreground(self, _formatColor(fgcolor))
883 _lib.TCOD_console_clear(self)
884
886 """
887 Sets a character.
888 This is called often and is designed to be as fast as possible.
889
890 Because of the need for speed this function will do NO TYPE CHECKING
891 AT ALL, it's up to the drawing functions to use the functions:
892 _format_char and _formatColor before passing to this."""
893 # buffer values as ctypes objects
894 console = self._as_parameter_
895
896 if char is not None and fgcolor is not None and bgcolor is not None:
897 _set_char_ex(console, x, y, char, fgcolor, bgcolor)
898 return
899 if char is not None:
900 _set_char(console, x, y, char)
901 if fgcolor is not None:
902 _set_fore(console, x, y, fgcolor)
903 if bgcolor is not None:
904 _set_back(console, x, y, bgcolor, bgblend)
905
907 """
908 Try to perform a batch operation otherwise fall back to _set_char.
909 If fgcolor and bgcolor are defined then this is faster but not by very
910 much.
911
912 batch is a iterable of [(x, y), ch] items
913 """
914 if fgcolor and not null_char:
915 # buffer values as ctypes objects
916 self._typewriter = None # clear the typewriter as colors will be set
917 console = self._as_parameter_
918 bgblend = ctypes.c_int(bgblend)
919
920 if not bgcolor:
921 bgblend = 0
922 else:
923 _lib.TCOD_console_set_default_background(console, bgcolor)
924 _lib.TCOD_console_set_default_foreground(console, fgcolor)
925 _putChar = _lib.TCOD_console_put_char # remove dots and make local
926 for (x, y), char in batch:
927 _putChar(console, x, y, char, bgblend)
928 else:
929 for (x, y), char in batch:
930 self._set_char(x, y, char, fgcolor, bgcolor, bgblend)
931
933 # inherit docstring
934 x, y = self._normalize_point(x, y)
935 char = _lib.TCOD_console_get_char(self, x, y)
936 bgcolor = _lib.TCOD_console_get_char_background_wrapper(self, x, y)
937 fgcolor = _lib.TCOD_console_get_char_foreground_wrapper(self, x, y)
938 return char, tuple(fgcolor), tuple(bgcolor)
939
942
946 """A Window contains a small isolated part of a Console.
947
948 Drawing on the Window draws on the Console.
949
950 Making a Window and setting its width or height to None will extend it to
951 the edge of the console.
952 """
953
954 __slots__ = ('parent', 'x', 'y')
955
957 """Isolate part of a L{Console} or L{Window} instance.
958
959 @type console: L{Console} or L{Window}
960 @param console: The parent object which can be a L{Console} or another
961 L{Window} instance.
962
963 @type x: int
964 @param x: X coordinate to place the Window.
965
966 This follows the normal rules for indexing so you can use a
967 negative integer to place the Window relative to the bottom
968 right of the parent Console instance.
969 @type y: int
970 @param y: Y coordinate to place the Window.
971
972 See x.
973
974 @type width: int or None
975 @param width: Width of the Window.
976
977 Can be None to extend as far as possible to the
978 bottom right corner of the parent Console or can be a
979 negative number to be sized relative to the Consoles total
980 size.
981 @type height: int or None
982 @param height: Height of the Window.
983
984 See width.
985 """
986 _MetaConsole.__init__(self)
987 assert isinstance(console,
988 (Console, Window)), \
989 'console parameter must be a Console or Window instance, got %s' % repr(console)
990 self.parent = console
991 self.x, self.y, self.width, self.height = console._normalize_rect(x, y, width, height)
992 if isinstance(console, Console):
993 self.console = console
994 else:
995 self.console = self.parent.console
996
998 """Conversion x and y to their position on the root Console"""
999 # we add our position relative to our parent and then call then next parent up
1000 return self.parent._translate((x + self.x), (y + self.y))
1001
1003 """Clears the entire Window.
1004
1005 @type fgcolor: (r, g, b)
1006 @param fgcolor: Foreground color.
1007
1008 Must be a 3-item list with integers that range 0-255.
1009
1010 Unlike most other operations you can not use None here.
1011 @type bgcolor: (r, g, b)
1012 @param bgcolor: Background color. See fgcolor.
1013 """
1014 assert _verify_colors(fgcolor, bgcolor)
1015 assert fgcolor and bgcolor, 'Can not use None with clear'
1016 self.draw_rect(0, 0, None, None, 0x20, fgcolor, bgcolor)
1017
1020
1022 my_x = self.x # remove dots for speed up
1023 my_y = self.y
1024 self.parent._set_char_batch((((x + my_x, y + my_y), ch) for ((x, y), ch) in batch),
1025 fgcolor, bgcolor, bgblend)
1026
1028 # inherit docstring
1029 x, y = self._normalize_point(x, y)
1030 self.parent.draw_char(x + self.x, y + self.y, char, fgcolor, bgcolor)
1031
1032 - def draw_rect(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
1033 # inherit docstring
1034 x, y, width, height = self._normalize_rect(x, y, width, height)
1035 self.parent.draw_rect(x + self.x, y + self.y, width, height, string, fgcolor, bgcolor)
1036
1037 - def draw_frame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
1038 # inherit docstring
1039 x, y, width, height = self._normalize_rect(x, y, width, height)
1040 self.parent.draw_frame(x + self.x, y + self.y, width, height, string, fgcolor, bgcolor)
1041
1043 # inherit docstring
1044 x, y = self._normalize_point(x, y)
1045 trans_x, trans_y = self._translate(x, y)
1046 return self.console.get_char(trans_x, trans_y)
1047
1052
1053
1054 # noinspection PyTypeChecker,PyProtectedMember
1055 -def init(width, height, title=None, fullscreen=False, renderer='SDL'):
1056 """Start the main console with the given width and height and return the
1057 root console.
1058
1059 Call the consoles drawing functions. Then remember to use L{untdl.flush} to
1060 make what's drawn visible on the console.
1061
1062 @type width: int
1063 @param width: width of the root console (in tiles)
1064
1065 @type height: int
1066 @param height: height of the root console (in tiles)
1067
1068 @type title: string
1069 @param title: Text to display as the window title.
1070
1071 If left None it defaults to the running scripts filename.
1072
1073 @type fullscreen: boolean
1074 @param fullscreen: Can be set to True to start in fullscreen mode.
1075
1076 @type renderer: string
1077 @param renderer: Can be one of 'GLSL', 'OPENGL', or 'SDL'.
1078
1079 Due to way Python works you're unlikely to see much of an
1080 improvement by using 'GLSL' or 'OPENGL' as most of the
1081 time Python is slow interacting with the console and the
1082 rendering itself is pretty fast even on 'SDL'.
1083
1084 @rtype: L{Console}
1085 @return: The root console. Only what is drawn on the root console is
1086 what's visible after a call to L{untdl.flush}.
1087 After the root console is garbage collected, the window made by
1088 this function will close.
1089 """
1090 renderers = {'GLSL': 0, 'OPENGL': 1, 'SDL': 2}
1091 global _root_initialized, _rootConsoleRef
1092 if not _font_initialized: # set the default font to the one that comes with untdl
1093 set_font(_unpackfile('terminal8x8.png'), None, None, True, True)
1094
1095 if renderer.upper() not in renderers:
1096 raise TDLError('No such render type "%s", expected one of "%s"' % (renderer, '", "'.join(renderers)))
1097 renderer = renderers[renderer.upper()]
1098
1099 # If a console already exists then make a clone to replace it
1100 if _rootConsoleRef and _rootConsoleRef():
1101 old_root = _rootConsoleRef()
1102 root_replacement = Console(old_root.width, old_root.height)
1103 root_replacement.blit(old_root)
1104 old_root._replace(root_replacement)
1105 del root_replacement
1106
1107 if title is None: # use a default title
1108 if sys.argv:
1109 # Use the script filename as the title.
1110 title = os.path.basename(sys.argv[0])
1111 else:
1112 title = 'python-untdl'
1113
1114 _lib.TCOD_console_init_root(width, height, _encode_string(title), fullscreen, renderer)
1115
1116 #event.get() # flush the libtcod event queue to fix some issues
1117 # issues may be fixed already
1118
1119 event._eventsflushed = False
1120 _root_initialized = True
1121 root_console = Console._new_console(ctypes.c_void_p())
1122 _rootConsoleRef = weakref.ref(root_console)
1123
1124 return root_console
1125
1128 """Make all changes visible and update the screen.
1129
1130 Remember to call this function after drawing operations.
1131 Calls to flush will enforce the frame rate limit set by L{untdl.set_fps}.
1132
1133 This function can only be called after L{untdl.init}
1134 """
1135 if not _root_initialized:
1136 raise TDLError('Cannot flush without first initializing with untdl.init')
1137
1138 _lib.TCOD_console_flush()
1139
1140
1141 # noinspection PyPep8Naming,PyUnboundLocalVariable
1142 -def set_font(path, columns=None, rows=None, column_first=False,
1143 greyscale=False, alt_layout=False):
1144 """Changes the font to be used for this session.
1145 This should be called before L{untdl.init}
1146
1147 If the font specifies its size in its filename (i.e. font_NxN.png) then this
1148 function can auto-detect the tileset formatting and the parameters columns
1149 and rows can be left None.
1150
1151 While it's possible you can change the font mid program it can sometimes
1152 break in rare circumstances. So use caution when doing this.
1153
1154 @type path: string
1155 @param path: Must be a string file path where a bmp or png file is found.
1156
1157 @type columns: int
1158 @param columns: Number of columns in the tileset.
1159
1160 Can be left None for auto-detection.
1161
1162 @type rows: int
1163 @param rows: Number of rows in the tileset.
1164
1165 Can be left None for auto-detection.
1166
1167 @type column_first: boolean
1168 @param column_first: Defines if the character order goes along the rows or
1169 columns.
1170 It should be True if the character codes 0-15 are in the
1171 first column.
1172 And should be False if the characters 0-15
1173 are in the first row.
1174
1175 @type greyscale: boolean
1176 @param greyscale: Creates an anti-aliased font from a greyscale bitmap.
1177 Otherwise it uses the alpha channel for anti-aliasing.
1178
1179 Unless you actually need anti-aliasing from a font you
1180 know uses a smooth greyscale channel you should leave
1181 this on False.
1182
1183 @type alt_layout: boolean
1184 @param alt_layout: An alternative layout with space in the upper left
1185 corner.
1186 The column parameter is ignored if this is True,
1187 find examples of this layout in the font/libtcod/
1188 directory included with the python-untdl source.
1189
1190 @raise TDLError: Will be raised if no file is found at path or if auto-
1191 detection fails.
1192
1193 @note: A png file that's been optimized can fail to load correctly on
1194 MAC OS X creating a garbled mess when rendering.
1195 Don't use a program like optipng or just use bmp files instead if
1196 you want your program to work on macs.
1197 """
1198 # put up some constants that are only used here
1199 FONT_LAYOUT_ASCII_INCOL = 1
1200 FONT_LAYOUT_ASCII_INROW = 2
1201 FONT_TYPE_GREYSCALE = 4
1202 FONT_LAYOUT_TCOD = 8
1203 global _font_initialized
1204 _font_initialized = True
1205 flags = 0
1206 if alt_layout:
1207 flags |= FONT_LAYOUT_TCOD
1208 elif column_first:
1209 flags |= FONT_LAYOUT_ASCII_INCOL
1210 else:
1211 flags |= FONT_LAYOUT_ASCII_INROW
1212 if greyscale:
1213 flags |= FONT_TYPE_GREYSCALE
1214 if not os.path.exists(path):
1215 raise TDLError('no file exists at: "%s"' % path)
1216 path = os.path.abspath(path)
1217
1218 # and the rest is the auto-detect script
1219 imgSize = _get_image_size(path) # try to find image size
1220 if imgSize:
1221 imgWidth, imgHeight = imgSize
1222 # try to get font size from filename
1223 match = re.match('.*?([0-9]+)[xX]([0-9]+)', os.path.basename(path))
1224 if match:
1225 font_width, font_height = match.groups()
1226 font_width, font_height = int(font_width), int(font_height)
1227
1228 # estimate correct tileset size
1229 estColumns, remC = divmod(imgWidth, font_width)
1230 estRows, remR = divmod(imgHeight, font_height)
1231 if remC or remR:
1232 warnings.warn("Font may be incorrectly formatted.")
1233
1234 if not columns:
1235 columns = estColumns
1236 if not rows:
1237 rows = estRows
1238 else:
1239 # the font name excluded the fonts size
1240 if not (columns and rows):
1241 # no matched font size and no tileset is given
1242 raise TDLError('%s has no font size in filename' % os.path.basename(path))
1243
1244 if columns and rows:
1245 # confirm user set options
1246 if font_width * columns != imgWidth or font_height * rows != imgHeight:
1247 warnings.warn(
1248 "font parameters are set as if the image size is (%d, %d) when the detected size is (%i, %i)"
1249 % (font_width * columns, font_height * rows,
1250 imgWidth, imgHeight))
1251 else:
1252 warnings.warn("%s is probably not an image." % os.path.basename(path))
1253
1254 if not (columns and rows):
1255 # didn't auto-detect
1256 raise TDLError('Can not auto-detect the tileset of %s' % os.path.basename(path))
1257
1258 _lib.TCOD_console_set_custom_font(_encode_string(path), flags, columns, rows)
1259
1262 """Maps characters in the bitmap font to ASCII codes.
1263 This should be called after L{untdl.init}
1264
1265 You can dynamically change the characters mapping at any time, allowing to use several fonts in the same screen.
1266
1267 @type first_ascii_code: int
1268 @param first_ascii_code: First ascii code to map.
1269
1270 @type num_codes: int
1271 @param num_codes: Number of consecutive ascii codes to map.
1272
1273 @type font_char_x: int
1274 @param font_char_x: Coordinate of the character in the bitmap font (in characters, not pixels)
1275 corresponding to the first ASCII code.
1276
1277 @type font_char_y: int
1278 @param font_char_y: Coordinate of the character in the bitmap font (in characters, not pixels)
1279 corresponding to the first ASCII code.
1280
1281 @rtype: None
1282 """
1283 _lib.TCOD_console_map_ascii_codes_to_font(first_ascii_code, num_codes, font_char_x, font_char_y)
1284
1287 """Returns True if program is fullscreen.
1288
1289 @rtype: boolean
1290 @return: Returns True if the window is in fullscreen mode.
1291 Otherwise returns False.
1292 """
1293 if not _root_initialized:
1294 raise TDLError('Initialize first with untdl.init')
1295 return _lib.TCOD_console_is_fullscreen()
1296
1299 """Changes the fullscreen state.
1300
1301 @type fullscreen: boolean
1302 """
1303 if not _root_initialized:
1304 raise TDLError('Initialize first with untdl.init')
1305 _lib.TCOD_console_set_fullscreen(fullscreen)
1306
1309 """Change the window title.
1310
1311 @type title: string
1312 """
1313 if not _root_initialized:
1314 raise TDLError('Not initilized. Set title with untdl.init')
1315 _lib.TCOD_console_set_window_title(_encode_string(title))
1316
1319 """Capture the screen and save it as a png file
1320
1321 @type path: string
1322 @param path: The file path to save the screenshot.
1323
1324 If path is None then the image will be placed in the current
1325 folder with the names:
1326 screenshot001.png, screenshot002.png, ...
1327 """
1328 if not _root_initialized:
1329 raise TDLError('Initialize first with untdl.init')
1330 if isinstance(path, str):
1331 _lib.TCOD_sys_save_screenshot(_encode_string(path))
1332 elif path is None: # save to screenshot001.png, screenshot002.png, ...
1333 file_list = os.listdir('.')
1334 n = 1
1335 filename = 'screenshot%.3i.png' % n
1336 while filename in file_list:
1337 n += 1
1338 filename = 'screenshot%.3i.png' % n
1339 _lib.TCOD_sys_save_screenshot(_encode_string(filename))
1340 else: # assume file like obj
1341 #save to temp file and copy to file-like obj
1342 tmp_name = os.tempnam()
1343 _lib.TCOD_sys_save_screenshot(_encode_string(tmp_name))
1344 with tmp_name as tmp_file:
1345 path.write(tmp_file.read())
1346 os.remove(tmp_name)
1347 #else:
1348 # raise TypeError('path is an invalid type: %s' % type(path))
1349
1350
1351 -def set_fps(frame_rate):
1352 """Set the maximum frame rate.
1353
1354 @type frame_rate: int
1355 @param frame_rate: Further calls to L{untdl.flush} will limit the speed of
1356 the program to run at <frame_rate> frames per second. Can
1357 also be set to 0 to run without a limit.
1358
1359 Defaults to None.
1360 """
1361 if frame_rate is None:
1362 frame_rate = 0
1363 assert isinstance(frame_rate, _INTTYPES), 'frame_rate must be an integer or None, got: %s' % repr(frame_rate)
1364 _lib.TCOD_sys_set_fps(frame_rate)
1365
1368 """Return the current frames per second of the running program set by
1369 L{set_fps}
1370
1371 @rtype: int
1372 @return: Returns the frameRate set by set_fps.
1373 If set to no limit, this will return 0.
1374 """
1375 return _lib.TCOD_sys_get_fps()
1376
1385
1386
1387 __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in
1388 ['sys', 'os', 'ctypes', 'array', 'weakref', 'itertools', 'textwrap',
1389 'struct', 're', 'warnings']] # remove modules from __all__
1390 __all__ += ['_MetaConsole'] # keep this object public to show the documentation in epydoc
1391
1392 __license__ = "New BSD License"
1393 __email__ = "4b796c65+pythonTDL@gmail.com"
1394
1395 file = open(os.path.join(os.path.dirname(__file__), 'VERSION.txt'), 'r')
1396 __version__ = file.read()
1397 file.close()
1398
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sat Aug 23 21:57:03 2014 | http://epydoc.sourceforge.net |