Reputation: 13
I am designing a program to edit DICOMs. Specifically, I am having issues appropriately interacting with my PyQt UI.
I want to be able to click on a "pause" and on a "stop" button to either pause or stop my editing function. My editing function takes a significant amount of time to process / loop through. Depending on the number of files that it is editing, it can take anywhere from 30 seconds to over an hour. Because of this, I decided to throw my editing function into its own thread using the native threading capabilities of Qt. I was able to get the thread working ie: from my MainWindow class I can click a button that initializes my editing class (class edit(QThread), however interacting with the GUI still crashes the program and I'm not sure why! Below I have added a sample of the general code structure / set up that I am using.
class anonymizeThread(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
#def sendAnon(self, progress_val):
# self.completed = 0
# return self.completed
def run(self):
# while self.completed < 100:
# self.completed += 0.00001
# self.emit(QtCore.SIGNAL('PROGRESS'), self.completed)
# ANONYMIZE FUNCTION!
i = 0
#flag = self.stop_flag
while i < 10000000: # and self.stop_flag is not 1:
print(i)
i+=1
print('i didnt enter the loop')
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
# connect the buttons
self.worker = anonymizeThread()
self.anonbtn.clicked.connect(self.anonymize)
self.open_directory.clicked.connect(self.open_dir)
self.pause.clicked.connect(self.paused)
self.stopbtn.clicked.connect(self.stopped)
# block button signals to start
self.pause.blockSignals(True)
self.stopbtn.blockSignals(True)
self.dir_name = None
self.pause_flag = None
self.stop_flag = None
self.anon_flag = None
# This is how we quit from the main menu "File" option
extractAction = self.actionQuit_Ctrl_Q
extractAction.setShortcut("Ctrl+Q")
extractAction.setStatusTip('Leave The App')
extractAction.triggered.connect(self.close_application)
def updateProgressBar(self,val):
self.progressBar.setValue(val)
def close_application(self):
choice = QMessageBox.question(self, 'Just had to check...', "Are you sure you want to exit?", QMessageBox.Yes | QMessageBox.No)
if choice == QMessageBox.Yes:
sys.exit()
else:
pass
def anonymize(self):
self.pause.blockSignals(False)
self.stopbtn.blockSignals(False)
self.worker.start()
# check if directory chosen
# self.progressBar.setMaximum(len(dcm)
# start our anon thread!
def paused(self):
#only if running
if self.pause_flag is 0:
self.pause_flag = 1
self.pause.setText('Start')
elif self.pause_flag is 1:
self.pause_flag = 0
self.pause.setText('Pause')
else:
pass
def stopped(self): # need a self.stop() for anonThread
choice = QMessageBox.question(self,'Stop', "Are you sure you want to stop? You will not be able to pick up exactly where you left off.",
QMessageBox.Yes | QMessageBox.No)
if choice == QMessageBox.Yes:
self.stop_flag = 1
#self.stopbtn.blockSignals(True)
#self.paused.blockSignals(True)
else:
pass
def open_dir(self):
self.dir_name = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
if len(self.dir_name) is not 0:
self.anon_flag = 0
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Upvotes: 0
Views: 1124
Reputation: 244291
It is advisable not to access the flags directly, it is better to do it through the functions to make use of it transparently, for this the same class should verify the tasks.
Also it is good to give a small delay so that the application can deal with the graphic part, another possible improvement is to avoid usat sys.exit, you could call the close method that closes the window.
In the following code I have implemented the stop and pause methods.
class anonymizeThread(QThread):
def __init__(self):
QThread.__init__(self)
self.onRunning = True
self.onStop = False
def __del__(self):
self.wait()
def stop(self):
self.onStop = True
def pause(self):
if self.isRunning():
self.onRunning = not self.onRunning
def run(self):
i = 0
#flag = self.stop_flag
while i < 10000000:
if self.onRunning: # and self.stop_flag is not 1:
print(i)
i+=1
if self.onStop:
break
QThread.msleep(10)
print('i didnt enter the loop')
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
# connect the buttons
self.worker = anonymizeThread()
self.anonbtn.clicked.connect(self.anonymize)
self.pause.clicked.connect(self.paused)
self.stopbtn.clicked.connect(self.stopped)
# block button signals to start
self.pause.blockSignals(True)
self.stopbtn.blockSignals(True)
def close_application(self):
choice = QMessageBox.question(self, 'Just had to check...', "Are you sure you want to exit?", QMessageBox.Yes | QMessageBox.No)
if choice == QMessageBox.Yes:
self.close()
def anonymize(self):
self.pause.blockSignals(False)
self.stopbtn.blockSignals(False)
self.worker.start()
def paused(self):
self.worker.pause()
def stopped(self): # need a self.stop() for anonThread
choice = QMessageBox.question(self,'Stop', "Are you sure you want to stop? You will not be able to pick up exactly where you left off.",
QMessageBox.Yes | QMessageBox.No)
if choice == QMessageBox.Yes:
self.worker.stop()
Upvotes: 1
Reputation: 13
Thanks to @eyllansec and @ekhumoro..
In the above code, all instances of self.stop_flag = ...
should have been self.worker.stop_flag = ...
as it is changing the variable that is to be used in the worker class/thread. My mistake was assuming both classes inherited the same "self".
If there are other errors and or better explanations of what I did incorrectly, please do post an answer and I'll accept it!
Upvotes: 0