Ian Ward wrote:
It's a good start.  I was hoping this could be done with
get_input_descriptors[1] and get_input_nonblocking[2] instead of using
threads.

[1] http://excess.org/urwid/reference.html#Screen-get_input_descriptors
[2] http://excess.org/urwid/reference.html#Screen-get_input_nonblocking

Done. Also, this version adds a handle_reactor parameter. If False, rather than looping the function simply returns a twisted Deferred that will trigger when the UI shuts down, whether via an ExitMainLoop call (callback True) or a different unhandled exception in one of the handlers passed in (errback).

-Walter

# Urwid Twisted-based main loop function
# (c) 2008 Walter Mundt

import urwid

from twisted.internet import reactor
from twisted.internet.defer import Deferred
from twisted.python.failure import Failure
from twisted.internet.abstract import FileDescriptor

class InputDescriptor(FileDescriptor):
    def __init__(self, fd, cb):
        self._fileno = fd
        self.cb = cb
        FileDescriptor.__init__(self, reactor)

    def fileno(self):
        return self._fileno

    def doRead(self):
        return self.cb()

class TwistedLoopManager(object):
    def __init__(self, screen, topmost_widget, input_filter, unhandled_input,
                       need_stop, handle_reactor):
        self.screen = screen
        self.topmost_widget = topmost_widget
        self.failure = None
        self.handle_reactor = handle_reactor
        if input_filter:
            self.input_filter = input_filter
        if unhandled_input:
            self.unhandled_input = unhandled_input
        self.need_stop = need_stop

    def input_filter(self, k):
        return k

    def unhandled_input(self, k):
        pass

    def stop(self, failure=None):
        if self.need_stop:
            self.screen.stop()
            self.need_stop = False
        self.running = False
        if failure:
            self.d.errback(failure)
        else:
            self.d.callback(True)
        if self.handle_reactor:
            reactor.stop()
            self.handle_reactor = False
        elif self.triggerid:
            self.removeSystemEventTrigger(self.triggerid)
            self.triggerid = None

    def refresh(self):
        if not self.running:
            return
        try:
            canvas = self.topmost_widget.render(self.size, focus=True)
            self.screen.draw_screen(self.size, canvas)
        except urwid.main_loop.ExitMainLoop:
            self.stop()
        except:
            self.stop(Failure())

    def onShutdown(self):
        if self.need_stop:
            self.screen.stop()
            self.need_stop = False
        if self.running:
            self.stop()

    def inputCallback(self):
        if self.running:
            timeout, events, raw = self.screen.get_input_nonblocking()
            if events:
                for event in events:
                    self.onInput(event)
        self.refresh()

    def run(self):
        self.d = Deferred()
        self.running = True
        self.triggerid = reactor.addSystemEventTrigger('before', 'shutdown', self.onShutdown)

        self.size = self.screen.get_cols_rows()
        self.refresh()
        for desc in self.screen.get_input_descriptors():
            id = InputDescriptor(desc, self.inputCallback)
            reactor.addReader(id)

        return self.d

    def onInput(self, event):
        if not self.running:
            return
        try:
            if event == 'window resize':
                self.size = self.screen.get_cols_rows()
            event = self.input_filter(event)
            if urwid.is_mouse_event(event):
                if self.topmost_widget.mouse_event(self.size, focus=True, *event):
                    event = None
            else:
                event = self.topmost_widget.keypress(self.size, event)
            if event:
                self.unhandled_input(event)
        except urwid.main_loop.ExitMainLoop:
            self.stop()
        except:
            self.stop(Failure())

def twisted_main_loop(topmost_widget, palette=[], screen=None,
               handle_mouse=True, input_filter=None, unhandled_input=None,
               handle_reactor=True):
    if not screen:
        import urwid.raw_display
        screen = urwid.raw_display.Screen()

    if palette:
        screen.register_palette(palette)

    needstop = False
    handleStartStop = not screen.started
    if handleStartStop:
        screen.start()
        needstop = True

    global mgr
    mgr = TwistedLoopManager(screen, topmost_widget, input_filter,
                             unhandled_input, needstop, handle_reactor)

    if handle_mouse:
        screen.set_mouse_tracking()

    d = mgr.run()
    if handle_reactor:
        d.addErrback(lambda fail: fail.printTraceback())
        reactor.run()
    else:
        return d

_______________________________________________
Urwid mailing list
[email protected]
http://lists.excess.org/mailman/listinfo/urwid

Reply via email to