3
import urwid

palette = [('header', 'white', 'black'),
('reveal focus', 'black', 'dark cyan', 'standout'),]
content = urwid.SimpleListWalker([
        urwid.AttrMap(w, None, 'reveal focus') for w in [
            urwid.Text("This is a text string that is fairly long"),
            urwid.Divider("-"),
            urwid.Text("Short one"),
            urwid.Text("Another"),
            urwid.Divider("-"),
            urwid.Text("What could be after this?"),
            urwid.Text("The end."),]
    ])

listbox = urwid.ListBox(content)
show_key = urwid.Text("", wrap='clip')
head = urwid.AttrMap(show_key, 'header')
top = urwid.Frame(listbox, head)

def show_all_input(input, raw):
    show_key.set_text("Pressed: " + " ".join([
        unicode(i) for i in input]))
    return input

def exit_on_cr(input):
    if input == 'enter':
        raise urwid.ExitMainLoop()

loop = urwid.MainLoop(top, palette, input_filter=show_all_input, unhandled_input=exit_on_cr)
loop.run()

Pressing up or down should scroll the list but in my case it doesn't. Is there something wrong ?

EDIT 1

Code works perfectly in fact I didn't understand what scrollable lists are. I thought pressing up or down would select item in the list but it doesn't. What this does is just scroll your terminal when it doesn't have enough space to display all items at once. You must resize your terminal to a very small size to understand what this code does.

EDIT 2

To change focus when pressing up or down, a strange API has to be used. I would have loved to see an API like listbox.focus_next() / listbox.focus_previous(), but you have to handle the positions yourself. Of course, you can create your own functions (or subclass ListBox) to offer a better API

def handle_input(input):
    if input == "esc":
        raise urwid.ExitMainLoop()
    head.original_widget.set_text("key pressed: %s" % input)
    lw = listbox.body        
    if input == "up":
        # this can raise an error if we scroll past first position
        try:
            lw.set_focus(lw.get_prev(lw.get_focus()[1])[1])
        except:
            pass
    elif input == "down":
        # this can raise an error if we scroll past last position
        try: 
            lw.set_focus(lw.get_next(lw.get_focus()[1])[1])
        except:
            pass

EDIT 3

A better API :

def urwid_test():
    """
            'black', 'dark red', 'dark green', 'brown', 'dark blue',
            'dark magenta', 'dark cyan', 'light gray', 'dark gray',
            'light red', 'light green', 'yellow', 'light blue', 
            'light magenta', 'light cyan', 'white'
    """

    class MyListBox(urwid.ListBox):
        def focus_next(self):
            try: 
                self.body.set_focus(self.body.get_next(self.body.get_focus()[1])[1])
            except:
                pass
        def focus_previous(self):
            try: 
                self.body.set_focus(self.body.get_prev(self.body.get_focus()[1])[1])
            except:
                pass            

    def handle_input(input):
        if input == "esc":
            raise urwid.ExitMainLoop()
        head.original_widget.set_text("key pressed: %s" % input)
        if input == "up":
            listbox.focus_previous()
        elif input == "down":
            listbox.focus_next()
    palette = [("top","white","black"),
               ("line","light green","dark green","standout"),
               ("frame","dark magenta","white"),
               ]
    widgets = [urwid.AttrMap(widget,None,"line") for widget in
                [
                    urwid.Text("Chemma!"),
                    urwid.Divider("-"),
                    urwid.Text("Another text widget!"),
                    urwid.Divider("-"),                   
                    urwid.Text("What is your name"),
                    urwid.Divider("-"),                   
                    urwid.Text("Boy ?"),                                                            
                ]
              ]
    head    = urwid.AttrMap(urwid.Text("key pressed :",wrap="clip"),"top")
    L       = urwid.SimpleListWalker(widgets)
    listbox = MyListBox(L)
    top     = urwid.AttrMap(urwid.Frame(listbox,head),"frame")
    loop    = urwid.MainLoop(top,palette,unhandled_input=handle_input)
    loop.screen.set_terminal_properties(colors=256)
    loop.run()

if __name__ == "__main__":
    urwid_test()
4

1 回答 1

1

令人惊讶的是,您的问题需要多长时间才能得到答案,但就是这样。我追求同样的效果,发现另一个不相关的问题,但显示的代码可以满足我们的需求。

长话短说,您必须使您的小部件可选择,否则它们将无法获得焦点并且您将无法选择它们。是相关的文档部分。

总之,您必须扩展Text(或您需要选择的任何小部件)并设置_selectableTrue,同时还实现keypress()

class SelectableText(urwid.Text):
    _selectable = True

    def keypress(self, size, key):
        return key

然后只需将SelectableText对象列表传递给您SimpleListWalker,它就会神奇地工作。

于 2021-01-08T23:29:05.330 回答