Solved 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?
-
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.
-
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 :)