问题
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:
- User Input to determine the number of images to batch together.
- A directory containing the images is selected, and the total number of images is calculated.
- A nested for loop is used to batch images together, and construct a command and arguments for each batch which is processed using a batch file.
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 contents:
@ECHO OFF
ECHO this is a test
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:
- The GUI freezes, so I can't stop the process if I need to. I thought running a thread was supposed to prevent this.
- I receive the following error:
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)
I believe this error is a result of the multiple QProcesses being generated through the nested for loop, but I'm not entirely sure.
Is there anyway to stop the GUI from freezing and avoid the generated error?
回答1:
Explanation
To understand the cause of the problem then the following concepts must be clear:
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.
Only what is inside QThread's run() method is executed in another thread.
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.
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" 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.
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.
Solution
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()
来源:https://stackoverflow.com/questions/61927243/gui-freezing-with-qthreading-and-qprocess