Why is my GUI not responding, although I am outsourcing the workflow?

I have a PyQt4 GUI in which I need to save a stack of Numpy arrays as * .tif images. This seems to take a considerable amount of time and makes my GUI work unanswered for several minutes, depending on the number of images on the stack.

The main part of the processing takes place in this cycle over the images:

for i in range(0, np.shape(dataStack)[2]): print('Saving slice ' + str(i + 1)) #Save tumor stack im1 = Image.fromarray(tumorStack[:,:,i]*255) im1.save(saveLocationStr + 'Slice' + str(i+1) + '.tif') #Set up correct number of subplots for review plot. if T0 is not None: plt.subplot(141) else: plt.subplot(131) #Plot current brain slice in 1st position plt.imshow(dataStack[:,:,i], cmap=mpl.cm.bone) plt.axis('off') plt.title(patient + '\n' + date + '\n' + 'Slice ' + str(i+1) + ' of ' + str(int(np.shape(dataStack)[2]))) #Select correct next subplot if T0 is not None: plt.subplot(142) else: plt.subplot(132) #Get a masked copy of the tumorStack tempTumorStack = copy.deepcopy(tumorStack[:,:,i]) tempTumorStack = np.ma.masked_where(tempTumorStack == 0, tempTumorStack) #Plot masked tumor stack over brain data plt.imshow(dataStack[:,:,i], cmap=mpl.cm.bone) plt.imshow(tempTumorStack, cmap=mpl.cm.jet_r, interpolation='nearest') plt.axis('off') plt.title(modality + ' Region') #Get the auto-zoomed region and plot it (x, y) = tumorStack[:,:,i].nonzero() if( int(np.shape(x)[0]) == 0 or int(np.shape(y)[0]) == 0): if T0 is not None: plt.subplot(143) plt.imshow(T0[:,:,i], cmap=mpl.cm.bone) plt.axis('off') plt.title('T0') #Plot autozoomed with perimiter over brain data plt.subplot(144) plt.imshow(np.zeros(np.shape(dataStack[:,:,i])), cmap=mpl.cm.bone) plt.title('Perimiter of \n' + modality + ' + T0 for SA') plt.axis('off') else: plt.subplot(133) plt.imshow(np.zeros(np.shape(dataStack[:,:,i])), cmap=mpl.cm.bone) plt.title('Perimiter of \n' + modality + ' for SA') plt.axis('off') else: minX = np.min(x) minY = np.min(y) maxX = np.max(x) maxY = np.max(y) zoomedXmin = minX - (minX * .10) zoomedXmax = (maxX * .10) + maxX zoomedYmin = minY - (minY * .10) zoomedYmax = (maxY * .10) + maxY widthOf = zoomedXmax - zoomedXmin heigthOf = zoomedYmax - zoomedYmin #Get perimiter of tumor for autozoomed region #Can do n=8 if we want #tempTumorStack = bwperim(tempTumorStack,n=8) tempTumorStack = mahotas.labeled.borders(tempTumorStack) tempTumorStack = np.where(tempTumorStack == np.max(tempTumorStack), 1, np.nan) #Plot T0 then auto-zoomed if user wanted T0 if T0 is not None: plt.subplot(143) plt.imshow(T0[:,:,i], cmap=mpl.cm.bone) plt.axis('off') plt.title('T0') #Plot autozoomed with perimiter over brain data plt.subplot(144) plt.imshow(dataStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax), i ], cmap=mpl.cm.bone) plt.imshow(tempTumorStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax) ], cmap=mpl.cm.jet_r) #plt.xlim(minX, maxX-minX) #plt.ylim(minY, maxY-minY) plt.title('Perimiter of \n' + modality + ' + T0 for SA') plt.axis('off') #Just plot autozoomed else: plt.subplot(133) plt.imshow(dataStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax), i ], cmap=mpl.cm.bone) plt.imshow(tempTumorStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax) ], cmap=mpl.cm.jet_r) plt.title('Perimiter of \n' + modality + ' for SA') plt.axis('off') #Finish setting up plot to specs, render, and save it plt.subplots_adjust(wspace=.5) plt.axis('off') plt.tick_params(bottom='off', top='off', left='off', right='off') plt.draw() plt.savefig(saveLocationStr + 'MRI_Comparison\\Slice' + str(i+1) + '.png', dpi=200) 

