Qt Programming-QTableView freezes rows and columns simultaneously

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]