Qt Programming-QTableView Freeze Rows or Freeze Columns or Freeze Partial Cells

Foreword

Qt Programming-QTableView freezes rows or columns or freezes partial cells. 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 need the effect of freezing rows and columns at the same time, please read the next blog Qt programming-QTableView freezes rows and columns at the same time.

Freeze columns:

Freeze rows:

Freeze local cells:

Principle

Principle of freezing rows or columns: Use two tableviews with the same content. The upper tableview displays the frozen rows or columns, hides the non-frozen content, and the lower tableview displays all the content. The lower tableview slides normally to have the effect of freezing rows or columns.

Code

The code transformation comes from Qt’s own examples. Freeze rows or columns can be controlled through the macro variables FREEZE_COL and FREEZE_ROW. 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 *frozenTableView;
      void init();
      void updateFrozenTableGeometry();


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

//! [constructor]
FreezeTableWidget::FreezeTableWidget(QAbstractItemModel * model)
{
      /*
Principle of freezing rows or columns:
Essentially there are 2 tableviews
            FreezeTableWidget inherits QTableView and displays all table data normally.
            The member variable frozenTableView of FreezeTableWidget is placed on top of FreezeTableWidget to only display the frozen rows or columns or the cells you want to display, so that the FreezeTableWidget below will have the effect of freezing rows or columns when sliding normally.
     */
      setModel(model);
      frozenTableView = new QTableView(this);

      init();

      //connect the headers and scrollbars of both tableviews together
      connect(horizontalHeader(), & amp;QHeaderView::sectionResized, this,
               & amp;FreezeTableWidget::updateSectionWidth);
      connect(verticalHeader(), & amp;QHeaderView::sectionResized, this,
               & amp;FreezeTableWidget::updateSectionHeight);

      //LUpdate
      //Freeze the column and the vertical scroll bar can slide normally
#if (FREEZE_COL & amp; & amp; !FREEZE_ROW)
      connect(frozenTableView->verticalScrollBar(), & amp;QAbstractSlider::valueChanged,
              verticalScrollBar(), & amp;QAbstractSlider::setValue);
      connect(verticalScrollBar(), & amp;QAbstractSlider::valueChanged,
              frozenTableView->verticalScrollBar(), & amp;QAbstractSlider::setValue);
#endif
      //Freeze the row and the horizontal scroll bar can slide normally
#if (!FREEZE_COL & amp; & amp; FREEZE_ROW)
      connect(frozenTableView->horizontalScrollBar(), & amp;QAbstractSlider::valueChanged,
              horizontalScrollBar(), & amp;QAbstractSlider::setValue);
      connect(horizontalScrollBar(), & amp;QAbstractSlider::valueChanged,
              frozenTableView->horizontalScrollBar(), & amp;QAbstractSlider::setValue);
#endif
}
//! [constructor]

FreezeTableWidget::~FreezeTableWidget()
{
      delete frozenTableView;
}

//! [init part1]
void FreezeTableWidget::init()
{
      frozenTableView->setModel(model());
      frozenTableView->setFocusPolicy(Qt::NoFocus);
      frozenTableView->verticalHeader()->setFixedWidth(verticalHeader()->width());
      frozenTableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
      frozenTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);

      viewport()->stackUnder(frozenTableView);
//! [init part1]

//! [init part2]
      frozenTableView->setStyleSheet("QTableView { border: none;"
                                     "background-color: #8EDE21;"
                                     "selection-background-color: #999}"); //for demo purposes
      frozenTableView->setSelectionModel(selectionModel());
      
      //LUpdate
#if FREEZE_COL
      //Hide data other than frozen columns
      for (int col = m_iFreezeCols; col < model()->columnCount(); + + col)
          frozenTableView->setColumnHidden(col, true);

      for(int i = 0; i < m_iFreezeCols; i + + )
      {
          frozenTableView->setColumnWidth(i, columnWidth(0));
      }
#endif

#if FREEZE_ROW
      //Hide data in rows other than frozen rows
      for (int row = m_iFreezeRows; row < model()->rowCount(); + + row)
          frozenTableView->setRowHidden(row, true);
      for(int i = 0; i < m_iFreezeRows; i + + )
      {
          frozenTableView->setRowHeight(i, rowHeight(0));
      }
#endif


      frozenTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
      frozenTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
      frozenTableView->show();

      updateFrozenTableGeometry();

      setHorizontalScrollMode(ScrollPerPixel);
      setVerticalScrollMode(ScrollPerPixel);
      frozenTableView->setVerticalScrollMode(ScrollPerPixel);
      frozenTableView->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 + + )
        {
            frozenTableView->setColumnWidth(i, (newSize + width)/m_iFreezeCols);
        }
        updateFrozenTableGeometry();
    }
#else
    frozenTableView->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 + + )
        {
            frozenTableView->setRowHeight(i, (newSize + height)/m_iFreezeRows);
        }
        updateFrozenTableGeometry();
    }
#else
    frozenTableView->setRowHeight(logicalIndex, newSize);
#endif
}
//! [sections]


//! [resize]
void FreezeTableWidget::resizeEvent(QResizeEvent * event)
{
      QTableView::resizeEvent(event);
      updateFrozenTableGeometry();
 }
//! [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() < frozenTableView->columnWidth(0) ){
            const int newValue = horizontalScrollBar()->value() + visualRect(current).topLeft().x()
                                 - frozenTableView->columnWidth(0);
            horizontalScrollBar()->setValue(newValue);
      }
#endif
#if FREEZE_ROW
      if(cursorAction == MoveDown & amp; & amp; current.row() > 0
               & amp; & amp; visualRect(current).topLeft().y() < frozenTableView->rowHeight(0))
      {
          const int newValue = verticalScrollBar()->value() + visualRect(current).topLeft().y()
                               - frozenTableView->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::updateFrozenTableGeometry()
{
    //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

#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;
    frozenTableView->setGeometry(x, y, width, height);
}
//! [geometry]