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

与世无争的帅哥 提交于 2019-12-13 02:11:20

问题


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:

  1. Is there a way to implement frozen columns without having multiple views?
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!