QHeaderView doesn't change when QTableView's model is reset.
-
Here's how the table is defined in the constructor of the widget/view:
table = new QTableView(this); tableHeader = new TableHeader(table); tableModel = new QSqlTableModel(table); table->setHorizontalHeader(tableHeader); table->setModel(tableModel); table->setItemDelegate(new TableItemDelegate(table));
on the combobox's selection change, this is called:
void TableWidget::onComboSelectionChanged(int index) { db.open(); tableModel->setTable(comboModel->data(comboModel->index(index, 0)).toString()); tableModel->select(); db.close(); }
and this is how it looks in the ui:
so initially, the table, Surah, had 6 columns and 6
QLineEdit
were shown in the header. When I selected the tableVerbDef
, it'd 4 columns BUTTableHeader
showed 6QLineEdit
and same thing happened with the table table,VerbForm
. Thecount()
hasn't been updated inTableHeader
. If I select some table that has more than 6 columns then the app crashes because there's a shortfall ofQLineEdit
in theQList<QLineEdit*>
, and, as aresult, I get index out of range exception.Which signal of
QTableView/QSqlTableModel
should be used to notifyQHeaderView
about that fact and update thecount()
and in which slot ofQHeaderView
?Here's how the
TableHeader
has been defined:TableHeader::TableHeader(QTableView * parent) : QHeaderView(Qt::Horizontal, parent) { setDefaultAlignment(Qt::AlignTop | Qt::AlignHCenter); connect(this, &TableHeader::sectionResized, this, &TableHeader::updateGeometries); connect(parent->horizontalScrollBar(), &QScrollBar::valueChanged, this, &TableHeader::updatePosition); } TableHeader::~TableHeader() { } void TableHeader::showEvent(QShowEvent *e) { qDeleteAll(lineEdits); for (int i = 0; i < count(); i++) { auto edit = new QLineEdit(this); lineEdits.append(edit); connect(edit, &QLineEdit::textChanged, this, &TableHeader::textChanged); edit->setGeometry(sectionViewportPosition(i), height() / 2, sectionSize(i), height() / 2); edit->show(); } QHeaderView::showEvent(e); } QSize TableHeader::sizeHint() const { int width = 0; int height = 0; for (int i = 0; i < count(); i++){ width += sectionSize(i); auto size = sectionSizeFromContents(i); width += size.width(); height = size.height(); } return QSize(width, height * 2); } QList<QString> TableHeader::getQueries() { return queries; } void TableHeader::updateGeometries() { if(!lineEdits.size()) return; for (int i = 0; i < count(); i++) { auto edit = lineEdits.at(i); edit->setGeometry(sectionViewportPosition(i), height() / 2, sectionSize(i), height() / 2); } QHeaderView::updateGeometries(); } void TableHeader::updatePosition() { int height = QHeaderView::sizeHint().height(); for (int i = 0; i < count(); i++){ auto edit = lineEdits.at(i); edit->move(sectionPosition(i) - offset(), height); } } void TableHeader::textChanged() { queries.clear(); for (int i = 0; i < count(); i++){ auto edit = lineEdits.at(i); queries.append(edit->text()); } emit queryChanged(); }
-
If diff is negative your loop should run for quite a long time.
Shouldn't you rather use the absolute value of count ?
-
This, probably, isn't the right way BUT somehow it works:
TableHeader::TableHeader(QTableView * parent) : QHeaderView(Qt::Horizontal, parent) { setDefaultAlignment(Qt::AlignTop | Qt::AlignHCenter); connect(this, &TableHeader::sectionResized, this, &TableHeader::updateGeometries); connect(parent->horizontalScrollBar(), &QScrollBar::valueChanged, this, &TableHeader::updatePosition); connect(this, &TableHeader::sectionCountChanged, this, &TableHeader::onSectionCountChanged); } TableHeader::~TableHeader() { } QSize TableHeader::sizeHint() const { if(count() != lineEdits.size()) return QSize(0, 0); int width = 0; int height = 0; for (int i = 0; i < count(); i++){ width += sectionSize(i); auto size = sectionSizeFromContents(i); width += size.width(); height = size.height(); } return QSize(width, height * 2); } QList<QString> TableHeader::getQueries() { return queries; } void TableHeader::updateGeometries() { if(count() != lineEdits.size()) return; for (int i = 0; i < count(); i++) { auto edit = lineEdits.at(i); edit->setGeometry(sectionViewportPosition(i), height() / 2, sectionSize(i), height() / 2); } } void TableHeader::updatePosition() { int height = QHeaderView::sizeHint().height(); for (int i = 0; i < count(); i++){ auto edit = lineEdits.at(i); edit->move(sectionPosition(i) - offset(), height); } } void TableHeader::textChanged() { queries.clear(); for (int i = 0; i < count(); i++){ auto edit = lineEdits.at(i); queries.append(edit->text()); } emit queryChanged(); } void TableHeader::onSectionCountChanged(int oldCount, int newCount) { if(oldCount > 0 && newCount == 0) { foreach(auto line, lineEdits) line->disconnect(); qDeleteAll(lineEdits); } else if(oldCount == 0 && newCount > 0){ lineEdits.clear(); for (int i = 0; i < count(); i++) { auto edit = new QLineEdit(this); lineEdits.append(edit); connect(edit, &QLineEdit::textChanged, this, &TableHeader::textChanged); edit->show(); } } }
What's the correct approach of
QHeaderView
implementation? It'd be nice if we could give it a Layout to render like:tableHeader = new TableHeader(table); tableHeader ->setLayout(new VBoxLayout(){ with the columnName and QLineEdit}); // doesn't exist. table->setHorizontalHeader(tableHeader);
-
Hi,
Since your number of line edits depends on the number sections I don't see a problem here.
However there's no need to nuke all and rebuild everything. Just add missing line edits and remove those exceeding.
-
@SGaist, here's what I've now to add/remove extras:
void TableHeader::resetBoxes(int count){ queries.clear(); if(count == lineEdits.size()) { for (int i = 0; i < lineEdits.size(); i++) { lineEdits[i]->setText(""); queries.append(""); } return; } int diff = count - lineEdits.size(); if(diff > 0){ for (int i = 0; i < diff; i++) { auto edit = new QLineEdit(this); edit->installEventFilter(this); lineEdits.append(edit); connect(edit, &QLineEdit::textChanged, this, &TableHeader::textChanged); edit->show(); } } else{ for (int i = 0; i < diff; i++) { auto line = lineEdits.takeLast(); line->disconnect(); //line->deleteLater(); delete line; } } qDebug() << lineEdits.size(); for (int i = 0; i < lineEdits.size(); i++) { lineEdits[i]->setText(""); queries.append(""); } }
It adds extra line edits if necessary BUT doesn't remove from the
QList
lineEdits or UI when necessary. So it looks like this:initially there was 6 line edits, in subsequent two tables there should've been 4 BUT I got 6. Then I've got 10 and finally when I got back to the first table with 6 columns It'd shown 7 line edits in the UI BUT there was 10 in the
QList
lineEdits. Here's the output ofqDebug() << lineEdits.size();
:6
6
6
10
10 -
If diff is negative your loop should run for quite a long time.
Shouldn't you rather use the absolute value of count ?
-
@SGaist, that was the issue. Now, with these in else block:
else{ diff = abs(diff); for (int i = 0; i < diff; i++) { auto line = lineEdits.takeLast(); delete line; } }
it shows the right number of line edits in header and the
qDebug
output is also correct. I haven't measured the time BUT feels like this approach is little bit slower than the previous approach (removing all old and adding new).I also have removed this
connect(this, &TableHeader::sectionCountChanged, this, &TableHeader::onSectionCountChanged);
and call reset directly in the TableWidget:
void TableWidget::queryDatabase(){ db.open(); tableModel->setTable(tableName); tableHeader->resetBoxes(tableModel->columnCount()); tableProxy->setQueries(tableHeader->getQueries()); tableModel->select(); while (tableModel->canFetchMore()) tableModel->fetchMore(); db.close(); }