1 回答

TA貢獻2012條經驗 獲得超12個贊
正如錯誤所解釋的,您無法從另一個線程啟動和停止 QTimer,并且由于 APScheduler 在不同的線程上工作,因此問題的原因:是self.hello從 APScheduler 線程調用的,而不是從創建計時器的線程調用的。
要訪問在不同線程中創建的對象,您需要使用信號和槽,以便讓 Qt 管理不同線程之間的通信。
因此,解決方案可能是通過繼承 QObject 來子類化您的 Scheduler(以便能夠創建信號并連接到它們),然后在每次執行作業時使用自定義信號并使用該信號重新啟動計時器。
為了實現這一目標,我使用了一個createJob實際運行作業的函數,并started在作業啟動時發出信號,并completed在作業完成時發出信號。
不幸的是,我無法測試以下代碼,因為我現在無法安裝 APScheduler,但邏輯應該沒問題。
Main.py
class MyMainWindow(QtWidgets.QMainWindow):
? ? def __init__(self):
? ? ? ? # ...
? ? ? ? self.sch = Scheduler()
? ? ? ? self.sch.completed.connect(self.start_my_timer)
? ? def hello(self):
? ? ? ? print("hello world")
? ? ? ? # no call to self.start_my_timer here!
Schedule.py
from datetime import datetime
from apscheduler.schedulers.qt import QtScheduler
from PyQt5 import QtCore
class Scheduler(QtCore.QObject):
? ? started = QtCore.pyqtSignal(object)
? ? completed = QtCore.pyqtSignal(object)
? ? def __init__(self):
? ? ? ? self.id = 'test_job'
? ? ? ? self.sched = QtScheduler()
? ? def add(self, job_function, *args, **kwargs):
? ? ? ? self.sched.add_job(self.createJob(job_function), 'cron',?
? ? ? ? ? ? day_of_week='mon-fri', hour='9-18',
? ? ? ? ? ? minute='2,7,12,17,22,27,32,37,42,47,52,57',
? ? ? ? ? ? second='5', id=self.id, *args, **kwargs)
? ? def createJob(self, job_function):
? ? ? ? def func(*args, **kwargs):
? ? ? ? ? ? self.started.emit(job_function)
? ? ? ? ? ? job_function(*args, **kwargs)
? ? ? ? ? ? self.completed.emit(job_function)
? ? ? ? return func
? ? def start(self):
? ? ? ? self.sched.start()
? ? def next_occurance(self):
? ? ? ? for job in self.sched.get_jobs():
? ? ? ? ? ? if job.id == self.id:
? ? ? ? ? ? ? ? return job.next_run_time
請注意,我使用參數(在您的情況下是對作業的引用)發出started和completed信號,如果您想對多個作業做出不同的反應,這可能有助于識別作業。我還添加了對位置和關鍵字參數的基本支持。job_functionself.hello
另請注意,我僅提供一個非常基本的實現(您的函數僅打印一條消息)。如果您需要與作業功能中的 UI 元素進行交互,那么 QTimer 也會出現同樣的問題,因為不允許從主 Qt 線程之外的線程訪問 UI 元素。
在這種情況下,您需要尋找另一種方法。例如,您可以添加一個作業(實際上并非從調度程序運行)并以該作業作為參數發出信號,然后連接到將在主線程中實際運行該作業的函數。
Main.py
class MyMainWindow(QtWidgets.QMainWindow):
? ? def __init__(self):
? ? ? ? # ...
? ? ? ? self.sch = Scheduler()
? ? ? ? self.sch.startJob.connect(self.startJob)
? ? def startJob(self, job, args, kwargs):
? ? ? ? job(*args, **kwargs)
? ? ? ? self.start_my_timer()
? ? def hello(self):
? ? ? ? self.someLabel.setText("hello world")
Schedule.py
from datetime import datetime
from apscheduler.schedulers.qt import QtScheduler
from PyQt5 import QtCore
class Scheduler(QtCore.QObject):
? ? startJob = QtCore.pyqtSignal(object, object, object)
? ? # ...
? ? def add(self, job_function, *args, **kwargs):
? ? ? ? self.sched.add_job(self.createJob(job_function, args, kwargs), 'cron',?
? ? ? ? ? ? day_of_week='mon-fri', hour='9-18',
? ? ? ? ? ? minute='2,7,12,17,22,27,32,37,42,47,52,57',
? ? ? ? ? ? second='5', id=self.id)
? ? def createJob(self, job_function, args, kwargs):
? ? ? ? def func():
? ? ? ? ? ? self.starJob.emit(job_function, args, kwargs)
? ? ? ? return func
如前所述,上面的代碼未經測試,您需要檢查可能的錯誤(也許我在通配符參數上犯了一些錯誤)。
最后,一些小建議:
實際上有必要使用裝飾器的情況非常少
pyqtSlot
;有趣的是,使用它們通常是問題或意外行為的根源。通常最好保留信號參數原樣而不進行任何轉換,因此您不應將時間轉換為信號的字符串
get_seconds
;setNum()
此外,QLabel 可以使用(浮點型和整數型)接受數值。對空格要更加小心(我指的是
self.sched.add_job
):對于關鍵字參數,空格只能存在于逗號之后;雖然它實際上并不代表問題,但它極大地提高了可讀性。
添加回答
舉報