gpt4 book ai didi

python - 如何指示 urwid 列表框的项目多于当前显示的项目?

转载 作者:太空宇宙 更新时间:2023-11-04 02:17:03 25 4
gpt4 key购买 nike

有没有办法向用户显示 urwid 列表框在显示部分的上方/下方有其他项目?

我正在考虑类似滚动条的东西,它可以显示条目的数量。

listbox with vertical scrollbar.

或列表框顶部/底部的单独栏。

transformation of listbox while scrolling down.

如果这个行为不能实现,有什么方法可以实现这个通知?

在我的研究过程中,我发现了 this question ,它试图最终实现相同的目标。给定的答案似乎检查所有元素是否可见。不幸的是,如果某些元素在任何时候因为终端未调整大小而被隐藏,这将失去其功能。

最佳答案

我想我已经找到了第二个可视化概念的实现(列表框顶部和底部的栏)。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import urwid

ENTRIES = [letter for letter in "abcdefghijklmnopqrstuvwxyz"]

PALETTE = [
("notifier_active", "dark cyan", "light gray"),
("notifier_inactive", "black", "dark gray"),
("reveal_focus", "black", "dark cyan", "standout")
]


class MyListBox(urwid.ListBox):
def __init__(self, body, on_focus_change=None):
super().__init__(body)

self.on_focus_change = on_focus_change

# Overriden
def change_focus(self, size, position, offset_inset=0, coming_from=None, cursor_coords=None, snap_rows=None):
super().change_focus(size,
position,
offset_inset,
coming_from,
cursor_coords,
snap_rows)

# Implement a hook to be able to deposit additional logic
if self.on_focus_change != None:
self.on_focus_change(size,
position,
offset_inset,
coming_from,
cursor_coords,
snap_rows)


class App(object):
def __init__(self, entries):
# Get terminal dimensions
terminal_cols, terminal_rows = urwid.raw_display.Screen().get_cols_rows()
list_rows = (terminal_rows - 2) if (terminal_rows > 7) else 5
# (available_rows - notifier_rows) OR my preferred minimum size

# At the beginning, "top" is always visible
self.notifier_top = urwid.AttrMap(urwid.Text('^', align="center"),
"notifier_inactive")

# Determine presentation depending on size and number of elements
self.notifier_bottom = urwid.AttrMap(urwid.Text('v', align="center"),
"notifier_inactive" if (len(entries) <= list_rows) else "notifier_active")

contents = [urwid.AttrMap(urwid.Button(entry), "", "reveal_focus")
for entry in entries]

self.listbox = MyListBox(urwid.SimpleFocusListWalker(contents),
self.update_notifiers) # Pass the hook

master_pile = urwid.Pile([
self.notifier_top,
urwid.BoxAdapter(self.listbox, list_rows),
self.notifier_bottom,
])

widget = urwid.Filler(master_pile,
'top')

self.loop = urwid.MainLoop(widget,
PALETTE,
unhandled_input=self.handle_input)

# Implementation for hook
def update_notifiers(self, size, position, offset_inset, coming_from, cursor_coords, snap_rows):
# which ends are visible? returns "top", "bottom", both or neither.
result = self.listbox.ends_visible(size)

if ("top" in result) and ("bottom" in result):
self.notifier_top.set_attr_map({None:"notifier_inactive"})
self.notifier_bottom.set_attr_map({None:"notifier_inactive"})
elif "top" in result:
self.notifier_top.set_attr_map({None:"notifier_inactive"})
self.notifier_bottom.set_attr_map({None:"notifier_active"})
elif "bottom" in result:
self.notifier_top.set_attr_map({None:"notifier_active"})
self.notifier_bottom.set_attr_map({None:"notifier_inactive"})
else:
self.notifier_top.set_attr_map({None:"notifier_active"})
self.notifier_bottom.set_attr_map({None:"notifier_active"})

def handle_input(self, key):
if key in ('q', 'Q', 'esc'):
self.exit()

def start(self):
self.loop.run()

def exit(self):
raise urwid.ExitMainLoop()


if __name__ == '__main__':
app = App(ENTRIES)
app.start()

基本上,我创建了一个 urwid.Listbox 的子类,并覆盖了它的 change_focus() 方法以添加一个 Hook 。显然,这个方法是在焦点改变时在内部调用的。

实际逻辑使用 ends_visible() 方法的结果,该方法返回列表框当前可见的末端(顶部、底部、两者或两者都不)。基于此,我修改了两个周围 urwid.Text 元素的呈现。

代码生成以下 TUI:

listbox while scrolling down.



我还编写了一个基于原始规范的代码变体:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import urwid

HEADERS = ["column 1",
"column 2",
"column 3",
"column 4"]

