我是 pyqt 新手,我在应用程序中使用 pyqtsignal,但不知道为什么它不能正常工作。我不明白为什么会发生这种情况,程序中的其余 pyqtsignal 工作正常,我想知道如果我遗漏了什么?
当我单击停止按钮时,程序不会停止。 如果我直接调用runner.stop(),程序可以正常退出。
所以我很困惑。下面是我的代码:
class Runner(QObject):
stopped = pyqtSignal()
def __init__(self, name):
super().__init__()
self.name = name
self.run_flag = True
def start(self):
print("start", self.run_flag)
while self.run_flag:
self.timeoutSlot()
time.sleep(2)
def stop(self):
logging.warning("stop called.")
self.run_flag = False
self.stopped.emit()
def timeoutSlot(self):
# long time work
time.sleep(10)
logging.info(f"{self.name} is running")
class MainWindow(QMainWindow):
stopSignal = pyqtSignal()
def __init__(self):
super().__init__()
self._central_widget = QWidget()
self._central_layout = QVBoxLayout()
self.start_btn = QPushButton("start")
self.stop_btn = QPushButton("stop")
self._central_layout.addWidget(self.start_btn)
self._central_layout.addWidget(self.stop_btn)
self._central_widget.setLayout(self._central_layout)
self.setCentralWidget(self._central_widget)
self.start_btn.clicked.connect(self.start)
self.stop_btn.clicked.connect(self.stop)
self.connects = []
self.runners = []
def start(self):
for i in range(2):
setattr(self, f"thread_{i}", QThread())
setattr(self, f"runner_{i}", Runner(f"runner_{i}"))
thread = getattr(self, f"thread_{i}")
runner = getattr(self, f"runner_{i}")
assert isinstance(thread, QThread)
assert isinstance(runner, Runner)
runner.moveToThread(thread)
thread.started.connect(runner.start)
conn = self.stopSignal.connect(runner.stop)
self.connects.append(conn)
runner.stopped.connect(thread.quit)
runner.stopped.connect(runner.deleteLater)
self.runners.append(runner)
thread.finished.connect(thread.deleteLater)
thread.start()
def stop(self):
# blow can work normally
# for runner in self.runners:
# runner.stop()
# self.runners = []
print("stop btn clicked!")
# can't work
self.stopSignal.emit()
# for conn in self.connects:
# self.stopSignal.disconnect(conn)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec())
信号确实有效,问题是由于它连接到一个线程,所以它的连接是一个
QueuedConnection
。这意味着只有在接收器线程允许的情况下才会调用连接的插槽。
您已将线程的
started
信号连接到 start
函数,该函数立即运行阻塞循环,防止 QThread 事件循环继续进行,直到该函数将控制权返回给它。由于排队信号只能由其事件循环处理,因此这种情况永远不会发生。
针对这种具体情况,有两种可能的解决方案:
stop()
函数;DirectConnection
代替 stopSignal
;在第一种情况下,只需迭代所有跑步者:
def stop(self):
for runner in self.runners:
runner.stop()
或者:
def start(self):
for i in range(2):
...
conn = self.stopSignal.connect(runner.stop, Qt.DirectConnection)
...
请注意,您对
getattr
和 setattr
的使用不必要地复杂:只需创建一个局部变量,然后调用 setattr
,然后删除 assert
,这没有任何意义,因为您刚刚创建了这些对象。
此外,假设您始终具有持久引用,当再次调用
start()
时,这些引用可能会被覆盖,从而导致线程在运行时被销毁。
更好的方法是对线程和运行器使用列表,以便您最终可以再次迭代现有对象并决定要做什么(重新启动线程、停止线程等)。