2

The following example (taken from here: http://urwid.org/tutorial/index.html) shows how to pass key values to a callback function show_or_exit.

import urwid

def show_or_exit(key):
    if key in ('q', 'Q'):
        raise urwid.ExitMainLoop()
    txt.set_text(repr(key))

txt = urwid.Text(u"Hello World")
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill, unhandled_input=show_or_exit)
loop.run()

How can I pass another argument to show_or_exit with this callback that depend on the state of the system, that would be something like this?

...: param_val = 4
...:
...: def my_fun():
...:     #do something
...:     return param_val
...:
...: def show_or_exit(key, param_val):
...:     if key in ('q', 'Q'):
...:         raise urwid.ExitMainLoop()
...:     txt.set_text(repr(key))
...:     do_something(param_val)
...:
...: txt = urwid.Text(u"Hello World")
...: fill = urwid.Filler(txt, 'top')
...: loop = urwid.MainLoop(fill, unhandled_input=show_or_exit)
...: loop.run()
anon01
  • 10,618
  • 8
  • 35
  • 58

1 Answers1

2

So, there are several ways of doing this. You could use global variables, but I imagine you are probably asking this question because you want a better way of doing that (plus, global variables would be clunky to mutate the state anyway).

For small programs like in this example, one technique could be to use one global object storing the state:

import urwid
from functools import partial


class State(object):
    param1 = 1
    param2 = 'ola'

    def __repr__(self):
        return 'State(param1={}, param2={})'.format(self.param1, self.param2)


def show_or_exit(app_state, key):
    if key in ('q', 'Q'):
        raise urwid.ExitMainLoop()
    app_state.param1 += 1
    txt.set_text('key: {!r} state: {!r}'.format(key, app_state))


txt = urwid.Text(u"Hello World")
fill = urwid.Filler(txt, 'top')
app_state = State()
callback = partial(show_or_exit, app_state)
loop = urwid.MainLoop(fill, unhandled_input=callback)
loop.run()

I kept this example minimal for illustration purposes, but that State class would benefit A LOT from using the attrs library. Highly recommended! :)

For more complicated programs, I would suggest to build custom widgets supporting callback events and manage the state separately: you can see an example of implementing that in this solitaire game.

Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107
  • 1
    I ended up subclassing a box widget (`Frame`), adding attributes that weren't subject to the scope issues I encountered when using callbacks; this isn't so different than your solution. Thanks! – anon01 Mar 10 '18 at 19:57
  • @ConfusinglyCuriousTheThird I'd say it's pretty well-designed, yes. There are some things that are a bit non-obvious about the way the widgets work, which as far as I understood is inherent to the approach "writing widgets for the terminal" and [I think someone is already working on improving it](https://github.com/urwid/urwid/issues/247). – Elias Dorneles Mar 10 '18 at 20:25
  • I've wrote a bit [some thoughts on urwid some time ago](https://eliasdorneles.github.io/2017/04/21/urwid-is-great.html) and I plan to write more about it soon. This library has a lot of potential yet to be explored. I encourage you to keep digging! =) – Elias Dorneles Mar 10 '18 at 20:28
  • Btw I'm very curious to know which things you've found confusing, that would be very interesting! Maybe you could open more questions? :) – Elias Dorneles Mar 10 '18 at 20:30
  • Right! Well, my understand is that indeed subclassing is kind of more normal in Python than other languages. But I also think that urwid being more of a library than a framework, it doesn't have that many things out of the box that are ready to use. – Elias Dorneles Mar 11 '18 at 22:11