Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-117879

slow QTreeWidget.clear when the items are expanded

    XMLWordPrintable

Details

    • Bug
    • Resolution: Incomplete
    • Not Evaluated
    • None
    • 6.3.1, 6.5.3
    • None
    • Windows 10 x64
      CPython 3.10 x64
      Qt 6.3.1 and 6.5.3 (probably other version behave the same but I've only used those version)
    • Windows

    Description

      It seems that there is a major slowdown when calling QTreeWidget.clear if there are many expanded items

      # Python
      import sys
      from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QTreeWidget, QTreeWidgetItem, QPushButton, QSpinBox, QCheckBox
      from PySide6.QtGui import QPalette, QColor
      
      
      class MainWindow(QMainWindow):
      
          def __init__(self):
              super(MainWindow, self).__init__()
      
              self.setWindowTitle("My App")
      
              layout = QVBoxLayout()
      
              self.t = QTreeWidget()
      
              b = QPushButton('clear tree')
              b.clicked.connect(self.clr)
      
              self.c = QCheckBox('Items collapsed')
      
              self.t.setHeaderLabels(
                  [
                      "Name",
                      "Group",
                      "Index",
                      "Unit",
                      "Source name",
                      "Source path",
                      "Comment",
                  ]
              )
      
              self.n = n = QSpinBox()
              n.setMaximum(1000000)
              n.setValue(1000)
      
              a = QPushButton('Add items')
              a.clicked.connect(self.add)
      
              layout.addWidget(self.t)
              layout.addWidget(n)
              layout.addWidget(a)
              layout.addWidget(self.c)
              layout.addWidget(b)
      
              widget = QWidget()
              widget.setLayout(layout)
              self.setCentralWidget(widget)
      
          def add(self):
              count = self.n.value()
              if count:
                  top_level = [QTreeWidgetItem([
                      f"Name{i}",
                      f"Group{i}",
                      f"Index{i}",
                      f"Unit{i}",
                      f"Source name{i}",
                      f"Source path{i}",
                      f"Comment{i}",
                  ]) for i in range(count)]
                  for item in top_level:
                      item.addChildren([QTreeWidgetItem([
                          f"_Name{i}",
                          f"_Group{i}",
                          f"_Index{i}",
                          f"_Unit{i}",
                          f"_Source name{i}",
                          f"_Source path{i}",
                          f"_Comment{i}",
                      ]) for i in range(3)])
      
                  self.t.addTopLevelItems(top_level)
                  self.t.setSortingEnabled(True)
      
          def clr(self):
              from time import perf_counter
              count = self.t.topLevelItemCount()
              all_collapsed = self.c.isChecked()
              if all_collapsed:
                  self.t.collapseAll()
              else:
                  self.t.expandAll()
              t1 = perf_counter()
              self.t.clear()
              t2 = perf_counter()
              print(f'{all_collapsed=} {count=} took {t2-t1}')
      
      
      app = QApplication(sys.argv)
      
      window = MainWindow()
      window.show()
      
      app.exec_() 

       

      // C++
      #include <QApplication>
      #include <QMainWindow>
      #include <QWidget>
      #include <QVBoxLayout>
      #include <QTreeWidget>
      #include <QTreeWidgetItem>
      #include <QPushButton>
      #include <QSpinBox>
      #include <QCheckBox>class MainWindow : public QMainWindow
      {
          Q_OBJECTpublic:
          MainWindow()
          {
              setWindowTitle("My App");        QVBoxLayout* layout = new QVBoxLayout;        treeWidget = new QTreeWidget;        QPushButton* clearButton = new QPushButton("Clear tree");
              connect(clearButton, &QPushButton::clicked, this, &MainWindow::clearTree);        checkBox = new QCheckBox("Items collapsed");        treeWidget->setHeaderLabels({
                  "Name",
                  "Group",
                  "Index",
                  "Unit",
                  "Source name",
                  "Source path",
                  "Comment"
              });        spinBox = new QSpinBox;
              spinBox->setMaximum(1000000);
              spinBox->setValue(1000);        QPushButton* addButton = new QPushButton("Add items");
              connect(addButton, &QPushButton::clicked, this, &MainWindow::addItems);        layout->addWidget(treeWidget);
              layout->addWidget(spinBox);
              layout->addWidget(addButton);
              layout->addWidget(checkBox);
              layout->addWidget(clearButton);        QWidget* centralWidget = new QWidget;
              centralWidget->setLayout(layout);
              setCentralWidget(centralWidget);
          }public slots:
          void addItems()
          {
              int count = spinBox->value();
              if (count > 0)
              {
                  QList<QTreeWidgetItem*> topItems;
                  for (int i = 0; i < count; ++i)
                  {
                      QTreeWidgetItem* item = new QTreeWidgetItem({
                          QString("Name%1").arg(i),
                          QString("Group%1").arg(i),
                          QString("Index%1").arg(i),
                          QString("Unit%1").arg(i),
                          QString("Source name%1").arg(i),
                          QString("Source path%1").arg(i),
                          QString("Comment%1").arg(i)
                      });                for (int j = 0; j < 3; ++j)
                      {
                          item->addChild(new QTreeWidgetItem({
                              QString("_Name%1").arg(j),
                              QString("_Group%1").arg(j),
                              QString("_Index%1").arg(j),
                              QString("_Unit%1").arg(j),
                              QString("_Source name%1").arg(j),
                              QString("_Source path%1").arg(j),
                              QString("_Comment%1").arg(j)
                          }));
                      }                topItems.append(item);
                  }            treeWidget->addTopLevelItems(topItems);
                  treeWidget->setSortingEnabled(true);
              }
          }    void clearTree()
          {
              bool allCollapsed = checkBox->isChecked();
              if (allCollapsed)
                  treeWidget->collapseAll();
              else
                  treeWidget->expandAll();        treeWidget->clear();
          }private:
          QTreeWidget* treeWidget;
          QSpinBox* spinBox;
          QCheckBox* checkBox;
      };int main(int argc, char *argv[])
      {
          QApplication app(argc, argv);    MainWindow window;
          window.show();    return app.exec();
      }
       

       

      When running this with 100, 1000 and 10000 items I get the following results

       

      all_collapsed=True count=100 took 0.0008545000018784776
      all_collapsed=True count=1000 took 0.00891409999167081
      all_collapsed=True count=10000 took 0.09732249999069609
      all_collapsed=False count=100 took 0.0017668999935267493
      all_collapsed=False count=1000 took 0.10975079999479931
      all_collapsed=False count=10000 took 17.177133699995466 

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              qt.team.quick.subscriptions Qt Quick and Widgets Team
              danielhrisca Daniel Hrisca
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes