Skip to content

Virtual list

A scrolling list for thousands or millions of items. Only the rows in view are built: render(item) is called when an item scrolls into view, so the list costs the same whatever its length. Every row has the same item_height. pz.virtual_column is the building block underneath (rows by index, inside any scrollable), and Table uses it for its rows too.

ui.virtual_list · Category: Lists and menus · Full reference →

Virtual listVirtual list
  • For logs, search results, file lists or any list that can grow past a few hundred rows.
  • For short lists, a pz.column of List items is simpler.
import pyzed as pz
from pyzed import ui
class Logs(pz.App):
def __init__(self):
self.lines = [f"[{i:06}] request handled" for i in range(500_000)]
def view(self):
return ui.virtual_list(self.lines, lambda line: pz.text(line, font="mono", size=12),
item_height=20, id="logs")
Virtual list: A million rowsVirtual list: A million rows
A million rows
Code
pz.container(ui.virtual_list([f"row {i:,}" for i in range(1_000_000)],
lambda r: ui.list_item(r, icon="file"), height=150), width=260)
Virtual list: item_height=20Virtual list: item_height=20
item_height=20
Code
pz.container(ui.virtual_list([f"[{i:06}] request handled" for i in range(100_000)],
lambda line: pz.text(line, font="mono", size=12), item_height=20, height=150),
width=260)
Virtual list: spacing=6Virtual list: spacing=6
spacing=6
Code
pz.container(ui.virtual_list(list(range(10_000)), lambda i: ui.card(ui.label(f"Card {i}"), padding=8),
item_height=40, spacing=6, height=150), width=260)

pz.virtual_column(count, item, item_height=...) calls item(index), handy when the rows come from an array, a database cursor or a computed value. Scroll it like any scrollable: row i starts at i * (item_height + spacing).

Virtual list: Rows by indexVirtual list: Rows by index

Virtual list has 1 event of its own, listed below. Like every widget it also has the 18 common events (click, double_click, right_click, mouse_enter, drag, key_down, resize…), attached with .on(...): see Events and Events of every component.

Event Handler gets When
on_scroll (info) Callable that receives the scroll viewport, as in pz.scrollable.

Handler gets is what Pyzed passes to your callable: () means nothing. Any handler can also be a message (a value that is not callable), which goes to update().

The same events written three ways. Imperative: methods of the app change self. Declarative: the handlers produce messages and one update() decides. Functional: a pure function draws an immutable state and asks for changes with set_state(...). See Imperative, declarative and functional.

class Logs(pz.App):
def __init__(self):
self.lines = [f"[{i:06}] request handled" for i in range(100_000)]
self.top = 0
def scrolled(self, info): # on_scroll: offsets and sizes of the viewport
self.top = int(info["y"] // 20)
def view(self):
return pz.column([
ui.label(f"Line {self.top + 1} of {len(self.lines)}"),
ui.virtual_list(self.lines, lambda line: pz.text(line, font="mono", size=12),
item_height=20, height=200, on_scroll=self.scrolled),
], spacing=6)
virtual_list(
items,
render,
*,
item_height=26,
spacing=0,
width='fill',
height='fill',
padding=None,
overscan=4,
id=None,
on_scroll=None,
theme=None,
)

A scrolling list that only builds the rows in view, for thousands or millions of items. render(item) returns the widget for one item and is called when it scrolls into view. Every row has the same height.

Property Default Description
items required The items: a list or any sequence (other iterables are turned into a list).
render required Function that receives an item and returns its widget.
item_height 26 Height of every row, in pixels.
spacing 0 Space between children, in pixels.
width "fill" Width: pixels, "fill", "shrink" or "fill:N" (proportion).
height "fill" Height: pixels, "fill", "shrink" or "fill:N" (proportion).
padding None Inner padding: 8, (vertical, horizontal) or (top, right, bottom, left).
overscan 4 Extra rows built above and below the visible ones.
id None Stable identifier used to focus or scroll it with a Command.
theme None Theme to use instead of the current one (pyzed.theme.current()).

Events: on_scroll (events).

Full reference →

virtual_column(count, item, *, item_height, spacing=0.0, overscan=4, width=None)

A column of count rows of the same height that only builds the rows in view: item(index) is called when row index scrolls into view, and rows that leave it are dropped. A column of a million rows costs the same as one of fifty. Put it in a scrollable (it virtualizes against the visible area of the closest one, or the window).

Property Default Description
count required Number of rows.
item required Function that receives a row index and returns its widget.
item_height required Height of every row, in pixels.
spacing 0.0 Space between rows, in pixels.
overscan 4 Extra rows built above and below the visible ones, so fast scrolling shows no gaps.
width None Width: pixels, "fill", "shrink" or "fill:N" (proportion).

Full reference →