Rearranging code: moving the content creation functions to a new class SimpleTextAndIcons to have them out of the way when importing only LedNameBadge.

rebase020124
Ben 1 year ago
parent 46574a7348
commit d13f9ebf9f
  1. 97
      led-badge-11x44.py
  2. 18
      tests/test_led-badge-11x44.py

@ -70,7 +70,9 @@ except:
__version = "0.12" __version = "0.12"
font_11x44 = (
class SimpleTextAndIcons:
font_11x44 = (
# 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' # 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00,
0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00,
@ -222,9 +224,9 @@ font_11x44 = (
0x10, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Û 0x10, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Û
0x60, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ù 0x60, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ù
0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, # Ÿ 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, # Ÿ
) )
charmap = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \ charmap = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \
u'abcdefghijklmnopqrstuvwxyz' + \ u'abcdefghijklmnopqrstuvwxyz' + \
u'0987654321^ !"\0$%&/()=?` °\\}][{' + \ u'0987654321^ !"\0$%&/()=?` °\\}][{' + \
u"@ ~ |<>,;.:-_#'+* " + \ u"@ ~ |<>,;.:-_#'+* " + \
@ -232,15 +234,12 @@ charmap = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \
u"àäòöùüèéêëôöûîïÿç" + \ u"àäòöùüèéêëôöûîïÿç" + \
u"ÀÅÄÉÈÊËÖÔÜÛÙŸ" u"ÀÅÄÉÈÊËÖÔÜÛÙŸ"
char_offsets = {} char_offsets = {}
for i in range(len(charmap)): for i in range(len(charmap)):
char_offsets[charmap[i]] = 11 * i char_offsets[charmap[i]] = 11 * i
# print(i, charmap[i], char_offset[charmap[i]]) # print(i, charmap[i], char_offset[charmap[i]])
bitmap_preloaded = [([], 0)] bitmap_named = {
bitmaps_preloaded_unused = False
bitmap_named = {
'ball': (array('B', ( 'ball': (array('B', (
0b00000000, 0b00000000,
0b00000000, 0b00000000,
@ -286,30 +285,48 @@ bitmap_named = {
'owncloud': (array('B', (0x00, 0x01, 0x02, 0x03, 0x06, 0x0c, 0x1a, 0x13, 0x11, 0x19, 0x0f, 'owncloud': (array('B', (0x00, 0x01, 0x02, 0x03, 0x06, 0x0c, 0x1a, 0x13, 0x11, 0x19, 0x0f,
0x78, 0xcc, 0x87, 0xfc, 0x42, 0x81, 0x81, 0x81, 0x81, 0x43, 0xbd, 0x78, 0xcc, 0x87, 0xfc, 0x42, 0x81, 0x81, 0x81, 0x81, 0x43, 0xbd,
0x00, 0x00, 0x00, 0x80, 0x80, 0xe0, 0x30, 0x10, 0x28, 0x28, 0xd0)), 3, '\x14'), 0x00, 0x00, 0x00, 0x80, 0x80, 0xe0, 0x30, 0x10, 0x28, 0x28, 0xd0)), 3, '\x14'),
} }
bitmap_builtin = {}
for i in bitmap_named: bitmap_builtin = {}
for i in bitmap_named:
bitmap_builtin[bitmap_named[i][2]] = bitmap_named[i] bitmap_builtin[bitmap_named[i][2]] = bitmap_named[i]
def bitmap_char(ch): def __init__(self):
self.bitmap_preloaded = [([], 0)]
self.bitmaps_preloaded_unused = False
def add_preload_img(self, filename):
self.bitmap_preloaded.append(SimpleTextAndIcons.bitmap_img(filename))
self.bitmaps_preloaded_unused = True
def are_preloaded_unused(self):
return self.bitmaps_preloaded_unused == True
@staticmethod
def get_named_bitmaps_keys():
return SimpleTextAndIcons.bitmap_named.keys()
def bitmap_char(self, ch):
""" Returns a tuple of 11 bytes, """ Returns a tuple of 11 bytes,
ch = '_' returns (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255) ch = '_' returns (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255)
The bits in each byte are horizontal, highest bit is left. The bits in each byte are horizontal, highest bit is left.
""" """
if ord(ch) < 32: if ord(ch) < 32:
if ch in bitmap_builtin: if ch in SimpleTextAndIcons.bitmap_builtin:
return bitmap_builtin[ch][:2] return SimpleTextAndIcons.bitmap_builtin[ch][:2]
global bitmaps_preloaded_unused self.bitmaps_preloaded_unused = False
bitmaps_preloaded_unused = False return self.bitmap_preloaded[ord(ch)]
return bitmap_preloaded[ord(ch)]
o = char_offsets[ch] o = SimpleTextAndIcons.char_offsets[ch]
return (font_11x44[o:o + 11], 1) return (SimpleTextAndIcons.font_11x44[o:o + 11], 1)
def bitmap_text(text): def bitmap_text(self, text):
""" returns a tuple of (buffer, length_in_byte_columns_aka_chars) """ returns a tuple of (buffer, length_in_byte_columns_aka_chars)
We preprocess the text string for substitution patterns We preprocess the text string for substitution patterns
"::" is replaced with a single ":" "::" is replaced with a single ":"
@ -326,21 +343,22 @@ def bitmap_text(text):
if re.match('^[0-9]*$', name): # py3 name.isdecimal() if re.match('^[0-9]*$', name): # py3 name.isdecimal()
return chr(int(name)) return chr(int(name))
if '.' in name: if '.' in name:
bitmap_preloaded.append(bitmap_img(name)) self.bitmap_preloaded.append(SimpleTextAndIcons.bitmap_img(name))
return chr(len(bitmap_preloaded) - 1) return chr(len(self.bitmap_preloaded) - 1)
return bitmap_named[name][2] return SimpleTextAndIcons.bitmap_named[name][2]
text = re.sub(r':([^:]*):', replace_symbolic, text) text = re.sub(r':([^:]*):', replace_symbolic, text)
buf = array('B') buf = array('B')
cols = 0 cols = 0
for c in text: for c in text:
(b, n) = bitmap_char(c) (b, n) = self.bitmap_char(c)
buf.extend(b) buf.extend(b)
cols += n cols += n
return (buf, cols) return (buf, cols)
def bitmap_img(file): @staticmethod
def bitmap_img(file):
""" returns a tuple of (buffer, length_in_byte_columns) """ returns a tuple of (buffer, length_in_byte_columns)
""" """
from PIL import Image from PIL import Image
@ -373,13 +391,13 @@ def bitmap_img(file):
return (buf, cols) return (buf, cols)
def bitmap(arg): def bitmap(self, arg):
""" if arg is a valid and existing path name, we load it as an image. """ if arg is a valid and existing path name, we load it as an image.
Otherwise, we take it as a string. Otherwise, we take it as a string.
""" """
if os.path.exists(arg): if os.path.exists(arg):
return bitmap_img(arg) return SimpleTextAndIcons.bitmap_img(arg)
return bitmap_text(arg) return self.bitmap_text(arg)
class LedNameBadge: class LedNameBadge:
@ -391,6 +409,7 @@ class LedNameBadge:
) )
_have_pyhidapi = False _have_pyhidapi = False
@staticmethod @staticmethod
def init_class(): def init_class():
try: try:
@ -429,11 +448,13 @@ or
print("""Please with Linux or MacOS or help us implement support for """ + sys.platform) print("""Please with Linux or MacOS or help us implement support for """ + sys.platform)
sys.exit(1) sys.exit(1)
@staticmethod @staticmethod
def _expand_tuple(l): def _expand_tuple(l):
l = l + (l[-1],) * (8 - len(l)) # repeat last element l = l + (l[-1],) * (8 - len(l)) # repeat last element
return l return l
@staticmethod @staticmethod
def header(lengths, speeds, modes, blinks, ants, brightness=100, date=datetime.now()): def header(lengths, speeds, modes, blinks, ants, brightness=100, date=datetime.now()):
""" lengths[0] is the number of chars of the first text """ lengths[0] is the number of chars of the first text
@ -476,6 +497,7 @@ or
return h return h
@staticmethod @staticmethod
def write(buf): def write(buf):
if len(buf) > 8192: if len(buf) > 8192:
@ -536,7 +558,7 @@ if __name__ == '__main__':
parser.add_argument('-p', '--preload', metavar='FILE', action='append', parser.add_argument('-p', '--preload', metavar='FILE', action='append',
help=argparse.SUPPRESS) # "Load bitmap images. Use ^A, ^B, ^C, ... in text messages to make them visible. Deprecated, embed within ':' instead") help=argparse.SUPPRESS) # "Load bitmap images. Use ^A, ^B, ^C, ... in text messages to make them visible. Deprecated, embed within ':' instead")
parser.add_argument('-l', '--list-names', action='version', help="list named icons to be embedded in messages and exit", parser.add_argument('-l', '--list-names', action='version', help="list named icons to be embedded in messages and exit",
version=':' + ': :'.join(bitmap_named.keys()) + ': :: or e.g. :path/to/some_icon.png:') version=':' + ': :'.join(SimpleTextAndIcons.get_named_bitmaps_keys()) + ': :: or e.g. :path/to/some_icon.png:')
parser.add_argument('message', metavar='MESSAGE', nargs='+', parser.add_argument('message', metavar='MESSAGE', nargs='+',
help="Up to 8 message texts with embedded builtin icons or loaded images within colons(:) -- See -l for a list of builtins") help="Up to 8 message texts with embedded builtin icons or loaded images within colons(:) -- See -l for a list of builtins")
parser.add_argument('--mode-help', action='version', help=argparse.SUPPRESS, version=""" parser.add_argument('--mode-help', action='version', help=argparse.SUPPRESS, version="""
@ -562,16 +584,17 @@ if __name__ == '__main__':
""" % sys.argv[0]) """ % sys.argv[0])
args = parser.parse_args() args = parser.parse_args()
creator = SimpleTextAndIcons()
if args.preload: if args.preload:
for file in args.preload: for filename in args.preload:
bitmap_preloaded.append(bitmap_img(file)) creator.add_preload_img(filename)
bitmaps_preloaded_unused = True
msg_bitmaps = [] msg_bitmaps = []
for msg_arg in args.message: for msg_arg in args.message:
msg_bitmaps.append(bitmap(msg_arg)) msg_bitmaps.append(creator.bitmap(msg_arg))
if bitmaps_preloaded_unused == True: if creator.are_preloaded_unused():
print( print(
"\nWARNING:\n Your preloaded images are not used.\n Try without '-p' or embed the control character '^A' in your message.\n") "\nWARNING:\n Your preloaded images are not used.\n Try without '-p' or embed the control character '^A' in your message.\n")
@ -580,8 +603,8 @@ if __name__ == '__main__':
for msg_bitmap in msg_bitmaps: for msg_bitmap in msg_bitmaps:
# trivial hack to support 12x48 badges: # trivial hack to support 12x48 badges:
# patch extra empty lines into the message stream. # patch extra empty lines into the message stream.
for o in reversed(range(1, int(len(msg_bitmap[0]) / 11) + 1)): for i in reversed(range(1, int(len(msg_bitmap[0]) / 11) + 1)):
msg_bitmap[0][o * 11:o * 11] = array('B', [0]) msg_bitmap[0][i * 11:i * 11] = array('B', [0])
else: else:
print("Type: 11x44") print("Type: 11x44")

@ -54,15 +54,29 @@ class Test(TestCase):
self.assertNotEqual(buf1[38:38 + 6], buf2[38:38 + 6]) self.assertNotEqual(buf1[38:38 + 6], buf2[38:38 + 6])
def test_bitmap_png(self): def test_bitmap_png(self):
buf = testee.bitmap("resources/bitpatterns.png") creator = testee.SimpleTextAndIcons()
buf = creator.bitmap("resources/bitpatterns.png")
self.assertEqual((array('B', self.assertEqual((array('B',
[128, 64, 32, 16, 8, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 32, 0, 1, 2, 3, [128, 64, 32, 16, 8, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 32, 0, 1, 2, 3,
4, 5, 15, 31, 63, 127, 255]), 4, 5, 15, 31, 63, 127, 255]),
3), buf) 3), buf)
def test_bitmap_text(self): def test_bitmap_text(self):
buf = testee.bitmap("/:HEART2:\\") creator = testee.SimpleTextAndIcons()
buf = creator.bitmap("/:HEART2:\\")
self.assertEqual((array('B', self.assertEqual((array('B',
[0, 0, 2, 6, 12, 24, 48, 96, 192, 128, 0, 0, 12, 30, 63, 63, 63, 31, 15, 7, 3, 1, 0, 96, [0, 0, 2, 6, 12, 24, 48, 96, 192, 128, 0, 0, 12, 30, 63, 63, 63, 31, 15, 7, 3, 1, 0, 96,
240, 248, 248, 248, 240, 224, 192, 128, 0, 0, 128, 192, 96, 48, 24, 12, 6, 2, 0, 0]), 240, 248, 248, 248, 240, 224, 192, 128, 0, 0, 128, 192, 96, 48, 24, 12, 6, 2, 0, 0]),
4), buf) 4), buf)
def test_preload(self):
creator = testee.SimpleTextAndIcons()
self.assertFalse(creator.are_preloaded_unused())
creator.add_preload_img("resources/bitpatterns.png")
self.assertTrue(creator.are_preloaded_unused())
buf = creator.bitmap("\x01")
self.assertFalse(creator.are_preloaded_unused())
self.assertEqual((array('B',
[128, 64, 32, 16, 8, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 32, 0, 1, 2, 3,
4, 5, 15, 31, 63, 127, 255]),
3), buf)

Loading…
Cancel
Save