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

PySide2中QTimer绑定lambda函数复用旧数据问题排查

QTimer绑定Lambda传递数据时旧数据残留问题

我使用Python 3.8搭配PySide2开发GUI项目,在QThread中执行后台任务,其中包含一个QTimer成员对象,需要周期性调用函数并每次传入不同数据。因需在特定场景下停止/重置定时器,未使用QTimer.singleShot静态方法。

目标函数执行完成后,会调用另一函数向定时器传入新数据并重新启动。我通过timer.timeout.connect(lambda: function_name(data))绑定函数,且在传入新数据前会停止定时器并断开旧槽函数,但第二次运行时目标函数仍使用旧数据。

复现代码

from PySide2.QtCore import QTimer
from PySide2 import QtWidgets
import time
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
FORMAT = "[%(asctime)s] %(funcName)s: %(message)s"
logging.basicConfig(format=FORMAT)

class QtTest(QtWidgets.QWidget):
    
    def __init__(self):
        super(QtTest, self).__init__()
        logger.info("Class initialized")
        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.setInterval(1000)
        self.timer.timeout.connect(lambda: self.target_func(0))
        self.timer.start()
        
        
    def target_func(self, data):
        logger.info("Target function called with data: {}".format(data))
        time.sleep(1.5)
        if self.timer.isActive():
            logger.info("Timer reset")
            self.timer.stop()
            self.timer.timeout.disconnect()
            
        self.func_done(data)
        
    def func_done(self, data):
        logger.info("Tasks completed for data: {}".format(data))
        
        if data == 0:
            logger.info("Setting next timer with data 1")
            #I have tried the following as well
            #self.timer.timeout.disconnect()
            #self.timer.stop()

            self.timer.setInterval(1000)
            self.timer.timeout.connect(lambda: self.target_func(1))
            self.timer.start()


def init_app():
    app = QtWidgets.QApplication([])

    gui = QtTest()
    gui.show()
    app.exec_()
    
if __name__ == "__main__":
    print("QT app running")
    init_app()
    test = QtTest()

测试输出

QT app running
[2024-07-30 22:44:53,204] __init__: Class initialized
[2024-07-30 22:44:54,255] target_func: Target function called with data: 0
[2024-07-30 22:44:55,757] func_done: Tasks completed for data: 0
[2024-07-30 22:44:55,757] func_done: Setting next timer with data 1
[2024-07-30 22:44:56,808] target_func: Target function called with data: 0
[2024-07-30 22:44:58,309] func_done: Tasks completed for data: 0
[2024-07-30 22:44:58,309] func_done: Setting next timer with data 1
[2024-07-30 22:44:58,309] target_func: Target function called with data: 1
[2024-07-30 22:44:59,810] target_func: Timer reset
[2024-07-30 22:44:59,811] func_done: Tasks completed for data: 1

问题根源

因为设置了setSingleShot(True),定时器触发timeout信号后会自动变为非活跃状态,导致target_func中的if self.timer.isActive()条件不成立,没有断开旧的槽函数。后续绑定新的lambda时,旧的槽函数依然存在,定时器触发时会同时执行新旧两个lambda,因此先输出旧数据0,再输出新数据1。

修复方案

每次绑定新的槽函数前,强制断开timeout信号的所有连接,不需要依赖isActive()判断:

修改后的代码

from PySide2.QtCore import QTimer
from PySide2 import QtWidgets
import time
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
FORMAT = "[%(asctime)s] %(funcName)s: %(message)s"
logging.basicConfig(format=FORMAT)

class QtTest(QtWidgets.QWidget):
    
    def __init__(self):
        super(QtTest, self).__init__()
        logger.info("Class initialized")
        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.setInterval(1000)
        self.timer.timeout.connect(lambda: self.target_func(0))
        self.timer.start()
        
        
    def target_func(self, data):
        logger.info("Target function called with data: {}".format(data))
        time.sleep(1.5)
        # 移除isActive判断,直接断开连接(即使定时器已非活跃)
        self.timer.stop()
        self.timer.timeout.disconnect()
            
        self.func_done(data)
        
    def func_done(self, data):
        logger.info("Tasks completed for data: {}".format(data))
        
        if data == 0:
            logger.info("Setting next timer with data 1")
            self.timer.setInterval(1000)
            self.timer.timeout.connect(lambda: self.target_func(1))
            self.timer.start()


def init_app():
    app = QtWidgets.QApplication([])

    gui = QtTest()
    gui.show()
    app.exec_()
    
if __name__ == "__main__":
    print("QT app running")
    init_app()
    test = QtTest()

预期输出

QT app running
[2024-XX-XX XX:XX:XX,XXX] __init__: Class initialized
[2024-XX-XX XX:XX:XX,XXX] target_func: Target function called with data: 0
[2024-XX-XX XX:XX:XX,XXX] func_done: Tasks completed for data: 0
[2024-XX-XX XX:XX:XX,XXX] func_done: Setting next timer with data 1
[2024-XX-XX XX:XX:XX,XXX] target_func: Target function called with data: 1
[2024-XX-XX XX:XX:XX,XXX] func_done: Tasks completed for data: 1

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:42:02