我正在向现有应用程序添加 PyQT UI。我必须通过从主线程上的应用程序收到的回调来初始化
QAppliaction
。我无法从回调同步执行此操作,因为我最终必须调用阻塞的 app.exec()
,从而阻止现有应用程序继续运行。显然,生成常规线程不起作用,而且我似乎找不到一种方法来使用 QThread
。
这个问题似乎正是我在C中想要的:
thread = new QThread();
connect(thread, SIGNAL(started()), this, SLOT(onStarted()), Qt::DirectConnection);
thread->start();
其中
onStarted
最终调用 exec()
。显然我不够聪明,无法在 Python 中实现完全相同的事情。它是如何工作的?
⚠️ 请注意:尽管这个答案主要针对
,但它应该与pyside
几乎完全相同。(当然有一些细微的变化)PyQt
两种方式,一种安全,一种稍微不安全:
QApplication
在“主”循环之外创建multiprocessing
的实例(作为不同的过程),再加上multiprocessing.Manager
来处理它们之间的共享数据。threding.thread
并可选择抑制潜在的警告问题。两者各有优缺点,主要是:“易用性”和安全性[...]这里我将简要演示这两种方式,最后我将附上一个实际案例我必须使用其中之一。
multiprocessing
multiprocessing
非常简单,尽管在共享引用数据时可能特别棘手。在这种情况下,您需要使用 multiprocessing.Manager
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
import multiprocessing
from time import sleep
import sys
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Multiprocessing with PySide - example")
self.setGeometry(100, 100, 300, 200)
layout = QVBoxLayout()
self.button = QPushButton("Click me!")
self.button.clicked.connect(self.on_button_click)
layout.addWidget(self.button)
central_widget = QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
def on_button_click(self):
print(f"Button clicked!")
def start_qt():
global window
app = QApplication(sys.argv)
window = MyWindow()
window.show()
app.exec()
if __name__ == "__main__":
proc = multiprocessing.Process(target=start_qt, args=())
proc.start()
print("Main thread is free for use")
sleep(2)
print("Global variables are not shared...")
sleep(2)
print("Use `multiprocessing.Manager`")
sleep(2)
print("It's a new procces!")
sleep(10)
proc.terminate()
threding.thread
threding.thread
根据您的需求,这可能是一个不错的选择(我想在某些时候它绝对适合我......)。但是诸如警告之类的东西以及诸如 Python 线程。线程、作用域和垃圾收集 之类的东西可能对您来说不太理想
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
from time import sleep
import threading
import sys
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Threading.thread with PySide - example")
self.setGeometry(100, 100, 300, 200)
layout = QVBoxLayout()
self.button = QPushButton("Click me!")
self.button.clicked.connect(self.on_button_click)
layout.addWidget(self.button)
central_widget = QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
def on_button_click(self):
print(f"Button clicked!")
def start_qt():
global window
app = QApplication(sys.argv)
window = MyWindow()
window.show()
app.exec()
if __name__ == "__main__":
t = threading.Thread(target=start_qt)
t.daemon = True
t.start()
print("Main thread is free for use")
sleep(2)
print("Global variables are shared!")
sleep(2)
print("But stuff are sensitive and unsafe!")
sleep(2)
print("It's a new thread!")
sleep(10)
您应该期待:
QObject::killTimer: Timers cannot be stopped from another thread
[1] 10216 segmentation fault (core dumped) python my_script.py
这是我无法找到终止守护线程进程的正确方法的结果。要抑制此类消息,您可以使用类似的方法:
def qtMessageHandler(type: QtCore.QtMsgType, context: QtCore.QMessageLogContext, msg: str):
pass # Handle specific messages
...
QtCore.qInstallMessageHandler(qtMessageHandler) # NOT recommended though
此外,这可能很方便:
class writerr(object):
def write(self, data): # Ignore warning
if not data.endswith("g_main_context_pop_thread_default: assertion 'stack != NULL' failed\n"):
print(data)
sys.stderr = writerr()
这是我找到的所有来源\链接,与以下内容相关:
请确保在实际应用此处演示的内容之前进行研究。这是我学习如何处理这个问题的过程的结果。正如所承诺的,这里+这里我必须使用这样的案例。