+ self.main_loop = urwid.MainLoop(self.setup_widgets())
+ self.server_output = ['']
+ input_handler = getattr(self.InputHandler(self.reply_widget,
+ self.map_widget,
+ self.server_output),
+ 'handle_input')
+ self.urwid_pipe_write_fd = self.main_loop.watch_pipe(input_handler)
+ self.recv_loop_thread = threading.Thread(target=self.recv_loop)
+
+ def setup_widgets(self):
+ """Return container widget with all widgets we want on our screen.
+
+ Sets up an urwid.Pile inside a returned urwid.Filler; top to bottom:
+ - an EditToSocket widget, prefixing self.socket input with 'SEND: '
+ - self.reply_widget, a urwid.Text widget printing self.socket replies
+ - a 50-col wide urwid.Padding container for self.map_widget, which is
+ to print clipped map representations
+ """
+ edit_widget = self.EditToSocket(self.socket, 'SEND: ')
+ self.reply_widget = urwid.Text('')
+ self.map_widget = urwid.Text('', wrap='clip')
+ map_box = urwid.Padding(self.map_widget, width=50)
+ widget_pile = urwid.Pile([edit_widget, self.reply_widget, map_box])
+ return urwid.Filler(widget_pile)
+
+ class EditToSocket(urwid.Edit):
+ """Extends urwid.Edit with socket to send input on 'enter' to."""
+
+ def __init__(self, socket, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.socket = socket
+
+ def keypress(self, size, key):
+ """Extend super(): on Enter, send .edit_text, and empty it."""
+ if key != 'enter':
+ return super().keypress(size, key)
+ plom_socket_io.send(self.socket, self.edit_text)
+ self.edit_text = ''
+
+ class InputHandler:
+ """Delivers data from other thread to widget via message_container.
+
+ The class only exists to provide handle_input as a bound method, with
+ widget and message_container pre-set, as (bound) handle_input is used
+ as a callback in urwid's watch_pipe – which merely provides its
+ callback target with one parameter for a pipe to read data from an
+ urwid-external thread.
+ """