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

PyQt5遍历QListWidget报NoneType无checkState属性错误排查

问题原因

报错的核心原因是正向遍历QListWidget的过程中直接删除列表项,引发索引错位。
你在循环启动时用range(0, self.listUndone.count())生成了固定的索引序列,这个序列的长度是循环开始瞬间listUndone的项目总数。但循环内部只要检测到勾选的项,就会调用takeItem(i)把该项从列表中移除:移除操作会让列表实际长度减1,被删项后面的所有项目索引都会自动往前挪1位。
举个实际场景:假设初始列表有3个项,对应索引0、1、2,循环原本要执行i=0、i=1、i=2三轮。如果i=1时你删掉了索引1的项,原本在索引2的项会自动变成新的索引1,等循环走到i=2时,self.listUndone.item(2)会去访问已经不存在的索引位置,方法直接返回None,再调用checkState()就会触发你看到的AttributeError。你打印出的0、2、0三个值,刚好是错位发生前能正常取到的三个状态值,再往后就取不到有效item了。
另外你原代码还有三个隐藏问题:

  • 移动项时重新新建QListWidgetItem没必要,takeItem()返回的就是被移除的项对象,可以直接复用
  • 用f-string拼接SQL语句存在注入风险,建议改用参数化查询
  • 你用tasks[i][0]给新项赋值也会受索引错位影响,容易出现移动后的任务文本和原项不匹配的问题
解决方法

选任意一种方案即可:

  • 方案1:倒序遍历列表。从最后一个项目的索引往0方向遍历,删除当前索引的项不会影响还没遍历到的前面项的索引,从根源避免错位。
# 从最后一项的索引开始,倒序走到索引0
for i in range(self.listUndone.count() - 1, -1, -1):
    item = self.listUndone.item(i)
    if item.checkState() == Qt.Checked:
        task_text = item.text()
        # 参数化查询避免SQL注入
        c.execute("UPDATE todo SET status = 'DONE' WHERE tasks = ?", (task_text,))
        # 直接取出被移除的项复用,不需要新建
        moved_item = self.listUndone.takeItem(i)
        moved_item.setCheckState(Qt.Checked)
        self.listDone.addItem(moved_item)
  • 方案2:分两轮处理,第一轮遍历只收集需要移动的项,不修改列表结构,收集完成后再统一做删除、移动操作。
to_move = []
# 第一轮:只读遍历,收集所有需要移动的项
for i in range(self.listUndone.count()):
    item = self.listUndone.item(i)
    if item.checkState() == Qt.Checked:
        to_move.append( (i, item) )

# 第二轮:倒序删除/移动,避免索引错位
for i, item in reversed(to_move):
    c.execute("UPDATE todo SET status = 'DONE' WHERE tasks = ?", (item.text(),))
    self.listUndone.takeItem(i)
    item.setCheckState(Qt.Checked)
    self.listDone.addItem(item)

内容的提问来源于stack exchange,提问作者Raf Dizon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:01:07