在 QTreeView 中锁定/冻结列的好方法是什么 [英] What is the good way to lock/freeze columns in QTreeView
问题描述
我正在尝试在 QTreeView
中实现冻结(当网格的其余部分水平滚动时不滚动的那些)列 - 就像在 Excel 中一样.
I am trying to implement frozen (those that don't scroll when the rest of the grid is scrolled horizontally) columns in a QTreeView
- just like in Excel.
我试图找到一些原生的 Qt 方法来冻结列,但我唯一能找到的是在 QAbstractScrollArea::setViewportMargins
中的一个侧面提及,如 this 帖子.因此,我遵循了帖子答案中的建议,并在顶部覆盖了另一个查看相同模型的 QTreeView.IE.现在我有 2 个 QTreeViews,一个只显示冻结的列,另一个显示水平滚动的其余列.我已经对完整视图进行了子类化,这样用户就不必知道冻结的视图并且只使用完整视图.
I tried to find some native Qt way to freeze columns, but the only thing I could find is a side-mention in QAbstractScrollArea::setViewportMargins
, as mentioned in this post. So I followed the suggestion in the answer to the post and overlaid another QTreeView looking at the same model on top. I.e. now I have 2 QTreeViews, one only showing the frozen columns and the other showing the rest of the columns with horizontal scrolling. I have subclassed the full view, so that the user doesn't have to know about the frozen view and only work with the full view.
但是,现在我面临一个同步问题:对主视图及其标题所做的所有更改都需要分别镜像到冻结视图及其标题.例如.如果我调用 mainView->header()->setStretchLastSection(false)
,则需要在冻结视图的标题上复制相同的调用.但是,调用该方法时我没有任何通知 - DynamicPropertyChanged 事件仅在调用 setProperty
时调用,而不是在调用单个 setter 时调用.
However, now I am faced with a synchronization issue: all the changes made to the main view and its header need to be mirrored to the frozen view and its header respectively.
E.g. if I call mainView->header()->setStretchLastSection(false)
, the same call needs to be replicated on the frozen view's header. However, I have no notification when the method is called - DynamicPropertyChanged events are only called when setProperty
is called and not when individual setter is.
所以,我的问题是:
- 有没有办法在没有多个视图的情况下实现冻结列?
- 如何实现主视图和冻结视图之间的同步,以便冻结视图与主视图中完成的所有内容及其标题相匹配?
非常感谢!
推荐答案
我遇到了类似的问题 QTableView.在我的研究中,我找不到另一种使用单个视图创建冻结表的方法.因此我实现了我自己的自定义视图,它源自 QTableview.您可以在此处找到它:
I had a similar problem QTableView. In my research I could't find another method to create a frozen table using a single view. Hence I implemented my own custom view which derives from QTableview. You can find it here:
头文件:
#pragma once
//--- include files -------------------------------------------------------------
#include <QtGui/QTableView>
#include <QtCore/QObject>
class CustomTableView : public QTableView
{
Q_OBJECT
public:
CustomTableView(QWidget *parent = 0);
~CustomTableView();
void setDataModel(QAbstractItemModel * model);
public slots:
void onSortComplete();
private slots:
void updateSectionWidth(int logicalIndex,int, int newSize);
void updateSectionHeight(int logicalIndex, int, int newSize);
signals:
void onFrozenRowClicked(QModelIndex index);
protected:
virtual void resizeEvent(QResizeEvent *event);
// virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
void scrollTo (const QModelIndex & index, ScrollHint hint = EnsureVisible);
private:
void init();
void updateFrozenTableGeometry();
QTableView *m_frozenTvLast;
QTableView * m_frozenTvFirst;
};
cpp 文件
//--- include files -------------------------------------------------------------
#include <QtGui/QtGui>
#include "UI/CustomTableView.h"
#include "CustomTableView.moc"
CustomTableView::CustomTableView(QWidget *parent/* = 0*/)
{
this->setParent(parent);
m_frozenTvLast = new QTableView(this);
m_frozenTvLast->setSelectionMode(QAbstractItemView::SingleSelection);
m_frozenTvFirst = new QTableView(this);
m_frozenTvFirst->setSelectionMode(QAbstractItemView::SingleSelection);
//connect the headers and scrollbars of both tableviews together
connect(horizontalHeader()
, SIGNAL(sectionResized(int,int,int))
, this
, SLOT(updateSectionWidth(int,int,int)));
connect(verticalHeader()
, SIGNAL(sectionResized(int,int,int))
, this
, SLOT(updateSectionHeight(int,int,int)));
connect(m_frozenTvLast->verticalScrollBar()
, SIGNAL(valueChanged(int))
, verticalScrollBar()
, SLOT(setValue(int)));
connect(verticalScrollBar()
, SIGNAL(valueChanged(int))
, m_frozenTvLast->verticalScrollBar()
, SLOT(setValue(int)));
connect(m_frozenTvLast
, SIGNAL(clicked( const QModelIndex &))
, this
, SIGNAL(onFrozenRowClicked( const QModelIndex &) ) );
connect(m_frozenTvFirst->verticalScrollBar()
, SIGNAL(valueChanged(int))
, verticalScrollBar()
, SLOT(setValue(int)));
connect(verticalScrollBar()
, SIGNAL(valueChanged(int))
, m_frozenTvFirst->verticalScrollBar()
, SLOT(setValue(int)));
connect(m_frozenTvFirst
, SIGNAL(clicked( const QModelIndex &))
, this
, SIGNAL(onFrozenRowClicked( const QModelIndex &) ) );
}
CustomTableView::~CustomTableView()
{
delete m_frozenTvLast;
delete m_frozenTvFirst;
}
void CustomTableView::setDataModel(QAbstractItemModel * model)
{
setModel(model);
init();
}
void CustomTableView::init()
{
m_frozenTvLast->setModel(model());
m_frozenTvFirst->setModel(model());
m_frozenTvLast->setFocusPolicy(Qt::NoFocus);
m_frozenTvLast->verticalHeader()->hide();
m_frozenTvLast->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
m_frozenTvFirst->setFocusPolicy(Qt::NoFocus);
m_frozenTvFirst->verticalHeader()->hide();
m_frozenTvFirst->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
viewport()->stackUnder(m_frozenTvLast);
//viewport()->stackUnder(m_frozenTvFirst);
m_frozenTvLast->setStyleSheet("QTableView { border: none;"
/* "background-color: #8EDE21;"*/
"selection-background-color: #999}");
m_frozenTvFirst->setStyleSheet("QTableView { border: none;"
/* "background-color: #8EDE21;"*/
"selection-background-color: #999}");
m_frozenTvLast->setSelectionModel(selectionModel());
m_frozenTvFirst->setSelectionModel(selectionModel());
for(int col=0; col<8; col++)
m_frozenTvLast->setColumnHidden(col, true);
for(int col=2; col<=8; col++)
m_frozenTvFirst->setColumnHidden(col, true);
for( int i = 0 ; i+2 <= model()->rowCount() ; i = i+2)
m_frozenTvFirst->setSpan(i,0,2,1);
//horizontalHeader()->setResizeMode(8, QHeaderView::Fixed);
//horizontalHeader()->setResizeMode(1, QHeaderView::Fixed);
// frozenTableView->setColumnWidth(0, columnWidth(0) );
m_frozenTvLast->setColumnWidth(8, columnWidth(8) );
m_frozenTvFirst->setColumnWidth(0, columnWidth(0) );
m_frozenTvFirst->setColumnWidth(1, columnWidth(1) );
//frozenTableView->setColumnWidth(0, columnWidth(0) );
m_frozenTvLast->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_frozenTvLast->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_frozenTvLast->show();
m_frozenTvFirst->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_frozenTvFirst->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_frozenTvFirst->show();
updateFrozenTableGeometry();
setHorizontalScrollMode(ScrollPerPixel);
setVerticalScrollMode(ScrollPerPixel);
m_frozenTvLast->setVerticalScrollMode(ScrollPerPixel);
m_frozenTvFirst->setVerticalScrollMode(ScrollPerPixel);
}
void CustomTableView::updateSectionWidth(int logicalIndex, int, int newSize)
{
m_frozenTvLast->setColumnWidth(logicalIndex,newSize);
m_frozenTvFirst->setColumnWidth(logicalIndex,newSize);
updateFrozenTableGeometry();
}
void CustomTableView::updateSectionHeight(int logicalIndex, int, int newSize)
{
m_frozenTvLast->setRowHeight(logicalIndex, newSize);
m_frozenTvFirst->setRowHeight(logicalIndex, newSize);
}
void CustomTableView::resizeEvent(QResizeEvent * event)
{
QTableView::resizeEvent(event);
updateFrozenTableGeometry();
}
/*
QModelIndex FreezeTableWidget::moveCursor(CursorAction cursorAction,
Qt::KeyboardModifiers modifiers)
{
QModelIndex current = QTableView::moveCursor(cursorAction, modifiers);
if(cursorAction == MoveRight
&& current.column()>0
&& visualRect(current).topLeft().x() < frozenTableView->columnWidth(8) )
{
const int newValue = horizontalScrollBar()->value()
+ visualRect(current).topLeft().x()
- frozenTableView->columnWidth(8);
horizontalScrollBar()->setValue(newValue);
}
return current;
}*/
void CustomTableView::scrollTo (const QModelIndex & index, ScrollHint hint)
{
if(index.column()>0)
QTableView::scrollTo(index, hint);
}
void CustomTableView::updateFrozenTableGeometry()
{
int widthToAdd = verticalHeader()->width()+frameWidth()+viewport()->width() - columnWidth(8);
int allColumnWidth = verticalHeader()->width() + frameWidth();
for(int col=0; col<8; col++)
allColumnWidth += columnWidth(col);
int toSet = 0;
if( widthToAdd < allColumnWidth )
toSet= widthToAdd;
else
toSet = allColumnWidth;
/*verticalHeader()->width()+frameWidth()+viewport()->width() - columnWidth(8)*/
m_frozenTvLast->setGeometry(toSet
, frameWidth()
, columnWidth(8) +0.1
, viewport()->height()+horizontalHeader()->height());
m_frozenTvFirst->setGeometry(verticalHeader()->width()+frameWidth()
, frameWidth()
, columnWidth(0) + columnWidth(1)+0.1
, viewport()->height()+horizontalHeader()->height());
}
void CustomTableView::onSortComplete()
{
for( int i = 0 ; i+2 <= model()->rowCount() ; i = i+2)
m_frozenTvFirst->setSpan(i,0,2,1);
}
工作方式:
- 所有视图共享相同的数据模型
- 所有视图都相互连接,因此当一个视图被点击时,另一个也会被自动点击.
- 对于每个冻结的列,我们只需创建一个新视图并隐藏所有其他列.
- 对于每个被冻结的视图,我们禁用滚动.
- 我们现在必须使用主表中的信息将每一列放置在正确的位置.
- 每次调整表格大小时,我们都会重新计算列的位置和大小.
这篇关于在 QTreeView 中锁定/冻结列的好方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!