Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Resizing verticalHeader in QTableView doesn't refresh?



  • Hi everyone,

    I have a QTableView with vertical header, and I need to programatically change the width of this header.

    So I call:

    myQTableView->verticalHeader()->setFixedWidth(newWidth);
    

    Looks simple enough. But nothing happens. The header doesn't change. It seems the new display just needs a kind of refresh.
    After this call, if I just manually minimize my window, and then show it again, for example, then the new width is taken into account.

    I tried calling myQTableView->repaint(), but it has no effect.

    I managed a hack to this issue by calling:

    myQTableView->verticalHeader()->setFixedWidth(newWidth);
    myQTableView->hide();
    myQTableView->show();
    

    This works, but it feels like a dirty hack. Do you have any cleaner solution, or explanations for why the header is not correctly resized in the first place?


  • Lifetime Qt Champion

    Hi,

    What version of Qt are you using ?
    On what OS ?
    Can you provide a minimal compilable example that shows this behaviour ?



  • I'm using Qt5.6.3 on windows 10.

    Originally, it's from a pretty big project, but I managed to reproduce the issue on something smaller. However, I must have changed something in the process, because it's now slightly different. Now the header is indeed resized, but it hides the first column instead of moving it. I guess in the big project version, the header is resized, but hidden by the first column instead.

    Here is a compilable code that shows the issue (I'd upload the files, but apparently I don't have the rights for that, it requires more reputation in the forum, I guess):

    The .pro file (nothing special):

    #-------------------------------------------------
    #
    # Project created by QtCreator 2018-04-06T08:49:03
    #
    #-------------------------------------------------
    
    QT       += core gui
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    TARGET = bug_minimal
    TEMPLATE = app
    
    SOURCES += main.cpp\
            mainwindow.cpp \
        tablemodel.cpp
    
    HEADERS  += mainwindow.h \
        tablemodel.h
    
    FORMS    += mainwindow.ui
    
    

    main.cpp (nothing special either):

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    

    mainWindow.h: I tried to reduce to the smallest case, and thus put everything in mainWindow.

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QTableView>
    #include <QPushButton>
    #include <QSplitter>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    public slots:
        void changeHeaderSize();
    
    private:
        Ui::MainWindow *ui;
        QTableView *myTableView ;
        QSplitter   * _splitter;
        QAbstractTableModel* _model;
        QPushButton *_myButton;
    };
    
    #endif // MAINWINDOW_H
    

    tableModel.h, the model we use for out table (Strongly reduced as well, here a simple subclassing of QAbstractTableModel):

    #ifndef DATAEDITOR_DATAEDITORTABLEMODEL_H_
    #define DATAEDITOR_DATAEDITORTABLEMODEL_H_
    
    #include <QAbstractTableModel>
    #include <QTableWidgetItem>
    
    /**
     * @brief TableModel : models that holds QTableWidgetItem, each representing a column in a dataframe.
     * It has thus only one row
     */
    class TableModel : public QAbstractTableModel{
        Q_OBJECT
    public:
        TableModel(QObject *parent = 0) : QAbstractTableModel(parent) {}
        ~TableModel(){ for (auto i: _itemList) delete i;}
    
        // Functions reimplemented as required in http://doc.qt.io/qt-5/qabstracttablemodel.html#subclassing
        virtual int rowCount(const QModelIndex &parent = QModelIndex()) const {return parent.isValid()?0:1;}   // only one line, we make extensive use of roles
        virtual int columnCount(const QModelIndex &parent = QModelIndex()) const {return parent.isValid()?0:_itemList.size();}
        virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;      // defines the header content
        virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
        virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
        virtual bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex());
    
    
    private:
        QList<QTableWidgetItem*> _itemList;
    
    
    };
    
    #endif /* DATAEDITOR_DATAEDITORTABLEMODEL_H_ */
    

    tableModel.cpp:

    
    #include "tablemodel.h"
    #include <QDebug>
    
    QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const{
    
        if(orientation == Qt::Horizontal) {
            return "horizontal header Name" ;
        }
    
        return QAbstractTableModel::headerData(section,orientation,role);
    }
    
    QVariant TableModel::data(const QModelIndex &index, int role) const{
        if (!index.isValid()) return QVariant();        // if index is invalid, return empty QVariant
        return _itemList.at(index.column())->data(role);
    }
    
    
    /**
     * @brief TableModel::setData modifies the data in the model
     * @param index index if the data to modify
     * @param value value to set
     * @param role role to set
     */
    bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role){
        // if the index corresponds to a valid culumn (less than the number of elements in _itemList, and on the  0th line)
        if (index.isValid() && index.column() < _itemList.size() && index.row() == 0){
            // set the data in the model
             _itemList[index.column()]->setData(role,value);
    
            // emit the approriate signal (dataChanged)
            emit dataChanged(index,index,QVector<int>(role));
        }
        // return success because we consider it can't fail (todo : take into accout errors)
        return true;
    }
    
    
    // insertColumns :
    // The items in each new column will be children of the item represented by the parent model index.
    // If column is 0, the columns are prepended to any existing columns.
    // If column is columnCount(), the columns are appended to any existing columns.
    // Parents are ignored in our implementation
    // Returns true if the columns were successfully inserted; TO DO: otherwise returns false.
    bool TableModel::insertColumns(int column, int count, const QModelIndex &parent){
        beginInsertColumns(parent,column, column+count-1);  // Mandatory call per Qt framework.
        while (count--){
            // creates new item
            QTableWidgetItem* newCol = new QTableWidgetItem();
            // adds to it a default TrueFalseMap
            _itemList.insert(column,newCol);
        }
        endInsertColumns();  // Mandatory call per Qt framework.
        return true;
    }
    

    And finally mainwindow.cpp, where we can comment or un-comment 2 lines to see the effect of my dirty fix:

    #include "tablemodel.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        _splitter =  new QSplitter(Qt::Vertical,this);
        _splitter->resize(350, 350);
    
        // create a model, and put some random data into it.
        _model = new TableModel();
        _model->insertColumns(0, 2, QModelIndex());
        _model->setData(_model->index(0,0,QModelIndex()), QVariant(42), Qt::EditRole) ;
        _model->setData(_model->index(0,1,QModelIndex()), QVariant(57), Qt::EditRole) ;
    
        // Create the QTableView
        myTableView = new QTableView(this);
        myTableView->setModel(_model);
        myTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    
        // Adds a button to trigger the change in header size.
        _myButton = new QPushButton(this);
        _myButton->setText("Change header size");
        connect(_myButton, &QPushButton::clicked, this, &MainWindow::changeHeaderSize);
    
        _splitter->insertWidget(0,_myButton);
        _splitter->insertWidget(1,myTableView);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::changeHeaderSize() {
        myTableView->verticalHeader()->setFixedWidth(100);
        // De-commenting the follwoing lines forces the update and "fixes" the issue.
        //myTableView->hide();
        //myTableView->show();
    }
    

    Thanks for your attention.


  • Qt Champions 2019

    I can reproduce the issue (and have an idea how to fix it): https://bugreports.qt.io/browse/QTBUG-67532

    /edit: fix will be in 5.12 :)


Log in to reply