I was looking over a bit of code rooted in urwid
:
import urwid
from functools import partial
from random import randint
class State(object):
def __init__(self, main_widget):
self.main_widget = main_widget
def handle_keystroke(app_state, key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
else:
loop.widget = urwid.Filler(urwid.Button('new rand int:' + str(randint(0, 100))))
app_state = State(urwid.Filler(urwid.Button('original widget')))
callback = partial(handle_keystroke, app_state)
loop = urwid.MainLoop(app_state.main_widget, unhandled_input=callback)
loop.run()
and noticed that loop
is referenced in the function unhandled_input
before it's defined. Furthermore, it's not passed as a parameter, it's just hard coded into the function by name. 1) Why is this possible, and: 2) is there a clearer alternative? It is difficult to do otherwise, as there is a circular dependencies of loop
, app_state
and callback
.
I'm not sure how much of your sample code represents the original code, but it looks like you may want to get familiar with the technique of using urwid's custom widgets wrapping text widgets, as shown in the answer with an example widget that displays a text content one line at the time.
Here is an example of writing something similar to the sample code you provided, in a design that fits urwid and Python a bit better:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function, absolute_import, division
import urwid
from random import randint
class RandomNumberWidget(urwid.WidgetWrap):
def __init__(self):
self.random_number = None
self.text_widget = urwid.Text(u'')
super(RandomNumberWidget, self).__init__(self.text_widget)
def roll(self):
self.random_number = randint(0, 100)
self.update()
def update(self):
"""Update UI
"""
if self.random_number is None:
self.text_widget.set_text('No number set')
else:
self.text_widget.set_text('Random number: %s' % self.random_number)
class App(object):
def __init__(self):
self.random_number_widget = RandomNumberWidget()
top_message = 'Press any key to get a random number, or q to quit\n\n\n'
widget = urwid.Pile([
urwid.Padding(urwid.Text(top_message),
'center', width=('relative', len(top_message))),
self.random_number_widget,
])
self.widget = urwid.Filler(widget, 'top')
def play(self):
self.random_number_widget.roll()
def play_or_exit(self, key):
if key in ('q', 'Q', 'esc'):
raise urwid.ExitMainLoop()
app.play()
if __name__ == '__main__':
app = App()
loop = urwid.MainLoop(app.widget, unhandled_input=app.play_or_exit)
loop.run()
Depending also on what you actually want to do, it could make sense to make the custom widgets respond to the keyboard events, instead of doing it all in the global handler (which is totally fine for simple programs, IMO).