def __init__(self, *args, **kwargs): super(CalculateDVHProgressWindow, self).__init__(*args, **kwargs) layout = QtWidgets.QVBoxLayout() text = QtWidgets.QLabel( "Calculating DVHs... (This may take several minutes)") layout.addWidget(text) self.setWindowTitle("Please wait...") self.setLayout(layout) self.threadpool = QtCore.QThreadPool() self.patient_dict_container = PatientDictContainer() dataset_rtss = self.patient_dict_container.dataset["rtss"] dataset_rtdose = self.patient_dict_container.dataset["rtdose"] rois = self.patient_dict_container.get("rois") dict_thickness = ImageLoading.get_thickness_dict( dataset_rtss, self.patient_dict_container.dataset) interrupt_flag = threading.Event() fork_safe_platforms = ['Linux'] if platform.system() in fork_safe_platforms: worker = Worker(ImageLoading.multi_calc_dvh, dataset_rtss, dataset_rtdose, rois, dict_thickness) else: worker = Worker(ImageLoading.calc_dvhs, dataset_rtss, dataset_rtdose, rois, dict_thickness, interrupt_flag) worker.signals.result.connect(self.dvh_calculated) self.threadpool.start(worker)
def load_patient_files(self, path, progress_callback, search_complete_callback): """ Load the patient files from directory. """ # Set the interrup flag self.interrupt_flag.set() # Release the current thread, and create new threadpool self.threadpool.releaseThread() self.threadpool = QThreadPool() # Clear the interrupt flag self.interrupt_flag.clear() # Create new worker worker = Worker(DICOMDirectorySearch.get_dicom_structure, path, self.interrupt_flag, progress_callback=True) # Connect callbacks worker.signals.result.connect(search_complete_callback) worker.signals.progress.connect(progress_callback) # Start the worker self.threadpool.start(worker)
def scan_directory_for_patient(self): # Reset tree view header and last patient self.open_patient_window_patients_tree.setHeaderLabels([""]) self.last_patient = None self.filepath = self.open_patient_directory_input_box.text() # Proceed if a folder was selected if self.filepath != "": # Update the QTreeWidget to reflect data being loaded # First, clear the widget of any existing data self.open_patient_window_patients_tree.clear() # Next, update the tree widget self.open_patient_window_patients_tree.addTopLevelItem( QTreeWidgetItem(["Loading selected directory..."])) # The choose button is disabled until the thread finishes executing self.open_patient_directory_choose_button.setEnabled(False) # Reveals the Stop Search button for the duration of the search self.open_patient_window_stop_button.setVisible(True) # The interrupt flag is then un-set if a previous search has been stopped. self.interrupt_flag.clear() # Then, create a new thread that will load the selected folder worker = Worker(DICOMDirectorySearch.get_dicom_structure, self.filepath, self.interrupt_flag, progress_callback=True) worker.signals.result.connect(self.on_search_complete) worker.signals.progress.connect(self.search_progress) # Execute the thread self.threadpool.start(worker)
def start_deleting(self, dataset_rtss, rois_to_delete): """ Creates a thread that generates the new dataset object. :param dataset_rtss: Dataset of RTSS :param rois_to_delete: List of ROI names to be deleted """ worker = Worker(ROI.delete_list_of_rois, dataset_rtss, rois_to_delete) worker.signals.result.connect(self.roi_deleted) self.threadpool.start(worker)
def start_loading(self, selected_files): image_loader = ImageLoader(selected_files) worker = Worker(image_loader.load, self.interrupt_flag) worker.signals.result.connect(self.on_finish) worker.signals.error.connect(self.on_error) worker.signals.progress.connect(self.update_progress) self.threadpool.start(worker)
def start_renaming(self, dataset_rtss, roi_id, new_name): """ Creates a thread that generates the new dataset object. :param dataset_rtss: Dataset of RTSS. :param roi_id: ID of the ROI to be renamed. :param new_name: The name the ROI will be changed to. """ worker = Worker(ROI.rename_roi, dataset_rtss, roi_id, new_name) worker.signals.result.connect(self.roi_renamed) self.threadpool.start(worker)
def start_saving(self, dataset_rtss, roi_name, roi_list): """ Creates a thread that generates the new dataset object. :param dataset_rtss: dataset of RTSS :param roi_name: ROIName :param roi_list: list of contours to be saved """ worker = Worker(ROI.create_roi, dataset_rtss, roi_name, roi_list) worker.signals.result.connect(self.roi_saved) self.threadpool.start(worker)
def start_loading(self, selected_files): image_loader = ImageLoader(selected_files, self) image_loader.signal_request_calc_dvh.connect(self.prompt_calc_dvh) worker = Worker(image_loader.load, self.interrupt_flag, progress_callback=True) worker.signals.result.connect(self.on_finish) worker.signals.error.connect(self.on_error) worker.signals.progress.connect(self.update_progress) self.threadpool.start(worker)
def test_worker_no_progress_callback(qtbot): """ Testing for the progress_callback parameter not being present in the called function when no progress_callback """ func_to_test = Mock() w = Worker(func_to_test, "test", 3) threadpool = QThreadPool() with qtbot.waitSignal(w.signals.finished) as blocker: threadpool.start(w) assert w.fn == func_to_test assert 'progress_callback' not in w.kwargs func_to_test.assert_called_with("test", 3)
def start(self, funct): """ Function that executes 'funct' on new thread :param funct: function to execute. :param progress_callback: signal that receives the current progress of the loading. """ self.interrupt_flag.clear() worker = Worker(funct, self.interrupt_flag, progress_callback=True) worker.signals.result.connect(self.on_finish) worker.signals.error.connect(self.on_error) worker.signals.progress.connect(self.update_progress) self.threadpool.start(worker) self.exec_()
def test_worker_progress_callback(qtbot): """ Testing for the progress_callback parameter being present in the called function when progress_callback=True """ func_to_test = Mock() w = Worker(func_to_test, "test", 3, progress_callback=True) # This starts the Worker in the threadpool and then blocks the test from progressing until the finished signal is # emitted. qtbot is a pytest fixture used to test PyQt5. threadpool = QThreadPool() with qtbot.waitSignal(w.signals.finished) as blocker: threadpool.start(w) assert w.fn == func_to_test assert w.kwargs['progress_callback'] is not None func_to_test.assert_called_with("test", 3, progress_callback=w.kwargs['progress_callback'])
def test_worker_result_signal(qtbot): """ Testing return value of worker's called function through result signal. """ func_to_test = Mock(return_value=5) func_result = Mock() w = Worker(func_to_test, "test", 3) w.signals.result.connect(func_result) threadpool = QThreadPool() with qtbot.waitSignal(w.signals.finished) as blocker: threadpool.start(w) func_to_test.assert_called_with("test", 3) func_result.assert_called_with(5)
def test_worker_result_signal(qtbot, monkeypatch): """ Testing return value of worker's called function through result signal. """ thing = FakeClass() thing.func_to_test = Mock(return_value=5, unsafe=True) w = Worker(thing.func_to_test, "test", 3) with mock.patch.object(FakeClass, 'func_result', wraps=thing.func_result) as mock_func_result: w.signals.result.connect(thing.func_result) threadpool = QThreadPool() with qtbot.waitSignal(w.signals.finished) as blocker: threadpool.start(w) thing.func_to_test.assert_called_with("test", 3) mock_func_result.assert_called_with(5)
def test_worker_error_signal(qtbot): """ Testing return value of worker's called function through result signal. """ func_to_test = Mock(side_effect=ValueError()) func_error = MagicMock() w = Worker(func_to_test, "test", 3) w.signals.error.connect(func_error) threadpool = QThreadPool() with qtbot.waitSignal(w.signals.finished) as blocker: threadpool.start(w) kall = func_error.call_args args, kwargs = kall func_to_test.assert_called_with("test", 3) assert isinstance(args[0][1], ValueError)
def test_worker_error_signal(qtbot): """ Testing return value of worker's called function through result signal. """ thing = FakeClass() thing.func_to_test = Mock(side_effect=ValueError()) w = Worker(thing.func_to_test, "test", 3) with mock.patch.object(FakeClass, 'func_error', wraps=thing.func_error): w.signals.error.connect(thing.func_error) threadpool = QThreadPool() with qtbot.waitSignal(w.signals.finished) as blocker: threadpool.start(w) kall = thing.func_error.call_args args, kwargs = kall thing.func_to_test.assert_called_with("test", 3) assert isinstance(args[0][1], ValueError)
def choose_button_clicked(self): """ Executes when the choose button is clicked. Gets filepath from the user and loads all files and subdirectories. """ # Get folder path from pop up dialog box self.filepath = QtWidgets.QFileDialog.getExistingDirectory( None, 'Select patient folder...', '') self.path_text_browser.setText(self.filepath) # Proceed if a folder was selected if self.filepath != "": # Update the QTreeWidget to reflect data being loaded # First, clear the widget of any existing data self.tree_widget.clear() # Next, update the tree widget self.tree_widget.addTopLevelItem( QTreeWidgetItem(["Loading selected directory..."])) # The choose button is disabled until the thread finishes executing self.choose_button.setEnabled(False) # Reveals the Stop Search button for the duration of the search self.stop_button.setVisible(True) # The interrupt flag is then un-set if a previous search has been stopped. self.interrupt_flag.clear() # Then, create a new thread that will load the selected folder worker = Worker(DICOMDirectorySearch.get_dicom_structure, self.filepath, self.interrupt_flag) worker.signals.result.connect(self.on_search_complete) worker.signals.progress.connect(self.search_progress) # Execute the thread self.threadpool.start(worker)