Как заставить Рича распечатать финальную таблицу, не обрезая нижнюю часть?

Я использую инструмент печати Rich на Python. Все работает отлично, но у меня проблема. Когда я запускаю его с большим количеством контента, чем помещается в окне терминала, он обрезает все внизу (как и ожидалось). Но когда он закончится, я хочу разрешить распечатку всей таблицы. Вот репродукция:

import asyncio
import logging
import random
import time
import uuid

from rich.console import Console
from rich.layout import Layout
from rich.live import Live
from rich.panel import Panel
from rich.progress import BarColumn, Progress, TextColumn
from rich.table import Table

all_statuses = {}
task_total = 30

console = Console()

table_update_event = asyncio.Event()
table_update_running = False

events_to_progress = []

max_time = 5  # Changed to 5 seconds

logging.basicConfig(level=logging.INFO)


class InstanceStatus:
    def __init__(self, region, zone):
        # Generate a unique ID for each instance - maximum 6 characters
        self.id = f"{region}-{zone}-{uuid.uuid4()}"[:6]
        self.region = region
        self.zone = zone
        self.status = "Initializing"
        self.detailed_status = "Initializing"
        self.elapsed_time = 0
        self.instance_id = None
        self.public_ip = None
        self.private_ip = None
        self.vpc_id = None

    def combined_status(self):
        return f"{self.status} ({self.detailed_status})"


def make_progress_table():
    table = Table(show_header=True, header_style = "bold magenta", show_lines=False)
    table.add_column("ID", width=8, style = "cyan", no_wrap=True)
    table.add_column("Region", width=8, style = "green", no_wrap=True)
    table.add_column("Zone", width=8, style = "green", no_wrap=True)
    table.add_column("Status", width=15, style = "yellow", no_wrap=True)
    table.add_column("Elapsed", width=8, justify = "right", style = "magenta", no_wrap=True)
    table.add_column("Instance ID", width=15, style = "blue", no_wrap=True)
    table.add_column("Public IP", width=15, style = "blue", no_wrap=True)
    table.add_column("Private IP", width=15, style = "blue", no_wrap=True)

    sorted_statuses = sorted(all_statuses.values(), key=lambda x: (x.region, x.zone))
    for status in sorted_statuses:
        table.add_row(
            status.id[:8],
            status.region[:8],
            status.zone[:8],
            status.combined_status()[:15],
            f"{status.elapsed_time:.1f}s",
            (status.instance_id or "")[:15],
            (status.public_ip or "")[:15],
            (status.private_ip or "")[:15],
        )
    return table


def create_layout(progress, table):
    layout = Layout()
    progress_panel = Panel(
        progress,
        title = "Progress",
        border_style = "green",
        padding=(1, 1),
    )
    layout.split(
        Layout(progress_panel, size=5),
        Layout(table),
    )
    return layout


async def update_table(live):
    global table_update_running, events_to_progress, all_statuses, console
    if table_update_running:
        logging.debug("Table update already running. Exiting.")
        return

    logging.debug("Starting table update.")

    try:
        table_update_running = True
        progress = Progress(
            TextColumn("[progress.description]{task.description}"),
            BarColumn(bar_width=None),
            TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
            TextColumn("[progress.completed]{task.completed} of {task.total}"),
            TextColumn("[progress.elapsed]{task.elapsed:>3.0f}s"),
            expand=True,
        )
        task = progress.add_task("Creating Instances", total=task_total)

        while not table_update_event.is_set() or events_to_progress:
            while events_to_progress:
                event = events_to_progress.pop(0)
                all_statuses[event.id] = event
                progress.update(task, completed=len(all_statuses))

            table = make_progress_table()
            layout = create_layout(progress, table)
            live.update(layout)

            await asyncio.sleep(0.05)  # Reduce sleep time for more frequent updates

    except Exception as e:
        logging.error(f"Error in update_table: {str(e)}")
    finally:
        table_update_running = False
        logging.debug("Table update finished.")


async def main():
    global events_to_progress, all_statuses

    start_time = time.time()
    end_time = start_time + 4  # Set to 4 seconds

    statuses_to_create = [
        InstanceStatus(str(random.randint(1, 100)), str(random.randint(1, 1000)))
        for _ in range(task_total)
    ]

    with Live(console=console, refresh_per_second=20) as live:
        update_table_task = asyncio.create_task(update_table(live))

        # Distribute status creation over 4 seconds
        for i in range(task_total):
            events_to_progress.append(statuses_to_create[i])
            if (i + 1) % 10 == 0:
                await asyncio.sleep(0.4)

        # Ensure all statuses are processed
        all_statuses.update({status.id: status for status in statuses_to_create})

        # If we finished early, wait until 4 seconds have passed
        time_elapsed = time.time() - start_time
        if time_elapsed < 4:
            await asyncio.sleep(4 - time_elapsed)

        table_update_event.set()


if __name__ == "__main__":
    asyncio.run(main())

Если task_total = 30 меньше высоты вашего терминала, он работает отлично. Если оно больше (например, 100), таблица обрезается и конечный результат никогда не печатается.

попробуйте установить Layout(table,minimum_size=task_total)

ticktalk 01.07.2024 01:36
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Редактировать:

Перечитав свой вопрос, кажется, вы просто спрашиваете, как распечатать таблицу после завершения обработки. В таком случае я бы update_table вернул финальный стол. Затем добавьте следующее в конце main.

        table = await update_table_task
        live.update(table)

Оригинальный ответ:

У вас есть пара проблем. Во-первых, ваш Live не настроен на отображение переполнения. Это простое решение.

with Live(console=console, refresh_per_second=20, vertical_overflow='visible') as live:

Во-вторых, класс Layout будет основан на размере терминала, а не на размере контента. Вам нужно будет измерить размер таблицы со всеми ее границами и заголовками, а затем вам нужно будет явно указать размер макетов (внешний и внутренний). Однако, поскольку вы разделяете только вертикально, существует гораздо более простое решение. Вместо использования макета создайте Group.

Замените create_layout следующим (требуется добавить Group в список импорта из rich.console).

def create_group(progress, table):
    progress_panel = Panel(
        progress,
        title = "Progress",
        border_style = "green",
        padding=(1, 1),
        height=5,
    )
    return Group(table, progress_panel)

Тогда вы, вероятно, захотите изменить имя переменной layout в следующем (или просто избавиться от присваивания и сделать live.update(create_group(progress, table)))

            layout = create_layout(progress, table)
            live.update(layout)

Если вы не хотите менять порядок панели выполнения и таблицы, другой вариант — отобразить Layout во время обработки, чтобы индикатор выполнения был виден, а затем отобразить Group после завершения обработки.

ох, потрясающие помощники, спасибо!

aronchick 14.07.2024 17:46

Другие вопросы по теме