Skip to content

Commit cd20e46

Browse files
4B796C65@gmail.com4B796C65@gmail.com
authored andcommitted
1 parent ad1f162 commit cd20e46

File tree

7 files changed

+191
-82
lines changed

7 files changed

+191
-82
lines changed

CHANGELOG.txt

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
1-
1.1.3
2-
* Some of the setFont parameters were incorrectly labeled and documented
3-
* setFont can auto-detect tilesets if the font sizes are in the filenames
4-
* Added some X11 unicode tilesets, including unifont.
5-
6-
1.1.2
7-
* Window title now defaults to the running scripts filename
8-
* Fixed incorrect deltaTime for App.update
9-
* App will no longer call tdl.flush on its own, you'll need to call this yourself
10-
* tdl.noise module added
11-
* clear method now defaults to black on black
12-
13-
1.1.1
14-
* map submodule added with AStar class and quickFOV function
15-
* new Typewriter class
16-
* most console functions can use Python-style negative indexes now
17-
* new App.runOnce method
18-
* rectangle geometry is less strict
19-
20-
1.1.0
21-
* KeyEvent.keyname is now KeyEvent.key
22-
* MouseButtonEvent.button now behaves like KeyEvent.keyname does
23-
* event.App class added
24-
* drawing methods no longer have a default for the character parameter
25-
* KeyEvent.ctrl is now KeyEvent.control
1+
2+
* Fixed the clear method on Window class
3+
* Fixed screenshot function
4+
* Fixed some drawing operations with unchanging backgrounds
5+
* Instances of Console and Noise can be pickled and copied
6+
* Added KeyEvent.keychar
7+
8+
1.1.3
9+
* Some of the setFont parameters were incorrectly labeled and documented
10+
* setFont can auto-detect tilesets if the font sizes are in the filenames
11+
* Added some X11 unicode tilesets, including unifont.
12+
13+
1.1.2
14+
* Window title now defaults to the running scripts filename
15+
* Fixed incorrect deltaTime for App.update
16+
* App will no longer call tdl.flush on its own, you'll need to call this yourself
17+
* tdl.noise module added
18+
* clear method now defaults to black on black
19+
20+
1.1.1
21+
* map submodule added with AStar class and quickFOV function
22+
* new Typewriter class
23+
* most console functions can use Python-style negative indexes now
24+
* new App.runOnce method
25+
* rectangle geometry is less strict
26+
27+
1.1.0
28+
* KeyEvent.keyname is now KeyEvent.key
29+
* MouseButtonEvent.button now behaves like KeyEvent.keyname does
30+
* event.App class added
31+
* drawing methods no longer have a default for the character parameter
32+
* KeyEvent.ctrl is now KeyEvent.control

dev/runRegressionTest.py

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,55 @@
66
import time
77
import random
88
import itertools
9+
import copy
10+
import pickle
911
import gc
1012

1113
sys.path.insert(0, '..')
1214
import tdl
1315

14-
ERROR_RANGE = 100 # a number to test out of bound errors
16+
#ERROR_RANGE = 100 # a number to test out of bound errors
17+
WIDTH, HEIGHT = 30, 20
18+
WINWIDTH, WINHEIGHT = 10, 10
19+
20+
DEFAULT_CHAR = (0x20, (0, 0, 0), (0, 0, 0))
1521

1622
class TDLTemplate(unittest.TestCase):
1723
"Nearly all tests need tdl.init to be called"
1824

1925
@classmethod
2026
def setUpClass(cls):
2127
tdl.setFont('../fonts/libtcod/terminal8x8_gs_ro.png')
22-
cls.console = tdl.init(30, 20, 'TDL UnitTest', False, renderer='SDL')
28+
cls.console = tdl.init(WIDTH, HEIGHT, 'TDL UnitTest', False, renderer='SDL')
29+
# make a small window in the corner
30+
cls.window = tdl.Window(cls.console, 0, 0, WINWIDTH, WINHEIGHT)
2331

