@@ -44,6 +44,7 @@
import platform
import math
import random
import textwrap

#
# Internal modules
@@ -59,8 +60,7 @@ class PositionHelperBase(ScreenHelperBase):
There are main methods available here:
* `get_terminal_size`: retrieves the current terminal dimensions, width
and height, to be stored in local properties `screen_width` and
`screen_height`.
and height, to be stored in local propertiy `geometry`.
* `center_text_vertically`: centers a specified text in vertical
@@ -74,32 +74,71 @@ class PositionHelperBase(ScreenHelperBase):
If the method `get_terminal_size` is never called, all other functionality,
relying on terminal dimensions, will just based on a default size, set as
80x25.
"""
screen_width = 80
The main properties available are:
* `geometry`: defines the size of the terminal (populated by the
`get_terminal_size` method, whenever called.
* `position`: defines the current position of the text to be printed,
based on usage of other methods that temper with text positioning
(eg. `randomize_text_horizontally`)
* `changed_geometry`: for every call to `get_terminal_size` method,
holds the information if the terminal has changed size or not.
"""
Defines the current terminal width. Its default value is set as 80.

geometry = {
'x' : 0,
'y' : 0,
}
"""
Defines the current terminal width and height.
Its default value is set as 80x25.
"""

screen_height = 25
__old_geometry = {
'x' : 0,
'y' : 0,
}
"""
Defines the current terminal height. Its default value is set as 25.
Private property to track changes in the geometry. Refer to
`changed_geometry` to identify any terminal size changes.
"""

pos_y = 1
changed_geometry = False
"""
Stores the latest position Y axis (height) position value after calling
the randomizing methods of this class (useful to allow you to know the
previous location of the text in the window)
A simple boolean that identifies if the terminal has changed size since the
last check with `get_terminal_size`. This is handy to decide what to do
with a particular screen (eg: clear and redraw again?).
"""

pos_x = 1

position = {
'x' : 0,
'y' : 0,
}
"""
Stores the latest position X axis (width) position value after calling
the randomizing methods of this class (useful to allow you to know the
previous location of the text in the window)
Stores the latest position X-Y axis (width and height) position value after
calling the randomizing methods of this class (useful to allow you to know
the previous location of the text in the window)
"""

def fix_text_wrap(self, text):
"""
Wraps the text to the geometry size, maintaining existing new lines.
"""

temp = text.split("\n")
longest_line = max([len(x) for x in temp])
new_text = []
for l in temp:
t = "\n".join(textwrap.wrap(l, width=self.geometry['x']))

# fill in trailing blanks
t += " " * (min(longest_line, self.geometry['x']) - min(len(t), self.geometry['x']))
new_text.append(t)
return "\n".join(new_text)

def center_text_vertically(self, text):
"""
Returns the text argument with additional new lines, calculated to
@@ -109,14 +148,12 @@ def center_text_vertically(self, text):
* text: the text to be vertically centered
"""
line_count = 0
for t in text.split("\n"):
line_count += 1 + math.ceil(len(t) / (self.screen_width - 1))

return "\n" * int(math.floor((self.screen_height - line_count) / 2)) \
+ text



temp = self.fix_text_wrap(text).split("\n")
self.position['y'] = int(math.floor(
(self.geometry['y'] - len(temp)) / 2))
return "\n" * self.position['y'] + text

def center_text_horizontally(self, text):
"""
Returns the text argument with additional blank spaces, calculated to
@@ -126,20 +163,16 @@ def center_text_horizontally(self, text):
* text: the text to be horizontally centered
"""

temp = self.fix_text_wrap(text).split("\n")
new_text = ""
for t in text.split("\n"):
temp = [""]
index = 0
for w in t.split(" "):
if len(temp[index] + w) < self.screen_width - 1:
temp[index] = " ".join([temp[index], w])
else:
index += 1
temp.append(w)
for l in temp:
new_text += " " * int(math.ceil(
(self.screen_width - len(l) - 1) / 2)) + l + "\n"

for t in temp:
self.position['x'] = int(math.ceil(
(self.geometry['x'] - len(t)) / 2))
new_text += " " * self.position['x'] + t
if len(temp) > 1:
new_text += "\n"

