You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySide Qt中dataChanged.emit无效但layoutChanged.emit可触发带QML报错问题

PySide6 QAbstractListModel更新不触发界面及QML报错问题解决方案

问题原因

  • 新增行调用dataChanged无效的原因:dataChanged信号仅用于通知视图已存在的行的数据发生修改,不适用于新增/删除行的场景。且原代码中dataChanged.emit传入的参数完全错误:self.index是模型的索引生成方法,并未调用生成实际新增行的索引,信号参数不合法自然无法触发更新。
  • 调用layoutChanged触发QML报错的原因:layoutChanged是全量布局重置信号,会触发视图销毁所有已渲染的列表项再全部重建,若QML委托存在未完成的属性绑定、动画逻辑,旧列表项销毁后绑定的对象变为null,就会抛出访问null属性的报错,同时该方法性能极差,完全不适合单条数据新增的场景。

解决方案

Qt的QAbstractItemModel家族有严格的更新约定:所有修改模型结构(新增/删除/移动行)的操作,必须在修改内部数据前调用对应的beginXXX方法,修改完成后调用对应的endXXX方法,由这两个方法自动发送合规的信号通知视图增量更新。

修改后完整代码如下:

import sys
import random
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide6.QtCore import QObject, Signal, Slot, Property, Qt, QUrl, QTimer, QAbstractListModel, QModelIndex


class CpuLoadModel(QAbstractListModel):
    def __init__(self):
        super().__init__()
        self.__update_timer = QTimer(self)
        self.__update_timer.setInterval(1000)
        self.__update_timer.timeout.connect(self.__update)
        self.__update_timer.start()
        self.todos = []

    def __update(self):
        new_row_pos = len(self.todos)
        # 新增行前调用beginInsertRows,传入父索引(列表为无效索引)、新增行的起止位置
        self.beginInsertRows(QModelIndex(), new_row_pos, new_row_pos)
        self.todos.append(random.randint(0, 99))
        # 数据修改完成后调用endInsertRows,自动通知视图增量更新
        self.endInsertRows()
        print(self.todos)

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid() or index.row() >= len(self.todos):
            return None
        if role == Qt.DisplayRole:
            return self.todos[index.row()]
        return None

    def rowCount(self, index=QModelIndex()):
        # 符合Qt模型约定:有效父索引下返回0(列表无层级结构)
        if index.isValid():
            return 0
        return len(self.todos)


if __name__ == '__main__':
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    qmlRegisterType(CpuLoadModel, 'PsUtils', 1, 0, 'CpuLoadModel')
    engine.load(QUrl("main.qml"))
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec())

核心修改点

  1. 新增行时使用beginInsertRows + endInsertRows的标准流程,无需额外发送dataChanged或layoutChanged信号
  2. 给data和rowCount方法增加了索引合法性校验,符合Qt模型接口规范,避免边界场景报错
  3. 补充了缺失的依赖导入,优化了父类初始化写法

修改后视图只会增量渲染新增的一行,不会销毁旧列表项,既不会出现界面不更新的问题,也不会触发QML端的空对象访问报错。

内容的提问来源于stack exchange,提问作者David Leopoldie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 23:15:05