2432
def setUp(self):
33+
tdl.setFont('../fonts/libtcod/terminal8x8_gs_ro.png')
34+
tdl.event.get()
2535
self.console.clear()
2636

2737
@classmethod
2838
def tearDownClass(cls):
2939
del cls.console
3040
gc.collect() # make sure console.__del__ is called quickly
3141

42+
def inWindow(self, x, y):
43+
"returns True if this point is in the Window"
44+
return 0 <= x < WINWIDTH and 0 <= y < WINHEIGHT
45+
46+
def randomizeConsole(self):
47+
"Randomize the console returning the random data"
48+
noise = [((x, y), self.getRandomCharacter()) for x,y in self.getDrawables()]
49+
for (x, y), graphic in noise:
50+
self.console.drawChar(x, y, *graphic)
51+
return noise # [((x, y), (cg, fg, bg)), ...]
52+
53+
def flush(self):
54+
'Pump events and refresh screen so show progress'
55+
tdl.event.get()
56+
tdl.flush()
57+
3258
def getRandomCharacter(self):
3359
"returns a tuple with a random character and colors (ch, fg, bg)"
3460
return (random.getrandbits(8), self.getRandomColor(), self.getRandomColor())
@@ -75,16 +101,34 @@ def getUndrawables(self, console=None):
75101
class BasicTests(TDLTemplate):
76102

77103
def test_clearConsole(self):
104+
self.randomizeConsole()
78105
_, fg, bg = self.getRandomCharacter()
79106
ch = 0x20 # space
80107
self.console.clear(fg, bg)
108+
self.flush()
109+
for x,y in self.getDrawables():
110+
self.assertEqual((ch, fg, bg), self.console.getChar(x, y), 'color should be changed with clear')
111+
_, fg2, bg2 = self.getRandomCharacter()
112+
self.window.clear(fg2, bg2)
113+
self.flush()
114+
for x,y in self.getDrawables():
115+
if self.inWindow(x, y):
116+
self.assertEqual((ch, fg2, bg2), self.console.getChar(x, y), 'color in window should be changed')
117+
else:
118+
self.assertEqual((ch, fg, bg), self.console.getChar(x, y), 'color outside of window should persist')
119+
120+
def test_cloneConsole(self):
121+
noiseData = self.randomizeConsole()
122+
clone = copy.copy(self.console)
123+
for x,y in self.getDrawables():
124+
self.assertEqual(self.console.getChar(x, y), clone.getChar(x, y), 'console clone should match root console')
125+
126+
def test_pickleConsole(self):
127+
noiseData = self.randomizeConsole()
128+
pickled = pickle.dumps(self.console)
129+
clone = pickle.loads(pickled)
81130
for x,y in self.getDrawables():
82-
self.assertEqual((ch, fg, bg), self.console.getChar(x, y), 'color should be changeable with clear')
83-
#fg = (255, 255, 255)
84-
#bg = (0, 0, 0)
85-
#self.console.clear()
86-
#for x,y in self.getDrawables():
87-
# self.assertEqual((ch, fg, bg), self.console.getChar(x, y), 'clear should default to white on black')
131+
self.assertEqual(self.console.getChar(x, y), clone.getChar(x, y), 'pickled console should match root console')
88132

89133
def test_changeFonts(self):
90134
"Fonts are changable on the fly... kind of"
@@ -97,8 +141,8 @@ def test_changeFonts(self):
97141
for x,y in self.getDrawables():
98142
self.console.drawChar(x, y, *self.getRandomCharacter())
99143
tdl.setTitle(font)
100-
tdl.flush()
101-
time.sleep(.25)
144+
self.flush()
145+
time.sleep(.05)
102146

103147

