冻结列示例
本示例演示如何在QTableView中冻结一列。
我们使用Qt的模型/视图框架来实现一个具有第一列冻结的表格。此技术可以应用于多个列或行,只要它们在表格的边缘。
模型/视图框架允许一个模型通过多个视图以不同的方式进行显示。对于这个示例,我们在同一个模型上使用两个视图 - 两个共享一个模型的tableViews。冻结列是主
FreezeTableWidget 类定义
FreezeTableWidget
类有一个构造函数和一个析构函数。还有一个两个私有成员:我们将用作叠加的表格视图和两个表格视图的共享模型。添加了两个槽来帮助同步区域大小,以及一个调整冻结列几何形状的功能。此外,我们还重写了两个函数:resizeEvent() 和 moveCursor()。
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 类实现
构造函数接受 模型 作为参数并创建一个表格视图,我们将使用它来显示冻结列。然后在构造函数中,我们调用 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() 来实现的。为了外观和功能,我们防止列从主表格视图中偷走焦点。我们还确保两个视图共享同一个选择模型,所以每次只能选择一个单元格。还对应用程序进行了其他一些调整,使其看起来良好并且与主表格视图的行为一致。请注意,我们调用 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()); }
© 2024 Qt 公司有限公司。本文件中包含的文档贡献属于其各自的版权所有者。所提供的文档是根据自由软件基金会发布的 GNU 自由文档许可证版本 1.3 的条款许可的。Qt及其相关标志是芬兰和/或全世界 Qt 公司的商标。所有其他商标均为其各自所有者的财产。