如何在pyqt中正确使用Threading?
How to use Threading correctly in pyqt?
我对这个 PyQt 很陌生,所以我需要一些帮助。我创建了一个从另一个文件导入的 MainWindow
。 collect_host_status
也是从另一个py文件导入的。基本上 GUI 可以工作,但很明显冻结,因此我需要对长 运行 进程使用线程。到目前为止,我已经将代码更改为这样,但是当我单击要检查主机的按钮时,什么也没有发生。 :( 我真的不明白如何将文本编辑从 MainWindow class 连接到 Worker class。就像现在一样,Worker class 似乎不知道什么是真正的 self.ui.textEdit
.
class Worker(QObject):
finished = Signal()
def __init__(self):
super(Worker, self).__init__()
def run(self):
hostname = self.ui.textEdit.toPlainText()
output_text = collect_host_status(hostname)
for i in output_text:
if "not found" in i:
w = i.replace(" not found", "")
self.ui.textEdit_3.append(w)
else:
self.ui.textEdit_2.append(i)
self.finished.emit()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.exitbutton.clicked.connect(self.close)
self.ui.actionExit_2.triggered.connect(self.close)
self.ui.actionOpen_2.triggered.connect(self.openfiles)
self.ui.pushButton.clicked.connect(self.ui.textEdit.clear)
self.ui.pushButton.clicked.connect(self.ui.textEdit_2.clear)
self.ui.pushButton.clicked.connect(self.ui.textEdit_3.clear)
self.ui.pushButton_2.clicked.connect(self.ui.textEdit_2.clear)
self.ui.pushButton_2.clicked.connect(self.ui.textEdit_3.clear)
self.connect(self.ui.pushButton_2, SIGNAL("clicked()",), self.buttonclicked)
def buttonclicked(self):
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.start()
Qt 与大多数 UI 框架一样,不允许从其主线程外部对对象进行任何类型的访问,这意味着外部线程无法创建小部件、读取属性(如 toPlainText()
) 不可靠,写入(例如使用 append()
)甚至可能导致崩溃。
即使假设这是可能的,正如您指出的那样,工作人员对 self.ui.textEdit
和其他对象一无所知,这很明显:self.ui
是在主 window 实例,线程没有 ui
对象。我建议您研究一下 类 和实例如何工作,以及如何访问它们的属性。
这样做的唯一安全和正确的方法是使用从线程发出并连接到将实际操作 UI.
的槽(函数)的自定义信号
在下面的代码中我做了一些调整以使其工作:
- 工作线程直接从QThread sub类;
- 只创建一个工作线程,它使用一个队列从主线程获取请求;
- 两个专门的信号用于通知请求是否有效,直接连接到QTextEdits的
append()
函数;
- 我删除了
finished
信号,这是不必要的,因为线程将被重用(而且,无论如何,QThread 已经提供了这样的信号);
- 更改了“旧样式”
self.connect
,因为它被认为已过时并且不会在较新版本的 Qt 中得到支持;
class Worker(QThread):
found = Signal(str)
notFound = Signal(str)
def __init__(self):
QThread.__init__(self)
self.queue = Queue()
def run(self):
while True:
hostname = self.queue.get()
output_text = collect_host_status(hostname)
for i in output_text:
if "not found" in i:
self.notFound.emit(i.replace(" not found", ""))
else:
self.found.emit(i)
def lookUp(self, hostname):
self.queue.put(hostname)
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.ui.pushButton_2.clicked.connect(self.buttonclicked)
self.thread = Worker()
self.thread.found.connect(self.ui.textEdit_2.append)
self.thread.notFound.connect(self.ui.textEdit_3.append)
self.thread.start()
def buttonclicked(self):
if self.ui.textEdit.toPlainText():
self.thread.lookUp(self.ui.textEdit.toPlainText())
我对这个 PyQt 很陌生,所以我需要一些帮助。我创建了一个从另一个文件导入的 MainWindow
。 collect_host_status
也是从另一个py文件导入的。基本上 GUI 可以工作,但很明显冻结,因此我需要对长 运行 进程使用线程。到目前为止,我已经将代码更改为这样,但是当我单击要检查主机的按钮时,什么也没有发生。 :( 我真的不明白如何将文本编辑从 MainWindow class 连接到 Worker class。就像现在一样,Worker class 似乎不知道什么是真正的 self.ui.textEdit
.
class Worker(QObject):
finished = Signal()
def __init__(self):
super(Worker, self).__init__()
def run(self):
hostname = self.ui.textEdit.toPlainText()
output_text = collect_host_status(hostname)
for i in output_text:
if "not found" in i:
w = i.replace(" not found", "")
self.ui.textEdit_3.append(w)
else:
self.ui.textEdit_2.append(i)
self.finished.emit()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.exitbutton.clicked.connect(self.close)
self.ui.actionExit_2.triggered.connect(self.close)
self.ui.actionOpen_2.triggered.connect(self.openfiles)
self.ui.pushButton.clicked.connect(self.ui.textEdit.clear)
self.ui.pushButton.clicked.connect(self.ui.textEdit_2.clear)
self.ui.pushButton.clicked.connect(self.ui.textEdit_3.clear)
self.ui.pushButton_2.clicked.connect(self.ui.textEdit_2.clear)
self.ui.pushButton_2.clicked.connect(self.ui.textEdit_3.clear)
self.connect(self.ui.pushButton_2, SIGNAL("clicked()",), self.buttonclicked)
def buttonclicked(self):
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.start()
Qt 与大多数 UI 框架一样,不允许从其主线程外部对对象进行任何类型的访问,这意味着外部线程无法创建小部件、读取属性(如 toPlainText()
) 不可靠,写入(例如使用 append()
)甚至可能导致崩溃。
即使假设这是可能的,正如您指出的那样,工作人员对 self.ui.textEdit
和其他对象一无所知,这很明显:self.ui
是在主 window 实例,线程没有 ui
对象。我建议您研究一下 类 和实例如何工作,以及如何访问它们的属性。
这样做的唯一安全和正确的方法是使用从线程发出并连接到将实际操作 UI.
的槽(函数)的自定义信号在下面的代码中我做了一些调整以使其工作:
- 工作线程直接从QThread sub类;
- 只创建一个工作线程,它使用一个队列从主线程获取请求;
- 两个专门的信号用于通知请求是否有效,直接连接到QTextEdits的
append()
函数; - 我删除了
finished
信号,这是不必要的,因为线程将被重用(而且,无论如何,QThread 已经提供了这样的信号); - 更改了“旧样式”
self.connect
,因为它被认为已过时并且不会在较新版本的 Qt 中得到支持;
class Worker(QThread):
found = Signal(str)
notFound = Signal(str)
def __init__(self):
QThread.__init__(self)
self.queue = Queue()
def run(self):
while True:
hostname = self.queue.get()
output_text = collect_host_status(hostname)
for i in output_text:
if "not found" in i:
self.notFound.emit(i.replace(" not found", ""))
else:
self.found.emit(i)
def lookUp(self, hostname):
self.queue.put(hostname)
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.ui.pushButton_2.clicked.connect(self.buttonclicked)
self.thread = Worker()
self.thread.found.connect(self.ui.textEdit_2.append)
self.thread.notFound.connect(self.ui.textEdit_3.append)
self.thread.start()
def buttonclicked(self):
if self.ui.textEdit.toPlainText():
self.thread.lookUp(self.ui.textEdit.toPlainText())