You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Python的Rich工具完整打印最终表格不截断底部?

解决Rich Live模式下大表格任务完成后无法完整显示的问题

问题场景

使用Rich的Live组件实时更新任务状态表格时,当表格行数超过终端窗口高度,底部内容会被截断;任务完成后,仍无法看到完整的表格内容。

解决方案

Live组件的作用是在终端固定区域动态刷新内容,因此会受限于终端窗口尺寸。要在任务完成后显示完整表格,只需在Live上下文结束后,直接用console.print()输出最终生成的完整表格即可——Rich会自动处理超出终端高度的内容,支持滚动查看。

修改步骤

  1. 在main()函数中,退出Live的with上下文后,调用console.print(make_progress_table())
  2. 确保此时all_statuses已经包含所有任务的最终状态,等待表格更新任务完全结束

修改后的完整代码

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 = 100  # 改为大数值测试

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()
        await update_table_task  # 等待更新任务完成,确保表格状态最新

    # 任务完成后打印完整表格
    console.print("\n[bold green]任务完成,完整状态表格如下:[/bold green]")
    console.print(make_progress_table())


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

关键修改点

  • 在main()函数的Live上下文结束后,添加了console.print(make_progress_table()),直接输出完整表格
  • 增加了await update_table_task,确保表格更新任务完全结束后再打印最终结果,避免数据不一致

内容的提问来源于stack exchange,提问作者aronchick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:44:55