I'm trying to display a help dialog over top my urwid application when a user presses the H key, but I can't seem to get it to go away. It displays without a problem. What am I missing? I've been at this for the good part of an entire day now.
I've looked at several examples describing different ways to implement this. I've played around with signals, but to no avail. I'd like to avoid having a visible button for help and rely solely on the keyboard shortcut.
import urwid
class Application(object):
'''
The console UI
'''
# The default color palette
_palette = [
('banner', 'black', 'light gray'),
('selectable', 'white', 'black'),
('focus', 'black', 'light gray')
]
def __init__(self):
self._content = [
urwid.Text('Initializing...', align = 'left')
]
# Body
self._body_walker = urwid.SimpleListWalker(self._content)
self._body_list = urwid.ListBox(self._body_walker)
self._body_padding = urwid.Padding(
self._body_list,
left = 1,
right = 1
)
self._body = urwid.LineBox(self._body_padding)
# Loop
self._loop = urwid.MainLoop(
self._body,
self._palette,
unhandled_input = self._handle_input
)
def reset_layout(self):
'''
Resets the console UI to the default layout
'''
self._loop.widget = self._body
self._loop.draw_screen()
def _handle_input(self, key):
'''
Handles user input to the console UI
Args:
key (object): A mouse or keyboard input sequence
'''
if type(key) == str:
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
elif key in ('h', 'H'):
self.dialog(
[
'Urwid v1.3.1\n',
'\n',
'Press Q to quit\n',
'Press H for help'
]
)
elif type(key) == tuple:
pass
def print(self, string = '', align = 'left'):
'''
Prints a string to the console UI
Args:
string (str): The string to print
align (str): The alignment of the printed text
'''
self._body_walker.append(
urwid.Text(string, align = align)
)
def printf(self, *strings):
'''
Prints multiple strings with different alignment
Args:
strings (tuple): A string, alignment pair
'''
self._body_walker.append(
urwid.Columns(
[
urwid.Text(string, align = align)
for string, align in strings
]
)
)
def start(self):
'''
Starts the console UI
'''
self._loop.run()
def dialog(self, text = ['']):
'''
Overlays a dialog box on top of the console UI
Args:
test (list): A list of strings to display
'''
# Header
header_text = urwid.Text(('banner', 'Help'), align = 'center')
header = urwid.AttrMap(header_text, 'banner')
# Body
body_text = urwid.Text(text, align = 'center')
body_filler = urwid.Filler(body_text, valign = 'top')
body_padding = urwid.Padding(
body_filler,
left = 1,
right = 1
)
body = urwid.LineBox(body_padding)
# Footer
footer = urwid.Button('Okay', self.reset_layout())
footer = urwid.AttrWrap(footer, 'selectable', 'focus')
footer = urwid.GridFlow([footer], 8, 1, 1, 'center')
# Layout
layout = urwid.Frame(
body,
header = header,
footer = footer,
focus_part = 'footer'
)
w = urwid.Overlay(
urwid.LineBox(layout),
self._body,
align = 'center',
width = 40,
valign = 'middle',
height = 10
)
self._loop.widget = w
Application().start()
Can you imagine how stupid I feel right now after realizing that all I needed to do was drop the parentheses on the button's callback?
footer = urwid.Button('Okay', self.reset_layout)
Here's some more sample code to play around with for whoever stumbles upon this in the future.
import urwid
class Application(object):
'''
The console UI
'''
# The default color palette
_palette = [
('banner', 'black', 'light gray'),
('selectable', 'white', 'black'),
('focus', 'black', 'light gray')
]
def __init__(self):
self._body = urwid.SolidFill('.')
self._pile = urwid.Pile(
[
self.dialog()
]
)
self._over = urwid.Overlay(
self._pile,
self._body,
align = 'center',
valign = 'middle',
width = 20,
height = 10
)
# Loop
self._loop = urwid.MainLoop(
self._over,
self._palette,
unhandled_input = self._handle_input
)
def _handle_input(self, key):
'''
Handles user input to the console UI
Args:
key (object): A mouse or keyboard input sequence
'''
if type(key) == str:
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
elif type(key) == tuple:
pass
def start(self):
'''
Starts the console UI
'''
self._loop.run()
def do(self, thing):
self._loop.widget = self._body
#self._pile.contents.clear()
def dialog(self):
'''
Overlays a dialog box on top of the console UI
'''
# Header
header_text = urwid.Text(('banner', 'Help'), align = 'center')
header = urwid.AttrMap(header_text, 'banner')
# Body
body_text = urwid.Text('Hello world', align = 'center')
body_filler = urwid.Filler(body_text, valign = 'top')
body_padding = urwid.Padding(
body_filler,
left = 1,
right = 1
)
body = urwid.LineBox(body_padding)
# Footer
footer = urwid.Button('Okay', self.do)
footer = urwid.AttrWrap(footer, 'selectable', 'focus')
footer = urwid.GridFlow([footer], 8, 1, 1, 'center')
# Layout
layout = urwid.Frame(
body,
header = header,
footer = footer,
focus_part = 'footer'
)
return layout
Application().start()