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()
按向上或向下键应该滚动列表,但在我的情况下却没有。有什么不对吗?
编辑1
代码完美无缺,事实上我不明白什么是可滚动列表。我想按向上或向下会选择列表中的项目,但事实并非如此。这样做只是在没有足够空间一次显示所有项目时滚动终端。您必须将终端的大小调整为非常小的尺寸,以了解此代码的作用。
编辑2
要在向上或向下按下时更改焦点,必须使用奇怪的API。我很想看到像listbox.focus_next()
/ listbox.focus_previous()
这样的API,但您必须自己处理这些职位。当然,您可以创建自己的函数(或子类ListBox)以提供更好的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
编辑3
更好的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()
答案 0 :(得分:2)
令人惊讶的是,您的问题花了多长时间才得到答案,但就是这样。我得到了同样的效果,发现这个 other question 是无关的,但显示的代码符合我们的要求。
长话短说,您必须使小部件可选,否则它们将无法获得焦点并且您将无法选择它们。 Here 是相关文档部分。
总而言之,您必须扩展 Text
(或您需要选择的任何小部件)并将 _selectable
设置为 True
,同时还要实现 keypress()
:
class SelectableText(urwid.Text):
_selectable = True
def keypress(self, size, key):
return key
然后只需将 SelectableText
对象列表传递给您的 SimpleListWalker
,它就会神奇地工作。