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. Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.
QtWS25 Last Chance

Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.

Scheduled Pinned Locked Moved Solved General and Desktop
11 Posts 4 Posters 503 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.
  • P Offline
    P Offline
    parameter2
    wrote on last edited by parameter2
    #1

    Hello. I have a sortable QTableView that is used to display a log from a CSV file.

    Here is how I initialize the QTableView:

    stdmodel_test = new QStandardItemModel(0,4,this);
    stdmodel_test->setHorizontalHeaderItem(0, new QStandardItem(QString("Column1")));
    stdmodel_test->setHorizontalHeaderItem(1, new QStandardItem(QString("Column2")));
    stdmodel_test->setHorizontalHeaderItem(2, new QStandardItem(QString("Column3")));
    
    sort_test = new QSortFilterProxyModel(this);
    sort_test->setDynamicSortFilter(true);
    sort_test->setSourceModel(stdmodel_test);
    
    ui->tableView_test->setModel(sort_test);
    ui->tableView_test->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableView_test->setFocusPolicy(Qt::NoFocus);
    ui->tableView_test->setColumnWidth(0,150);
    ui->tableView_test->setColumnWidth(1,150);
    ui->tableView_test->setColumnWidth(2,150);
    
    ui->tableView_test->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    ui->tableView_test->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    ui->tableView_test->verticalHeader()->setVisible(false);
    ui->tableView_test->setSortingEnabled(true);
    ui->tableView_test->sortByColumn(0,Qt::AscendingOrder);
    

    Here is how I load (and reload) the data into the view:

    void MainWindow::load_testLog() {
    stdmodel_test->setRowCount(0);
    
    tmpmodel_test = new QStandardItemModel(0,4);
    tmpmodel_test->setHorizontalHeaderItem(0, new QStandardItem(QString("Column1")));
    tmpmodel_test->setHorizontalHeaderItem(1, new QStandardItem(QString("Column2")));
    tmpmodel_test->setHorizontalHeaderItem(2, new QStandardItem(QString("Column3")));
    
    tmpmodel_test->setRowCount(0);
    
    QFile file;
    file.setFileName(test.csv);
    
    if (file.open(QIODevice::ReadOnly)) {
        int lineindex = 0;                     // file line counter
        QTextStream in(&file);                 // read to text stream
    
        while (!in.atEnd()) {
    
            QString fileLine = in.readLine();
            QStringList lineToken = fileLine.split(",", QString::KeepEmptyParts);
            for (int j = 0; j < 3; j++) {
                const QString& value = lineToken.at(j);
    
                QStandardItem *item = new QStandardItem(value);
                tmpmodel_test->setItem(lineindex, j, item);
            }
            lineindex++;
        }
        file.close();
    
        sort_test->setSourceModel(tmpmodel_test);
    
        ui->tableView_test->setModel(sort_test);
    }
    }
    

    And finally this is how call the load_testLog function:

    void MainWindow::reload(){
    
    QFuture<void> t0 = QtConcurrent::run(this,&MainWindow::load_testLog);
    }
    

    Here is a sample log.csv file with a few lines:

    00-00-0000 12:00:10.001,TEST LOG ENTRY,System (TEST_LOG)
    00-00-0000 12:00:10.002,TEST LOG ENTRY,System (TEST_LOG)
    00-00-0000 12:00:10.003,TEST LOG ENTRY,System (TEST_LOG)
    

    Even though my approach sort of works, and the log seems to load properly into QTableview, I get a bunch of worrying messagess in the application output:

    QObject: Cannot create children for a parent that is in a different thread.
    (Parent is QHeaderView(0x564279395eb0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
    QBasicTimer::start: Timers cannot be started from another thread
    QObject: Cannot create children for a parent that is in a different thread.
    (Parent is QHeaderView(0x5642793637b0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
    QBasicTimer::start: Timers cannot be started from another thread
    QObject: Cannot create children for a parent that is in a different thread.
    (Parent is QTableView(0x56427938f9a0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
    

    Would you help me fix this? Fyi. I need to load all the data into the log at once.

    1 Reply Last reply
    0
    • A Offline
      A Offline
      Asperamanca
      wrote on last edited by Asperamanca
      #7

      The part where you set the model inside the thread is the core problem. As mentioned, there are multiple solutions.

      Christian is right, QtConcurrent::map it's not really needed here. I think the easiest solution is to use a QFutureWatcher and connect to the "finished" signal. In the called slot, you can set the model (in context of the UI thread).

      The only question is: How to get access to the result?
      Multiple options:

      • QtConcurrent::map would put the result in the QFuture (although this isn't really the use case for QtConcurrent::map)
      • You can pass something to the QtConcurrent::run function where it can put the result. Of course, in this case, proper locking is your job
      • You can, of course, use QThread instead, but unless you plan to do more than that inside the thread, I see no compelling reason to do so
      1 Reply Last reply
      3
      • A Offline
        A Offline
        Asperamanca
        wrote on last edited by Asperamanca
        #2

        @parameter2 said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

        ui->tableView_events->setModel(sort_test);

        You can't just call the tableView from a different thread like this.
        I would suggest that instead of using QtConcurrent::run, you use QtConcurrent::map and map from an input file to a complete QAbstractItemModel.

        That way, you can get the final model from the QFuture once the model is complete, and do the setModel inside the UI thread.

        P 1 Reply Last reply
        6
        • A Asperamanca

          @parameter2 said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

          ui->tableView_events->setModel(sort_test);

          You can't just call the tableView from a different thread like this.
          I would suggest that instead of using QtConcurrent::run, you use QtConcurrent::map and map from an input file to a complete QAbstractItemModel.

          That way, you can get the final model from the QFuture once the model is complete, and do the setModel inside the UI thread.

          P Offline
          P Offline
          parameter2
          wrote on last edited by
          #3

          @Asperamanca said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

          I would suggest that instead of using QtConcurrent::run, you use QtConcurrent::map and map from an input file to a complete QAbstractItemModel.

          Thank you for your suggestions. Is there any way you could show me how to accomplish that with QtConcurrent::map using some minimal example?

          1 Reply Last reply
          0
          • Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #4

            No need for QtConcurrent::map(). A separate thread or even better QtConcurrent::run() is fine here.

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            P 1 Reply Last reply
            2
            • Christian EhrlicherC Christian Ehrlicher

              No need for QtConcurrent::map(). A separate thread or even better QtConcurrent::run() is fine here.

              P Offline
              P Offline
              parameter2
              wrote on last edited by
              #5

              @Christian-Ehrlicher said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

              No need for QtConcurrent::map(). A separate thread or even better QtConcurrent::run() is fine here.

              What might I be doing wrong, since I'm using QtConcurrent::run() to populate the model and getting all these QThread errors?

              QObject: Cannot create children for a parent that is in a different thread.
              (Parent is QHeaderView(0x564279395eb0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
              QBasicTimer::start: Timers cannot be started from another thread
              QObject: Cannot create children for a parent that is in a different thread.
              (Parent is QHeaderView(0x5642793637b0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
              QBasicTimer::start: Timers cannot be started from another thread
              QObject: Cannot create children for a parent that is in a different thread.
              (Parent is QTableView(0x56427938f9a0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
              
              Christian EhrlicherC 1 Reply Last reply
              0
              • P parameter2

                @Christian-Ehrlicher said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

                No need for QtConcurrent::map(). A separate thread or even better QtConcurrent::run() is fine here.

                What might I be doing wrong, since I'm using QtConcurrent::run() to populate the model and getting all these QThread errors?

                QObject: Cannot create children for a parent that is in a different thread.
                (Parent is QHeaderView(0x564279395eb0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
                QBasicTimer::start: Timers cannot be started from another thread
                QObject: Cannot create children for a parent that is in a different thread.
                (Parent is QHeaderView(0x5642793637b0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
                QBasicTimer::start: Timers cannot be started from another thread
                QObject: Cannot create children for a parent that is in a different thread.
                (Parent is QTableView(0x56427938f9a0), parent's thread is QThread(0x564278e9b540), current thread is QThread(0x56427974f4b0)
                
                Christian EhrlicherC Offline
                Christian EhrlicherC Offline
                Christian Ehrlicher
                Lifetime Qt Champion
                wrote on last edited by
                #6

                @parameter2 @Asperamanca already told you what you're doing wrong and how to fix it.

                Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                Visit the Qt Academy at https://academy.qt.io/catalog

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Asperamanca
                  wrote on last edited by Asperamanca
                  #7

                  The part where you set the model inside the thread is the core problem. As mentioned, there are multiple solutions.

                  Christian is right, QtConcurrent::map it's not really needed here. I think the easiest solution is to use a QFutureWatcher and connect to the "finished" signal. In the called slot, you can set the model (in context of the UI thread).

                  The only question is: How to get access to the result?
                  Multiple options:

                  • QtConcurrent::map would put the result in the QFuture (although this isn't really the use case for QtConcurrent::map)
                  • You can pass something to the QtConcurrent::run function where it can put the result. Of course, in this case, proper locking is your job
                  • You can, of course, use QThread instead, but unless you plan to do more than that inside the thread, I see no compelling reason to do so
                  1 Reply Last reply
                  3
                  • Christian EhrlicherC Offline
                    Christian EhrlicherC Offline
                    Christian Ehrlicher
                    Lifetime Qt Champion
                    wrote on last edited by
                    #8
                    • Use QtConcurrent::run() and get the result through the future.

                    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                    Visit the Qt Academy at https://academy.qt.io/catalog

                    P A 2 Replies Last reply
                    3
                    • Christian EhrlicherC Christian Ehrlicher
                      • Use QtConcurrent::run() and get the result through the future.
                      P Offline
                      P Offline
                      parameter2
                      wrote on last edited by
                      #9

                      @Asperamanca said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

                      The part where you set the model inside the thread is the core problem. As mentioned, there are multiple solutions.

                      Christian is right, QtConcurrent::map it's not really needed here. I think the easiest solution is to use a QFutureWatcher and connect to the "finished" signal. In the called slot, you can set the model (in context of the UI thread).

                      The only question is: How to get access to the result?
                      Multiple options:

                      • QtConcurrent::map would put the result in the QFuture (although this isn't really the use case for QtConcurrent::map)
                      • You can pass something to the QtConcurrent::run function where it can put the result. Of course, in this case, proper locking is your job
                      • You can, of course, use QThread instead, but unless you plan to do more than that inside the thread, I see no compelling reason to do so

                      @Christian-Ehrlicher said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

                      • Use QtConcurrent::run() and get the result through the future.

                      Thank you. I appreciate your responses. Hopefully I can figure this out now.

                      1 Reply Last reply
                      0
                      • Christian EhrlicherC Christian Ehrlicher
                        • Use QtConcurrent::run() and get the result through the future.
                        A Offline
                        A Offline
                        Asperamanca
                        wrote on last edited by
                        #10

                        @Christian-Ehrlicher said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

                        • Use QtConcurrent::run() and get the result through the future.

                        I didn't know you can do that! I though for QtConcurrent::run the future object is void.
                        Shows you learn new tricks every day :-)

                        Pl45m4P 1 Reply Last reply
                        0
                        • A Asperamanca

                          @Christian-Ehrlicher said in Best way to efficiently load 20MB of data from CSV file into QTableView without hanging the gui thread.:

                          • Use QtConcurrent::run() and get the result through the future.

                          I didn't know you can do that! I though for QtConcurrent::run the future object is void.
                          Shows you learn new tricks every day :-)

                          Pl45m4P Offline
                          Pl45m4P Offline
                          Pl45m4
                          wrote on last edited by Pl45m4
                          #11

                          @Asperamanca

                          If you set your QFuture's type to a type T != void, you actually can "use" the result later. Of course it has to match the return type of the function used in run.

                          As described here


                          If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                          ~E. W. Dijkstra

                          1 Reply Last reply
                          0

                          • Login

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