import asyncio import urwid import weakref import struct import base64 import pyqrcode import socket from datetime import datetime loop = asyncio.get_event_loop() def unhandled(key): if key == 'ctrl c': raise urwid.ExitMainLoop def get_primary_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # doesn't even have to be reachable s.connect(('10.255.255.255', 1)) IP = s.getsockname()[0] except Exception as exc: print(exc) IP = '127.0.0.1' finally: s.close() return IP def get_qrcode(): primary_ip = get_primary_ip() octets = [1] octets.extend([int(_) for _ in primary_ip.split('.')[::-1]]) url = 'http://superbe.am/q?' + base64.b64encode( struct.pack('BBBBB', *octets) ).decode('utf-8') return pyqrcode.create(url) class ClientModel(object): def __init__(self): self.clients = ['127.0.0.1', '192.168.178.34'] def get_clients(self): return self.clients class SuperBeamView(urwid.WidgetWrap): palette = [ ('body', 'black', 'light gray', 'standout'), ('header', 'white', 'dark red', 'bold'), ('screen edge', 'light blue', 'dark cyan'), ('main shadow', 'dark gray', 'black'), ('line', 'black', 'light gray', 'standout'), ('bg background', 'light gray', 'black'), ('bg 1', 'black', 'dark blue', 'standout'), ('bg 1 smooth', 'dark blue', 'black'), ('bg 2', 'black', 'dark cyan', 'standout'), ('bg 2 smooth', 'dark cyan', 'black'), ('button normal', 'light gray', 'dark blue', 'standout'), ('button select', 'white', 'dark green'), ('line', 'black', 'light gray', 'standout'), ('pg normal', 'white', 'black', 'standout'), ('pg complete', 'white', 'dark magenta'), ('pg smooth', 'dark magenta', 'black'), ('qr code', 'black', 'white'), ] def __init__(self, controller): self.controller = controller self.client_model = ClientModel() urwid.WidgetWrap.__init__(self, self.main_window()) def main_shadow(self, w): bg = urwid.AttrWrap(urwid.SolidFill(u"\u2592"), 'screen edge') shadow = urwid.AttrWrap(urwid.SolidFill(u" "), 'main shadow') bg = urwid.Overlay(shadow, bg, ('fixed left', 3), ('fixed right', 1), ('fixed top', 2), ('fixed bottom', 1)) w = urwid.Overlay(w, bg, ('fixed left', 2), ('fixed right', 3), ('fixed top', 1), ('fixed bottom', 2)) return w def update_clock(self, widget_ref): widget = widget_ref() if not widget: return widget.set_text(datetime.now().isoformat()) loop.call_later(1, self.update_clock, widget_ref) def qrcode(self): qrcode = get_qrcode().terminal() qrcode = qrcode.replace('\x1b[0m\x1b[49m ', '██') qrcode = qrcode.replace('\x1b[0m\x1b[7m ', ' ') qrcode = qrcode.replace('\x1b[0m', '') qrcode = qrcode.replace('\x1b[7m', '') widgets = [] for row in qrcode.split('\n'): widgets.append(urwid.AttrWrap(urwid.Text(row), 'qr code')) w = urwid.Pile(widgets) return urwid.Filler(urwid.AttrWrap(w, 'body')) def client_list(self): clients = [] for client in self.client_model.get_clients(): clients.append(urwid.Filler(urwid.AttrWrap(urwid.Text(client), 'line'))) return urwid.Pile([(1, _) for _ in clients]) def main_window(self): header = urwid.AttrWrap(urwid.Text('SuperBeam Tui'), 'header') clock = urwid.Text('') self.update_clock(weakref.ref(clock)) footer = urwid.AttrWrap(clock, 'header') body = urwid.Pile([(44, self.qrcode()), self.client_list()]) window = urwid.Frame(body, header, footer) return self.main_shadow(window) class SuperBeamController(object): def __init__(self): self.model = ClientModel() self.view = SuperBeamView(self) def run(self): # self.loop = urwid.MainLoop(self.view, self.view.palette) self.loop = urwid.MainLoop( self.view, self.view.palette, event_loop=urwid.AsyncioEventLoop(loop=loop), unhandled_input=unhandled ) self.loop.run() def run(): screen = urwid.raw_display.Screen() size = screen.get_cols_rows() if size[0] < 87 or size[1] < 58: raise Exception('Not enough screen space to render QR code.') else: SuperBeamController().run() if __name__ == '__main__': run()