Foreword
Qt Programming – QTableView freezes rows and columns simultaneously. Like the title, first check if the effect is what you need. The code snippets found online are either incomplete or not what you want. If you only need to freeze rows or columns, please read the previous blog Qt Programming-QTableView Freeze Rows or Freeze Columns or Freeze Partial Cells, with less code.
Freeze rows and columns with headers at the same time:
Also freeze rows and columns without headers:
Principle
Principle of freezing rows or columns: Use three tableviews with the same content. The top tableview displays the intersecting content, the middle layer displays the frozen row tableview and the frozen column tableview. Hide the non-frozen content, and the lower tableview displays all the content. The lower tableview slides normally. This has the effect of freezing rows or columns.
Code
The code transformation comes from Qt’s own examples. The macro variables FREEZE_COL and FREEZE_ROW can be used to control frozen rows or columns, and the macro variable TABLE_HEAD can control the header display. Download the complete project code.
The main code is as follows:
freezetablewidget.h
#ifndef FREEZETABLEWIDGET_H #define FREEZETABLEWIDGET_H #include <QTableView> //! [Widget definition] class FreezeTableWidget : public QTableView { Q_OBJECT public: FreezeTableWidget(QAbstractItemModel * model); ~FreezeTableWidget(); protected: void resizeEvent(QResizeEvent *event) override; QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override; void scrollTo (const QModelIndex & amp; index, ScrollHint hint = EnsureVisible) override; private: QTableView *frozenCroTableView; //Freeze rows and freeze column intersections in TableView QTableView *frozenColTableView; //TableView of frozen columns QTableView *frozenRowTableView; //TableView for frozen rows void initCroTable(); void initColTable(); void initRowTable(); void updateFrozenCroTableGeometry(); void updateFrozenColTableGeometry(); void updateFrozenRowTableGeometry(); private slots: void updateSectionWidth(int logicalIndex, int oldSize, int newSize); void updateSectionHeight(int logicalIndex, int oldSize, int newSize); private: //The number of frozen rows and columns int m_iFreezeCols = 3; int m_iFreezeRows = 3; }; //! [Widget definition] #endif // FREEZETABLEWIDGET_H
freezetablewidget.cpp
#include "freezetablewidget.h" #include <QScrollBar> #include <QHeaderView> #include <QDebug> #define FREEZE_COL 1 //Freeze column switch #define FREEZE_ROW 1 //Freeze row switch #define TABLE_HEAD 0 //Whether the table header is displayed //! [constructor] FreezeTableWidget::FreezeTableWidget(QAbstractItemModel * model) { /* Freezing rows or freezing columns Principle: There are essentially two tableviews FreezeTableWidget displays all table data normally The frozenColTableView table is placed on top of the FreezeTableWidget and only displays the frozen columns, so that the FreezeTableWidget below will have the effect of freezing the columns if it slides normally. The principle of freezing rows and columns at the same time is similar, but there are three tableviews. The intersection of the tableviews of frozen rows and frozen columns should be placed at the top as a single tableview. The following is the tableview of frozen rows and frozen columns. The bottom is the tableview of FreezeTableWidget. */ verticalHeader()->setVisible(TABLE_HEAD); horizontalHeader()->setVisible(TABLE_HEAD); setModel(model); #if (FREEZE_COL & amp; & amp; FREEZE_ROW) frozenCroTableView = new QTableView(this); initCroTable(); #endif #if FREEZE_COL frozenColTableView = new QTableView(this); initColTable(); #endif #if FREEZE_ROW frozenRowTableView = new QTableView(this); initRowTable(); #endif //connect the headers and scrollbars of both tableviews together #if FREEZE_COL connect(horizontalHeader(), & amp;QHeaderView::sectionResized, this, & amp;FreezeTableWidget::updateSectionWidth); #endif #if FREEZE_ROW connect(verticalHeader(), & amp;QHeaderView::sectionResized, this, & amp;FreezeTableWidget::updateSectionHeight); #endif //LUpdate //Freeze the column and the vertical scroll bar can slide normally #if FREEZE_COL connect(frozenColTableView->verticalScrollBar(), & amp;QAbstractSlider::valueChanged, verticalScrollBar(), & amp;QAbstractSlider::setValue); connect(verticalScrollBar(), & amp;QAbstractSlider::valueChanged, frozenColTableView->verticalScrollBar(), & amp;QAbstractSlider::setValue); #endif //Freeze the row and the horizontal scroll bar can slide normally #if FREEZE_ROW connect(frozenRowTableView->horizontalScrollBar(), & amp;QAbstractSlider::valueChanged, horizontalScrollBar(), & amp;QAbstractSlider::setValue); connect(horizontalScrollBar(), & amp;QAbstractSlider::valueChanged, frozenRowTableView->horizontalScrollBar(), & amp;QAbstractSlider::setValue); #endif } //! [constructor] FreezeTableWidget::~FreezeTableWidget() { #if FREEZE_COL delete frozenColTableView; #endif #if FREEZE_ROW delete frozenRowTableView; #endif #if FREEZE_COL & amp; & amp; FREEZE_ROW delete frozenCroTableView; #endif } //! [init part1] void FreezeTableWidget::initCroTable() { frozenCroTableView->setModel(model()); frozenCroTableView->setObjectName("frozenCroTableView"); frozenCroTableView->setFocusPolicy(Qt::NoFocus); frozenCroTableView->verticalHeader()->setFixedWidth(verticalHeader()->width()); frozenCroTableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); frozenCroTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); #if !TABLE_HEAD frozenCroTableView->horizontalHeader()->hide(); frozenCroTableView->verticalHeader()->hide(); #endif viewport()->stackUnder(frozenCroTableView); //! [init part1] //! [init part2] frozenCroTableView->setStyleSheet("#frozenCroTableView{ border: none;" "background-color: #AEC8FF;" "selection-background-color: #999}"); //for demo purposes frozenCroTableView->setSelectionModel(selectionModel()); //LUpdate //Hide data other than frozen columns for (int col = m_iFreezeCols; col < model()->columnCount(); + + col) frozenCroTableView->setColumnHidden(col, true); for(int i = 0; i < m_iFreezeCols; i + + ) { frozenCroTableView->setColumnWidth(i, columnWidth(0)); } //Hide data in rows other than frozen rows for (int row = m_iFreezeRows; row < model()->rowCount(); + + row) frozenCroTableView->setRowHidden(row, true); for(int i = 0; i < m_iFreezeRows; i + + ) { frozenCroTableView->setRowHeight(i, rowHeight(0)); } frozenCroTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); frozenCroTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); frozenCroTableView->show(); updateFrozenCroTableGeometry(); setHorizontalScrollMode(ScrollPerPixel); setVerticalScrollMode(ScrollPerPixel); frozenCroTableView->setVerticalScrollMode(ScrollPerPixel); frozenCroTableView->setHorizontalScrollMode(ScrollPerPixel); } //! [init part1] void FreezeTableWidget::initColTable() { frozenColTableView->setModel(model()); frozenColTableView->setObjectName("frozenColTableView"); frozenColTableView->setFocusPolicy(Qt::NoFocus); frozenColTableView->verticalHeader()->setFixedWidth(verticalHeader()->width()); frozenColTableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); frozenColTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); #if !TABLE_HEAD frozenColTableView->horizontalHeader()->hide(); frozenColTableView->verticalHeader()->hide(); #endif #if FREEZE_COL & amp; & amp; FREEZE_ROW frozenColTableView->stackUnder(frozenCroTableView); #else viewport()->stackUnder(frozenColTableView); #endif //! [init part1] //! [init part2] frozenColTableView->setStyleSheet("#frozenColTableView{ border: none;" "background-color: #8EDE21;" "selection-background-color: #999}"); //for demo purposes frozenColTableView->setSelectionModel(selectionModel()); //LUpdate //Hide data other than frozen columns for (int col = m_iFreezeCols; col < model()->columnCount(); + + col) frozenColTableView->setColumnHidden(col, true); for(int i = 0; i < m_iFreezeCols; i + + ) { frozenColTableView->setColumnWidth(i, columnWidth(0)); } frozenColTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); frozenColTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); frozenColTableView->show(); updateFrozenColTableGeometry(); setHorizontalScrollMode(ScrollPerPixel); setVerticalScrollMode(ScrollPerPixel); frozenColTableView->setVerticalScrollMode(ScrollPerPixel); frozenColTableView->setHorizontalScrollMode(ScrollPerPixel); } void FreezeTableWidget::initRowTable() { frozenRowTableView->setModel(model()); frozenRowTableView->setObjectName("frozenRowTableView"); frozenRowTableView->setFocusPolicy(Qt::NoFocus); frozenRowTableView->verticalHeader()->setFixedWidth(verticalHeader()->width()); frozenRowTableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); frozenRowTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); #if !TABLE_HEAD frozenRowTableView->horizontalHeader()->hide(); frozenRowTableView->verticalHeader()->hide(); #endif #if FREEZE_COL frozenRowTableView->stackUnder(frozenColTableView); #else viewport()->stackUnder(frozenRowTableView); #endif //! [init part1] //! [init part2] frozenRowTableView->setStyleSheet("#frozenRowTableView{ border: none;" "background-color: #f44c46;" "selection-background-color: #999}"); //for demo purposes frozenRowTableView->setSelectionModel(selectionModel()); //LUpdate //Hide data in rows other than frozen rows for (int row = m_iFreezeRows; row < model()->rowCount(); + + row) frozenRowTableView->setRowHidden(row, true); for(int i = 0; i < m_iFreezeRows; i + + ) { frozenRowTableView->setRowHeight(i, rowHeight(0)); } frozenRowTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); frozenRowTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); frozenRowTableView->show(); updateFrozenRowTableGeometry(); setHorizontalScrollMode(ScrollPerPixel); setVerticalScrollMode(ScrollPerPixel); frozenRowTableView->setVerticalScrollMode(ScrollPerPixel); frozenRowTableView->setHorizontalScrollMode(ScrollPerPixel); } //! [init part2] //! [sections] void FreezeTableWidget::updateSectionWidth(int logicalIndex, int /* oldSize */, int newSize) { qDebug() << "updateSectionWidth" << logicalIndex << newSize; //LUpdate #if FREEZE_COL if (logicalIndex == m_iFreezeCols-1){ int width = 0; for(int i = 0; i< m_iFreezeCols-1; i + + ) { width + = columnWidth(i); } for(int i = 0; i< m_iFreezeCols; i + + ) { frozenColTableView->setColumnWidth(i, (newSize + width)/m_iFreezeCols); } updateFrozenColTableGeometry(); } #else frozenColTableView->setColumnWidth(logicalIndex, newSize); #endif } void FreezeTableWidget::updateSectionHeight(int logicalIndex, int /* oldSize */, int newSize) { qDebug() << "updateSectionHeight" << logicalIndex << newSize; //LUpdate #if FREEZE_ROW if (logicalIndex == m_iFreezeRows-1){ int height = 0; for(int i = 0; i< m_iFreezeRows-1; i + + ) { height + = rowHeight(i); } for(int i = 0; i< m_iFreezeRows; i + + ) { frozenRowTableView->setRowHeight(i, (newSize + height)/m_iFreezeRows); } updateFrozenRowTableGeometry(); } #else frozenRowTableView->setRowHeight(logicalIndex, newSize); #endif } //! [sections] //! [resize] void FreezeTableWidget::resizeEvent(QResizeEvent * event) { QTableView::resizeEvent(event); #if FREEZE_COL updateFrozenColTableGeometry(); #endif #if FREEZE_ROW updateFrozenRowTableGeometry(); #endif #if FREEZE_ROW & amp; & amp; FREEZE_COL updateFrozenCroTableGeometry(); #endif } //! [resize] //! [navigate] QModelIndex FreezeTableWidget::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) { QModelIndex current = QTableView::moveCursor(cursorAction, modifiers); #if FREEZE_COL if (cursorAction == MoveLeft & amp; & amp; current.column() > 0 & amp; & amp; visualRect(current).topLeft().x() < frozenColTableView->columnWidth(0) ){ const int newValue = horizontalScrollBar()->value() + visualRect(current).topLeft().x() - frozenColTableView->columnWidth(0); horizontalScrollBar()->setValue(newValue); } #endif #if FREEZE_ROW if(cursorAction == MoveDown & amp; & amp; current.row() > 0 & amp; & amp; visualRect(current).topLeft().y() < frozenRowTableView->rowHeight(0)) { const int newValue = verticalScrollBar()->value() + visualRect(current).topLeft().y() - frozenRowTableView->rowHeight(0); verticalScrollBar()->setValue(newValue); } #endif return current; } //! [navigate] void FreezeTableWidget::scrollTo (const QModelIndex & amp; index, ScrollHint hint){ if (index.column() > 0) QTableView::scrollTo(index, hint); } //! [geometry] void FreezeTableWidget::updateFrozenCroTableGeometry() { qDebug() << "updateFrozenCroTableGeometry =="; //LUpdate int width = 0, height = 0, x = 0, y = 0; qDebug() << "ver:" << verticalHeader()->width() << verticalHeader()->height(); qDebug() << "hor:" << horizontalHeader()->width() << horizontalHeader()->height(); qDebug() << "frame:" << frameWidth() << frameRect().width()<< frameRect().height() << frameRect().x() << frameRect().y (); x = frameWidth(); y = frameWidth(); #if FREEZE_COL & amp; & amp; FREEZE_ROW width = verticalHeader()->width(); for(int i = 0; i< m_iFreezeCols; i + + ) { width + = columnWidth(i); } height = horizontalHeader()->height(); for(int i = 0; i< m_iFreezeRows; i + + ) { height + = rowHeight(i); } #else width = viewport()->width() + verticalHeader()->width(); height = viewport()->height() + horizontalHeader()->height(); #endif qDebug() << "x, y, width, height" << x << y << width << height; frozenCroTableView->setGeometry(x, y, width, height); } //! [geometry] //! [geometry] void FreezeTableWidget::updateFrozenColTableGeometry() { qDebug() << "updateFrozenColTableGeometry =="; //LUpdate int width = 0, height = 0, x = 0, y = 0; qDebug() << "ver:" << verticalHeader()->width() << verticalHeader()->height(); qDebug() << "hor:" << horizontalHeader()->width() << horizontalHeader()->height(); qDebug() << "frame:" << frameWidth() << frameRect().width()<< frameRect().height() << frameRect().x() << frameRect().y (); x = frameWidth(); y = frameWidth(); #if FREEZE_COL width = verticalHeader()->width(); for(int i = 0; i< m_iFreezeCols; i + + ) { width + = columnWidth(i); } #else width = viewport()->width() + verticalHeader()->width(); #endif height = viewport()->height() + horizontalHeader()->height(); qDebug() << "x, y, width, height" << x << y << width << height; frozenColTableView->setGeometry(x, y, width, height); } //! [geometry] //! [geometry] void FreezeTableWidget::updateFrozenRowTableGeometry() { qDebug() << "updateFrozenRowTableGeometry =="; //LUpdate int width = 0, height = 0, x = 0, y = 0; qDebug() << "ver:" << verticalHeader()->width() << verticalHeader()->height(); qDebug() << "hor:" << horizontalHeader()->width() << horizontalHeader()->height(); qDebug() << "frame:" << frameWidth() << frameRect().width()<< frameRect().height() << frameRect().x() << frameRect().y (); x = frameWidth(); y = frameWidth(); width = viewport()->width() + verticalHeader()->width(); #if FREEZE_ROW height = horizontalHeader()->height(); for(int i = 0; i< m_iFreezeRows; i + + ) { height + = rowHeight(i); } #else height = viewport()->height() + horizontalHeader()->height(); #endif qDebug() << "x, y, width, height" << x << y << width << height; frozenRowTableView->setGeometry(x, y, width, height); } //! [geometry]