Вертикальное переполнение таблицы в режиме реального времени должно прокручивать содержимое

#python #rich

#python #богатый

Вопрос:

Я использую Live дисплей для отображения содержимого Table , которое растет со временем. В конечном итоге происходит вертикальное переполнение, и в этом случае я бы хотел, чтобы самые старые (т. Е. Самые верхние) Строки Исчезли, в то время как самые последние строки должны отображаться вместе с заголовком, т. Е. Содержимое должно прокручиваться. vertical_overflow Параметр live display предоставляет "visible" опцию, но при этом заголовок таблицы исчезает. Очевидно, что это Table специфическая проблема, поскольку заголовок должен оставаться, но содержимое должно прокручиваться.

 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' , а правая часть показывает желаемое поведение:

Пример

Пока я использую обходной путь с отдельной структурой данных для хранения строк, а затем создаю таблицу с нуля каждый раз, когда нужно добавить новую строку. Это не кажется очень эффективным, поэтому мне интересно, есть ли лучшее решение. Этот обходной путь также не работает для многострочных строк, поскольку он считает их как одну строку (следовательно, произойдет переполнение).

 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))
 

Ответ №1:

Недавно я работал над тем же самым и тоже не смог найти встроенное решение. Поскольку вы отображаете отображение в реальном времени, в таблице не будет более ~ 100 строк, поэтому эффективность не должна вызывать беспокойства.

Вот мое решение. Он повторно удаляет строки сверху, пока таблица не будет соответствовать. Это измеряется путем помещения таблицы в a Layout , которая обрезает таблицу внизу, если она не подходит.

 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: .
Это простой способ определить, печатается ли таблица полностью.
Если это так, последний элемент 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('                                                                         ',)
]
 

Комментарии:

1. Это умное решение. Это кажется несколько странным, но это определенно решает проблему. И поскольку он использует только общедоступный API rich, он также кажется надежным / стабильным.

2. Я согласен, что это халтурно — я думаю, основная проблема заключается в том, что Rich заранее не знает, насколько большой будет таблица. Я бы предположил, что существуют и более надежные решения, но основная идея по-прежнему будет «попробуйте сделать это и посмотреть».