Skip to main content

DataTable: virtualization & server-side data

Recipes for QCustomDataTablePro. Pro is the commercial add-on (dev-time entitlement, royalty-free runtime — see Licensing); the free table is in-memory only.

from custom_widgets_pro import QCustomDataTablePro
from Custom_Widgets.QCustomDataTable import DataTableColumn

Large in-memory data (still virtualized)

setData on the Pro table feeds rows through the virtualized path — only the scrolled-to window is materialised, so 100k rows stay smooth:

table = QCustomDataTablePro()
table.setColumns([DataTableColumn("id", type="number"),
DataTableColumn("name"), DataTableColumn("score", type="number")])
table.setData([{"id": i, "name": f"row {i}", "score": i * 7 % 1000}
for i in range(100_000)])
table.show()
print(table.loadedRowCount(), "of", table.totalRowCount()) # window vs total

A lazy fetch callback

When the data lives in a database or API, give the table a window-fetching callback instead of all the rows. The table calls it as the user scrolls:

def fetch(offset, limit):
# return up to `limit` row dicts starting at `offset`
return db.query("SELECT * FROM sales LIMIT ? OFFSET ?", (limit, offset))

table.setFetchCallback(fetch, total=db.count("sales"))

Pass total=None for a streaming/unknown-size source — the table fetches until a short/empty window comes back.

A full DataProvider (server-side sort & filter)

For correct ordering and filtering across the whole dataset (not just the loaded window), implement a DataProvider and push sort/filter down to the source:

from custom_widgets_pro import DataProvider

class SalesProvider(DataProvider):
def totalRowCount(self, filter=None):
return db.count("sales", search=filter)

def fetch(self, offset, limit, sort=None, filter=None):
# sort is (column_key, ascending) or None; filter is a search string or None
order = ""
if sort:
key, ascending = sort
order = f"ORDER BY {key} {'ASC' if ascending else 'DESC'}"
return db.query_sales(offset, limit, search=filter, order=order)

table.setDataProvider(SalesProvider())

# these now push down to the provider (whole-set correctness):
from qtpy.QtCore import Qt
table.sortBy(2, Qt.DescendingOrder)
table.setFilterText("acme")

Adapting a plain callable

CallableDataProvider wraps a function; it auto-detects whether your callable takes (offset, limit) or (offset, limit, sort, filter):

from custom_widgets_pro import CallableDataProvider
table.setDataProvider(CallableDataProvider(fetch, total=100_000))