gpt4 book ai didi

Why pyqtsignal failed to trigger normally?(为什么PYQTSIGNAL无法正常触发?)

转载 作者:bug小助手 更新时间:2023-10-28 10:21:09 26 4
gpt4 key购买 nike



I am a pyqt newbie and I am using pyqtsignal in my application and don't know why it is not working properly.I don't understand why this is happening, the rest of pyqtsignal in the program works fine, I'm wondering if I'm missing something?

我是一名PYQT新手,我在我的应用程序中使用了PYQTSIGNAL,我不知道为什么它不能正常工作。我不明白为什么会发生这种情况,程序中的其他PYQTSIGNAL运行正常,我想知道我是不是遗漏了什么?


When I click the stop button, the program does not stop.
If I call runner.stop() directly, the program can exit normally.

当我点击停止按钮时,程序不会停止。如果我直接调用runner.top(),程序就可以正常退出。


so i'm very confused. Below is my code:

所以我很困惑。以下是我的代码:



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())

更多回答
优秀答案推荐

The signal does work, the problem is that since it's connected to a thread, its connection is a QueuedConnection. This means that the connected slots will only be called as soon as the receiver thread allows so.

信号确实起作用了,但问题是,因为它连接到线程,所以它的连接是QueuedConnection。这意味着,只有在接收器线程允许时,才会调用连接的时隙。


You've connected the started signal of the thread to the start function, which immediately run a blocking loop, preventing the QThread event loop to go further until that function returns control to it. Since queued signals can only be processed by their event loop, that never happens.

您已经将线程的START信号连接到了START函数,该函数会立即运行一个阻塞循环,从而防止QThread事件循环继续运行,直到该函数将控制权返回给它。因为排队的信号只能由它们的事件循环处理,所以这种情况永远不会发生。


There are two possible solutions for this specific case:

对于此特定情况,有两种可能的解决方案:



  1. directly call the stop() function of each runner;

  2. use a DirectConnection for the stopSignal;


In the first case, just iterate through all runners:

在第一种情况下,只需遍历所有Runner:


    def stop(self):
for runner in self.runners:
runner.stop()

Alternatively:

或者:


    def start(self):
for i in range(2):
...
conn = self.stopSignal.connect(runner.stop, Qt.DirectConnection)
...

Note that your usage of getattr and setattr is unnecessarily convoluted: just create a local variable and then call setattr, then get rid of the assert, which makes no sense since you just created those objects.

注意,getattr和setattr的用法不必要地复杂:只需创建一个局部变量,然后调用setattr,然后去掉Assert,这是没有意义的,因为您刚刚创建了这些对象。


Also, that assumes that you always have persistent references that may be overwritten when start() is called again, with the result of threads being destroyed while running.

此外,这还假设您总是有持久引用,当再次调用Start()时,这些引用可能会被覆盖,从而导致线程在运行时被销毁。


A better approach is to use a list for both threads and runners, so that you can eventually iterate again existing objects and decide what to do (restart the threads, stop them, etc.).

一种更好的方法是同时为线程和运行器使用列表,这样您最终就可以再次迭代现有对象并决定要做什么(重新启动线程、停止线程等)。


更多回答

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com