问题
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.
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.
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.
So, my questions are:
- Is there a way to implement frozen columns without having multiple views?
- How can I implement the synchronization between main and the frozen views, so that the frozen veiw would match everything done in the main view and its header?
Thanks a lot!
回答1:
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:
header file:
#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 file
//--- 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);
}
The way it works:
- All view share the same datamodel
- All views are connected to each other so when one view is clicked the other is also clicked automatically.
- For each frozen column we simply create a new view and hide all the other columns.
- For each view that is frozen we disable the scrolling.
- We must now position each column in the right place with information from the main table.
- Each time the table is resized we recalculate the position and size of the column.
来源:https://stackoverflow.com/questions/34004673/what-is-the-good-way-to-lock-freeze-columns-in-qtreeview