PyQt6使用QTimer实现冒泡排序每次交换后延迟3秒刷新
问题原因
- 排序逻辑写在
paintEvent中:paintEvent是Qt同步执行的界面渲染事件,进入该方法后会直接跑完整个冒泡排序的双层循环,所有QTimer.singleShot几乎在同一时间被注册,3秒后会同时触发所有刷新回调,自然无法分步展示。 - 同步循环阻塞事件循环:双层for循环是阻塞式执行的,循环运行期间Qt的事件循环无法响应界面刷新、定时任务等操作,等循环执行完成时数组已经是最终排序结果,后续触发的刷新只会绘制最终状态。
- 存在递归触发风险:
paintEvent中调用排序,排序完成后触发update()会再次进入paintEvent,重复执行排序逻辑,会导致逻辑混乱。
修正后代码
import sys from PyQt6.QtWidgets import QWidget, QApplication from PyQt6.QtGui import QPainter from PyQt6.QtCore import Qt, QRect, QTimer import random class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.values = random.sample(range(10, 100), 20) self.h = 50 self.w = len(self.values)*self.h self.setGeometry(300, 300, self.w, self.h) self.setWindowTitle('冒泡排序可视化') # 初始化排序状态变量 self.arr_len = len(self.values) self.sort_i = 0 self.sort_j = 0 self.swapped = False self.show() # 启动第一步排序,延迟3秒 QTimer.singleShot(3000, self.sortSingleStep) def paintEvent(self, event): qp = QPainter() qp.begin(self) i = 0 for x in self.values: rect = QRect(i, 0, 50, 50) qp.drawRect(rect) qp.drawText(rect, Qt.AlignmentFlag.AlignCenter, str(x)) i += 50 qp.end() def sortSingleStep(self): # 所有排序轮次完成,直接退出 if self.sort_i >= self.arr_len - 1: return # 处理当前内循环位置的对比 if self.sort_j < self.arr_len - self.sort_i - 1: if self.values[self.sort_j] > self.values[self.sort_j + 1]: self.swapped = True self.values[self.sort_j], self.values[self.sort_j + 1] = self.values[self.sort_j + 1], self.values[self.sort_j] # 交换完成后刷新界面 self.update() self.sort_j += 1 else: # 当前轮次无交换,说明数组已经有序,直接终止排序 if not self.swapped: return # 进入下一轮排序,重置内循环索引和交换标记 self.swapped = False self.sort_i += 1 self.sort_j = 0 # 注册下一次单步排序任务,间隔3秒 QTimer.singleShot(3000, self.sortSingleStep) def main(): app = QApplication(sys.argv) ex = Example() sys.exit(app.exec()) if __name__ == '__main__': main()
核心修改说明
- 移除
paintEvent中的排序调用,paintEvent仅保留绘图逻辑,避免阻塞渲染和递归触发问题。 - 将原本同步阻塞的双层冒泡循环拆分为单步执行的
sortSingleStep方法,每次调用仅处理一次元素对比/交换,执行完成后立刻把控制权交还给Qt事件循环,保证界面可以正常渲染更新。 - 用类成员变量记录排序进度(外循环索引、内循环索引、交换标记),每次定时触发时基于当前进度推进排序,不再一次性跑完所有循环。
- 每完成一次单步操作后,再注册下一次3秒的定时任务,保证每一步的间隔准确,不会出现所有定时任务同时触发的问题;仅在元素发生交换时触发界面刷新,匹配需求要求。
内容的提问来源于stack exchange,提问作者Arundeep Chohan
相关产品推荐
相关产品推荐

