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
相关产品推荐
相关产品推荐