ENTRIES = [["{}1".format(letter),
"{}2".format(letter),
"{}3".format(letter),
"{}4".format(letter)] for letter in "abcdefghijklmnopqrstuvwxyz"]

PALETTE = [
("column_headers", "white, bold", ""),
("notifier_active", "dark cyan", "light gray"),
("notifier_inactive", "black", "dark gray"),
("reveal_focus", "black", "dark cyan", "standout")
]


class SelectableRow(urwid.WidgetWrap):
def __init__(self, contents, on_select=None):
self.contents = contents
self.on_select = on_select

self._columns = urwid.Columns([urwid.Text(c) for c in contents])
self._focusable_columns = urwid.AttrMap(self._columns, '', 'reveal_focus')

super(SelectableRow, self).__init__(self._focusable_columns)

def selectable(self):
return True

def update_contents(self, contents):
# update the list record inplace...
self.contents[:] = contents

# ... and update the displayed items
for t, (w, _) in zip(contents, self._columns.contents):
w.set_text(t)

def keypress(self, size, key):
if self.on_select and key in ('enter',):
self.on_select(self)
return key

def __repr__(self):
return '%s(contents=%r)' % (self.__class__.__name__, self.contents)


class MyListBox(urwid.ListBox):
def __init__(self, body, on_focus_change=None):
super().__init__(body)

self.on_focus_change = on_focus_change

# Overriden
def change_focus(self, size, position, offset_inset=0, coming_from=None, cursor_coords=None, snap_rows=None):
super().change_focus(size,
position,
offset_inset,
coming_from,
cursor_coords,
snap_rows)

# Implement a hook to be able to deposit additional logic
if self.on_focus_change != None:
self.on_focus_change(size,
position,
offset_inset,
coming_from,
cursor_coords,
snap_rows)


class App(object):
def __init__(self, entries):
# Get terminal dimensions
terminal_cols, terminal_rows = urwid.raw_display.Screen().get_cols_rows()
list_rows = (terminal_rows - 6) if (terminal_rows > 11) else 5
# (available_rows - divider_rows - column_headers_row - notifier_rows) OR my preferred minimum size

column_headers = urwid.AttrMap(urwid.Columns([urwid.Text(c) for c in HEADERS]),
"column_headers")

# At the beginning, "top" is always visible
self.notifier_top = urwid.AttrMap(urwid.Text('^', align="center"),
"notifier_inactive")

# Determine presentation depending on size and number of elements
self.notifier_bottom = urwid.AttrMap(urwid.Text('v', align="center"),
"notifier_inactive" if (len(entries) <= list_rows) else "notifier_active")

contents = [SelectableRow(entry) for entry in entries]

self.listbox = MyListBox(urwid.SimpleFocusListWalker(contents),
self.update_notifiers) # Pass the hook

master_pile = urwid.Pile([
urwid.Divider(u'─'),
column_headers,
urwid.Divider(u'─'),
self.notifier_top,
urwid.BoxAdapter(self.listbox, list_rows),
self.notifier_bottom,
urwid.Divider(u'─'),
])

widget = urwid.Filler(master_pile,
'top')

self.loop = urwid.MainLoop(widget,
PALETTE,
unhandled_input=self.handle_input)

# Implementation for hook
def update_notifiers(self, size, position, offset_inset, coming_from, cursor_coords, snap_rows):
# which ends are visible? returns "top", "bottom", both or neither.
result = self.listbox.ends_visible(size)

if ("top" in result) and ("bottom" in result):
self.notifier_top.set_attr_map({None:"notifier_inactive"})
self.notifier_bottom.set_attr_map({None:"notifier_inactive"})
elif "top" in result:
self.notifier_top.set_attr_map({None:"notifier_inactive"})
self.notifier_bottom.set_attr_map({None:"notifier_active"})
elif "bottom" in result:
self.notifier_top.set_attr_map({None:"notifier_active"})
self.notifier_bottom.set_attr_map({None:"notifier_inactive"})
else:
self.notifier_top.set_attr_map({None:"notifier_active"})
self.notifier_bottom.set_attr_map({None:"notifier_active"})

def handle_input(self, key):
if key in ('q', 'Q', 'esc'):
self.exit()

def start(self):
self.loop.run()

def exit(self):
raise urwid.ExitMainLoop()


if __name__ == '__main__':
app = App(ENTRIES)
app.start()

唯一真正的区别是,我使用 SelectableRow 的实例而不是 urwid.Button。 (SelectableRow 取自 this answer of user elias。)

这是对应的TUI:

list box while scrolling down (more complex list content)

关于python - 如何指示 urwid 列表框的项目多于当前显示的项目?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52428684/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com