+ def clear_things(self, _):
+ self.things = []
+
+ 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.
+ """
+
+ def __init__(self, log_widget, map_widget, turn_widget,
+ message_container):
+ self.log_widget = log_widget
+ self.map_widget = map_widget
+ self.turn_widget = turn_widget
+ self.message_container = message_container
+
+ def handle_input(self, trigger):
+ """On input from other thread, either quit or write to widget text.
+
+ Serves as a receiver to urwid's watch_pipe mechanism, with trigger
+ the data that a pipe defined by watch_pipe delivers. To avoid
+ buffering trouble, we don't care for that data beyond the fact that
+ its receival triggers this function: The sender is to write the
+ data it wants to deliver into the container referenced by
+ self.message_container, and just pipe the trigger to inform us
+ about this.
+
+ If the message delivered is 'BYE', quits Urwid.
+ """
+
+ def mapdraw_command(prefix, func):
+ n = len(prefix)
+ if len(msg) > n and msg[:n] == prefix:
+ m = getattr(self.map_widget, func)
+ m(msg[n:])
+ return True
+ return False
+
+ def turndraw_command(prefix, func):
+ n = len(prefix)
+ if len(msg) > n and msg[:n] == prefix:
+ m = getattr(self.turn_widget, func)
+ m(msg[n:])
+ return True
+ return False
+
+ msg = self.message_container[0]
+ if msg == 'BYE':
+ raise urwid.ExitMainLoop()
+ return
+ found_command = False
+ try:
+ found_command = turndraw_command('NEW_TURN ', 'set_turn') or (
+ mapdraw_command('NEW_TURN ', 'clear_things') or
+ mapdraw_command('TERRAIN\n', 'update_terrain') or
+ mapdraw_command('THING ', 'update_things') or
+ mapdraw_command('MAP_SIZE ', 'update_map_size'))
+ except ArgumentError as e:
+ self.log_widget.add('ARGUMENT ERROR: ' + msg + '\n' + str(e))
+ else:
+ if not found_command:
+ self.log_widget.add('UNHANDLED INPUT: ' + msg)
+ del self.message_container[0]
+
+ def recv_loop(self):
+ """Loop to receive messages from socket and deliver them to urwid.
+
+ Waits for self.server_output to become empty (this signals that the
+ input handler is finished / ready to receive new input), then writes
+ finished message from socket to self.server_output, then sends a single
+ b' ' through self.urwid_pipe_write_fd to trigger the input handler.
+ """
+ import os
+ for msg in plom_socket_io.recv(self.socket):
+ while len(self.server_output) > 0:
+ pass
+ self.server_output += [msg]
+ os.write(self.urwid_pipe_write_fd, b' ')
+
+ def run(self):
+ """Run in parallel main and recv_loop thread."""
+ self.recv_loop_thread.start()
+ self.main_loop.run()
+ self.recv_loop_thread.join()
+
+
+s = socket.create_connection(('127.0.0.1', 5000))
+u = UrwidSetup(s)
+u.run()