QML TableView 实时刷新仅每 1 秒更新 UI

QML TableView refresh in realtime only updates UI every 1 second

我的任务是实时显示大量数据(尽可能接近),UI 每 10 到 100 毫秒更新一次(最佳值到最差值),我设法创建了一个测试模型生成随机数并使用带有计时器的新随机值集填充 table 视图。我尝试设置从 1 到 100 毫秒的不同时间间隔,我可以看到计时器触发并创建了新的数据集,但是 UI 尽管有计时器间隔值,但每次都严格在 1 秒内更新。 你能指导我如何在不到 1 秒的时间内处理 UI 更新。

我尝试了不同数量的数据,从 table 的 50x1000 到 50x50。每次我得到 UI 1 秒的更新率。

它基于 "Game Of Life" Qt 示例,因此一些元素只是未使用且已过时,但一旦它们 "disabled" 我认为它们不会产生任何影响对其余代码和问题本身的影响。 您可以看到,在 nextStep() 方法中,我调用了时间戳记录到控制台,并且我可以在输出中看到该方法是根据计时器调用的,但是 UI 每秒仅在视觉上更新一次。

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]

好的,问题解决了。问题似乎出在随机数生成过程中,而不是在 table 视图中。 在我使用的代码中

srand(time(NULL))

The time(NULL) returns 秒而不是毫秒,因此我得到了相同的数字并且它们仅在 1 秒后更新并且在视觉上类似于仅更新 UI 每第二。为了解决这个问题,我转而使用它来生成随机数并且一切正常:

struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);

/* using nano-seconds instead of seconds */
srand((time_t)ts.tv_nsec);