104148
class DrawingTests(TDLTemplate):
@@ -111,6 +155,8 @@ def test_drawCharTuples(self):
111155
record[x,y] = (ch, fg, bg)
112156
self.console.drawChar(x, y, ch, fg, bg)
113157
self.assertEqual(record[x,y], self.console.getChar(x, y), 'console data should be overwritten')
158+
self.flush() # show progress
159+
114160
for (x,y), data in record.items():
115161
self.assertEqual(data, self.console.getChar(x, y), 'drawChar should not overwrite any other tiles')
116162

@@ -125,6 +171,7 @@ def test_drawCharWebcolor(self):
125171
bg = bg[0] << 16 | bg[1] << 8 | bg[2]
126172
self.console.drawChar(x, y, ch, fg, bg)
127173
self.assertEqual(record[x,y], self.console.getChar(x, y), 'console data should be overwritten')
174+
self.flush() # show progress
128175
for (x,y), data in record.items():
129176
self.assertEqual(data, self.console.getChar(x, y), 'drawChar should not overwrite any other tiles')
130177

@@ -155,6 +202,7 @@ def test_drawStrArray(self):
155202
y += 1
156203
if y == height:
157204
break # end of console
205+
self.flush() # show progress
158206

159207
#@unittest.skipIf(not __debug__, 'python run with optimized flag, skipping an AssertionError test')
160208
#def test_drawStrErrors(self):
@@ -171,9 +219,10 @@ def test_drawRect(self):
171219
width, height = self.console.getSize()
172220
width, height = random.randint(1, width - x), random.randint(1, height - y)
173221
self.console.drawRect(x, y, width, height, ch, fg, bg)
222+
self.flush() # show progress
174223
for testX,testY in self.getDrawables():
175224
if x <= testX < x + width and y <= testY < y + height:
176-
self.assertEqual(self.console.getChar(testX, testY), (ch, fg, bg), 'rectangle are should be overwritten')
225+
self.assertEqual(self.console.getChar(testX, testY), (ch, fg, bg), 'rectangle area should be overwritten')
177226
else:
178227
self.assertEqual(self.console.getChar(testX, testY), consoleCopy.getChar(testX, testY), 'this area should remain untouched')
179228

@@ -185,6 +234,7 @@ def test_drawFrame(self):
185234
width, height = self.console.getSize()
186235
width, height = random.randint(1, width - x), random.randint(1, height - y)
187236
self.console.drawFrame(x, y, width, height, ch, fg, bg)
237+
self.flush() # show progress
188238
for testX,testY in self.getDrawables():
189239
if x + 1 <= testX < x + width - 1 and y + 1 <= testY < y + height - 1:
190240
self.assertEqual(self.console.getChar(testX, testY), consoleCopy.getChar(testX, testY), 'inner frame should remain untouched')
@@ -204,27 +254,29 @@ def test_drawFrame(self):
204254
# with self.assertRaises(AssertionError):
205255
# self.console.drawFrame(x, y, width, height, ch, fg, bg)
206256

207-
@unittest.skip("Need this to be faster before unskipping")
257+
#@unittest.skip("Need this to be faster before unskipping")
208258
def test_scrolling(self):
209259
"""marks a spot and then scrolls the console, checks to make sure no
210260
other spots are marked, test also knows if it's out of bounds.
211261
212262
This test is a bit slow, it could be made more efficent by marking
213263
several areas and not clearing the console every loop.
214264
"""
215-
for sx, sy in itertools.product(range(-30, 30, 5), range(-20, 20, 5)):
216-
self.console.clear()
217-
char = self.getRandomCharacter()
218-
dx, dy = random.choice(list(self.getDrawables()))
219-
self.console.drawChar(dx, dy, *char)
265+
scrollTests = set([(0, 0), (WIDTH, HEIGHT)]) # include zero and out of bounds
266+
while len(scrollTests) < 5: # add 3 more randoms
267+
scrollTests.add((random.randint(-WIDTH, WIDTH),
268+
random.randint(-HEIGHT, HEIGHT)))
269+
for sx, sy in scrollTests:
270+
noiseData = dict(self.randomizeConsole())
220271
self.console.scroll(sx, sy)
221-
dx += sx # if these go out of bounds then the check will make sure everything is cleared
222-
dy += sy
272+
self.flush() # show progress
223273
for x, y in self.getDrawables():
224-
if x == dx and y == dy:
225-
self.assertEqual(self.console.getChar(x, y), char, 'marked position should have scrolled here')
274+
nX = x - sx
275+
nY = y - sy
276+
if (nX, nY) in noiseData:
277+
self.assertEqual(self.console.getChar(x, y), noiseData[nX, nY], 'random noise should be scrolled')
226278
else:
227-
self.assertEqual(self.console.getChar(x, y), (0x20, (255, 255, 255), (0, 0, 0)), 'every other place should be clear')
279+
self.assertEqual(self.console.getChar(x, y), DEFAULT_CHAR, 'scrolled away positions should be clear')
228280

