What is a good way to lock / freeze columns in a QTreeView

I'm trying to implement frozen (those that don't scroll when the rest of the grid scrolls horizontally) columns in QTreeView - exactly the same as in Excel.

I tried to find some native Qt way to freeze columns, but the only thing I could find was a mention in QAbstractScrollArea::setViewportMargins , as mentioned in this post. So I followed the suggestion in response to the message and put another QTreeView on top of the same model. That is, now I have 2 QTreeViews, one shows only frozen columns, and the other shows the remaining columns with horizontal scrolling. I have subclassed the full view, so the user does not need to know about the frozen view and only work with the full view.

However, now I am faced with a synchronization problem: all changes made to the main view and its title should be mirrored on the frozen view and its title, respectively. For instance. if I call mainView->header()->setStretchLastSection(false) , the same call should be replicated in the frozen header. However, I have no notifications when the method is called. Events DynamicPropertyChanged is raised only when setProperty is setProperty , and not when the individual installer.

So my questions are:

  • Is there a way to implement frozen columns without having multiple views?
  • How can I implement synchronization between the main and frozen views so that the frozen insert matches everything that is done in the main view and its title?

Thanks a lot!

+1
source share
1 answer

I had a similar QTableView problem. In my research, I could not find another method for creating a frozen table using a single view. Therefore, I implemented my own custom view, which comes 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); } 

How it works:

  • All views use the same datamodel.
  • All views are connected to each other, so when you click on one of them, a click is also automatically performed.
  • For each frozen column, we simply create a new view and hide all other columns.
  • For each frozen view, disable scrolling.
  • Now we need to arrange each column in the right place with the information from the main table.
  • Each time the table changes, we recalculate the position and size of the column.
+2
source

Source: https://habr.com/ru/post/1495282/


All Articles