HTML output screenshot with Urwid ncurses library
Urwid is a ncurses library for Python, you may check older posts if you’re not familiar about it. So far, we’ve seen some useful functionalities and tricks, to run urwid, therefor, it’s time to see something (almost) useless but geek, and in the spirit of ncurses programs, as urwid allow to take “screenshots” of the application, with a HTML output.
The problem with the reference guide of urwid, it’s that is not so easy to
figure out how to get things done as we want. This corresponds to the screen
capture part of the reference guide. But the easiest way to get started it’s
to check source code, and not the real code of urwid, but the code who
actually generate the tutorial of urwid, they do use this functionality for
this part. Check in the tarball, docgen_tutorial.py to get examples.
Let’s take our own example, based on the code we’ve done in earlier post, for
reminder, we obtain a list of item (couple title/description), in a
urwid.Frame that allow to divide the space in three parts, header (the
selected item), the body (list of the items), the footer (edit the description
of current item). It was where we was last time. So let’s add a screenshot of
it.
The idea
The screenshot will be triggered by the s keystroke, the idea is to start over the main loop, and pass in arguments the sequence of keystroke wanted.
Trick about screen size
We need to pass in the screenshot the size of the screen. It will be a tuple (y, x), it could be some random numbers, but it could be useful for the screenshot, and many other functionalities, to get the real size of the screen, and their is a variable for that.
self.loop = urwid.MainLoop(self.view, palette, unhandled_input=self.keystroke)
#[...]
#This variable contain the size in a tuple (x, y)
size = self.loop.screen_size
About the output
The output will come in a variable, but after every stroke we ask, their will be a screenshot, retrieve in a list, we need to choose the good one, probably the last one.
The screenshot code
import urwid.html_fragment
# [...]
def keystroke(self, input)
# [...]
if input is 's':
init = urwid.html_fragment.screenshot_init
collect = urwid.html_fragment.screenshot_collect
init([self.loop.screen_size], [['down']*3, ['enter'], ['q']])
MyApp()
with open ('output.html', 'w') as f:
for l in collect()[-1]:
f.write(l)
In this code, we prepare two functions.
initwill be pass the sequence of keystroke, quite forward to understand.collectretrieve the result, as it was said earlier, it gives a list, we want only the latest element.
The result it place in the file output.html that we can see with firefox.
The result
selected: item 3: Cras a magna sit amet fel... item 0 Lorem ipsum dolor sit amet, consectetur adipiscing elit. item 1 Cras a magna sit amet felis fringilla lobortis. item 2 Sed sollicitudin, nulla id viverra pulvinar. item 3 Cras a magna sit amet felis fringilla lobortis. item 4 Lorem ipsum dolor sit amet, consectetur adipiscing elit. item 5 Sed sollicitudin, nulla id viverra pulvinar. item 6 Sed sollicitudin, nulla id viverra pulvinar. item 7 Lorem ipsum dolor sit amet, consectetur adipiscing elit. item 8 Cras a magna sit amet felis fringilla lobortis. item 9 Lorem ipsum dolor sit amet, consectetur adipiscing elit. item 10 Lorem ipsum dolor sit amet, consectetur adipiscing elit. item 11 Sed sollicitudin, nulla id viverra pulvinar. item 12 Sed sollicitudin, nulla id viverra pulvinar. item 13 Cras a magna sit amet felis fringilla lobortis. item 14 Cras a magna sit amet felis fringilla lobortis. item 15 Cras a magna sit amet felis fringilla lobortis. item 16 Sed sollicitudin, nulla id viverra pulvinar. item 17 Sed sollicitudin, nulla id viverra pulvinar. item 18 Lorem ipsum dolor sit amet, consectetur adipiscing elit. item 19 Lorem ipsum dolor sit amet, consectetur adipiscing elit. item 20 Cras a magna sit amet felis fringilla lobortis. item 21 Cras a magna sit amet felis fringilla lobortis. item 22 Sed sollicitudin, nulla id viverra pulvinar. item 23 Cras a magna sit amet felis fringilla lobortis.
The full code
import urwid
import random
import urwid.html_fragment
class ItemWidget (urwid.WidgetWrap):
def __init__ (self, id, description):
self.id = id
self.title = urwid.Text('item %s' % str(id))
self.description = urwid.Text(description)
self.item = [
('fixed', 15, urwid.Padding(urwid.AttrWrap( self.title, 'body', 'focus'), left=2)),
urwid.AttrWrap(self.description, 'body', 'focus'),
]
self.content = 'item %s: %s...' % (str(id), description[:25])
w = urwid.Columns(self.item)
self.__super.__init__(w)
def selectable (self):
return True
def keypress(self, size, key):
return key
class CustomEdit(urwid.Edit):
__metaclass__ = urwid.signals.MetaSignals
signals = ['done']
def keypress(self, size, key):
if key == 'enter':
urwid.emit_signal(self, 'done', self.get_edit_text())
return
elif key == 'esc':
urwid.emit_signal(self, 'done', None)
return
urwid.Edit.keypress(self, size, key)
class MyApp(object):
def __init__(self):
palette = [
('body','dark blue', '', 'standout'),
('focus','dark red', '', 'standout'),
('head','light red', 'black'),
]
lorem = [
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'Sed sollicitudin, nulla id viverra pulvinar.',
'Cras a magna sit amet felis fringilla lobortis.',
]
items = []
for i in range(100):
item = ItemWidget(i, random.choice(lorem))
items.append(item)
header = urwid.AttrMap(urwid.Text('selected:'), 'head')
walker = urwid.SimpleListWalker(items)
self.listbox = urwid.ListBox(walker)
self.view = urwid.Frame(urwid.AttrWrap(self.listbox, 'body'), header=header)
self.loop = urwid.MainLoop(self.view, palette, unhandled_input=self.keystroke)
urwid.connect_signal(walker, 'modified', self.update)
self.loop.run()
def update(self):
focus = self.listbox.get_focus()[0].content
self.view.set_header(urwid.AttrWrap(urwid.Text('selected: %s' % str(focus)), 'head'))
def keystroke (self, input):
if input in ('q', 'Q'):
raise urwid.ExitMainLoop()
if input is 'enter':
focus = self.listbox.get_focus()[0].content
self.view.set_header(urwid.AttrWrap(urwid.Text('selected: %s' % str(focus)), 'head'))
if input == 'e':
self.edit()
if input is 's':
init = urwid.html_fragment.screenshot_init
collect = urwid.html_fragment.screenshot_collect
init([self.loop.screen_size], [['down']*3, ['enter'], ['q']])
MyApp()
with open ('output.html', 'w') as f:
for l in collect()[-1]:
f.write(l)
def edit(self):
self.foot = CustomEdit(' >> ')
self.view.set_footer(self.foot)
self.view.set_focus('footer')
urwid.connect_signal(self.foot, 'done', self.edit_done)
def edit_done(self, content):
self.view.set_focus('body')
urwid.disconnect_signal(self, self.foot, 'done', self.edit_done)
if content:
focus = self.listbox.get_focus()[0]
focus.description.set_text(content)
self.view.set_footer(None)
if __name__ == '__main__':
MyApp()
About