I recently asked a question about how to prevent QThread from freezing my GUI when running multiple instances of QProcess are generated through using a nested loop. The solution provided worked like a charm! I was able to finish my program, and it runs smoothly! I recently added a feature to allow the user to stop the QThread at any point during the process. The stop feature works in that no new instances of QProcesse are called; however, there are a number of unfinished instances of QProcess still processing. Is there a way to determine when there are no longer any instances of QProcess running and notify the user? I am familiar with the QProcess method waitForFinished(), but I'm not sure how to apply that to multiple instances of QProcess generated using a nested loop.
Here is a sample of code that simulates the data processing aspect of my actual program:
# This Python file uses the following encoding: utf-8
import sys
import os
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)
self.ui.pushButton_Update.clicked.connect(self.stopThread)
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
self.runTest = testThread(self.xValue, self.yValue, self) # Creates an instance of testThread
self.runTest.start() # Starts the instance of testThread
def stopThread(self):
self.runTest.stop()
self.ui.lineEdit_x.setText("")
self.ui.lineEdit_y.setText("")
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):
processd = []
for x in range(int(self.xValue)): # For loop to iterate througeach batch
pass
for y in range(
int(self.yValue)): # For loop to iterate through each image in each batch
time.sleep(.1)
QProcess.startDetached(os.path.dirname(os.path.realpath(__file__)) + r"\test.bat") # Runs test.bat
def stop(self):
self.terminate()
self.wait()
print("\nStopping thread")
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyle("Fusion")
window = MainWindow()
window.show()
sys.exit(app.exec_())
test.bat
@ECHO OFF
ECHO Processing Batched Images.
TIMEOUT /T 15
ECHO Process Finished.
As was already noted, stopping the QThread works; however, I want to be able to determine when all QProcess instances are done. This will allow the program to notify the user that they can restart the process using new parameters. Currently, I'm having to tell my labmates to just wait until no more output files are being written before trying a new set of parameters.
Any help will be much appreciated. Thank you for your time!
While not elegant, I figured out a way to determine when multiple instances of QProcess are finished after terminating a QThread:
Here is a sample of the code:
# This Python file uses the following encoding: utf-8
import sys
import os
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import test
import time
from glob import glob1
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)
self.ui.pushButton_Update.clicked.connect(self.stopThread)
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
self.runTest = testThread(self.xValue, self.yValue, self) # Creates an instance of testThread
self.runTest.signals.batchesSubmitted.connect(self.testThreadFinished)
self.runTest.start() # Starts the instance of testThread
def stopThread(self):
self.runTest.stop()
@pyqtSlot(int)
def testThreadFinished(self, batchesSubmitted):
while len(glob1(outputFilePath + fileName, "*.txt")) < batchesSubmitted: #Compare the number of *.txt files in the output directory to the number of isntances of QProcess called
time.sleep(1)
self.ui.lineEdit_x.setText("")
self.ui.lineEdit_y.setText("")
print("All instance of QProcess are finished!")
class testThreadSignals(QObject):
batchesSubmitted = pyqtSignal(int)
class testThread(QThread):
def __init__(self, xValue, yValue, parent=None):
super().__init__(parent)
self.xValue = xValue
self.yValue = yValue
self.signals = testThreadSignals()
def run(self):
self.batchCounter = 0
for x in range(int(self.xValue)): # For loop to iterate througeach batch
pass
for y in range(
int(self.yValue)): # For loop to iterate through each image in each batch
time.sleep(.1)
QProcess.startDetached(os.path.dirname(os.path.realpath(__file__)) + r"\test.bat") # Runs test.bat
self.batchCounter += 1 #Counts the successful number *.txt files written
@pyqtSlot(int)
def stop(self):
self.terminate()
self.threadactive = False
self.wait()
self.signals.batchesSubmitted.emit(self.batchCounter)
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyle("Fusion")
window = MainWindow()
window.show()
sys.exit(app.exec_())
test.bat:
@ECHO OFF
ECHO Processing Batched Images.
TIMEOUT /T 15
REM write a unique text file
ECHO Process Finished.
I was able to adapt the above python code to my program with no problems.