Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QStandardItemModel* gained from non-GUI thread isn`t emit itemChanged() signal



  • I have started database loading in non-GUI thread with QtCuncurrent::run. In this nonGui thread I have to create QStandardItemModel* :

    QStandardItemModel* model = new QStandardItemModel;
    QStandardItem* parentItem = model->invisibleRootItem();
    

    and after that I received model in GUI thread with

    model = modelWatcher.result();
    

    on QFutureWatcher finished() signal. It works pretty (UI is builded successfully), but itemChanged() signal is not emitted on item data changes (checkbox state changed). When I creates the model in GUI thread, there are no collisions. Connect works without assert fails:

    bool ok = connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(onFolderStateChanged(QStandardItem*)), static_cast<Qt::ConnectionType>(Qt::UniqueConnection));
    Q_ASSERT(ok);
    

    I know that I can't create the model (part of Qt5Gui) in nonGui thread. But what should I do and which way is correct? Also I have to declare sended type with:

    qRegisterMetaType<QStandardItemModel*>("QStandardItemModel*");
    

    Other sends like:

    qRegisterMetaType<QList<QTreeWidgetItem*> >("QList<QTreeWidgetItem*>");
    

    works good (though its also Qt5Gui part).

    How can I get the model from nonGui thread with valid pointer/meta and working itemChagned signals ?


  • Qt Champions 2019

    You should not use GUI related classes in non GUI threads!
    Why do you want to create QStandardItemModel in that second thread?
    That thread should not care about UI, it should read the data from the database and pass it to the UI via signals.
    UI then knows what to do with the data and how to present it to the user.
    Is there really a need for a thread to access the database?



  • @jsulm In worker thread I have read database structure and at that moment creates the QStandardItem*s with its structure. (I have read DB with external dll with code:)

    while(hasItem)
        readItem(level1) // dll function
            while(hasItem)
                readItem(level2)
                      while(hasItem)
                              readItem(level3)
    

    In that case I dont know the structure If I dont creates QStandardItems and model in that place. Or I need to resend all readItem`s data to GUI thread and do while loops in GUI? But it is also not a true way, as I think). Is the model can not be created in worker thread? Its a pattern MVC - model thread, view thread, correct me if I m wrong ... The best way - it creates my own class with useful values from readItem() function and send to GUI, for example, QList<MyItemClass> level1itemsList etc (level 2, level3) and do while on QLists in GUI?


  • Qt Champions 2019

    MVC does not say anything about multithreading. It only separates the data layer (model) from its representation (view) and the logic (controller).
    I'm wondering why you do not know the structure of the data you read from the database?
    Yes, you should pass the data from your database thread to the GUI thread, for example as QList.
    Are you sure you need an extra thread?
    Did you already encounter any issues with only one thread?



  • @jsulm Yes, with only one thread UI is freezing(database may be >1 Gb). Structure of db is a simple recursive hierarchy, displayed as a tree with QTreeView. I will try to send only data from worker thread and build the model in GUI-thread.


  • Qt Champions 2017

    You can create the model in worker thread. If you create the model in worker thread, you need to have event loop in thread to make the signal/slot works. Just look at the following snippet

    void MyThread::run()
    {
    qDebug() << Q_FUNC_INFO << " Creating thread"<<endl;
    model = new QStandardItemModel;
    model->setRowCount(5);
    model->setColumnCount(5);
    for (int row = 0; row < 5; ++row) {
    for (int column = 0; column < 5; ++column) {
    QStandardItem item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
    model->setItem(row, column, item);
    }
    }
    bool ok = connect(model, SIGNAL(itemChanged(QStandardItem
    )), this, SLOT(onFolderStateChanged(QStandardItem*)), static_castQt::ConnectionType(Qt::UniqueConnection));
    Q_ASSERT(ok);
    QTimer *tim = new QTimer;
    tim->setInterval(2000);
    tim->start();
    connect(tim,&QTimer::timeout,this,&MyThread::updateModel);
    qRegisterMetaType<QVector<int> >();
    exec();
    }

    QStandardItemModel *MyThread::getModel()
    {
    return model;
    }

    void MyThread::updateModel()
    {
    qDebug() << "UPdating the model"<<endl;
    QStandardItem *item = new QStandardItem("Gururaja");
    model->setItem(3,3,item);
    }

    void MyThread::onFolderStateChanged(QStandardItem *)
    {
    qDebug() << "Item is changed" << endl;
    }



  • @dheerendra Sorry, but I don`t understand what do you mean as "event loop"? You mean timer that updates the model? And the reason for itemChanged signal does not work - is that the model don't know about changes and I need to update the model manually?
    In that case how can I update model with checkbox state changed data, if itemChanged() signal didn't works. (or I need use state snapshots with all items and check its difference)?


  • Lifetime Qt Champion

    Hi,

    Here you can find a nice piece of documentation about QThread/QObject/Event loops.


Log in to reply