Vertical overflow of table in live display should scroll the content

2024/4/15 0:39:25

I'm using a 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 parameter of the live display provides a "visible" option, but this makes the header of the table vanish. Obviously this is a Table specific issue, since the header should stay but the content should be scrolled.

import time
from rich.live import Live
from rich.table import Tabletable = 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}')

The left part shows the behavior with vertical_overflow='visible' and the right part shows the desired behavior:

Example

So far I'm using a workaround with a separate data structure to hold the rows and then creating the table from scratch every time a new row is to be added. This doesn't seem to be very efficient, so I'm wondering if there's a better solution. This workaround also fails for multi-line rows as it counts them as a single row (hence overflow will occur).

from collections import deque
import os
import time
from rich.live import Live
from rich.table import Tabledef generate_table(rows):table = Table()table.add_column('Time')table.add_column('Message')for row in rows:table.add_row(*row)return tablewidth, height = os.get_terminal_size()
messages = deque(maxlen=height-4)  # save space for header and footerwith 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))
Answer

I was working on the same thing recently and couldn't find a built-in solution either. Since you're rendering a live display, the table won't have more than ~100 rows, so efficiency should not be a concern.

Here is my solution. It repeatedly removes rows from the top until the table fits. This is measured by putting a table into a Layout which truncates the table at the bottom if it does not fit.

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 Consoledef 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.heightn_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 overflowingn_rows -= 1else:breakreturn tablewidth, height = os.get_terminal_size()
messages = deque(maxlen=height-4)  # save space for header and footerwith 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))

The magic line here is if len(render_map[layout].render[-1]) > 2:. It's a hacky way to tell if the table is being printed in its entirety. If it is, the last element of render_map[layout].render will look like

[Segment('└──────────────────────────┘', Style()),Segment('                                                                         ',)
]

or like

[Segment('
',)
]

but if it is truncated it will look like

[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('                                                                         ',)
]
https://en.xdnf.cn/q/73235.html

Related Q&A

Reading KML Files Using Fastkml

Ive searched around quite a bit for this specific Python module, and havent been able to find a source which points me in the right direction.Im trying to read a KML file and display all of the feature…

Adding extra fields to django-registration form

I have a model called "Organization" that Ive setup as a User profile and I would like to have the fields from the "Organization" model show up on the registration page. How do I go…

Need to do a daily log rotation (0utc) using Python

Im an admitted noob to Python. Ive written a little logger that takes data from the serial port and writes it to a log file. Ive got a small procedure that opens the file for append, writes, then close…

Save/Load a Dictionary

Ive found a couple of others asking for help with this, but not specifically what Im trying to do. I have a dictionary full of various formats (int, str, bool, etc) and Im trying to save it so I can lo…

py2exe error handling redirection and popup

Been trying to figure out how to get py2exe to handle errors more gracefully. There are basically 2 weird things happening:1) Popup message after shutting down the program => want to suppress (not …

SQL statement for CSV files on IPython notebook

I have a tabledata.csv file and I have been using pandas.read_csv to read or choose specific columns with specific conditions.For instance I use the following code to select all "name" where …

How to draw ellipsoid with plotly

Are there any way to plot a surface like ellipsoid with plotly 3D?Currently only surfaces of the form z=f(x,y) are discussed in the docs. There is also Mesh 3D, but I found no examples for it. It seem…

PyTorch DataLoader uses same random seed for batches run in parallel

There is a bug in PyTorch/Numpy where when loading batches in parallel with a DataLoader (i.e. setting num_workers > 1), the same NumPy random seed is used for each worker, resulting in any random f…

How to fix 502 Bad Gateway Error in production(Nginx)?

When I tried to upload a big csv file of size about 600MB in my project which is hosted in the digital ocean, it tries to upload but shows 502 Bad Gateway Error (Nginx). The application is a data conve…

Shift theorem in Discrete Fourier Transform

Im trying to solve a problem with python+numpy in which Ive some functions of type that I need to convolve with another function . In order to optimize code, I performed the fft of f and g, I multipli…