Solved QML TableView refresh in realtime only updates UI every 1 second
-
Hi there!
I have a task of showing big amount of data in realtime (as close to that as possible) with UI update every 10 to 100ms (best to worst values), I've managed to create a test model generating random numbers and populating the table view with new set of random values with a Timer. I tried to set different time intervals from 1 to 100 ms, and I can see the timer fires and the new set of data is created, but the UI updates strictly in 1 second every time despite the timer interval value.
Can you guide me on how to handle UI updates in less than 1 second.P.S.: I tried different amount of data from tables of 50x1000 to just 50x50. Every time I get UI update rate of 1 second.
Update:
It is based on the "Game Of Life" Qt example, so some of the elements are just not used and are obsolete, but as soon as they are "disabled" I think they don't make any influence on the rest of the code and the problem itself.
You can see that in nextStep() method I invoke timestamp logging to console and I can see in the output that the method is invoked according to the timer, but the UI only updates visually every second.main.qml
ApplicationWindow { id: root visible: true width: 760 height: 810 minimumWidth: 475 minimumHeight: 300 color: "#09102B" title: qsTr("Conway’s Game of Life") //! [tableview] TableView { id: tableView anchors.fill: parent rowSpacing: 1 columnSpacing: 1 ScrollBar.horizontal: ScrollBar {} ScrollBar.vertical: ScrollBar {} delegate: Rectangle { id: cell implicitWidth: 45 implicitHeight: 15 color: model.value > 100 ? "#f3f3f4" : "#b5b7bf" Label { width: parent.width height: parent.height text: model.value } } //! [tableview] //! [model] model: GameOfLifeModel { id: gameOfLifeModel } //! [model] //! [scroll] contentX: 0; contentY: 0; //! [scroll] } footer: Rectangle { signal nextStep id: footer height: 50 color: "#F3F3F4" RowLayout { anchors.centerIn: parent //! [next] Button { text: qsTr("Next") onClicked: gameOfLifeModel.nextStep() } //! [next] Item { width: 50 } Button { text: timer.running ? "❙❙" : "▶️" onClicked: timer.running = !timer.running } } FpsItem { id: fpsItem anchors.left: parent color: "black" } Timer { id: timer interval: 10 running: true repeat: true onTriggered: gameOfLifeModel.nextStep() } } }
gameoflifemodel.cpp
GameOfLifeModel::GameOfLifeModel(QObject *parent) : QAbstractTableModel(parent) { } //! [modelsize] int GameOfLifeModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return height; } int GameOfLifeModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return width; } //! [modelsize] //! [read] QVariant GameOfLifeModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || role != CellRole) return QVariant(); return m_state[index.column()][index.row()]; } //! [read] //! [write / not used] bool GameOfLifeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role != CellRole || data(index, role) == value) return false; m_state[index.column()][index.row()] = value.toBool(); emit dataChanged(index, index, {role}); return true; } //! [write] Qt::ItemFlags GameOfLifeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; return Qt::ItemIsEditable; } //! [update] void GameOfLifeModel::nextStep() { srand(time(NULL)); qDebug() << QTime::currentTime().toString("yyyy/MM/dd hh:mm:ss,zzz"); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { m_state[j][i] = (rand() % 1000) + 1; } } emit dataChanged(index(0, 0), index(height - 1, width - 1), {CellRole}); } //! [update]
gameoflifemodel.h
//! [modelclass] class GameOfLifeModel : public QAbstractTableModel { Q_OBJECT Q_ENUMS(Roles) public: enum Roles { CellRole }; QHash<int, QByteArray> roleNames() const override { return { { CellRole, "value" } }; } explicit GameOfLifeModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; Q_INVOKABLE void nextStep(); private: static constexpr int width = 50; static constexpr int height = 50; static constexpr int size = width * height; template <class T, size_t ROW, size_t COL> using NativeMatrix = T[ROW][COL]; NativeMatrix<int, height, width> m_state; }; //! [modelclass]
Thank you!
-
Okay, the problem is solved. The issue appeared to be in the random number generation process, not in the table view.
In the code I usedsrand(time(NULL))
The time(NULL) returns seconds and not milliseconds, thus I was getting the same numbers and they were updated only after 1 second. In order to solve this I switched to using this to generate random numbers and everything worked well:
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); /* using nano-seconds instead of seconds */ srand((time_t)ts.tv_nsec);
-
Please share a minimalist reproducible example so we can help you.
-
Sorry, updated my question with details.
-
@GrecKo Any ideas? Thanks.
-
Okay, the problem is solved. The issue appeared to be in the random number generation process, not in the table view.
In the code I usedsrand(time(NULL))
The time(NULL) returns seconds and not milliseconds, thus I was getting the same numbers and they were updated only after 1 second. In order to solve this I switched to using this to generate random numbers and everything worked well:
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); /* using nano-seconds instead of seconds */ srand((time_t)ts.tv_nsec);
-
Why do you use srand at each step?
-
Hi
just as a note.
rand() is not very random. or considered not optimal.
If you can use c++ 11 then look into the new random function#include <random> #include <iostream> int main() { std::random_device rd; std::mt19937 mt(rd()); std::uniform_real_distribution<double> dist(1.0, 10.0); for (int i=0; i<16; ++i) std::cout << dist(mt) << "\n"; }
https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful
Its not critical/important if your Life game works.