On 01/06/2017 05:03 AM, Steve D'Aprano wrote:
The second hardest problem in computer science is cache invalidation.
The *hardest* problem is naming things.
In a hierarchical tree view widget that displays items like this:
Fiction
├─ Fantasy
│ ├─ Terry Pratchett
│ │ ├─ Discworld
[snip]
but what do I call XXX and YYY?
Seriously-considering-just-hard-coding-them-as-magic-constants-ly y'rs,
I don't know if this is helpful or even relevant, but here is a class for using these
box-drawing characters that I wrote for my own use. (I'm just a hobby programmer...)
It's not very elegant and not particularly convenient to use, but it is usable. And I hesitate
to post it because it is so long (approx 90 line docstring to explain its use, and ending with
some test code). In any case, here it is...
<code>
# boxch.py
"""
BoxChr class to handle box-drawing characters.
The character is selected by a two-character string:
'tl', 'tm', 'tr' -- Top Left, Top Mid, Top Right: ┌ ┬ ┐
'ml', 'mm', 'mr' -- Mid Left, Mid Mid, Mid Right: ├ ┼ ┤
'bl', 'bm', 'br' -- Bot Left, Bot Mid, Bot Right: └ ┴ ┘
'hz', 'vt' -- Horizontal and Vertical lines: ─ │
Case is ignored. Invalid selection string returns a dot: '·'
NOTE: It is currently disabled, but the code is still
available to handle small and large dots with 'dt' and 'dd'.
These both ignore the style.
The style is selected by a two-character string:
The characters are 's', 'd' and 'b' for single, double and bold.
The first character defines the horizontal components,
the second defines the vertical components.
The valid styles are 'ss', 'sd', 'sb', 'ds', 'dd', 'bs', 'bb'.
The potential styles 'db' and 'bd' are invalid.
The class defaults to 'ss' -- single horizontal, single vertical.
Case is ignored. Invalid style string raises ValueError.
NOTE: The following examples assume bc has been set to a BoxChr class.
bc = BoxChr() # Style defaults to 'ss'
or
bc = BoxChr('sd') # Style set to single/double, or whatever desired.
(Examples assume 'ss' style.)
Attributes:
style: set or return the style-code string.
Case is ignored. Invalid style raises ValueError.
Examples: bc.style = 'ds' -- sets style to double/single.
st = bc.style -- sets variable st to current style code.
Methods:
[] (__getitem__): Returns selected box character.
Case is ignored. Invalid selector returns a dot: '·'
Example: bc['tm'] returns '┬'
bxstr(): Returns a string created from a sequence of box character codes
and 'normal' characters. (in this description 'selector' refers
to the two-character codes as defined above.)
Each element of the given sequence (list or tuple) must be a
string, either a series of selector codes, or a 'normal' string
(one without any box selectors). The box character selector string
must start with 'BX' followed by the selector codes, separated by
spaces. The 'BX' must be upper case, but the case of the selector
codes is ignored. A space between the 'BX' prefix and the first
selector code is optional. This selector string can only contain
selector codes. 'Normal' characters are simply given as normal
strings, interspersed with the selector strings in the given
sequence.
If the selection is only box characters, it can opionally be passed
as a single string rather than enclosing it in a list.
Example:
seq = ['BX ml hz', ' TEXT ', 'BX hz mr']
bc.bxstr(seq) returns '├─ TEXT ─┤'
Note:
If you need a string beginning with 'BX' it has to be given
as a single-character string 'B' followed by a string containing
the remaining text. Example 'BX etc' must be given as:
['B', 'X etc'].
boxtext(): Create boxed text, returned as a single string with
embedded newlines.
Parameters:
txt The text to use
tsize Expand tabs to specified number of spaces, Default is 4
just '<', '^' or '>' as left/center/right justification.
Default is '<' — left-justified.
tall If True, inserts a blank line above and below the text.
If False, no extra blank lines are used.
Default is True.
Text can be either a single string with embedded newlines (ie. a
triple-quoted string) or list of strings. Trailing blank lines
are removed. Center and right justification will strip whitespace
from both ends of the lines. Left justification (the default)
only strips trailing whitespace.
Width is based on the length of the longest line, plus two spaces
before and after.
"""
class BoxChr:
def __init__(self, style='ss'):
self._styles = ('ss', 'sd', 'sb', 'ds', 'dd', 'bs', 'bb')
self._keys = ('tl', 'tm', 'tr', 'ml', 'mm', 'mr',
'bl', 'bm', 'br', 'hz', 'vt') # 'dt', 'dd')
self._boxchrs = {
'tl' : ('┌', '╓', '┎', '╒', '╔', '┍', '┏'),
'tm' : ('┬', '╥', '┰', '╤', '╦', '┯', '┳'),
'tr' : ('┐', '╖', '┒', '╕', '╗', '┑', '┓'),
'ml' : ('├', '╟', '┠', '╞', '╠', '┝', '┣'),
'mm' : ('┼', '╫', '╂', '╪', '╬', '┿', '╋'),
'mr' : ('┤', '╢', '┨', '╡', '╣', '┥', '┫'),
'bl' : ('└', '╙', '┖', '╘', '╚', '┕', '┗'),
'bm' : ('┴', '╨', '┸', '╧', '╩', '┷', '┻'),
'br' : ('┘', '╜', '┚', '╛', '╝', '┙', '┛'),
'hz' : ('─', '─', '─', '═', '═', '━', '━'),
'vt' : ('│', '║', '┃', '│', '║', '│', '┃')
# 'dt' : ('·', '·', '·', '·', '·', '·', '·'),
# 'dd' : ('•', '•', '•', '•', '•', '•', '•')
}
self._setStyle(style)
def _setStyle(self, style):
"""Set the class style"""
try:
self._style = self._styles.index(style.lower())
except IndexError:
raise ValueError
def _getStyle(self):
"""Return the class style code"""
return self._styles[self._style]
style = property(_getStyle, _setStyle)
def __getitem__(self, key):
"""Select and return the box character"""
try:
return self._boxchrs[key.lower()][self._style]
except KeyError:
return '·'
def bxstr(self, slst):
"""Create a string from a given sequence of strings"""
def cnvst(s):
"""Convert selector string to box character string"""
bs = []
for sc in s: # For each selector code
bs.append(self.__getitem__(sc)) # Get box char
return ''.join(bs)
out = []
if isinstance(slst, str): # Input is string?
slst = [slst] # Make it a list
for ss in slst: # For each string in list
if ss.startswith('BX'): # Check if selector or normal string
out.append(cnvst(ss[2:].split())) # Selector
else:
out.append(ss) # Normal string
return ''.join(out)
def boxtext(self, txt, tsize=4, just='<', tall=True):
"""Create boxed text"""
if just not in '<^>': # Check for valid justification code
just = '<'
if isinstance(txt, str): # Check for string input
txt = txt.split('\n') # Convert to list
while txt[-1].rstrip() == '': # Delete trailing blank lines
txt = txt[:-1]
if just == '<': # Left just, only strip on right
txt = [line.rstrip() for line in txt]
else: # Otherwise strip both ends
txt = [line.strip() for line in txt]
txt = [line.expandtabs(tsize) for line in txt] # Cnvt tabs to spaces
maxlen = max([len(line) for line in txt]) + 4 # Find longest line
# Create the boxed text
out = []
out.append(self.bxstr('BXtl ' + 'hz ' * maxlen + 'tr'))
if tall:
out.append(self.bxstr(['BXvt', ' ' * maxlen, 'BXvt']))
for line in txt:
out.append(self.bxstr(['BXvt', ' {:{}{}} '.
format(line, just, maxlen-4), 'BXvt']))
if tall:
out.append(self.bxstr(['BXvt', ' ' * maxlen, 'BXvt']))
out.append(self.bxstr('BXbl ' + 'hz ' * maxlen + 'br'))
return '\n'.join(out)
#======================================
if __name__ == '__main__':
def grid(bc):
"""Print a box"""
print(bc.bxstr(['BXtl hz hz hz TM hz Hz hZ tr']))
print(bc.bxstr(['BXvt', ' ', 'BXvt', ' ', 'BXvt']))
print(bc.bxstr('BX ml hz hz hz mm hz hz hz mr'))
print(bc.bxstr(['BX vt', ' ', 'BXvt', ' ', 'BXvt']))
print(bc.bxstr('BXbl hz hz hz bm hz hz hz br'))
bc = BoxChr()
# Check all chars in all styles
for style in bc._styles:
bc.style = style
grid(bc)
# Verify error test
try:
bc.style = 'xx'
except ValueError:
print('Invaled style')
# Test boxtext() method
jab = """'Twas brillig, and the slithy toves
\tDid gyre and gimbol in the wabe.
All mimsy were the borogoves,
\tAnd the momraths outgrabe.
"""
bc.style='ds'
print(bc.boxtext(jab))
print(bc.boxtext(jab, tsize=8))
print(bc.boxtext(jab, just='^'))
print(bc.boxtext(jab, just='>'))
print(bc.boxtext(jab, tall=False))
</code>
--
-=- Larry -=-
--
https://mail.python.org/mailman/listinfo/python-list