I thought that I would hand over the expensive work to the QQhread PyQt4 employee, therefore, following the example of http://diotavelli.net/PyQtWiki/Threading,_Signals_and_Slots , I created a working class that inherits from QtCore.QThread, all the above code is called inside the run method of this class and creates and starts () ed an instance of this class, where saving on the * .tif stack should occur in the main window.

The GUI is still not responding as it was before, but the thread seems to be successfully executing the code. I am worried that maybe some package used in the above code is not releasing the GIL correctly (as mentioned here http://www.riverbankcomputing.com/pipermail/pyqt/2011-August/030470.html ). Is there a way to manually verify that this works?

I commented and replaced the save code on the * .tif stack with a delay of time.sleep, and the GUI is still not responding, so maybe I am implementing QThread incorrectly?

Did I miss something else? Have I provided sufficient information?

edit:

I create a stream in the main window here:

 # create instance of saveImageStackWorker(QThread) thread class self.saveThread = brain_io.saveImageStackWorker() self.saveThread.populate(self.tumorBrain.pixData, self.tumorBrain.tumor, saveLocationStr, self.measurer, self.tumorBrain.patient, dateForPlots, imageModality, T0pass) 

And here is the whole workflow class that I defined, where the saveImageStack (args) function is the same * .tif save code given above:

 class saveImageStackWorker(QThread): """ """ def __init__(self, parent=None): QThread.__init__(self, parent) def __del__(self): self.wait() def populate(self, dataStack, tumorStack, saveLocationStr, measurer, patient, date, modality, T0): self.dataStack = dataStack self.tumorStack = tumorStack self.saveLocationStr = saveLocationStr self.measurer = measurer self.patient = patient self.date = date self.modality = modality self.T0 = T0 self.start() def run(self): self.saveImageStack(self.dataStack, self.tumorStack, self.saveLocationStr, self.measurer, self.patient, self.date, self.modality, self.T0) def saveImageStack(self, dataStack, tumorStack, saveLocationStr, measurer, patient, date, modality, T0): #, dateStr, measurer,, saveLocationStr): """ Input: dataStack: numpy array of the brain image data. tumorStack: numpy binary array of tumor data. modality: the modality of the image. T0: numpy binary array of T0, if you do not wish to show T0 (ie for flair or something) leave as default None. Output: None Description: Saves the image stack of tumor and the review plots to the output directory. """ print('Saving image stack from within worker thread...') font = {'size' : 10} matplotlib.rc('font', **font) np.seterr(all='ignore') warnings.simplefilter('ignore') for i in range(0, np.shape(dataStack)[2]): print('Saving slice ' + str(i + 1)) #Save tumor stack im1 = Image.fromarray(tumorStack[:,:,i]*255) im1.save(saveLocationStr + 'Slice' + str(i+1) + '.tif') #Set up correct number of subplots for review plot. if T0 is not None: plt.subplot(141) else: plt.subplot(131) #Plot current brain slice in 1st position plt.imshow(dataStack[:,:,i], cmap=mpl.cm.bone) plt.axis('off') plt.title(patient + '\n' + date + '\n' + 'Slice ' + str(i+1) + ' of ' + str(int(np.shape(dataStack)[2]))) #Select correct next subplot if T0 is not None: plt.subplot(142) else: plt.subplot(132) #Get a masked copy of the tumorStack tempTumorStack = copy.deepcopy(tumorStack[:,:,i]) tempTumorStack = np.ma.masked_where(tempTumorStack == 0, tempTumorStack) #Plot masked tumor stack over brain data plt.imshow(dataStack[:,:,i], cmap=mpl.cm.bone) plt.imshow(tempTumorStack, cmap=mpl.cm.jet_r, interpolation='nearest') plt.axis('off') plt.title(modality + ' Region') #Get the auto-zoomed region and plot it (x, y) = tumorStack[:,:,i].nonzero() if( int(np.shape(x)[0]) == 0 or int(np.shape(y)[0]) == 0): if T0 is not None: plt.subplot(143) plt.imshow(T0[:,:,i], cmap=mpl.cm.bone) plt.axis('off') plt.title('T0') #Plot autozoomed with perimiter over brain data plt.subplot(144) plt.imshow(np.zeros(np.shape(dataStack[:,:,i])), cmap=mpl.cm.bone) plt.title('Perimiter of \n' + modality + ' + T0 for SA') plt.axis('off') else: plt.subplot(133) plt.imshow(np.zeros(np.shape(dataStack[:,:,i])), cmap=mpl.cm.bone) plt.title('Perimiter of \n' + modality + ' for SA') plt.axis('off') else: minX = np.min(x) minY = np.min(y) maxX = np.max(x) maxY = np.max(y) zoomedXmin = minX - (minX * .10) zoomedXmax = (maxX * .10) + maxX zoomedYmin = minY - (minY * .10) zoomedYmax = (maxY * .10) + maxY widthOf = zoomedXmax - zoomedXmin heigthOf = zoomedYmax - zoomedYmin #Get perimiter of tumor for autozoomed region #Can do n=8 if we want #tempTumorStack = bwperim(tempTumorStack,n=8) tempTumorStack = mahotas.labeled.borders(tempTumorStack) tempTumorStack = np.where(tempTumorStack == np.max(tempTumorStack), 1, np.nan) #Plot T0 then auto-zoomed if user wanted T0 if T0 is not None: plt.subplot(143) plt.imshow(T0[:,:,i], cmap=mpl.cm.bone) plt.axis('off') plt.title('T0') #Plot autozoomed with perimiter over brain data plt.subplot(144) plt.imshow(dataStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax), i ], cmap=mpl.cm.bone) plt.imshow(tempTumorStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax) ], cmap=mpl.cm.jet_r) #plt.xlim(minX, maxX-minX) #plt.ylim(minY, maxY-minY) plt.title('Perimiter of \n' + modality + ' + T0 for SA') plt.axis('off') #Just plot autozoomed else: plt.subplot(133) plt.imshow(dataStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax), i ], cmap=mpl.cm.bone) plt.imshow(tempTumorStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax) ], cmap=mpl.cm.jet_r) plt.title('Perimiter of \n' + modality + ' for SA') plt.axis('off') #Finish setting up plot to specs, render, and save it plt.subplots_adjust(wspace=.5) plt.axis('off') plt.tick_params(bottom='off', top='off', left='off', right='off') plt.draw() plt.savefig(saveLocationStr + 'MRI_Comparison\\Slice' + str(i+1) + '.png', dpi=200) 

edit # 2: Adding code for a method to the main window that starts the stream -

 def uploadAndSave(self): [db, unused] = initGUI.getDbDataBetter() self.db = db subtypeID = str(self.dbImage.image_subtype_id.toString()) query = QtSql.QSqlQuery("SELECT subtype_name FROM image_subtypes where " + "id = " + subtypeID, self.db) query.next() imageModality = str(query.value(0).toString()) dateForPlots = str(self.dbImage.date_of_image.toString()).replace('T',' ') date = dateForPlots.replace('-','').replace(':','.') basePath = 'S:\Lab_KSwanson\MRI Project\Test measurements\\' + self.tumorBrain.patient + '\\' + self.measurer + '\\' + date + '\\' print('Saving images...') seriesDescription = str(self.dbImage.series_description.toString()) saveLocationStr = brain_io.createOutputFilepath(basePath, seriesDescription, imageModality) if imageModality.upper().find('T1') < 0: T0pass = None else: T0pass = self.tumorBrain.T0 operation_time = datetime.datetime.now().isoformat().replace('T',' ')[0:19] # create instance of saveImageStackWorker(QThread) thread class self.saveThread = brain_io.saveImageStackWorker() self.saveThread.populate(self.tumorBrain.pixData, self.tumorBrain.tumor, saveLocationStr, self.measurer, self.tumorBrain.patient, dateForPlots, imageModality, T0pass) # brain_io.saveImageStack(self.tumorBrain.pixData, self.tumorBrain.tumor, saveLocationStr, self.measurer, self.tumorBrain.patient, dateForPlots, imageModality, T0pass) self.tumorBrain.save(saveLocationStr + date + '.dat') [db, unused] = initGUI.getDbDataBetter() query = QtSql.QSqlQuery('SELECT file_path FROM measurements m WHERE m.user_id = ' + self.userIDStr + ' AND m.image_id = ' + str(self.dbImage.id.toString()) + ' AND m.status = "R"', db) #If there was a rejected measurement, this will return True remeasure = query.next() print('Computing volume, surface area, etc...') T1 = algorithms.vtk_stats(self.tumorBrain.tumor, spacing=(self.tumorBrain.xSpacing, self.tumorBrain.ySpacing, np.mean(self.tumorBrain.SliceThickness))) T0 = algorithms.vtk_stats(self.tumorBrain.T0, spacing=(self.tumorBrain.xSpacing, self.tumorBrain.ySpacing, np.mean(self.tumorBrain.SliceThickness))) mass = tvtk.MassProperties(input=T1.output) T0mass = tvtk.MassProperties(input=T0.output) #mySA = algorithms.calculateSurfaceArea(self.tumorBrain.tumor, # self.tumorBrain.xSpacing, # self.tumorBrain.ySpacing, # self.tumorBrain.SliceThickness) #mySAT0 = algorithms.calculateSurfaceArea(self.tumorBrain.T0, # self.tumorBrain.xSpacing, # self.tumorBrain.ySpacing, # self.tumorBrain.SliceThickness) #print('mysa = ' + str(mySA)) #area = 0 #for i in range(0, int(self.tumorBrain.tumor.shape[2])): # tumor_filt = self.tumorBrain.tumor[:,:,i] # currThreshold = self.thresholdList[i] # tumor_filt = np.where(tumor_filt > currThreshold, 1, 0) # area = area + np.sum(np.sum(tumor_filt)) #myVolumeT1 = np.sum(self.tumorBrain.xSpacing**2 * area * self.tumorBrain.SliceThickness) myVolumeT1 = mass.volume #T0sum = np.sum(np.sum(np.sum(self.tumorBrain.T0))) #myVolumeT0 = np.sum(self.tumorBrain.xSpacing**2 * T0sum * self.tumorBrain.SliceThickness) myVolumeT0 = T0mass.volume myVolume_T0_T1 = myVolumeT1 + myVolumeT0 T0_radius = ((3.0*(myVolumeT0))/(4.0*math.pi))**(1.0/3.0) T0_T1_radius = ((3.0*(myVolume_T0_T1))/(4.0*math.pi))**(1.0/3.0) #print('volume vtk = ' + str(mass.volume)) #print('my volume = ' + str(myVolume_T0_T1)) #print('my radius = ' + str(T0_T1_radius)) if mass.volume + T0mass.volume == 0 or mass.surface_area == 0: circularity = 0 else: circularity = ((math.pi)**(1.0/3.0))*((6.0*(myVolume_T0_T1))**(2.0/3.0) / mass.surface_area) print('SA = ' + str(mass.surface_area)) print('T0 SA = ' + str(T0mass.surface_area)) print('Volume = ' + str(myVolume_T0_T1)) print('T0 Volume = ' + str(myVolumeT0)) print('Radius = ' + str(T0_T1_radius)) print('T0 Radius = ' + str(T0_radius)) print('Circularity = ' + str(circularity)) # Ask to see rendering msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, QtCore.QString('Render'), QtCore.QString('Show Tumor Rendering?'), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) ret = msgBox.exec_() if ret == QtGui.QMessageBox.Yes: algorithms.render_surface(T1, T0) query = QtSql.QSqlQuery('select max(group_id) from ' + 'measurement_audit_log where measurement_type_id = ' + "'1'", db) query.next() group_id = str(query.value(0).toInt()[0] + 1) # Check for a measurement assignment. osUserName = os.environ.get("USERNAME").lower() query = QtSql.QSqlQuery('SELECT id from users where pathology_user_name = "' + osUserName + '"') query.next() user_id = str(query.value(0).toInt()[0]) query = QtSql.QSqlQuery('select id from ' + 'measurement_assignments_audit ' + 'where image_id = ' + str(self.dbImage.id.toString()) + ' ' + 'and measurer_id = ' + user_id + ' ' + 'and status = "P"') assignment = query.next() date_of_completion = operation_time[0:10] if not assignment: # Create a new assignment newAssignmentQuery = ('insert into measurement_assignments_audit ' + '(measurement_assignment_id, assigner_id, measurer_id, image_id, patient_id, date_of_assignment, ' + 'comments, date_of_completion, priority, measurement_group_id, operation_user_id, operation_code, ' + 'operation_time, status) values (0, ' + user_id + ', ' + user_id + ', ' + str(self.dbImage.id.toString()) + ', ' + str(self.dbImage.patient_id.toString()) + ', "' + date_of_completion + '", ' + '"Self-assigned through brainsegment", "' + date_of_completion + '", 2, ' + group_id + ', ' + user_id + ', "I", "' + operation_time + '", "P")') query = QtSql.QSqlQuery(newAssignmentQuery) else: # Update the assignment updateAssignmentQuery = ('update measurement_assignments_audit set date_of_completion = "' + date_of_completion + '", ' + 'measurement_group_id = ' + group_id + ' where id = ' + str(query.value(0).toInt()[0])) query = QtSql.QSqlQuery(updateAssignmentQuery) brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 1, 1, T0_T1_radius, saveLocationStr + 'MRI_Comparison', operation_time, str(self.dbImage.id.toString()), group_id) brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 2, 2, myVolume_T0_T1, saveLocationStr + 'MRI_Comparison', operation_time, str(self.dbImage.id.toString()), group_id) brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 7, 3, mass.surface_area, saveLocationStr + 'MRI_Comparison', operation_time, str(self.dbImage.id.toString()),group_id) brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 11, 11, circularity, saveLocationStr + 'MRI_Comparison', operation_time, str(self.dbImage.id.toString()),group_id) if T0pass is not None: query = QtSql.QSqlQuery('SELECT image_file_path from images i where i.id = ' + str(self.dbImage.id.toString()), db) query.next() #print('SELECT i.image_file_path from images i where i.id = ' + str(self.dbImage.id.toString())) image_file_path = str(query.value(0).toString()).replace('\\','\\\\') query = QtSql.QSqlQuery('SELECT id from images i where i.image_file_path = "' + image_file_path + '" and i.image_subtype_id in (14, 15, 20, 21) ', db) #print('SELECT id from images i where i.image_file_path = "' + image_file_path + '" and i.image_subtype_id in (14, 15, 20, 21) ') query.next() T0idStr = str(query.value(0).toString()) T0radius = ((3.0*T0mass.volume)/(4.0*math.pi))**(1.0/3.0) brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 1, 1, T0_radius, saveLocationStr + 'MRI_Comparison', operation_time, T0idStr, group_id) brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 2, 2, myVolumeT0, saveLocationStr + 'MRI_Comparison', operation_time, T0idStr, group_id) brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 7, 3, T0mass.surface_area, saveLocationStr + 'MRI_Comparison', operation_time, T0idStr, group_id) 
+6
source share
2 answers

Be sure to create your images in the backend using matplotlib.use ('Agg'). There is some need for ordering to call matplotlib.use () before the trigger.

http://matplotlib.org/faq/howto_faq.html

Creating images without a window appearing The easiest way to do this is to use a non-interactive backend (see the section β€œWhat is a backend?”), For example, Agg (for PNG), PDF, SVG or PS. In your script generator, just call the matplotlib.use () directive before importing pylab or pyplot:

 import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt plt.plot([1,2,3]) plt.savefig('myfig') 
+4
source

If image processing is time consuming, you should use multiprocessing.Pool .

For this to work, you must create a list (or another iterable) with data for each image. And you must create a function from the processing code that you wrapped in the loop.

Then you use the map_async pools map_async to apply the function to all images. This will be performed in different processes, so it should not bother the graphical interface. As a bonus, he will use all the processor cores on your computer to do the job.

+1
source

Source: https://habr.com/ru/post/920837/


All Articles