I am having trouble initializing a QTableView with fixed column header column widths.
When I display the table for the first time, the columns seem to use the default horizontalHeaderDefaultSectionSize property from the Qt Designer's property editor.
I also have the horizontalHeaderStretchLastSection property set to true.
If I double-click on each of the columns, the layout snaps to the correct width, however I have to do this for each of the columns (except the last which is set to stretch as I just described). I am not sure why the initial layout does not respect the widths in my code.
Has this something to do with sizeHints in a model for a horizontalHeader? I do not want to automatically size to the contents of the table (as the table can grow to a very large size, I just want the initial sizes to be correct).
I set up my views using a table driven approach. The table contains a sample fixed size string or a length that I use to size the header (using font metrics):
using TableSpec = std::vector<
std::tuple<
QTableView*,
QStandardItemModel*,
std::vector<TableColumnSpec>
>
>;
Define the layout for tables in the main window.
// Views with proxy sort filters need special handling
// as the view does not directly have access to the model.
const TableSpec gTableSpecs = { {
// Thread colored logger.
{
ui->logView,
mpLogViewModel.get(),
{
TableColumnSpec("Time", "Timestamp", "06-09-2023 20:01:33.334"),
TableColumnSpec("Thread", "Thread ID", "0x00000000"),
TableColumnSpec("Level", "Severity level", "critical"),
TableColumnSpec("Description", "Description", "description")
}
},
} };
The above code shows just one of my tables (the one that shows a log table). This table uses a custom delegate that is assigned as follows (I don't think the delegate is involved with laying out the initial view of the horizontal header though), I also assign the mpLogViewModel (std::unique_ptr<QStandardItemModel>) to the view from here:
// Assign item delegate to color the rows using the threadID (column1).
ui->logView->setItemDelegate(mpLogItemDelegate.get());
// Associate each model with its respective view.
ui->logView->setModel(mpLogViewModel.get());
Now that the item delegate and model have been associated with the ui->logView (QTableView), I initialize the table(s) as follows:
// Layout the main application tables & headers.
initTableHeaders(gTableSpecs);
void
initTableHeaders(const TableSpec& rTableSpec)
{
for (const auto& [pView, pModel, colSpecs] : rTableSpec) {
const auto fm = pView->fontMetrics();
const auto rowHeight = fm.lineSpacing();
pView->verticalHeader()->setDefaultSectionSize(rowHeight);
pView->verticalHeader()->setMaximumSectionSize(rowHeight);
pView->verticalHeader()->setMinimumSectionSize(rowHeight);
pView->verticalHeader()->setVisible(false);
pView->horizontalHeader()->setMinimumSectionSize(25);
pView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
pView->horizontalHeader()->setStretchLastSection(true);
pView->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
pView->horizontalHeader()->setSortIndicatorShown(true);
pView->setSelectionBehavior(QAbstractItemView::SelectRows);
for (auto col = 0; col < static_cast<int>(colSpecs.size()); ++col) {
const auto headerLabel = new QStandardItem(colSpecs[col].mColumnTitle);
headerLabel->setTextAlignment(colSpecs[col].mAlignment);
pModel->setHorizontalHeaderItem(col, headerLabel);
const auto columnWidth = std::visit(Overload{
[&](const QString& rSampleStr)->int {
if (!rSampleStr.isEmpty()) {
return fm.horizontalAdvance(rSampleStr);
}
return fm.horizontalAdvance(colSpecs[col].mColumnTitle);
},
[&](const auto stringLength)->int {
if (stringLength > 0) {
return stringLength * fm.averageCharWidth();
}
return fm.horizontalAdvance(colSpecs[col].mColumnTitle);
},
}, colSpecs[col].mSizeInfo);
// Add 20 to make room for the sort arrow.
pView->setColumnWidth(col, columnWidth + 20);
}
}
}
The problem is that when the log is displayed, it looks like this:
