pixelreceiver.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. from __future__ import print_function, absolute_import, division
  2. print('receiver start')
  3. import sys
  4. import time
  5. import socket
  6. import traceback
  7. from path import Path
  8. from rgbmatrix import RGBMatrix, RGBMatrixOptions
  9. from rgbmatrix import graphics
  10. from PIL import Image
  11. from sender import decode
  12. def asc_desc_modulation(x, interval, clamp=0.1):
  13. sub_idx = x % interval
  14. if sub_idx < interval / 2:
  15. f = (x % interval) / interval
  16. else:
  17. f = (interval - x % interval) / interval
  18. f = max(clamp, f)
  19. modulate = lambda c: (int(c[0] * f), int(c[1] * f), int(c[2] * f))
  20. return modulate
  21. def Color(colorstring):
  22. """ convert #RRGGBB to an (R, G, B) tuple """
  23. colorstring = colorstring.strip()
  24. if colorstring[0] == '#': colorstring = colorstring[1:]
  25. if len(colorstring) != 6:
  26. raise ValueError, "input #%s is not in #RRGGBB format" % colorstring
  27. r, g, b = colorstring[:2], colorstring[2:4], colorstring[4:]
  28. r, g, b = [int(n, 16) for n in (r, g, b)]
  29. return (r, g, b)
  30. class Renderer(object):
  31. def __init__(self, textColor=(255, 255, 255), brightness=255):
  32. print('initializing renderer')
  33. self._color = textColor
  34. self.setup()
  35. self._brightness = brightness
  36. self.set_brightness(brightness)
  37. def set_brightness(self, value):
  38. self._brightness = value
  39. self.matrix.brightness = value
  40. def setup(self):
  41. o = RGBMatrixOptions()
  42. o.chain_length = 4
  43. self.matrix = RGBMatrix(options=o)
  44. self.canvas = self.matrix.CreateFrameCanvas()
  45. self.color = graphics.Color(*self._color)
  46. def destroy(self):
  47. del self.canvas
  48. del self.matrix
  49. del self.color
  50. def swap(self):
  51. self.canvas = self.matrix.SwapOnVSync(self.canvas)
  52. self.canvas.Clear()
  53. def progress(self, step=0, total=0):
  54. c = self.canvas
  55. w, h = c.width, c.height
  56. col = Color('#AACCFF')
  57. if total > 0:
  58. padding = ' ' * (len(str(total-1)) - len(str(step+1)))
  59. s_len = graphics.DrawText(c, fonts['10x20'], 0, 20, self.color, '%s%s' % (padding, step+1))
  60. w = w - s_len
  61. stepsize = max(1, int(w / total))
  62. for x in range(w):
  63. modulate = asc_desc_modulation(x, stepsize)
  64. for y in range(h):
  65. c.SetPixel(s_len + x, y, *modulate(col))
  66. if x > ((step + 1) / total) * w:
  67. break
  68. self.swap()
  69. def print_text(self, text):
  70. graphics.DrawText(self.canvas, fonts['10x20'], 0, 20, self.color, text)
  71. self.swap()
  72. def scroll_text(self, text, loop=1):
  73. pos = self.canvas.width
  74. i = 1
  75. while True:
  76. len = graphics.DrawText(self.canvas, fonts['10x20'], pos, 20, self.color, text)
  77. pos -= 1
  78. if (pos + len < 0):
  79. pos = self.canvas.width
  80. i += 1
  81. if i > loop:
  82. break
  83. time.sleep(0.02)
  84. self.swap()
  85. def fit_text(self, text, width=0, fade=0):
  86. if width == 0:
  87. width = self.canvas.width
  88. ff = reversed(sorted(fonts.items(), key=lambda e: int(e[0].split('x')[0])))
  89. temp_canvas = self.matrix.CreateFrameCanvas()
  90. for name, font in ff:
  91. w = graphics.DrawText(temp_canvas, font, 0, -100, self.color, text)
  92. if w < width:
  93. break
  94. else:
  95. print('str len %s exceeds space' % w)
  96. if fade:
  97. t = time.time()
  98. while time.time() < t + fade:
  99. x = 1 - ((time.time() - t) / fade)
  100. #~ self.matrix.brightness = self._brightness * x
  101. c = graphics.Color(int(self.color.red * x), int(self.color.green * x), int(self.color.blue * x))
  102. graphics.DrawText(self.canvas, font, 0, 20, c, text)
  103. self.swap()
  104. time.sleep(0.02)
  105. else:
  106. graphics.DrawText(self.canvas, font, 0, 20, self.color, text)
  107. self.swap()
  108. try:
  109. fonts = {}
  110. fo = graphics.Font()
  111. fo.LoadFont('/matrix/fonts/10x20.bdf')
  112. fonts['10x20'] = fo
  113. r = Renderer(textColor=(96, 128, 255))
  114. print('loading fonts')
  115. files = Path('/matrix/fonts').files('*.bdf')
  116. for i, f in enumerate(files):
  117. #~ if i % 3 != 0:
  118. #~ continue
  119. r.progress(i, len(files))
  120. if not f.namebase.split('x')[0].isdigit() or not f.namebase[-1].isdigit():
  121. continue
  122. fo = graphics.Font()
  123. fo.LoadFont(f)
  124. fonts[f.namebase] = fo
  125. r.progress(0, 0)
  126. # Create a TCP/IP socket
  127. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  128. # Bind the socket to the port
  129. server_address = ('0.0.0.0', 10000)
  130. print('starting up on %s port %s' % server_address)
  131. sock.bind(server_address)
  132. r.fit_text('Matrix ready.', fade=4)
  133. while True:
  134. try:
  135. print('waiting to receive message')
  136. data, address = sock.recvfrom(4096)
  137. api_call = decode(data)
  138. # get name of namedtuple
  139. method = type(api_call).__name__
  140. print('received %s bytes from %s [method: %s]' % (len(data), address, method))
  141. r.set_brightness(255)
  142. if method == 'destroy':
  143. # kill SwapOnVSync
  144. r.destroy()
  145. # XXX: how to re-setup
  146. #~ r.setup()
  147. else:
  148. kwargs = api_call._asdict()
  149. getattr(r, method)(**kwargs)
  150. except Exception:
  151. print(traceback.format_exc())
  152. except Exception:
  153. print(traceback.format_exc())
  154. finally:
  155. print('receiver exit')