python 在一个使用QtCore.QTimer()更新一些标签的PyQt5应用程序中,QTimer一开始工作正常,但在我停止它一次后就无法正常重新启动

5f0d552i  于 2023-01-01  发布在  Python
关注(0)|答案(1)|浏览(333)

这可以很好地工作,并通过调用update_label_colors函数来更新标签:

timer = QtCore.QTimer()
# connect the timer to the update function
timer.timeout.connect(update_label_colors)
# start the timer with an interval of 1000 milliseconds (1 second)
timer.start(1000)

但在这之后叫做:

def stop_build_timer():
    ...
    timer.stop()
    ...

然后这个

def start_build_timer():
    ...
    timer.timeout.connect(update_label_text)
    ...

计时器重新初始化,但似乎从未调用update_label_colors函数。

重现该行为的最小示例:启动程序,然后按键盘上的o停止计时器。然后按l重新启动,您将看到标签不再更新。

import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication
from pynput import keyboard

def on_press(key):
    if key.char == 'o':
        stop_build_timer()
    if key.char == 'l':
        start_build_timer()

def stop_build_timer():
    global timer, elapsed_time
    if timer:
        timer.stop()
        timer.deleteLater()
    elapsed_time = 0
    update_label_text()

def start_build_timer():
    global timer, elapsed_time
    elapsed_time = 0
    # create a new timer object
    timer = QtCore.QTimer()
    # connect the timer to the update function
    timer.timeout.connect(update_label_text)
    # start the timer with an interval of 1000 milliseconds (1 second)
    timer.start(1000)

def update_label_text():
    global elapsed_time
    elapsed_time += 1
    label.setText(f'Elapsed time: {elapsed_time} seconds')

elapsed_time = 0
app = QApplication(sys.argv)
window = QtWidgets.QWidget()
window.setGeometry(100, 100, 300, 200)
layout = QtWidgets.QVBoxLayout(window)
label = QtWidgets.QLabel('Elapsed time: 0 seconds')
layout.addWidget(label)
timer = QtCore.QTimer()
timer.timeout.connect(update_label_text)
timer.start(1000)
listener = keyboard.Listener(on_press=on_press)
listener.start()
window.show()
sys.exit(app.exec_())

Carl HR在评论中提到,当按o或l键时,他们会在终端中收到错误消息:

QObject::killTimer: Timers cannot be stopped from another thread
    QObject::startTimer: Timers can only be used with threads started with QThread

由于某种原因,我在pycharm中运行测试程序时没有得到这些,但这表明计时器没有从正确的主线程中调用。我知道PyQT5中有内置的方法来添加计时器的快捷方式。遗憾的是,我需要使用另一个库,如pyinput,因为我还需要捕获窗口不在焦点上时发生的击键。

luaexgnf

luaexgnf1#

注意:我不知道这个答案是否适用,因为即使应用程序工作时没有任何崩溃(至少在我的PC上),我以前从未测试过这种方法。
为了从PyQt5线程到主线程进行通信,必须使用PyqtSignal。根据文档,我们可以使用QueuedConnection连接以安全的方式在线程之间传递数据。但是从我在互联网上看到的关于 * 在qt5上多线程化 * 主题的所有示例来看,只有使用QThreads的示例。这让我产生了疑问:这真的适用于所有类型的线程,还是只适用于QThreads
在文档中,它没有指定需要使用Qt API创建这些线程的任何地方。因此,理论上,这种方法应该适用于所有类型的线程(甚至来自其他模块)。如果这是真的,那就太有趣了。
所以,如果我们把它付诸实践,这就是你的最小可再现示例的修改版本:

import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication
from pynput import keyboard
from PyQt5.QtCore import QObject, pyqtSignal, Qt

# Added this custom class to store the signals. In order to
# initialize signals, they must be part of a QObject.
class SignalHandler(QObject):
    signal_stop = pyqtSignal()
    signal_start = pyqtSignal()

def on_press(key):
    if key.char == 'o':
        sh.signal_stop.emit() # Emit the signal instead of calling the
        #                       function directly
    if key.char == 'l':
        sh.signal_start.emit() # Same here

def stop_build_timer():
    global timer, elapsed_time

    print('Stop')
    if timer:
        timer.stop()
        timer.deleteLater()
    elapsed_time = 0
    update_label_text()

def start_build_timer():
    global timer, elapsed_time
    elapsed_time = 0

    print('Start')
    # create a new timer object
    timer = QtCore.QTimer()
    # connect the timer to the update function
    timer.timeout.connect(update_label_text)
    # start the timer with an interval of 1000 milliseconds (1 second)
    timer.start(1000)

def update_label_text():
    global elapsed_time
    elapsed_time += 1
    label.setText(f'Elapsed time: {elapsed_time} seconds')

elapsed_time = 0
app = QApplication(sys.argv)
window = QtWidgets.QWidget()
window.setGeometry(100, 100, 300, 200)
layout = QtWidgets.QVBoxLayout(window)
label = QtWidgets.QLabel('Elapsed time: 0 seconds')
layout.addWidget(label)
timer = QtCore.QTimer()
timer.timeout.connect(update_label_text)
timer.start(1000)

# Create the signals, and connect them to the respective functions
# using the QueuedConnection mode.
sh = SignalHandler()
sh.signal_stop.connect(stop_build_timer, Qt.QueuedConnection)
sh.signal_start.connect(start_build_timer, Qt.QueuedConnection)

listener = keyboard.Listener(on_press=on_press)
listener.start()
window.show()
sys.exit(app.exec_())

当执行它时,计时器应该停止和启动没有问题。如果发生崩溃,请在评论中发布发生了什么:)

相关问题