Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Fast search through QTableWidget rows
QtWS25 Last Chance

Fast search through QTableWidget rows

Scheduled Pinned Locked Moved General and Desktop
13 Posts 4 Posters 4.5k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    VRonin
    wrote on 15 Dec 2016, 08:06 last edited by
    #4

    either you use the QAbstarctItemModel interface model->setData(model->index(row,column),d,Qt::UserRole) or you just act on the underlying QStandardItemModel and use item->setData(Qt::UserRole, d); on it

    "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
    ~Napoleon Bonaparte

    On a crusade to banish setIndexWidget() from the holy land of Qt

    1 Reply Last reply
    2
    • M Offline
      M Offline
      mardzo
      wrote on 16 Dec 2016, 18:22 last edited by
      #5

      Okay, so I have implemented the QSortFilterProxyModel and the filtering is much faster like this. I have another question and that is how to convert this model to html table so i can export it as pdf? Looping with a for loop between the data in the rows in order to create html is fine when i have 1000 rows, but when i have 6000 rows i have to wait 2-3 minutes in order to create the html. Any suggestions?

      1 Reply Last reply
      0
      • C Offline
        C Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on 16 Dec 2016, 18:43 last edited by
        #6

        Minutes? There's got to be something wrong with how you generate the html.
        Consider this code:

            QElapsedTimer timer;
            timer.start();
        
            QStandardItemModel model;
            for (int i = 0; i < 100000; ++i) {
                 QDate d = QDate::fromJulianDay(qrand());
                 QStandardItem* item = new QStandardItem(d.toString("dd.MM.yyyy"));
                 item->setData(d, Qt::UserRole);
                 model.appendRow(item);
            }
        
            qDebug() << timer.restart() << "ms";
        
            QString html = "<html><body><table>";
            html.reserve(40 * model.rowCount()); //40 is some heuristic value
            for (int i = 0; i < model.rowCount(); ++i)
            {
                html += "<tr><td>";
                html += model.data(model.index(i, 0), Qt::DisplayRole).toString();
                html += "</td></tr>";
            }
            html += "</table></body></html>";
        
            qDebug() << timer.restart() << "ms";
        

        For 100 000 elements it takes on my machine 230 milliseconds to create the model and 40ms to generate the html.

        Btw. Make sure you're not doing measurements in Debug mode.

        1 Reply Last reply
        2
        • M Offline
          M Offline
          mardzo
          wrote on 16 Dec 2016, 19:21 last edited by
          #7

          Ok so this is my code and im trying in release mode. Im sorry for the bad formatting Still its very slow.

          
          void AccControl::exportToPdf()  {
          
          
              ACCSortFilterProxyModel* proxyModel = this->findChild<ACCSortFilterProxyModel*>("proxy");
          
              QComboBox* searchCombo = this->findChild<QComboBox*>("searchCombo");
          
              QDateEdit* c1 = this->findChild<QDateEdit*>("c1");
          
              QDateEdit* c2 = this->findChild<QDateEdit*>("c2");
          
              QString tablecss = " style = 'margin-top:10px; margin-right:20px; border-collapse: separate; background-color: #000;'";
          
              QString tdthcss = " style = 'text-align: left; padding: 1px; background-color: #fff; font-size:6pt; ' ";
          
              QString htmltable = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">"
                                                           "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><meta name=\"qrichtext\" content=\"1\">"
                                                           "<title>QTextEdit Example</title>"
                                                           "<style type=\"text/css\">p, li { white-space: pre-wrap; }"
                                                           "</style></head><body style=\" font-family:Tahoma; font-size:9pt; font-weight:300; font-"
                                                           "style:normal;\">" "<table align=center style = 'margin-top:50px;  margin-right:20px; '><tr><th><h2><span>Извештај за настани од контрола на пристап</span></h2></th></tr></table><table align=center style='margin-top:10px;  margin-right:20px; '><tr><th"+ tdthcss +"><span>Филтер:</span></th><th"+ tdthcss +"><span>" +  searchCombo->currentText() + "</span></th><th"+ tdthcss +"><span>Од:</span></th><th"+ tdthcss +"><span>"+ c1->date().toString("dd.MM.yyyy") +"</span></th><th"+ tdthcss +"><span>До:</span></th><th"+ tdthcss +"><span>"+ c2->date().toString("dd.MM.yyyy") +"</span></th></tr></table>";
          
              htmltable= htmltable + "<table align=center cellspacing=1 width=440 " + tablecss + "> <tr> <th" + tdthcss  + "><span>Бр.</span></th> <th" + tdthcss  + "><span>Уред</span></th> <th" + tdthcss  + "><span>Лог</span></th> <th" + tdthcss  + "><span>Врата</span></th> <th" + tdthcss  + "><span>UID</span></th> <th" + tdthcss  + "><span>Корисник</span></th> <th" + tdthcss  + "><span>Датум и Време</span></th> </tr>";
          
          
              int j = 1;
          
              int k = 1;
          
              QStringList split1;
              QStringList split2;
          
              for (int i =0; i < proxyModel->rowCount(); i++) {
          
          
          
                      //ova za koloritnost na redici
                     if ((j%2)!=0) { tdthcss = " style = 'text-align: left; padding: 1px; background-color: #dddddd; font-size:6pt;' "; } else { tdthcss = " style = 'text-align: left; padding: 1px; background-color: #fff; font-size:6pt;' "; }
          
                     //za reden broj
          
                     htmltable = htmltable+"<tr> <td" + tdthcss  + "><span>" + QString::number(j) + "</span></td>";
          
          
                      //za device
          
                      htmltable = htmltable+"<td" + tdthcss  + "><span>" + proxyModel->index(i,0).data(Qt::DisplayRole).toString() + "</span></td>";
          
          
                      //za log
          
                      htmltable = htmltable+"<td" + tdthcss  + "><span>" + proxyModel->index(i,1).data(Qt::DisplayRole).toString() + "</span></td>";
          
                      //za vrata
          
                      htmltable = htmltable+"<td" + tdthcss  + "><span>" + proxyModel->index(i,2).data(Qt::DisplayRole).toString() + "</span></td>";
          
                      //za uid go zakomentirav jer realno ne se sobira na A4  dokolku go vrakam treba da ja dodadam kolonatai i vo html tabelata
          
                      //pravam splitovi da go izvadam samo edniot tip na prikaz kartica
          
                       if(proxyModel->index(i,3).data(Qt::DisplayRole).toString().contains('(')) {
          
                       split1 = proxyModel->index(i,3).data(Qt::DisplayRole).toString().split('(');
          
                       // ovaj if e za zastita da ne vlezam u nedefiniran element na lista
                       if(split1.length()==2) {
          
                       split2 = split1[1].split(')');
          
                       htmltable = htmltable+"<td" + tdthcss  + ">" + split2[0] + "</td>";
          
                       } else {htmltable = htmltable+"<td" + tdthcss  + ">" + "</td>";}
          
                       split1.clear();
                       split2.clear();
          
                       }
                       else { htmltable = htmltable+"<td" + tdthcss  + ">" + "</td>";}
          
          
          
                      //za name
          
                      htmltable = htmltable+"<td" + tdthcss  + "><span>" + proxyModel->index(i,4).data(Qt::DisplayRole).toString() + "</span></td>";
          
          
                      //za datum
          
                      htmltable = htmltable+"<td" + tdthcss  + "><span>" + proxyModel->index(i,5).data(Qt::DisplayRole).toString() + "</span></td></tr>";
          
                      j++;
                      k++;
          
                      //za da ja seckam na stranici
                      if (k == 51) {
          
                      k = 1;
          
                      htmltable = htmltable + "</table> </body> </html>";
          
                      htmltable = htmltable + "<html  > <head>  </head> <body style = ''><table align=center style = 'margin-top:50px;  margin-right:20px; '><tr><th><h2><span>Извештај за настани од контрола на пристап</h2></span></th></tr></table><table align=center style='margin-top:10px;  margin-right:20px; '><tr><th"+ tdthcss +"><span>Филтер:</span></th><th"+ tdthcss +"><span>" +  searchCombo->currentText() + "</span></th><th"+ tdthcss +"><span>Од:</span></th><th"+ tdthcss +"><span>"+ c1->date().toString("dd.MM.yyyy") +"</span></th><th"+ tdthcss +"><span>До:</span></th><th"+ tdthcss +"><span>"+ c2->date().toString("dd.MM.yyyy") +"</span></th></tr></table>";
          
                      htmltable= htmltable + "<table align=center cellspacing=1 width=440 " + tablecss + "> <tr> <th" + tdthcss  + "><span>Бр.</span></th> <th" + tdthcss  + "><span>Уред</span></th> <th" + tdthcss  + "><span>Лог</span></th> <th" + tdthcss  + "><span>Врата</span></th> <th" + tdthcss  + "><span>UID</span></th><th" + tdthcss  + "><span>Корисник</span></th> <th" + tdthcss  + "><span>Датум и Време</span></th> </tr>";
          
          
                      };
          
          
              }
          
              htmltable = htmltable + "</table> </body> </html>";
          
              QString fileName = QFileDialog::getSaveFileName(this,tr("Снимај листа на логови како PDF"),"", tr("PDF files (*.pdf)"));
          
                  if (fileName.isEmpty())
          
                      return;
          
          
          
          
                  QPrinter printer;
          
                  printer.setResolution(QPrinter::HighResolution);
          
                  printer.setOutputFormat(QPrinter::PdfFormat);
          
                  printer.setOutputFileName(fileName);
          
                  // printer.setFullPage(true);
          
                  printer.setOrientation(QPrinter::Portrait);
          
                  printer.setPaperSize(QPrinter::A4);
          
                  QSizeF size = printer.paperSize(QPrinter::Point);
          
                 // QTextEdit *edit = new QTextEdit(this);
          
                  //edit->setHtml(htmltable);
          
                  QTextDocument doc;
          
                  doc.setHtml(htmltable);
          
                  doc.setPageSize(size);
          
                  doc.print(&printer);
          
          
          }
          
          1 Reply Last reply
          0
          • C Offline
            C Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on 16 Dec 2016, 19:47 last edited by Chris Kawa
            #8

            The problem in this code is the classical one - it's that there's no one super-slow place in it. It's that all of it is kinda slow in small amounts and it just piles up.
            Couple of examples that hopefully you can expand on:

            if ((j%2)!=0) { tdthcss = " style = 'text-align: left; padding: 1px; background-color: #dddddd; font-size:6pt;' "; }
                    else { tdthcss = " style = 'text-align: left; padding: 1px; background-color: #fff; font-size:6pt;' "; }
            

            Thousands of times you recreate new string tdthcss, while you could just once create both versions as separate constants and use one or the other.

            htmltable = htmltable+"<tr> <td" + tdthcss  + "><span>" + QString::number(j) + "</span></td>";
            

            Each + and = here is a costly reallocation:

            htmltable+"<tr> <td" //allocates a temporary and copies both strings into it
            + tdthcss // reallocates the temporary to make room and copies
            + "><span>" //reallocates the temporary to make room and copies
            + QString::number(j) // I hope you see where this is going...
            

            Instead of doing all those reallocations create the string once, reserve some expected amount of storage and then add to it:

            htmlTable.reserve(<whatever you think is gonna be enouh>);
            for (...) {
               htmlTable += "<tr> <td"; //no reallocations
               htmlTable += tdthcss; //no reallocations
               ...
            }
            

            On to the next lines:

            if(proxyModel->index(i,3).data(Qt::DisplayRole).toString().contains('(')) {
                        split1 = proxyModel->index(i,3).data(Qt::DisplayRole).toString().split('(');
            

            Conversions do cost. Don't do them twice if you don't have to:

            QString foo = proxyModel->index(i,3).data(Qt::DisplayRole).toString();
            if(foo.contains('(')) {
                        split1 = foo.split('(');
            

            Next - the splits. Splitting string is very costly. It creates copies of each part and QStringList is not a great structure either. Instead use splitRef, which creates a nice vector of small QStringRef's. These are just pointers into the existing data so no character copying is made at all.

            Better yet - why split at all? What you really want to do is find text between ( and ) so why allocate any dynamic container (even for refs).
            Here's how you could just find the fragment in-place without any dynamic allocations:

            QString foo = proxyModel->index(i,3).data(Qt::DisplayRole).toString();
            int start = foo.indexOf('('); //just search no allocations
            if (start >= 0) {
               int end = foo.indexOf(')', start); //just search no allocations
               if (end >=0)
                  htmlTable += foo.midRef(start, end - start); //just copy, no reallocations
               else ...
            }
            else ...
            

            And finally

            split1.clear();
            split2.clear();
            

            That's just waste of time. You're reassigning the variables each time so clearing them afterwards is just wasted cycles.

            1 Reply Last reply
            2
            • H Offline
              H Offline
              hskoglund
              wrote on 16 Dec 2016, 20:19 last edited by
              #9

              Hi, just want to add to @Chris-Kawa's great advice, to ease the burden on that poor QString htmltable that gets reassigned lots of times, you could try using a local QString inside your loop, like this:

              for (int i =0; i < proxyModel->rowCount(); i++) {
                  ...
                  //za reden broj
                  QString htmlrow;
                  htmlrow.reserve(1337);
                  htmlrow = "<tr> <td" + tdthcss  + "><span>" + QString::number(j) + "</span></td>";
                  
                  //za device
                  htmlrow += "<td" + tdthcss  + "><span>" + proxyModel->index(i,0).data(Qt::DisplayRole).toString() + "</span></td>";
                  ...
              

              then at the end of the loop you add htmlrow to htmltable

                  htmlrow += "<table align=center cellspacing=1 width=440 " + tablecss + "> <tr> <th" + tdthcss  + "><span>Бр.</span></th> <th" + tdthcss  + "><span>Уред</span></th> <th" + tdthcss  + "><span>Лог</span></th> <th" + tdthcss  + "><span>Врата</span></th> <th" + tdthcss  + "><span>UID</span></th><th" + tdthcss  + "><span>Корисник</span></th> <th" + tdthcss  + "><span>Датум и Време</span></th> </tr>";
                  };
                  
                  htmltable += htmlrow;
               }
              1 Reply Last reply
              1
              • M Offline
                M Offline
                mardzo
                wrote on 17 Dec 2016, 11:04 last edited by
                #10

                Thanks guys for your advice. Your optimisations increased the generation speed of my table a lot. The only thing that seems still to be slow is the QPrinter when table has thousands of rows. Should i move the printer in a different thread in order to prevent gui freezing?

                  QPrinter printer;
                
                        printer.setResolution(QPrinter::HighResolution);
                
                        printer.setOutputFormat(QPrinter::PdfFormat);
                
                        printer.setOutputFileName(fileName);
                
                        // printer.setFullPage(true);
                
                        printer.setOrientation(QPrinter::Portrait);
                
                        printer.setPaperSize(QPrinter::A4);
                
                        QSizeF size = printer.paperSize(QPrinter::Point);
                
                       // QTextEdit *edit = new QTextEdit(this);
                
                        //edit->setHtml(htmltable);
                
                        QTextDocument doc;
                
                        doc.setHtml(htmltable);
                
                        doc.setPageSize(size);
                
                        doc.print(&printer);
                
                1 Reply Last reply
                0
                • M Offline
                  M Offline
                  mardzo
                  wrote on 17 Dec 2016, 11:17 last edited by mardzo
                  #11

                  debug shows that it is not actually the printer that is making the gui freezes. It is the line that sets the html to the QTextDocument that is slowing down. Any suggestions?

                  doc.setHtml(htmltable);
                  
                  1 Reply Last reply
                  0
                  • C Offline
                    C Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on 17 Dec 2016, 11:53 last edited by
                    #12

                    I guess you're doing the work twice. Instead of building html string and then parse it to convert to QTextDocument maybe you could just build the QTextDocument directly using QTextCursor. That should be faster.

                    1 Reply Last reply
                    0
                    • M Offline
                      M Offline
                      mardzo
                      wrote on 17 Dec 2016, 13:02 last edited by
                      #13

                      Thanks for the suggestion but as i already have a worker thread used for stg else, i am now printing in the worker thread and im sending the string via a signal.

                      1 Reply Last reply
                      0

                      13/13

                      17 Dec 2016, 13:02

                      • Login

                      • Login or register to search.
                      13 out of 13
                      • First post
                        13/13
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved