首页 > 编程问答 >具有 Python lambda 函数的 QTimer 使用先前的数据运行

具有 Python lambda 函数的 QTimer 使用先前的数据运行

时间:2024-07-31 06:00:52浏览次数:13  
标签:python qt pyside pyside2

我有一个 GUI 项目,它使用 PySide2 和 Python 3.8,它在 QThread 中执行一些后台任务。在该 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

我期望的是,一旦计时器设置为新值 1,目标函数也会打印 1。

我也尝试过使用 lambda 的默认值,如 What do lambda 中所述函数闭包捕获? 具有相同的结果:

self.timer.timeout.connect(lambda x=1: self.target_func(x))

问题在于你的 lambda 函数的方式正在捕获 data 变量。在 Python 中,lambda 函数会捕获它们定义时在其周围范围内的变量的值。它们不会像你期望的那样在每次调用时都获取变量的当前值。

因此,你的 lambda 函数 lambda: self.target_func(data) 总是使用 data 的初始值,即 0。

要解决此问题,你需要在 lambda 函数本身内部创建一个新的变量范围,该范围可以存储 data 的当前值。可以通过将 data 作为默认参数传递给 lambda 函数来实现:

self.timer.timeout.connect(lambda data=data: self.target_func(data))

在此代码中, data=data 为 lambda 函数创建了一个名为 data 的新参数,该参数的默认值设置为 data 的当前值。这意味着每次调用 lambda 函数时,它都会使用作为默认参数传递的 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 data=0: self.target_func(data))
        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")
            self.timer.setInterval(1000)
            self.timer.timeout.connect(lambda data=1: self.target_func(data))
            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()

此代码现在应该按预期工作,并在每次调用计时器时使用 data 的正确值。

标签:python,qt,pyside,pyside2
From: 78813672

相关文章

  • 我如何在 python 上使用 spire.pdf 修复此错误
    我使用spirepdf,但出现以下错误:“DllNotFound_Windows,libSkiaSharp,Nosepuedeencontrarelmóduloespecificado.:....”defextract_text_from_pdf(file_path,output_file):#LoadaPDFdocumentdoc=PdfDocument()doc.LoadFromFile(file_path)ex......
  • C++ 函数返回极其缓慢,远慢于功能等效的 python 代码
    我有一个在我编写的脚本中使用的函数,用于从列表中删除多余的阻塞关键字。基本上,输入(以任何顺序):{"apple","bapple","banana","cherry","bananaman","sweetherrypie","sweet","b"}它应该输出一个缩小的字符串数组(以任何顺序):......
  • 在预定时间从 python telegram bot 发起对话
    对于没有提供代码,我提前表示歉意。我明天会尝试添加它,但我现在还没有接近它,思考如何解决这个问题让我一直在思考。我已经为一个机器人创建了一个程序,该程序的数据帧充满了之前请求的用户添加到机器人的消息列表中。现在,机器人使用job_queue在一天中的随机时间向每个用户发......
  • pyinstaller + pyside6问题:dlopen Shiboken.abi3.so:找不到符号
    我有一个基于PySide6的应用程序,使用pyinstaller创建的macOS二进制文件。运行构建的应用程序时遇到一个奇怪的问题-有时构建很好,应用程序启动时没有任何问题。但是然后我更改了一些不重要的代码行,重建了应用程序-并且构建已损坏。当我执行二进制文件时,我收......
  • PyQt:最大化窗口时如何防止处理多个调整大小事件?
    我有一个QMainWindow包含一个子QWidget包含自身aQLabel当窗口最大化时(例如,通过单击窗口上的最大化图标),QLabel.resizeEvent()处理程序被调用多次(据说跟随窗口的逐渐放大,直到占据整个桌面空间)。事件处理程序中的代码调用setPixmap(......
  • python函数:带星号的参数一个接一个
    我想知道为什么定义以下函数被认为是好的。deffoo(*x,y):pass据我所知,现在有调用此函数的方法,因为它总是缺少y的值。(如果我错了,请纠正我。)这有什么我不知道的用处吗?你说得对,在Python中定义一个像deffoo(*x,y):...这样的函数,在试图以常规方......
  • 基于遗传(GA)、粒子群(PSO)、模拟退火(SA)、禁忌搜索(ST)、蚁群算法(ACO)、自自组织神
        ......
  • 10个append()函数在Python程序开发中的创新应用
    文末赠免费精品编程资料~~在Python编程的世界里,append()函数是列表操作中最常见的方法之一。它允许我们在列表的末尾添加一个元素,这一简单的功能却能激发无限的创造力。今天,我们将探讨append()函数在Python程序开发中的10种创新应用,从基本用法到高级技巧,逐步深入。1.构......
  • 全网最适合入门的面向对象编程教程:28 类和对象的Python实现-Python编程原则、哲学和规
    全网最适合入门的面向对象编程教程:28类和对象的Python实现-Python编程原则、哲学和规范大汇总摘要:本文主要介绍了在使用Python进行面向对象编程时,Python异常处理的原则-“请求谅解,而非许可”,以及软件设计和Python的编程原则,同时介绍了PEP8规范。原文链接:FreakStud......
  • python生成器
    一前言环境:python3.10win10二生成器1关于生成器先看一个例子    定义了一个函数,当我们运行该函数时,并未像普通函数那样执行函数体内的代码    从其中的英文可知,执行函数得到了一个生成器对象,这个生成器对象也叫做generatoriterator(生成器迭代器),generatorit......