4
votes

THE CODE:

populateTable()
{
  tableWidget->clearContents();
  tableWidget->setRowCount(stringList.size());

  for(int i = 0; i < stringList.size(); ++i)
  {
    tableWidget->setItem(i, 0, new QTableWidgetItem(stringList.at(i)));
  }
}

THE PROBLEM:

The first time populateTable() is run, everything is fine. But the next consequent times, it runs significantly slower than before.

DISCUSSION:

After careful testing I suspect clearContents() to be the problem. Because simply changing the code from

tableWidget->clearContents();

to:

tableWidget->setRowCount(0);

fixes the problem, but now it makes another problem; Setting the row count to '0' does not seem to delete the heap allocated QTableWidgetItems, it just seems to leave ownership of the items, so it leaves a memory leak. (or at least i just think so...)

Qt's documentation in QTableWidget is rather vague, so i don't exactly know what clearContents() actually does. In the documentation it says "Removes all items not in the headers from the view" so that has left me asking, do the contents of the table just hide? does it get deleted? I'm not exactly sure. My theory is that clearContents() only hides the items and any next attempts to populate the table actually deletes and removes each item then allocates a new one to set on the table which in turn is an expensive operation.

Another interesting thing is that Qt's documentation on QTableWidget suggests that the proper way to populate a QTableWidget is to allocate a QTableWidgetItem on heap then set it on a table cell with setItem() just like i presented in the code above, which I find to be wierd...

IN SUMMARY:

Is there an alternate way to populate and repopulate a Qt table without having all of these problems? if not, is there a way to fix these problems?

2

2 Answers

5
votes

"Qt's documentation on QTableWidget suggests that the proper way to populate a QTableWidget is to allocate a QTableWidgetItem on heap then set it on a table cell with setItem() just like i presented in the code above, which I find to be weird..."

I don't find it weird, considering that the documentation for QTableWidget::setItem says explicitly that the widget takes ownership of the item!

"Setting the row count to '0' does not seem to delete the heap allocated QTableWidgetItems, it just seems to leave ownership of the items, so it leaves a memory leak. (or at least i just think so...)"

Rather than guessing whether it's leaking or not, you could create your own subclass of QTableWidgetItem...and put code in its destructor so you have a place to put a breakpoint and know for sure. Alternatively, the Qt sources are quite legible. setRowCount calls removeRows, which does indeed delete QTableWidgetItems:

http://qt.gitorious.org/qt/qt/blobs/4.8/src/gui/itemviews/qtablewidget.cpp#line370

http://qt.gitorious.org/qt/qt/blobs/4.8/src/gui/itemviews/qtablewidget.cpp#line100

But this is again consistent with the documentation for QTableWidget::setRowCount

Sets the number of rows in this model to rows. If this is less than rowCount(), the data in the unwanted rows is discarded.

You really shouldn't be seeing much of a difference between clearContents() and setRowCount(0). Have you generated a small reproducible example, not entwined in any larger program, that demonstrates this phenomenon?

3
votes

I've noticed a general slowness not just in repopulating QTableWidget, but with making any changes.

For example, I have one with about 1000 cells that show checkboxes on them via setCheckState(). The first time through checking/unchecking them, they are fast. The second time through, it takes ~15s, which is completely unacceptable. I tried blockSignals(true/false) on the table and nothing worked.

The trick is to block the signals on the model and not the table. The model is what's eating up CPU.

auto table = ui.tableWidget;
auto rows = table->rowCount();
auto cols = table->columnCount();

table->model()->blockSignals(true);

for (auto row = 0; row < rows; row++)
    for (auto col = 0; col < cols; col++)
        if (auto cb = table->item(row, col))
            cb->setCheckState(Qt::CheckState::Checked);

table->model()->blockSignals(false);
table->model()->layoutChanged();//Required, or else you won't see your changes immediately.