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

PyQt5实现蓝色方块逐格右移动画:QTimer使用指导求助

PyQt5实现方块逐帧移动动画(解决UI阻塞问题)

问题描述

我希望实现蓝色方块逐个向右移动的动画效果,直至到达最后一个方块的位置。目前编写的PyQt5代码在点击Start按钮后,会先执行完所有操作再更新界面,无法呈现逐帧动画。有人建议使用QTimer,但不清楚如何正确使用,恳请指导。

原代码

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QGridLayout
from PyQt5.QtGui import QFont
import time


class UI_main(QWidget):     

  def __init__(self):
    super().__init__()
    self.Main = QWidget()
    self.Main.setFixedSize(400, 400)

    self.Main.setWindowTitle('PyQt5')

    self.pushStart = QPushButton('Start', self.Main)
    self.pushStart.setGeometry(50, 50, 120, 90)

    self.pushClose = QPushButton('Close', self.Main)
    self.pushClose.setGeometry(250, 250, 100, 100)
    self.ui_Block()
    self.pushStart.clicked.connect(self.push_Start)
    self.pushClose.clicked.connect(self.Main.close)
    self.Main.show()

  def ui_Block(self):
    num = [0, 1, 2, 3]
    self.GridLayout = QGridLayout()
    self.Main.setLayout(self.GridLayout)
    self.numBlock(num)

  def numBlock(self, num):
    self.num = num
    for i in range(len(self.num)):
        self.GridLayout.addWidget(Label_Block(self.num[i]), 0, i)

    self.Main.setLayout(self.GridLayout)    

  def push_Start(self):
    i = 0
    self.num = self.Function(i)

  def Function(self, i):
    self.i = i
    self.i = i + 1
    print(i)
    num, self.i = self.Function2(self.i)
    print("2s")
    time.sleep(2)
    self.numBlock(num)
    self.i = self.i + 1
    num, self.i = self.Function3(self.i)
    print("2s")
    time.sleep(2)
    self.numBlock(num)
    self.i = self.i + 1
    num, self.i = self.Function4(self.i)
    print("2s")
    time.sleep(2)
    self.numBlock(num)

  def Function2(self, i):
    block = [0, 1, 2, 3]
    self.i = i
    print(self.i)
    if self.i == 1:
        block[0] = 1
        block[1] = 0
        block[2] = 2
        block[3] = 3
        self.i = self.i+1
    return block, i

  def Function3(self, i):
    block = [0, 1, 2, 3]
    self.i = i
    print(self.i)
    if self.i == 2:
        block[0] = 1
        block[1] = 2
        block[2] = 0
        block[3] = 3
        self.i = self.i + 1
    return block, i

  def Function4(self, i):
    block = [0, 1, 2, 3]
    self.i = i
    print(self.i)
    if self.i == 3:
        block[0] = 1
        block[1] = 2
        block[2] = 3
        block[3] = 0
        self.i = self.i + 1
    return block, i

class Label_Block(QLabel):
  def __init__(self, block_):
    super().__init__()
    self.block_ = block_
    self.setFixedSize(80, 80)
    font = QFont()

    font.setPointSize(30)
    font.setBold(True)
    self.setFont(font)
    if self.block_ == 0:
        self.setStyleSheet("background-color:blue;border-radius:10px;")
        print('block: ', block_)
    elif self.block_ == 1:
        self.setStyleSheet("background-color:red;border-radius:10px;")
        print('block: ', block_)
    elif self.block_ == 2:
        self.setStyleSheet("background-color:green;border-radius:10px;")
        print('block: ', block_)
    elif self.block_ == 3:
        self.setStyleSheet("background-color:yellow;border-radius:10px;")
        print('block: ', block_)

if __name__ == "__main__":
app = QApplication(sys.argv)
ui = UI_main()
sys.exit(app.exec_())

问题分析

原代码使用time.sleep()会阻塞PyQt的UI事件循环,导致界面无法实时更新——所有布局修改都要等所有sleep执行完毕后才会一次性渲染,因此看不到逐帧动画。QTimer是PyQt的异步定时器,它会在指定时间后触发回调函数,不会阻塞事件循环,能让界面在等待期间正常响应。

修改后的代码

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QGridLayout
from PyQt5.QtGui import QFont
from PyQt5.QtCore import QTimer

class UI_main(QWidget):     
    def __init__(self):
        super().__init__()
        self.Main = QWidget()
        self.Main.setFixedSize(400, 400)
        self.Main.setWindowTitle('PyQt5')

        self.pushStart = QPushButton('Start', self.Main)
        self.pushStart.setGeometry(50, 50, 120, 90)
        self.pushClose = QPushButton('Close', self.Main)
        self.pushClose.setGeometry(250, 250, 100, 100)

        # 初始化定时器和步骤计数器
        self.timer = QTimer(self)
        self.current_step = 0
        self.timer.timeout.connect(self.perform_next_step)

        self.ui_Block()
        self.pushStart.clicked.connect(self.start_animation)
        self.pushClose.clicked.connect(self.Main.close)
        self.Main.show()

    def ui_Block(self):
        self.num = [0, 1, 2, 3]
        self.GridLayout = QGridLayout()
        self.Main.setLayout(self.GridLayout)
        self.update_blocks()

    def update_blocks(self):
        # 清空布局内的旧控件,避免叠加
        while self.GridLayout.count():
            item = self.GridLayout.takeAt(0)
            widget = item.widget()
            if widget:
                widget.deleteLater()
        # 添加新的方块
        for i in range(len(self.num)):
            self.GridLayout.addWidget(Label_Block(self.num[i]), 0, i)

    def start_animation(self):
        # 重置步骤并启动定时器,每2秒触发一次
        self.current_step = 1
        self.timer.start(2000)

    def perform_next_step(self):
        if self.current_step == 1:
            # 第一步:蓝色方块右移一位
            self.num = [1, 0, 2, 3]
            self.update_blocks()
            self.current_step += 1
        elif self.current_step == 2:
            # 第二步:蓝色方块再右移一位
            self.num = [1, 2, 0, 3]
            self.update_blocks()
            self.current_step += 1
        elif self.current_step == 3:
            # 第三步:蓝色方块移到最后一位
            self.num = [1, 2, 3, 0]
            self.update_blocks()
            # 动画完成,停止定时器
            self.timer.stop()

class Label_Block(QLabel):
    def __init__(self, block_):
        super().__init__()
        self.block_ = block_
        self.setFixedSize(80, 80)
        font = QFont()
        font.setPointSize(30)
        font.setBold(True)
        self.setFont(font)
        if self.block_ == 0:
            self.setStyleSheet("background-color:blue;border-radius:10px;")
        elif self.block_ == 1:
            self.setStyleSheet("background-color:red;border-radius:10px;")
        elif self.block_ == 2:
            self.setStyleSheet("background-color:green;border-radius:10px;")
        elif self.block_ == 3:
            self.setStyleSheet("background-color:yellow;border-radius:10px;")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ui = UI_main()
    sys.exit(app.exec_())

关键修改说明

  1. 导入QTimer:从PyQt5.QtCore引入定时器类,用于异步触发动画步骤。
  2. 移除time.sleep():改用QTimer的start(2000)设置每2秒触发一次回调。
  3. 布局更新优化:update_blocks方法会先清空布局内的旧控件,避免新控件叠加在旧控件上。
  4. 步骤化动画逻辑:用current_step计数器跟踪当前动画阶段,每次定时器触发执行对应步骤,完成后停止定时器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 08:05:38