このページでは

「固定列」の例

この例では、QTableView 内で列を固定する方法を示します。

テーブルの列がフリーズしている様子を示すアプリケーション

Qtのモデル/ビューフレームワークを使用して、最初の列が固定されたテーブルを実装します。この手法は、テーブルの端にある列や行であれば、複数の列や行に適用できます。

モデル/ビューフレームワークでは、1つのモデルを複数のビューを使用してさまざまな方法で表示することができます。この例では、同じモデルに対して2つのビュー、つまり1つのモデルを共有する2つのtable views を使用します。固定された列はメインの の子要素であり、後続のセクションで順を追って説明するオーバーレイ手法を用いて、目的の視覚効果を実現します。

テーブルビュー、ヘッダー、スクロールバーのレイアウト

FreezeTableWidget クラスの定義

FreezeTableWidget クラスには、コンストラクタとデストラクタがあります。また、オーバーレイとして使用するテーブルビューと、両方のテーブルビューで共有されるモデルという 2 つのプライベートメンバーを持っています。 セクションのサイズを同期させるための2つのスロットと、フリーズされた列のジオメトリを再調整する関数が追加されています。さらに、resizeEvent() およびmoveCursor() の2つの関数を再実装しています。

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 & 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);

};

注: QAbstractItemView は QTableView の親クラスです。

FreezeTableWidget クラスの実装

コンストラクタはmodel を引数として受け取り、固定列の表示に使用するテーブルビューを作成します。その後、コンストラクタ内でinit() 関数を呼び出し、固定列の設定を行います。 最後に、QHeaderView::sectionResized() シグナル(水平および垂直ヘッダー用)を適切なスロットに接続します。これにより、固定列のセクションがヘッダーと同期するようになります。また、垂直スクロールバー同士を接続することで、固定列がテーブルの他の部分と同様に垂直方向にスクロールするようにします。

FreezeTableWidget::FreezeTableWidget(QAbstractItemModel * model)
{
      setModel(model);
      frozenTableView = new QTableView(this);

      init();

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

      connect(frozenTableView->verticalScrollBar(), &QAbstractSlider::valueChanged,
              verticalScrollBar(), &QAbstractSlider::setValue);
      connect(verticalScrollBar(), &QAbstractSlider::valueChanged,
              frozenTableView->verticalScrollBar(), &QAbstractSlider::setValue);

}

init() 関数内では、固定列の表示を担当するオーバーレイテーブルビューが適切に設定されていることを確認します。つまり、このテーブルビュー(frozenTableView )は、メインのテーブルビューと同じモデルを持つ必要があります。ただし、ここでの違いは、frozenTableView の表示される列は最初の列のみであり、他の列はsetColumnHidden()を使用して非表示にしている点です。

void FreezeTableWidget::init()
{
      frozenTableView->setModel(model());
      frozenTableView->setFocusPolicy(Qt::NoFocus);
      frozenTableView->verticalHeader()->hide();
      frozenTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);

      viewport()->stackUnder(frozenTableView);

固定列のZ順序については、ビューポートの上に重ねて配置します。これは、ビューポートに対してstackUnder()を呼び出すことで実現します。見た目を整えるため、この列がメインのテーブルビューからフォーカスを奪わないようにしています。 また、両方のビューが同じ選択モデルを共有するようにし、一度に選択できるセルが1つだけになるようにしています。アプリケーションの見栄えを良くし、メインのTableViewと一貫した動作を実現するために、他にもいくつかの微調整を行っています。なお、列が正しい位置に配置されるように、updateFrozenTableGeometry() を呼び出している点に注意してください。

      frozenTableView->setStyleSheet("QTableView { border: none;"
                                     "background-color: #8EDE21;"
                                     "selection-background-color: #999}"); //for demo purposes
      frozenTableView->setSelectionModel(selectionModel());
      for (int col = 1; col < model()->columnCount(); ++col)
            frozenTableView->setColumnHidden(col, true);

      frozenTableView->setColumnWidth(0, columnWidth(0) );

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

      updateFrozenTableGeometry();

      setHorizontalScrollMode(ScrollPerPixel);
      setVerticalScrollMode(ScrollPerPixel);
      frozenTableView->setVerticalScrollMode(ScrollPerPixel);
}

固定された列のサイズを変更する際、シームレスな統合を実現するために、メインのテーブルビュー上の同じ列もそれに応じてサイズを変更する必要があります。これは、水平ヘッダーと垂直ヘッダーの両方から発せられるsectionResized()シグナルを通じて、newSize の値から列の新しいサイズを取得することで実現されます。

void FreezeTableWidget::updateSectionWidth(int logicalIndex, int /* oldSize */, int newSize)
{
      if (logicalIndex == 0){
            frozenTableView->setColumnWidth(0, newSize);
            updateFrozenTableGeometry();
      }
}

void FreezeTableWidget::updateSectionHeight(int logicalIndex, int /* oldSize */, int newSize)
{
      frozenTableView->setRowHeight(logicalIndex, newSize);
}

固定列の幅が変更されるため、updateFrozenTableGeometry() を呼び出してウィジェットのジオメトリを適宜調整します。この関数については、以下でさらに詳しく説明します。

QTableView::resizeEvent()の再実装では、基底クラスの実装を呼び出した後にupdateFrozenTableGeometry() を呼び出します。

void FreezeTableWidget::resizeEvent(QResizeEvent * event)
{
      QTableView::resizeEvent(event);
      updateFrozenTableGeometry();
 }

キーボードでテーブル内を移動する際、現在の選択範囲が固定列の背後に隠れてしまわないようにする必要があります。これを同期させるために、QTableView::moveCursor()を再実装し、基底クラスの実装を呼び出した後、必要に応じてスクロールバーの位置を調整します。

QModelIndex FreezeTableWidget::moveCursor(CursorAction cursorAction,
                                          Qt::KeyboardModifiers modifiers)
{
      QModelIndex current = QTableView::moveCursor(cursorAction, modifiers);

      if (cursorAction == MoveLeft && current.column() > 0
              && visualRect(current).topLeft().x() < frozenTableView->columnWidth(0) ){
            const int newValue = horizontalScrollBar()->value() + visualRect(current).topLeft().x()
                                 - frozenTableView->columnWidth(0);
            horizontalScrollBar()->setValue(newValue);
      }
      return current;
}

固定列のジオメトリ計算は、その下にあるテーブルのジオメトリに基づいているため、常に正しい位置に表示されます。QFrame::frameWidth() 関数を使用することで、どのスタイルが使用されていても、このジオメトリを正しく計算できます。固定列の境界を設定する際には、ビューポートとヘッダーのジオメトリを利用しています。

void FreezeTableWidget::updateFrozenTableGeometry()
{
      frozenTableView->setGeometry(verticalHeader()->width() + frameWidth(),
                                   frameWidth(), columnWidth(0),
                                   viewport()->height()+horizontalHeader()->height());
}

サンプルプロジェクト @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.