使用 QThreading 和 QProcess 冻结 GUI [英] GUI Freezing with QThreading and QProcess

查看:177
本文介绍了使用 QThreading 和 QProcess 冻结 GUI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一些软件来处理从一些晶体学实验中收集的大量图像.数据处理包括以下步骤:

I'm attempting to write some software that will process large amounts of images collected from some crystallography experiments. The data process involves the following steps:

  1. 用户输入以确定要一起批处理的图像数量.
  2. 选择包含图像的目录,并计算图像总数.
  3. 嵌套的 for 循环用于将图像批处理在一起,并为使用批处理文件处理的每个批处理构建命令和参数.

以下代码可用于模拟使用 QThread 和 QProcess 描述的过程:

The following code can be used to simulate the process described using QThread and QProcess:

# This Python file uses the following encoding: utf-8
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import test
import time

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui=test.Ui_test()
        self.ui.setupUi(self)
        self.ui.pushButton_startThread.clicked.connect(self.startTestThread)

    def startTestThread(self):
        self.xValue = self.ui.lineEdit_x.text() #Represents number of batches
        self.yValue = self.ui.lineEdit_y.text() #Represents number of images per batch
        runTest = testThread(self.xValue, self.yValue) #Creates an instance of testThread
        runTest.start() #Starts the instance of testThread

class testThread(QThread):
    def __init__(self, xValue, yValue):
        super().__init__()
        self.xValue = xValue
        self.yValue = yValue

    def __del__(self):
        self.wait()

    def run(self):
        for x in range(int(self.xValue)): #For loop to iterate througeach batch
            print(str(x) + "\n")
            for y in range(int(self.yValue)): #For loop to iterate through each image in each batch
                print(str(y) + "\n")
            print(y)
            process = QProcess(self) #Creates an instance of Qprocess
            process.startDetached("test.bat") #Runs test.bat

    def stop(self):
        self.terminate()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle("Fusion")
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

test.bat 内容:

test.bat contents:

@ECHO OFF
ECHO this is a test

GUI 包含 xValue 和 yValue 的两个用户输入以及一个用于启动线程的按钮.例如,一项实验产生 150,000 张图像,需要以 500 张为一组进行处理.这将需要每批次处理 300 张图像.您可以为 xValue 输入 500,为 yValue 输入 300.有两个问题:

The GUI contains two user inputs for xValue and yValue and a button to start a thread. For example, one experiment yields 150,000 images that need to be processed in batches of 500. This will require 300 images per batch to be processed. You can enter 500 for xValue and 300 for yValue. There are two problems:

  1. GUI 冻结,因此如果需要,我无法停止该过程.我认为运行线程应该可以防止这种情况发生.
  2. 我收到以下错误:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is testThread(0x1a413f3c690), parent's thread is QThread(0x1a4116cb7a0), current thread is testThread(0x1a413f3c690)

我相信这个错误是通过嵌套的 for 循环生成多个 QProcesses 的结果,但我不完全确定.

I believe this error is a result of the multiple QProcesses being generated through the nested for loop, but I'm not entirely sure.

有没有办法阻止 GUI 冻结并避免生成的错误?

Is there anyway to stop the GUI from freezing and avoid the generated error?

推荐答案

说明

要了解问题的原因,必须明确以下概念:

Explanation

To understand the cause of the problem then the following concepts must be clear:

  1. QThread 不是 Qt 线程,也就是说它不是 Qt 创建的线程,而是每个 OS 本地线程的 QObject 处理程序.

  1. A QThread is not a Qt thread, that is to say it is not a thread created by Qt but it is a QObject handler of the native threads of each OS.

只有在 QThread 的 run() 方法中的内容在另一个线程中执行.

Only what is inside QThread's run() method is executed in another thread.

如果 QThread 被销毁,那么 run() 方法将不会在辅助线程上执行,而是在 QThread 所属的线程上执行.

If a QThread is destroyed then the run() method will not be executed on a secondary thread but on the thread to which the QThread belongs.

QObject 与父对象属于同一个线程,如果它没有父对象,则它属于创建它的线程.

A QObject belongs to the same thread as the parent, and if it has no parent then it belongs to the thread where it was created.

考虑到上述情况,可以解释这两个错误:

Considering the above, both errors can be explained:

  • runTest"是一个局部作用域的对象,在startTestThread方法执行完后会立即销毁,所以根据(3)run方法会在QThread所属的线程中执行,根据 (4) 这将是 GUI.

  • "runTest" is an object with local scope that will be destroyed an instant after the startTestThread method is finished executing, so according to (3) the run method will be executed in the thread to which the QThread belongs, and according to (4) this will be the the GUI.

考虑到 (4) 显然 QProcess 属于主线程(因为它的父线程是 QThread 并且它属于主线程),但是您是在可能导致问题的辅助线程 (2) 中创建它Qt 会警告您这一点.

Considering (4) clearly the QProcess belongs to the main thread (since its parent is QThread and it belongs to the main thread), but you are creating it in a secondary thread (2) that can cause problems so Qt warns you of this.

对于第一个问题,只需延长其生命周期,例如通过将其传递给父级(或使其成为类的属性).对于第二个问题,不需要创建 QProcess 的实例,因为您可以使用静态方法(QProcess::startDetached()).考虑到这一点,解决方案是:

For the first problem, simply extend its life cycle, for example by passing it a parent (or make it an attribute of the class). For the second problem it is not necessary to create an instance of QProcess since you can use the static method(QProcess::startDetached()). Considering this, the solution is:

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui=test.Ui_test()
        self.ui.setupUi(self)
        self.ui.pushButton_startThread.clicked.connect(self.startTestThread)

    def startTestThread(self):
        self.xValue = self.ui.lineEdit_x.text() #Represents number of batches
        self.yValue = self.ui.lineEdit_y.text() #Represents number of images per batch
        runTest = testThread(
            self.xValue, self.yValue, self
        )  # Creates an instance of testThread
        runTest.start()  # Starts the instance of testThread


class testThread(QThread):
    def __init__(self, xValue, yValue, parent=None):
        super().__init__(parent)
        self.xValue = xValue
        self.yValue = yValue

    def __del__(self):
        self.wait()

    def run(self):
        for x in range(int(self.xValue)):  # For loop to iterate througeach batch
            print(str(x) + "\n")
            for y in range(
                int(self.yValue)
            ):  # For loop to iterate through each image in each batch
                print(str(y) + "\n")
            print(y)
            QProcess.startDetached("test.bat")  # Runs test.bat

    def stop(self):
        self.terminate()

这篇关于使用 QThreading 和 QProcess 冻结 GUI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