return new_text

def randomize_text_horizontally(self, text):
@@ -151,10 +184,21 @@ def randomize_text_horizontally(self, text):
* text: the text to be horizontally randomized
"""
self.pos_x = random.randint(0, self.screen_width - len(text))

return " " * self.pos_x + text \
+ " " * (self.screen_width - self.pos_x - len(text))
# find longest line
temp = self.fix_text_wrap(text).split("\n")
longest_line = max([len(x) for x in temp])

self.position['x'] = random.randint(0,
self.geometry['x'] - longest_line)

new_text = ""
for t in temp:
new_text += " " * self.position['x'] + t
if len(temp) > 0:
new_text += "\n"

return new_text

def randomize_text_vertically(self, text):
"""
@@ -165,15 +209,17 @@ def randomize_text_vertically(self, text):
* text: the text to be vertically randomized
"""
self.pos_y = random.randint(0, self.screen_height)
total_lines = len(self.fix_text_wrap(text).split("\n"))
self.position['y'] = random.randint(0,
max(0, self.geometry['y'] - total_lines))

return "\n" * self.pos_y + text
return "\n" * self.position['y'] + text

def get_terminal_size(self):
"""
Retrieves the screen terminal dimensions, returning a tuple
(width, height), and will also store them in internal properties
`screen_width` and `screen_height`.
(width, height), and will also store them in internal property
`geometry`.
Copyright note:
This code has been adapted from:
@@ -267,7 +313,15 @@ def ioctl_GWINSZ(fd):
if tuple_xy is None:
tuple_xy = (80, 25) # default value

# assign result to
self.screen_width, self.screen_height = tuple_xy

return tuple_xy
self.geometry['x'], self.geometry['y'] = tuple_xy

# store geometry changes
if self.__old_geometry == {'x': 0, 'y': 0}:
# first time checking geometry
self.__old_geometry = self.geometry.copy()
self.changed_geometry = False
elif self.__old_geometry != self.geometry:
self.__old_geometry = self.geometry.copy()
self.changed_geometry = True
else:
self.changed_geometry = False
@@ -190,12 +190,6 @@ class MatrixScreen(ScreenBase, PositionHelperBase):
of half-width size.
"""

cur_geo = (0, 0)
"""
A helper property to identify if the terminal has changed its geometry.
This is calculated in `_run_cycle`.
"""

def __init__(self):
"""
The constructor of this class.
@@ -220,13 +214,8 @@ def _run_cycle(self):

# Identify terminal geometry
self.get_terminal_size()
geometry = (self.screen_width, self.screen_height)
changed_geometry = False
if self.cur_geo != geometry:
changed_geometry = True
self.cur_geo = geometry

if len(self.screen_map) == 0 or changed_geometry:

if len(self.screen_map) == 0 or self.changed_geometry:
# build it for the first time or whenever the geometry changes
self.__build_screen_map()

@@ -251,17 +240,13 @@ def _usage_options_example(self):
an integer value to define how dirt should the screen be.
Default value is [%(granularity)s]. Use something like [1]
for clean style, or a [100] for total dirt.
-d, --delay Defines the speed (in seconds) of the character movement
Default value is [%(line_delay)s] (in seconds).
-k, --kana-only
Displays only Japanese characters (excludes alpha numeric).
-z, --zenkaku
Displays full-width (fattish) Japanese characters.
By default it displays half-width characters.
-h, --help Displays this help message
Examples:
@@ -353,13 +338,13 @@ def __build_screen_map(self):
# clean up previous
self.screen_map = []

for __ in range(0, self.screen_width / self.proportion):
for __ in range(0, self.geometry['x'] / self.proportion):

if random.random() > 0.5:
char_list = self.get_char_list()
else:
char_list = [self.space for __ in \
range(0, random.randint(0, self.screen_height))]
range(0, random.randint(0, self.geometry['y']))]
self.screen_map.append(char_list)

