Efficient Qt Frozen Columns and Rows | Objexx Labs
Qt provides a rich GUI framework with excellent support for tables but when your need a feature that isn't directly supported things can get complicated quickly. One such feature for tables is a so-called "frozen" column or row that should remain fixed like the headers when scrolling. Qt table widget and view classes do not support frozen columns or rows and implementing them can be both tricky to get right and an efficiency problem.
Frozen rows and columns are useful when you they contain label-like content that doesn't belong in the header for some reason, such as it is editable. In our case, we use a frozen row in numerical tables for the dimensional units of the columns as in the example shown here. As you scroll the table it is useful to keep the units in view and sometimes the units can be changed via drop-down combo boxes to convert the values.
Qt's Frozen Column Example presents the basics of an approach but we found it lacking in a few ways:
- Frozen row support isn't covered but is readily built by analogy.
It is seriously and unnecessarily inefficient.
The reason is that the frozen table view shares the regular table view model so it has to call setColumnHidden on all the non-frozen columns (or the analogous call for a frozen row).
For a large table this is bad enough if the table size is static but for an editable table you have to re-hide all those columns or rows whenever the table structure changes.
We found this to be a real performance problem even with tables of modest size.
The fix is relatively simple: derive a model for the frozen table that only has one column or row.
For the frozen row case it looks something like this in PySide/PyQt:
class MyTableFrozenRowModel( MyTableModel ): def __init__( self, parent = None ): MyTableModel.__init__( self, parent ) def rowCount( self, index = None ): return 1 # Treat the frozen table as having just the one frozen row
The effect on performance is dramatic. This could probably also be done as a proxy model with similar performance benefits.
- It is fragile and has visual defects if used as written. For example, the updateFrozenTableGeometry implementation is wrong because the verticalHeader width is not always set yet when it gets called. The work-around is to set the table and frozen table verticalHeader widths yourself to a reasonable value and then call setGeometry.
- It neglects to mention that when you insert or remove rows, for a frozen column, or columns, for a frozen row, you have to emit layoutChanged on the frozen table model.
- It forgets some other details that you need, such as coupling setFont for the two table views.
With careful attention to these details a defect-free and efficient Qt frozen column or row table can be implemented.