QTreeview and refreshing the model freezes the GUI momentarily.



  • I have been trying to help an open source project to fix a particular problem. This project uses a custom treemodel derived from qabstractitemmodel and a custom delegate derived from qitemdelegate. The model has an internal timer which when it expires it fetches the updated values for each cell and emits a dataChanged() signal which contains the whole model. The model usually has 22 columns enabled. This works very well if the rows are few, but when they get above 200 the GUI freezes for almost a second when the timer expires. I initially thought that the retrieval of the updated values was the bottleneck, but with further tests it seems the freeze is caused by the repainting of the treeview.

    I managed to make a small example project. I just output rand() numbers so to be sure that the retrieval of the values isn't the bottleneck. Just click-drag the scrollbar up and down and eventually you will notice the freeze.
    Do you have any idea how to solve this? Any alternative way of updating the model?

    main.cpp
    @#include <QApplication>
    #include "mainwindow.h"

    #include <cstdlib>
    #include <ctime>
    int main(int argc, char *argv[])
    {
    srand(time(NULL));
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
    } @

    mainwindow.h
    @#ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include <QMainWindow>
    #include <QStandardItemModel>
    #include <QTimer>
    #include <QTreeView>

    class TModel : public QStandardItemModel
    {
    Q_OBJECT

    public:
    TModel( int rows, int columns, QObject * parent = 0 );
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole);
    void setTimer(const int &msec);

    private:
    bool expired_timer;
    QTimer timer;

    private slots:
    void forceUpdate();
    };

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow
    {
    Q_OBJECT

    public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    private:
    Ui::MainWindow *ui;
    TModel *model;
    QTreeView *view;
    };

    #endif // MAINWINDOW_H @

    mainwindow.cpp
    @#include "mainwindow.h"
    #include "ui_mainwindow.h"

    #include <cstdlib>

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    view = new QTreeView();
    model = new TModel(0, 22, view);
    view->setModel(model);
    setCentralWidget(view);

    for (unsigned int i = 0; i < 300; i++)
    {
    model->insertRow(model->rowCount());
    model->setData(model->index(i,0), i+1);
    for (unsigned int c = 1; c < model->columnCount(); c++)
    {
    model->setData(model->index(i,c), rand());
    }
    }
    model->setTimer(750);
    }

    MainWindow::~MainWindow()
    {
    delete ui;
    delete view;
    delete model;
    }

    TModel::TModel( int rows, int columns, QObject * parent): QStandardItemModel(rows, columns, parent), expired_timer(false)
    {
    connect(&timer, SIGNAL(timeout()), SLOT(forceUpdate()));
    }

    void TModel::setTimer(const int &msec)
    {
    timer.stop();
    timer.start(msec);
    }

    bool TModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
    if (expired_timer) blockSignals(true);
    bool ret = QStandardItemModel::setData(index, value, role);
    if (expired_timer) blockSignals(false);
    return ret;
    }

    void TModel::forceUpdate()
    {
    expired_timer = true;
    for (unsigned int i = 0; i < rowCount(); i++)
    {
    for (unsigned int c = 1; c < columnCount(); c++)
    {
    setData(index(i,c), rand());
    }
    }

    emit dataChanged(index(0,0), index(rowCount()-1 ,columnCount()-1));
    expired_timer = false;
    } @

    mainwindow.ui
    @<?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
    <class>MainWindow</class>
    <widget class="QMainWindow" name="MainWindow">
    <property name="geometry">
    <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
    </rect>
    </property>
    <property name="windowTitle">
    <string>MainWindow</string>
    </property>
    <widget class="QWidget" name="centralWidget"/>
    </widget>
    <layoutdefault spacing="6" margin="11"/>
    <resources/>
    <connections/>
    </ui>@

    untitled.pro
    @#-------------------------------------------------

    Project created by QtCreator 2013-04-28T15:53:26

    #-------------------------------------------------

    QT += core gui

    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

    TARGET = untitled
    TEMPLATE = app

    SOURCES += main.cpp
    mainwindow.cpp

    HEADERS += mainwindow.h

    FORMS += mainwindow.ui @



  • Oke, try to use the datachanged in the setData function only for the item that will be changed into the model. The will make sure the GUI keep on running between the updates on screen when data is being changed.
    In the forceUpdate use the reset() signal, this will automaticly update the entire View, but takes LONG time when a large list is displayed.



  • emitting the dataChanged singal inside setData when I update ALL cells is horribly inefficient. It locks up the whole application for several seconds, and in the meantime the timer has expired again causing a chained reaction.

    If I use reset() in place of the emit dataChanged() it slightly improves the update with almost no noticeable gui freeze BUT: It has nasty drawback. The selected row is forgotten.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.