def get_char_list(self):
@@ -369,12 +354,12 @@ def get_char_list(self):
result = []
while(len(result) == 0):
bt = [self.space for __ in range(0,
min((self.screen_height, random.randint(0,
self.screen_height * 20 / (self.granularity * \
min((self.geometry['y'], random.randint(0,
self.geometry['y'] * 20 / (self.granularity * \
self.proportion)))))]
cl = [self.digmap[random.randint(0, len(self.digmap)) - 1] \
for __ in range(0, random.randint(0,
self.screen_height - len(bt)))]
self.geometry['y'] - len(bt)))]
result = bt
result.extend(cl)
return result
@@ -127,7 +127,7 @@ def _usage_options_example(self):
Options:
-w, --word Sets the word to be displayed
default is the name of this application (if you need to use
default is the name of this application (if you need to use
spaces, don't forget to place the word with quotes)
-d, --delay Sets how long the word will be displayed before
randomized again. Default is %(default_delay)s second(s)
@@ -1,15 +1,151 @@
#import os
#import random
#import unittest
#import string
#import shutil
###############################################################################
#
#import sys
#sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
# os.path.pardir)))
# file: test.py
#
#from termsaverlib import common
# Purpose: refer to module documentation for details
#
# Note: This file is part of Termsaver application, and should not be used
# or executed separately.
#
###############################################################################
#
# Copyright 2012 Termsaver
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
###############################################################################

#
# Python built-in modules
#
import os
import unittest
import sys
#
# Import from parent path
#
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
os.path.pardir)))

#
# Internal Modules (can only call this after the above PATH update)
#
from termsaverlib.screen.helper import position


class PositionHelperTestCase(unittest.TestCase):

p = None

def setUp(self):
self.p = position.PositionHelperBase()
self.p.get_terminal_size()

def testCenterHorizontallyText(self):

# check no text
t = ""
self.p.center_text_horizontally(t)

# test small word(s)
t = "x"
for __ in range(3):
t += t
nt = self.p.center_text_horizontally(t)
self.assertEqual(nt.find(t),
(self.p.geometry['x'] - len(t)) / 2)

# test long word(s)
t = "x" * 500
for __ in range(3):
t += t
nt = self.p.center_text_horizontally(t)
self.assertEqual(nt.find(t[0]), 0)

# test multiple lines
t = "\n".join(["x" * 25 for __ in range(10)])
for __ in range(3):
t += t
nt = self.p.center_text_horizontally(t)

# test long multiple lines
t = "\n".join(["x" * 500 for __ in range(10)])
for __ in range(3):
t += t
nt = self.p.center_text_horizontally(t)

def testHorizontallyRandomizeText(self):

# check no text
t = ""
self.p.randomize_text_horizontally(t)

# test small word(s)
t = "x"
for __ in range(3):
t += t
self.p.randomize_text_horizontally(t)

# test long word(s)
t = "x" * 500
for __ in range(3):
t += t
self.p.randomize_text_horizontally(t)

# test multiple lines
t = "\n".join(["x" * 25 for __ in range(10)])
for __ in range(3):
t += t
self.p.randomize_text_horizontally(t)

# test long multiple lines
t = "\n".join(["x" * 500 for __ in range(10)])
for __ in range(3):
t += t
self.p.randomize_text_horizontally(t)

def testCenterVerticallyText(self):

# check no text
t = ""
self.p.center_text_vertically(t)

# test small word(s)
t = "x"
for __ in range(5):
t += t
nt = self.p.center_text_vertically(t)
self.assertEqual(len(nt.split('\n')),
(self.p.geometry['y'] - len(t.split('\n'))) / 2 + 1)
self.assertEqual(len(nt.split('\n')) - 1, self.p.position['y'])

# test long word(s)
t = "x" * 500
for __ in range(3):
t += t
self.p.center_text_vertically(t)

# test multiple lines
t = "\n".join(["x" * 25 for __ in range(10)])
for __ in range(3):
t += t
self.p.center_text_vertically(t)

# test long multiple lines
t = "\n".join(["x" * 500 for __ in range(10)])
for __ in range(3):
t += t
self.p.center_text_vertically(t)

#class CommonTestCase(unittest.TestCase):
#
@@ -83,7 +219,7 @@
# self.files_list.sort()
#
# self.assertListEqual(files_list, self.files_list)
#
#
#if __name__ == '__main__':
# unittest.main()


if __name__ == '__main__':
unittest.main()