229281

230282
def suite():

examples/eventget.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
console = tdl.init(WIDTH, HEIGHT)
1414

1515
# the scrolling text window
16-
textWindow = tdl.Window(console, 0, 0, WIDTH, HEIGHT-2)
16+
textWindow = tdl.Window(console, 0, 0, WIDTH, -2)
1717

1818
# slow down the program so that the user can more clearly see the motion events
1919
tdl.setFPS(24)

setup.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#!/usr/bin/env python
22

3-
import os.path
4-
import subprocess
5-
from distutils.core import setup
3+
try:
4+
# use setuptools or distribute if available
5+
from setuptools import setup
6+
except ImportError:
7+
from distutils.core import setup
68

79
setup(name='tdl',
810
version='1.1.3',

tdl/__init__.py

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -547,11 +547,11 @@ def getCover(x, length):
547547
self.blit(self, x, y, width, height, srcx, srcy)
548548

549549
if uncoverX: # clear sides (0x20 is space)
550-
self.drawRect(uncoverX[0], coverY[0], uncoverX[1], coverY[1], 0x20)
550+
self.drawRect(uncoverX[0], coverY[0], uncoverX[1], coverY[1], 0x20, 0x000000, 0x000000)
551551
if uncoverY: # clear top/bottom
552-
self.drawRect(coverX[0], uncoverY[0], coverX[1], uncoverY[1], 0x20)
552+
self.drawRect(coverX[0], uncoverY[0], coverX[1], uncoverY[1], 0x20, 0x000000, 0x000000)
553553
if uncoverX and uncoverY: # clear corner
554-
self.drawRect(uncoverX[0], uncoverY[0], uncoverX[1], uncoverY[1], 0x20)
554+
self.drawRect(uncoverX[0], uncoverY[0], uncoverX[1], uncoverY[1], 0x20, 0x000000, 0x000000)
555555

556556
def getChar(self, x, y):
557557
"""Return the character and colors of a tile as (ch, fg, bg)
@@ -625,6 +625,26 @@ def __del__(self):
625625
except StandardError:
626626
pass # I forget why I put this here but I'm to afraid to delete it
627627

628+
def __copy__(self):
629+
# make a new class and blit
630+
clone = self.__class__(self.width, self.height)
631+
clone.blit(self)
632+
return clone
633+
634+
def __getstate__(self):
635+
# save data from getChar
636+
data = [self.getChar(x, y) for x,y in
637+
itertools.product(range(self.width), range(self.height))]
638+
return self.width, self.height, data
639+
640+
def __setstate__(self, state):
641+
# make console from __init__ and unpack a getChar array
642+
width, height, data = state
643+
self.__init__(width, height)
644+
for (x, y), graphic in zip(itertools.product(range(width),
645+
range(height)), data):
646+
self.drawChar(x, y, *graphic)
647+
628648
def _replace(self, console):
629649
"""Used internally
630650
@@ -655,7 +675,7 @@ def clear(self, fgcolor=(0, 0, 0), bgcolor=(0, 0, 0)):
655675
656676
Must be a 3-item list with integers that range 0-255.
657677
658-
Unlike most other operations you can not use None here.
678+
Unlike most other operations you cannot use None here.
659679
@type bgcolor: (r, g, b)
660680
@param bgcolor: Background color. See fgcolor.
661681
"""
@@ -699,11 +719,12 @@ def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1, nullChar=False):
699719
# buffer values as ctypes objects
700720
self._typewriter = None # clear the typewriter as colors will be set
701721
console = self._as_parameter_
702-
if not bgcolor:
703-
bgblend = 0
704722
bgblend = ctypes.c_int(bgblend)
705723

706-
_lib.TCOD_console_set_default_background(console, bgcolor)
724+
if not bgcolor:
725+
bgblend = 0
726+
else:
727+
_lib.TCOD_console_set_default_background(console, bgcolor)
707728
_lib.TCOD_console_set_default_foreground(console, fgcolor)
708729
_putChar = _lib.TCOD_console_put_char # remove dots and make local
709730
for (x, y), char in batch:
@@ -792,7 +813,7 @@ def clear(self, fgcolor=(0, 0, 0), bgcolor=(0, 0, 0)):
792813
"""
793814
assert _verify_colors(fgcolor, bgcolor)
794815
assert fgcolor and bgcolor, 'Can not use None with clear'
795-
self.draw_rect(0, 0, None, None, 0x20, fgcolor, bgcolor)
816+
self.drawRect(0, 0, None, None, 0x20, fgcolor, bgcolor)
796817

797818
def _setChar(self, x, y, char=None, fgcolor=None, bgcolor=None, bgblend=1):
798819
self.parent._setChar((x + self.x), (y + self.y), char, fgcolor, bgcolor, bgblend)
@@ -1234,24 +1255,25 @@ def screenshot(path=None):
12341255
"""
12351256
if not _rootinitialized:
12361257
raise TDLError('Initialize first with tdl.init')
1237-
if isinstance(fileobj, str):
1238-
_lib.TCOD_sys_save_screenshot(_encodeString(fileobj))
1239-
elif isinstance(fileobj, file): # save to temp file and copy to file-like obj
1240-
tmpname = os.tempnam()
1241-
_lib.TCOD_sys_save_screenshot(_encodeString(tmpname))
1242-
with tmpname as tmpfile:
1243-
fileobj.write(tmpfile.read())
1244-
os.remove(tmpname)
1245-
elif fileobj is None: # save to screenshot001.png, screenshot002.png, ...
1258+
if isinstance(path, str):
1259+
_lib.TCOD_sys_save_screenshot(_encodeString(path))
1260+
elif path is None: # save to screenshot001.png, screenshot002.png, ...
12461261
filelist = os.listdir('.')
12471262
n = 1
12481263
filename = 'screenshot%.3i.png' % n
12491264
while filename in filelist:
12501265
n += 1
1251-
filename = 'screenshot%.4i.png' % n
1266+
filename = 'screenshot%.3i.png' % n
12521267
_lib.TCOD_sys_save_screenshot(_encodeString(filename))
1253-
else:
1254-
raise TypeError('fileobj is an invalid type: %s' % type(fileobj))
1268+
else: # assume file like obj
1269+
#save to temp file and copy to file-like obj
1270+
tmpname = os.tempnam()
1271+
_lib.TCOD_sys_save_screenshot(_encodeString(tmpname))
1272+
with tmpname as tmpfile:
1273+
path.write(tmpfile.read())
1274+
os.remove(tmpname)
1275+
#else:
1276+
# raise TypeError('path is an invalid type: %s' % type(path))
12551277

12561278
def setFPS(frameRate):
12571279
"""Set the maximum frame rate.

0 commit comments

Comments
 (0)