A powerful DataTable widget for PySide6 applications with functionality similar to jQuery DataTable.
- Customizable Table: Easily configure columns, types, and formatting
- Data Type Detection: Automatically detect and handle different data types [NOT-IMPLEMENTED-YET]
- Type-based Sorting: Different column types sort appropriately
- Search Functionality: Global and column-specific search
- Row Collapsing: Support for expandable/collapsible rows
- Pagination: Built-in pagination with configurable page sizes [SEMI-IMPLEMENTED]
- Column Visibility: Show/hide columns easily
- Row Numbering: Show/hide vertical row numbers
- Aggregation Functions: Calculate sums, averages, percentages, etc. [SEMI-IMPLEMENTED]
- Custom Formatting: Format data display for different column types
- Observer Pattern: Event-driven architecture for clear code organization
- Builtin-Delegates: Packages has some useful delegates built-in for popular datatypes (ProgressBar, IconBoolean, ActionButtons)
- Fluent-Interface: Allow you channing calls on most methods of
DataTableinstance (return self)
pip install pyside6-datatable-widget| Version | Date | Notes |
|---|---|---|
| 1.2.2 | 2026-04-03 | - |
| 1.2.1 | 2026-03-25 | - |
| 1.2.0 | 2026-02-10 | - |
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from datatable import DataTable, DataType
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("DataTable Example")
self.resize(800, 600)
# Create central widget and layout
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
# Create DataTable
self.data_table = DataTable()
layout.addWidget(self.data_table)
# Set up columns (key, header, data_type)
columns = [
("id", "ID", DataType.NUMERIC),
("name", "Name", DataType.STRING),
("age", "Age", DataType.NUMERIC),
("active", "Active", DataType.BOOLEAN),
("progress", "Progress", DataType.PROGRESS)
]
# Set up data
data = [
{"id": 1, "name": "John", "age": 30, "active": True, "progress": 75},
{"id": 2, "name": "Jane", "age": 25, "active": False, "progress": 30},
{"id": 3, "name": "Bob", "age": 40, "active": True, "progress": 100}
]
# Apply to table
self.data_table.setColumns(columns).setData(data)
# Connect signals
self.data_table.rowSelected.connect(self.on_row_selected)
def on_row_selected(self, row, row_data):
print(f"Row {row} selected: {row_data}")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())from datatable import DataTableModel
# Create model with custom formatting
model = DataTableModel()
model.setFormattingFunction("price", lambda value: f"${value:.2f}")
model.setFormattingFunction("percentage", lambda value: f"{value:.1f}%")
# Set model to table
data_table.setModel(model)# Enable row collapsing
data_table.enableRowCollapsing(True, "subrows")
# Example data with subrows
data = [
{
"id": 1,
"name": "Category A",
"total": 1000,
"subrows": [
{"id": 101, "name": "Item A1", "total": 500},
{"id": 102, "name": "Item A2", "total": 500}
]
},
{
"id": 2,
"name": "Category B",
"total": 2000,
"subrows": [
{"id": 201, "name": "Item B1", "total": 1200},
{"id": 202, "name": "Item B2", "total": 800}
]
}
]
data_table.setData(data)
# Connect expansion signals
data_table.rowExpanded.connect(lambda row, data: print(f"Row {row} expanded"))
data_table.rowCollapsed.connect(lambda row, data: print(f"Row {row} collapsed"))# Get aggregate values
total = data_table.getAggregateValue("amount", "sum")
average = data_table.getAggregateValue("amount", "avg")
count = data_table.getAggregateValue("id", "count")
# Calculate percentage of a row value relative to total
row_data = data_table.getSelectedRow()
if row_data:
amount = row_data["amount"]
total = data_table.getAggregateValue("amount", "sum")
percentage = data_table.calculateRowPercentage(row_index, "amount")# Set custom search function for a column
model.setSearchFunction("complex_data", lambda value, term: term in str(value["name"]))
# Search in table
data_table.search("search term")
# Search specific column
matching_rows = model.searchColumn("name", "John")The library provides several built-in delegates for enhanced data visualization.
A highly customizable progress bar delegate.
- Default Behavior: Displays a progress bar using the theme's highlight color.
- Customization:
setProgressBarColor(column_key, color): Set a static base color.setProgressBarGradient(column_key, enabled): Enable a gradient effect (fade from 0% to 100% opacity).addProgressBarRange(column_key, min_pct, max_pct, color): Add color ranges (e.g., Red for <50%, Green for >80%).
# Configure Progress Bar
self.data_table.setProgressBarColor('completion', "#3b82f6") # Base color
self.data_table.setProgressBarGradient('completion', True) # Enable gradient
# Or use ranges
self.data_table.addProgressBarRange('completion', 0, 50, "#ef4444") # Red for low
self.data_table.addProgressBarRange('completion', 50, 80, "#eab308") # Yellow for medium
self.data_table.addProgressBarRange('completion', 80, 100, "#22c55e") # Green for highDisplays boolean values using SVG icons instead of text or checkboxes.
- Default Behavior: Displays a checkmark for
Trueand an 'X' forFalse. - Customization:
setIconBooleanColors(column_key, yes_color, no_color): Customize the colors for the Yes and No states.
# Configure Icon Boolean (defaults are Green/Red)
self.data_table.setIconBooleanColors('status', yes_color="#00FF00", no_color="#FF0000")Displays inline action buttons within a cell. Useful for per-row actions like Start, Stop, Delete, Edit, etc.
- Customization:
setActionButtons(column_key, buttonDefs): Configure the buttons for the column.buttonDefsis a list of dictionaries.# button_def dictionary structure: { "key": "action_id", "label": "Button Text", "color": "#HEX_COLOR", "visibleWhen": lambda row_data: True # optional visibility condition }
- Events:
- The table emits
rowActionClicked(column_key, action_key, row_data)when a button is clicked.
- The table emits
# Setup Action Buttons
button_defs = [
{"key": "start", "label": "▶ Start", "color": "#22c55e", "visibleWhen": lambda d: d.get('status') == 'PENDING'},
{"key": "stop", "label": "⏹ Stop", "color": "#ef4444", "visibleWhen": lambda d: d.get('status') == 'RUNNING'}
]
self.data_table.setActionButtons('actions', button_defs)
# Connect to the signal
self.data_table.rowActionClicked.connect(
lambda col, action, data: print(f"Action '{action}' clicked on column '{col}'")
)Main widget class that provides the UI and functionality.
setData(data) -> Self: Set table dataappendRow(row_data) -> bool: Append a row to the tableinsertRow(row_index, row_data) -> bool: Insert a row at a specific indexsetColumns(columns) -> Self: Set table columnssetVisibleColumns(columns) -> Self: Set which columns are visibleenableRowCollapsing(enabled, child_row_key) -> Self: Enable/disable row collapsingsetFormattingFunction(column_key, func) -> Self: Set formatting function. Actually, this method is alias ofModel.setFormattingFunctionsearch(term) -> Self: Search the tablesort(column_key, order) -> Self: Sort the tablesetPage(page) -> Self: Set current pagesetRowsPerPage(rows) -> Self: Set rows per pagegetData(): Get current table datagetSelectedRow(): Get selected row datagetAggregateValue(column_key, agg_type): Get aggregate value for columnsetUiSelectionType(mode, behavior) -> Self: Set selection mode and behavior for the table viewshowRowNumbers(enabled) -> Self: Show or hide row numbers (vertical header)selectAll() -> Self: This is fluent alias ofModel.selectAll()selectNone() -> Self: This is fluent alias ofModel.clearSelection()clearSelection() -> Self: This is fluent alias ofDataTable.selectNone()selectInverse() -> Self: Fluent select inversesetProgressBarColor(column_key, color) -> Self: Set base color for a progress bar columnsetProgressBarGradient(column_key, enabled) -> Self: Enable/disable gradient for a progress bar columnaddProgressBarRange(column_key, min_pct, max_pct, color) -> Self: Add a color range for a progress bar columnsetIconBooleanColors(column_key, yes_color, no_color) -> Self: Set colors for an icon boolean columnsetActionButtons(column_key, buttonDefs) -> Self: Configure inline action buttons for an ACTION_BUTTONS columnconfigureTableView(methodName, *args, **kwargs) -> Self: Generic proxy — delegate anyQTableViewsetter call by name, without coupling to a specific named facade method. Useful for one-off configurations that don't need a dedicated wrapper.
from PySide6.QtWidgets import QAbstractScrollArea
# Prevent row-count-driven sizeHint from expanding the parent window
table.configureTableView('setSizeAdjustPolicy', QAbstractScrollArea.SizeAdjustPolicy.AdjustIgnored)
# Other examples
table.configureTableView('setShowGrid', False)
table.configureTableView('setColumnWidth', 0, 120)pageChanged(page): Emitted when page changesrowSelected(row, row_data): Emitted when row is selectedrowExpanded(row, row_data): Emitted when row is expandedrowCollapsed(row, row_data): Emitted when row is collapseddataFiltered(rows): Emitted when data is filteredsortChanged(column, order): Emitted when sort order changesselectionChanged(selected, deselected): Emitted when selection changesrowActionClicked(column_key, action_key, row_data): Emitted when an inline action button is clicked
Model class that manages data and operations.
setData(data): Set model datasetColumns(columns): Set model columnssetFormattingFunction(column_key, func): Set formatting functionsetEditableColumns(editable_columns): Set which columns are editablesetVisibleColumns(visible_columns): Set which columns are visiblesetSearchFunction(column_key, func): Set search functionsetSortFunction(column_key, func): Set sort functionsetAggregationFunction(column_key, agg_type, func): Set aggregation functionenableRowCollapsing(enabled, child_row_key): Enable row collapsingsearch(term): Search all rowssearchColumn(column_key, term): Search specific columnaggregate(column_key, agg_type): Aggregate column valuescalculateRowPercentage(row_index, column_key): Calculate row percentage
rowExpandedCollapsed(int, bool): row, is_expanded. Emitted when a row, sub-rows expanded or collapsed
Note: The model is child class of
QAbstractTableModel. So these signals below are inherited (fromQAbstractItemModel).
dataChanged(topLeft, bottomRight, roles): Emitted when data in the specified range has been modified.headerDataChanged(orientation, first, last): Emitted when header data for a section changes.layoutChanged(): Emitted when layout of the model changes drastically.layoutAboutToBeChanged(): Emitted before a major layout change.modelReset(): Emitted after the model is reset.rowsAboutToBeInserted(parent, start, end): Emitted before rows are inserted.rowsInserted(parent, start, end): Emitted after rows are inserted.rowsAboutToBeRemoved(parent, start, end): Emitted before rows are removed.rowsRemoved(parent, start, end): Emitted after rows are removed.columnsAboutToBeInserted(parent, start, end): Emitted before columns are inserted.columnsInserted(parent, start, end): Emitted after columns are inserted.columnsAboutToBeRemoved(parent, start, end): Emitted before columns are removed.columnsRemoved(parent, start, end): Emitted after columns are removed.rowsMoved(parent, start, end, destination, row): Emitted after rows are moved.columnsMoved(parent, start, end, destination, column): Emitted after columns are moved.
This project is licensed under the GNU General Public License v3.0 (GPLv3).
See the LICENSE file for details.