Hi all,

I'm experimenting with urwid in order to port an old program [1],
written using plain curses, and that nowdays is becoming a bit aged
due to the lack of flexibity of curses (no unicode, ugly UI code,
etc).

I've written a simple and quick proof of concept both to train myself
in urwid and to test the capabilities and speed of the library.

Using 0.9.8.

***test.py*** Lines 227-9. Class Panel, Method Display
I have a Frame whose body is a Columns widget with 2 elements, both identical.
Each element contains a list of Text => "content".

If this Text List is wrapped inside a
ListBox(SimpleListWalker(content), it 's rendered correctly.

But if I embed it inside a Pile(content) or Filler(Pile(content),
height=h) it fails in Text.render method (widget.py:350).

***test2.py***
I've written a small simple code to test it and it works there.


Any idea?


[1] http://inigo.katxi.org/devel/lfm/
Note that this url contains an outdated version of the program.
You can find the last - not public released - at:
http://inigo.katxi.org/misc/lfm.tar.gz

Thanks in advance,
Iñigo Serna
#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import os.path
import stat
import time

import urwid
import urwid.raw_display


ui = None
ui_size = None
c1, c2 = None, None


######################################################################
##### Utils
def get_path_contents(path):
    # FIXME: try different encodings
    if type(path) != type(u' '):
        path = unicode(path)
    files, dirs = [], []
    its = os.listdir(path)

    for it in its:
        # FIXME: try different encodings
        fname = os.path.join(path, it)
        st = os.stat(fname)
        size = st.st_size
        mtime = st.st_mtime
        mode = stat.S_IMODE(st.st_mode)
        uid = st.st_uid
        gid = st.st_gid
        if os.path.isdir(fname):
            dirs.append((it, size, mtime, mode, uid, gid, '/'))
        else:
            files.append((it, size, mtime, mode, uid, gid, ' '))
    dirs.sort()
    files.sort()
    st = os.stat(path)
    if path != os.sep:
        dirs.insert(0, ('..', st.st_size, st.st_mtime,
                        stat.S_IMODE(st.st_mode),
                        st.st_uid, st.st_gid, '/'))
    return dirs + files


def size2str(size):
    if size >= 1000000000L:
        size = '%dM' % (size/(1024*1024))
    elif size >= 1000000L:
        size = '%dK' % (size/1024)
    else:
        size = str(size)
    return size


def time2str(mtime):
    if -15552000 < (time.time() - mtime) < 15552000:
        # filedate < 6 months from now, past or future
        mtime = time.strftime('%d %b %H:%M', time.localtime(mtime))
    else:
        mtime = time.strftime('%d %b  %Y', time.localtime(mtime))
#    mtime = time.strftime('%d/%m/%Y %H:%M', time.localtime(mtime))
    return mtime


def mode2str(mode):
    return oct(mode)


######################################################################
##### MyBox
class MyBox(urwid.WidgetWrap):
    def __init__(self, widget, w, h, box_attr=None,
                 header='', header_attr=None, header_align='left', 
                 footer='', footer_attr=None, footer_align='left' ):
        """Draw a box of (w, h) fixed size around widget, optionally
        with header and footer messages."""

        assert header_align in ('left', 'center', 'right')
        assert footer_align in ('left', 'center', 'right')

        hline = u'\u2500'    # "─"
        vline = u'\u2502'    # "│"
        tlcorner = u'\u250c' # "┌"
        trcorner = u'\u2510' # "┐"
        blcorner = u'\u2514' # "└"
        brcorner = u'\u2518' # "┘"

        if not box_attr:
            box_attr = 'body'
        if not header_attr:
            header_attr = 'body'
        if not footer_attr:
            footer_attr = 'body'
        def use_attr(t):
            return urwid.AttrWrap(t, box_attr)

        if header:
            header = header[:w-4]
            if header_align == 'left':
                ll = 1
                rl = w - 3 - len(header)
            elif header_align == 'center':
                ll = (w - 2 - len(header)) / 2
                rl = ll + 1
            elif header_align == 'right':
                ll = w - 3 - len(header)
                rl = 1
            tline = urwid.Text([(box_attr, hline*ll),
                                (header_attr, header),
                                (box_attr, hline*rl)],
                               wrap='clip')
        else:
            tline = use_attr(urwid.Divider(hline))
        if footer:
            footer = footer[:w-4]
            if footer_align == 'left':
                ll = 1
                rl = w - 3 - len(footer)
            elif footer_align == 'center':
                ll = (w - 2 - len(footer)) / 2
                rl = ll + 1
            elif footer_align == 'right':
                ll = w - 3 - len(footer)
                rl = 1
            bline = urwid.Text([(box_attr, hline*ll),
                                (footer_attr, footer),
                                (box_attr, hline*rl)],
                               wrap='clip')
        else:
            bline = use_attr(urwid.Divider(hline))
        lline = use_attr(urwid.SolidFill(vline))
        rline = use_attr(urwid.SolidFill(vline))
        tlcorner = use_attr(urwid.Text(tlcorner))
        trcorner = use_attr(urwid.Text(trcorner))
        blcorner = use_attr(urwid.Text(blcorner))
        brcorner = use_attr(urwid.Text(brcorner))
        top = urwid.Columns([('fixed', 1, tlcorner),
                             tline,
                             ('fixed', 1, trcorner)])
        middle = urwid.Columns([('fixed', 1, lline),
                                widget,
                                ('fixed', 1, rline)],
                               box_columns = [0,2], focus_column = 0)
        bottom = urwid.Columns([('fixed', 1, blcorner),
                                bline,
                                ('fixed', 1, brcorner)])
        pile = urwid.Pile([('flow', top), middle, ('flow', bottom)],
                          focus_item = 0)

        urwid.WidgetWrap.__init__(self, pile)


######################################################################
##### Panels
class Panel:
    def __init__(self, path, size):
        self.active = False
        self.path = path
        self.files = get_path_contents(path)
        self.i = 0
        self.numfiles = len(self.files)
        self.SELECTED = ('montse.xcf', 'album.py')
        self.resize(size)
        self.recalc_display_area()

    def resize(self, size):
        self.w, self.h = size[0], size[1]
        self.SSIZE = 7
        self.SDATE = 12
        self.SNAME = self.w - 4 - self.SSIZE - self.SDATE
        self.gap = 3
        self.recalc_display_area()

    def recalc_display_area(self):
        h = self.h - self.gap
        n = divmod(self.i, h)[0]
        self.a = h * n
        self.z = self.a + h

    def display(self):
        if self.active:
            attr_box = 'box'
            attr_boxheader = 'boxheader'
        else:
            attr_box = attr_boxheader = 'body'
        vline = u'\u2502'    # "│"
        content = []
        t = urwid.Text([('boxtitle', 'Name'.center(self.SNAME)),
                        (attr_box, vline),
                        ('boxtitle', 'Size'.center(self.SSIZE)),
                        (attr_box, vline),
                        ('boxtitle', 'Date'.center(self.SDATE))])
        content.append(t)
        i = 0
        for name, size, mtime, mode, uid, gid, typ in self.files[self.a:self.z+1]:
            if self.active and (self.a+i == self.i):
                attr = 'cursor'
                attr2 = 'boxcursor'
            else:
                attr = 'body'
                attr2 = attr_box
            if name in self.SELECTED:
                attr += 'selected'
            t = urwid.Text([(attr, (typ + name).ljust(self.SNAME)[:self.SNAME]),
                            (attr2, vline),
                            (attr, size2str(size).rjust(self.SSIZE)),
                            (attr2, vline),
                            (attr, time2str(mtime).ljust(self.SDATE))],
                           wrap='clip')
            content.append(t)
            i += 1
        while i < self.h:
            t = urwid.Text([('body', ' '.ljust(self.SNAME)),
                            (attr_box, vline),
                            ('body', ' '.ljust(self.SSIZE)),
                            (attr_box, vline),
                            ('body', ' '.ljust(self.SDATE))],
                           wrap='clip')
            content.append(t)
            i += 1
        w = urwid.SimpleListWalker(content)
        lb = urwid.ListBox(w)
#        lb = urwid.Pile(content) # DOES NOT WORK!!!!!!!!!
        v = MyBox(lb, self.w, self.h, box_attr=attr_box,
                  header=self.path, header_attr=attr_boxheader)
        return v

    def get_filepath(self):
        return os.path.join(self.path, self.files[self.i][0])

    def toggle_active(self):
        self.active = not self.active


######################################################################
##### Main
def init_ui():
    palette = [
        ('header', 'brown', 'default'),
        ('body', 'light gray', 'default', 'standout'),
        ('boxheader', 'light red', 'default'),
        ('boxheader2', 'body'),
        ('boxtitle', 'white', 'default', ('bold','standout')),
        ('box', 'dark green', 'default'),
        ('box2', 'body'),
        ('boxcursor', 'dark blue', 'dark cyan'),
        ('bodyselected', 'yellow', 'default'),
        ('cursor', 'dark blue', 'dark cyan'),
        ('cursorselected', 'yellow', 'dark cyan')]
    ui.register_palette(palette)


def display_ui(panel):
    header = urwid.Text(' Testing this stuff ')
    header = urwid.AttrWrap(header, 'header')
    body = urwid.Columns([c1.display(), c2.display()])
    body = urwid.AttrWrap(body, 'body')
    txt = urwid.Text('File: %5d of %5d   Path: %s' % \
                     (panel.i+1, panel.numfiles, panel.get_filepath()),
                     wrap='clip')
    cmd = urwid.Edit('[EMAIL PROTECTED] %s]$ ' % (os.path.basename(panel.path) or '/'), '')
    cmd = urwid.AttrWrap(cmd, 'body')
    pile = urwid.Pile([txt, cmd])
    footer = urwid.AttrWrap(pile, 'header')
    frame = urwid.Frame(body, header=header, footer=footer)
    canvas = frame.render(ui_size, focus=1)
    ui.draw_screen(ui_size, canvas)


def parse_args():
    import sys
    if len(sys.argv) == 1:
        path1 = path2 = os.path.expanduser('~')
    elif len(sys.argv) == 2:
        path1 = path2 = sys.argv[1]
    else:
        path1 = sys.argv[1]
        path2 = sys.argv[2]
    return path1, path2


def run():
    global c1, c2, ui_size
    init_ui()
    ui_size = ui.get_cols_rows()
    cols, rows = ui_size[0], ui_size[1]
    path1, path2 = parse_args()
    c1 = Panel(path1, (cols/2, rows-3))
    c2 = Panel(path2, (cols/2, rows-3))
    c1.toggle_active()
    panel = c1
    display_ui(panel)
    quit = False
    while not quit:
        keys = None
        while not keys:
            keys = ui.get_input()
        for k in keys:
            if k == 'q':
                quit = True
                break
            elif k == 'home':
                panel.i = 0
                panel.recalc_display_area()
            elif k == 'end':
                panel.i = len(panel.files)-1
                panel.recalc_display_area()
            elif k == 'up':
                if panel.i > 0:
                    panel.i -= 1
                    if panel.i < panel.a:
                        panel.recalc_display_area()
            elif k == 'down':
                if panel.i < panel.numfiles-1:
                    panel.i += 1
                    if panel.i+1 > panel.z:
                        panel.recalc_display_area()
            elif k == 'page up':
                panel.i -= panel.h + panel.gap
                if panel.i < 0:
                    panel.i = 0
                panel.recalc_display_area()
            elif k == 'page down':
                panel.i += panel.h - panel.gap
                if panel.i > panel.numfiles - 1:
                    panel.i = panel.numfiles - 1
                panel.recalc_display_area()
            elif k == 'tab':
                if panel == c1:
                    panel = c2
                else:
                    panel = c1
                c1.toggle_active()
                c2.toggle_active()
            elif k == 'left':
                if panel.path == os.sep:
                    continue
                newpath = os.path.dirname(panel.path)
                if panel == c1:
                    c1 = Panel(newpath, (ui_size[0]/2, ui_size[1]-3))
                    del(panel)
                    panel = c1
                else:
                    c2 = Panel(newpath, (ui_size[0]/2, ui_size[1]-3))
                    del(panel)
                    panel = c2
                panel.toggle_active()
            elif k == 'right':
                file = panel.files[panel.i]
                if file[-1] == '/':
                    newpath = os.path.join(panel.path, file[0])
                    newpath = os.path.normpath(newpath)
                    if panel == c1:
                        c1 = Panel(newpath, (ui_size[0]/2, ui_size[1]-3))
                        del(panel)
                        panel = c1
                    else:
                        c2 = Panel(newpath, (ui_size[0]/2, ui_size[1]-3))
                        del(panel)
                        panel = c2
                    panel.toggle_active()
            elif k == 'window resize':
                ui_size = ui.get_cols_rows()
                cols, rows = ui_size[0], ui_size[1]
                c1.resize((ui_size[0]/2, ui_size[1]-3))
                c2.resize((ui_size[0]/2, ui_size[1]-3))
                display_ui(panel)
        display_ui(panel)


def init():
    global ui, ui_size
    ui = urwid.raw_display.Screen()
    ui_size = ui.get_cols_rows()
    ui.run_wrapper(run)


if __name__ == '__main__':
    init()
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urwid
import urwid.raw_display

ui = None
size = None


def display():
    contents = []
    for i in xrange(15):
        t = urwid.Text('Line #%d' % i)
        contents.append(t)

    header = urwid.Text('Testing this stuff', align='center')
    body = urwid.Filler(urwid.Pile(contents))
    footer = urwid.Text('More testing')
    frame = urwid.Frame(body, header=header, footer=footer)
    canvas = frame.render(size)
    ui.draw_screen(size, canvas)

def run():
    display()
    while True:
        keys = None
        while not keys:
            keys = ui.get_input()
        for k in keys:
            if k == 'q':
                return

def init():
    global ui, size
    ui = urwid.raw_display.Screen()
    size = ui.get_cols_rows()
    ui.run_wrapper(run)


if __name__ == '__main__':
    init()
_______________________________________________
Urwid mailing list
Urwid@lists.excess.org
http://lists.excess.org/mailman/listinfo/urwid

Reply via email to