实时显示中 table 的垂直溢出应滚动内容
Vertical overflow of table in live display should scroll the content
我正在使用实时显示的Live
display to show the content of a Table
which grows over time. Eventually there is a vertical overflow and in that case I'd like the oldest (i.e. topmost) rows to vanish while the most recent rows should be shown together with the header, i.e. the content should be scrolled. The vertical_overflow
参数提供了一个"visible"
选项,但这使得table的header消失了。显然这是一个 Table
特定问题,因为 header 应该保留但内容应该滚动。
import time
from rich.live import Live
from rich.table import Table
table = Table()
table.add_column('Time')
table.add_column('Message')
with Live(table, refresh_per_second=5, vertical_overflow='visible'):
for i in range(100):
time.sleep(0.2)
table.add_row(time.asctime(), f'Event {i:03d}')
左侧部分显示 vertical_overflow='visible'
的行为,右侧部分显示所需的行为:
到目前为止,我使用的变通方法是使用单独的数据结构来保存行,然后每次添加新行时都从头开始创建 table。这似乎不是很有效,所以我想知道是否有更好的解决方案。此变通方法对于 multi-line 行也失败,因为它将它们计为单行(因此会发生溢出)。
from collections import deque
import os
import time
from rich.live import Live
from rich.table import Table
def generate_table(rows):
table = Table()
table.add_column('Time')
table.add_column('Message')
for row in rows:
table.add_row(*row)
return table
width, height = os.get_terminal_size()
messages = deque(maxlen=height-4) # save space for header and footer
with Live(generate_table(messages), refresh_per_second=5) as live:
for i in range(100):
time.sleep(0.2)
messages.append((time.asctime(), f'Event {i:03d}'))
live.update(generate_table(messages))
我最近在做同样的事情,但也找不到 built-in 解决方案。由于您正在渲染实时显示,table 不会超过 ~100 行,因此效率不应该是一个问题。
这是我的解决方案。它反复从顶部删除行,直到 table 适合。这是通过将 table 放入 Layout
来衡量的,如果 table 不合适,它会截断底部的 table。
from collections import deque
import os
import time
from rich.live import Live
from rich.table import Table
from rich.layout import Layout
from rich.console import Console
def generate_table(rows):
layout = Layout()
console = Console()
table = Table()
table.add_column('Time')
table.add_column('Message')
rows = list(rows)
# This would also get the height:
# render_map = layout.render(console, console.options)
# render_map[layout].region.height
n_rows = os.get_terminal_size()[1]
while n_rows >= 0:
table = Table()
table.add_column('Time')
table.add_column('Message')
for row in rows[-n_rows:]:
table.add_row(*row)
layout.update(table)
render_map = layout.render(console, console.options)
if len(render_map[layout].render[-1]) > 2:
# The table is overflowing
n_rows -= 1
else:
break
return table
width, height = os.get_terminal_size()
messages = deque(maxlen=height-4) # save space for header and footer
with Live(generate_table(messages), refresh_per_second=5) as live:
for i in range(100):
time.sleep(0.2)
messages.append((time.asctime(), f'Event {i:03d}'))
live.update(generate_table(messages))
这里的魔法线是if len(render_map[layout].render[-1]) > 2:
。
这是判断 table 是否完整打印的一种 hacky 方法。
如果是,render_map[layout].render
的最后一个元素将看起来像
[
Segment('└──────────────────────────┘', Style()),
Segment(' ',)
]
或喜欢
[
Segment(
'
',
)
]
但如果它被截断,它将看起来像
[
Segment('│', Style()),
Segment(' ', Style()),
Segment(
'37',
Style(color=Color('cyan', ColorType.STANDARD, number=6), bold=True, italic=False)
),
Segment(' ', Style()),
Segment(' ', Style()),
Segment('│', Style()),
Segment(' ',)
]
我正在使用实时显示的Live
display to show the content of a Table
which grows over time. Eventually there is a vertical overflow and in that case I'd like the oldest (i.e. topmost) rows to vanish while the most recent rows should be shown together with the header, i.e. the content should be scrolled. The vertical_overflow
参数提供了一个"visible"
选项,但这使得table的header消失了。显然这是一个 Table
特定问题,因为 header 应该保留但内容应该滚动。
import time
from rich.live import Live
from rich.table import Table
table = Table()
table.add_column('Time')
table.add_column('Message')
with Live(table, refresh_per_second=5, vertical_overflow='visible'):
for i in range(100):
time.sleep(0.2)
table.add_row(time.asctime(), f'Event {i:03d}')
左侧部分显示 vertical_overflow='visible'
的行为,右侧部分显示所需的行为:
到目前为止,我使用的变通方法是使用单独的数据结构来保存行,然后每次添加新行时都从头开始创建 table。这似乎不是很有效,所以我想知道是否有更好的解决方案。此变通方法对于 multi-line 行也失败,因为它将它们计为单行(因此会发生溢出)。
from collections import deque
import os
import time
from rich.live import Live
from rich.table import Table
def generate_table(rows):
table = Table()
table.add_column('Time')
table.add_column('Message')
for row in rows:
table.add_row(*row)
return table
width, height = os.get_terminal_size()
messages = deque(maxlen=height-4) # save space for header and footer
with Live(generate_table(messages), refresh_per_second=5) as live:
for i in range(100):
time.sleep(0.2)
messages.append((time.asctime(), f'Event {i:03d}'))
live.update(generate_table(messages))
我最近在做同样的事情,但也找不到 built-in 解决方案。由于您正在渲染实时显示,table 不会超过 ~100 行,因此效率不应该是一个问题。
这是我的解决方案。它反复从顶部删除行,直到 table 适合。这是通过将 table 放入 Layout
来衡量的,如果 table 不合适,它会截断底部的 table。
from collections import deque
import os
import time
from rich.live import Live
from rich.table import Table
from rich.layout import Layout
from rich.console import Console
def generate_table(rows):
layout = Layout()
console = Console()
table = Table()
table.add_column('Time')
table.add_column('Message')
rows = list(rows)
# This would also get the height:
# render_map = layout.render(console, console.options)
# render_map[layout].region.height
n_rows = os.get_terminal_size()[1]
while n_rows >= 0:
table = Table()
table.add_column('Time')
table.add_column('Message')
for row in rows[-n_rows:]:
table.add_row(*row)
layout.update(table)
render_map = layout.render(console, console.options)
if len(render_map[layout].render[-1]) > 2:
# The table is overflowing
n_rows -= 1
else:
break
return table
width, height = os.get_terminal_size()
messages = deque(maxlen=height-4) # save space for header and footer
with Live(generate_table(messages), refresh_per_second=5) as live:
for i in range(100):
time.sleep(0.2)
messages.append((time.asctime(), f'Event {i:03d}'))
live.update(generate_table(messages))
这里的魔法线是if len(render_map[layout].render[-1]) > 2:
。
这是判断 table 是否完整打印的一种 hacky 方法。
如果是,render_map[layout].render
的最后一个元素将看起来像
[
Segment('└──────────────────────────┘', Style()),
Segment(' ',)
]
或喜欢
[
Segment(
'
',
)
]
但如果它被截断,它将看起来像
[
Segment('│', Style()),
Segment(' ', Style()),
Segment(
'37',
Style(color=Color('cyan', ColorType.STANDARD, number=6), bold=True, italic=False)
),
Segment(' ', Style()),
Segment(' ', Style()),
Segment('│', Style()),
Segment(' ',)
]