def change_dicomdir(self): """A new window appears in which the working directory can be set; besides, data is stored in the preferences file so that they will be loaded automatically next time""" self.dicomdir = FileOperations.set_wdir_in_config(self.cfg, foldername='dicom') self.label_dicomdir.setText('dicomDIR: {}'.format(self.dicomdir)) self.cfg['folders']['dicom'] = self.dicomdir self.mInput.clear() items = FileOperations.list_folders(inputdir=self.cfg['folders']['dicom'], prefix='', files2lookfor='') self.add_available_subj(items)
def change_wdir(self): """A new window appears in which the working directory for NIFTI-files can be set; if set, this is stored in the configuration file, so that upon the next start there is the same folder selected automatically""" self.niftidir = FileOperations.set_wdir_in_config(self.cfg, foldername='nifti') self.lblWdirTab.setText('wDIR: {}'.format(self.niftidir)) self.cfg['folders']['nifti'] = self.niftidir self.availableNiftiTab.clear() itemsChanged = FileOperations.list_folders(self.niftidir, self.cfg['folders']['prefix']) self.add_available_items(self.availableNiftiTab, itemsChanged)
def run_reload_files(self): """Reloads files, e.g. after renaming them""" self.cfg = Configuration.load_config(ROOTDIR) self.availableNiftiTab.clear() itemsChanged = FileOperations.list_folders(self.cfg['folders']['nifti'], prefix=self.cfg['folders']['prefix']) self.add_available_items(self.availableNiftiTab, itemsChanged)
def run_reload_files(self): """Reloads files, e.g. after renaming them""" self.cfg = Configuration.load_config(self.cfg['folders']['rootdir']) self.availableTemplates.clear() itemsTab = set(FileOperations.list_files_in_folder(self.wdirTemplate)) self.add_available_templates(self.availableTemplates, itemsTab)
def N4BiasCorrection_multiprocessing(self, file2rename, subj, input_folder, status): """Does the Bias correction taking advantage of the multicores, so that multiple subjects can be processed in parallel; For that a list of tuples including the entire filename and the subject to be processed are entered""" status.put(tuple([mp.current_process().name, subj, os.path.split(file2rename)[1]])) filename_save = os.path.join(input_folder, self.cfg['preprocess']['ANTsN4']['prefix'] + os.path.split(file2rename)[1]) # Start with N4 Bias correction for sequences specified before original_image = ants.image_read(os.path.join(input_folder, file2rename)) rescaler_nonneg = ants.contrib.RescaleIntensity(10, 100) # to avoid values <0 causing problems w/ log data if self.cfg['preprocess']['ANTsN4']['denoise'] == 'yes': # takes forever and therefore not used by default original_image = ants.denoise_image(image=original_image, noise_model='Rician') min_orig, max_orig = original_image.min(), original_image.max() if not os.path.split(file2rename)[1].startswith(self.cfg['preprocess']['ANTsN4']['dti_prefix']): original_image_nonneg = rescaler_nonneg.transform(original_image) else: original_image_nonneg = original_image bcorr_image = n4biascorr(original_image_nonneg, mask=None, shrink_factor=self.cfg['preprocess']['ANTsN4']['shrink-factor'], convergence={'iters': self.cfg['preprocess']['ANTsN4']['convergence'], 'tol': self.cfg['preprocess']['ANTsN4']['threshold']}, spline_param=self.cfg['preprocess']['ANTsN4']['bspline-fitting'], verbose=bool(self.verbose), weight_mask=None) if not os.path.split(file2rename)[1].startswith(self.cfg['preprocess']['ANTsN4']['dti_prefix']): rescaler = ants.contrib.RescaleIntensity(min_orig, max_orig) bcorr_image = rescaler.transform(bcorr_image) # difference between both images is saved for debugging purposes diff_image = original_image - bcorr_image FileOperations.create_folder(os.path.join(input_folder, "debug")) # only creates folder if not present ants.image_write(diff_image, filename=os.path.join(input_folder, "debug", "diff_biasCorr_" + os.path.split(file2rename)[1])) spacing = self.cfg['preprocess']['registration']['resample_spacing'] bcorr_image = Imaging.resampleANTs(mm_spacing=spacing, ANTsImageObject=bcorr_image, file_id=filename_save, method=int(self.cfg['preprocess'] ['registration'] ['resample_method'])) ants.image_write(bcorr_image, filename=filename_save)
def view_template(self): """this function opens a list dialog and enables selecting NIFTI files for e.g. check the content (identical function as in GUITabPreprocessANTs.py.""" if not self.selected_subj_Gen: Output.msg_box(text="No image selected. Please indicate image(s) to load.", title="No templates selected") return else: template_list = [FileOperations.return_full_filename(self.wdirTemplate, x) for x in self.selected_subj_Gen] Imaging.load_imageviewer('itk-snap', template_list) # to-date, only itk-snap available. could be changed
def CoregisterCT2MRI(self, subjects, input_folder, fixed_image='reg_run[0-9]_bc_t1'): """Co-registration of postoperative CT to preoperative MRI for further analyses in same space; before registration presence of registered MRI data is ensured to avoid redundancy""" print('\nStarting co-registration for {} subject(s)'.format( len(subjects))) allfiles = FileOperations.get_filelist_as_tuple(inputdir=input_folder, subjects=subjects) self.check_for_normalisation(subjects) regex_complete = ['CT_', '{}_'.format(fixed_image.upper())] included_sequences = [ x for x in list( filter(re.compile(r"^(?!~).*").match, regex_complete)) ] file_ID_CT, file_ID_MRI = ([] for _ in range(2)) [ file_ID_CT.append(x) for x in allfiles if 'run' not in x[0] and re.search( r'\w+{}.'.format(included_sequences[0]), x[0], re.IGNORECASE) and x[0].endswith('.nii') ] [ file_ID_MRI.append(x) for x in allfiles # for simplicity written in a second line as regexp is slightly different if re.search(r'\w+(?!_).({}).'.format(included_sequences[1]), x[0], re.IGNORECASE) and x[0].endswith('.nii') ] if not file_ID_MRI: Output.msg_box(text="Bias-corrected MRI not found!", title="Preprocessed MRI unavailable") return fileIDs = list(FileOperations.inner_join(file_ID_CT, file_ID_MRI)) self.wrapper_multiprocessing(fileIDs, subjects, 'CT')
def redefineDefault(self): """redefines which template is set as default on the right list and in the cfg-file""" if not self.selected_subj_Gen: Output.msg_box(text="No template selected. Please indicate new default one.", title="No data selected") return elif len(self.selected_subj_Gen) > 1: Output.msg_box(text="Please select only one image as default.", title="Too many templates selected") return else: default_template = [FileOperations.return_full_filename(self.wdirTemplate, x) for x in self.selected_subj_Gen] self.cfg['folders']['default_template'] = default_template[0] Configuration.save_config(ROOTDIR, self.cfg) self.run_reload_files()
def CoregisterMRI2template(self, subjects): """Co-Registration of preoperative MRI to specific template""" print('\nStarting Co-Registration for {} subject(s)'.format( len(subjects))) all_files = FileOperations.get_filelist_as_tuple( inputdir=self.cfg['folders']['nifti'], subjects=subjects) sequences = self.cfg['preprocess']['normalisation']['sequences'].split( sep=',') # sequences of interest template = glob.glob( os.path.join( ROOTDIR, 'ext', 'templates', self.cfg['preprocess']['normalisation']['template_image'] + '/*')) fileIDs = [] for idx, seqs in enumerate(sequences): file_template = [ x for x in template if re.search(r'\w+.({}).'.format(seqs), x, re.IGNORECASE) ] # corresponding template if not file_template: Output.msg_box( text= "No template found. Please ensure templates are installed at './ext/templates'", title="Template missing!" ) # TODO install templates by default! return regex_complete = '{}{}'.format( self.cfg['preprocess']['ANTsN4']['prefix'], seqs) files_subj = [ x for x in all_files if x[0].endswith('.nii') and 'run' not in x[0] and re.search(r'\w+(?!_).({}).'.format(regex_complete), x[0], re.IGNORECASE) ] fileIDs.extend( tuple([(file_template[0], file_id, subj) for file_id, subj in files_subj])) if not fileIDs: Output.msg_box( text="No bias-corrected MRI found. Please double-check", title="Preprocessed MRI missing") return self.wrapper_multiprocessing(fileIDs, subjects, 'MRI')
def change_workingdir(self): """A new window appears in which the working directory can be set; besides, data is stored in the preferences file so that they will be loaded automatically next time""" self.working_dir = QFileDialog.getExistingDirectory( self, directory=self.working_dir, caption='Change working directory') self.label_workingdir.setText('Working DIR: {}'.format( self.working_dir)) self.mInput.clear() items = FileOperations.list_folders(self.working_dir, prefix='') self.addAvailableItems(items) if self.option_gui == 'dcm2niix': self.cfg['folders']['dicom'] = self.working_dir Configuration.save_config(self.cfg['folders']["rootdir"], self.cfg)
def ANTsCoregisterMultiprocessing(self, file_fixed, file_moving, subj, input_folder, flag, step, status, run=1): """Performs Co-Registration taking advantage of multicores, i.e. multiple subjects processed in parallel""" status.put(tuple([file_fixed, file_moving, subj])) prev_reg = glob.glob( os.path.join(input_folder + "/" + self.cfg['preprocess'][flag]['prefix'] + 'run*' + os.path.basename(file_moving))) if not prev_reg: print('\tNo previous registration found, starting with first run') filename_save = os.path.join( input_folder, self.cfg['preprocess'][flag]['prefix'] + 'run' + str(run) + '_' + os.path.basename(file_moving)) elif re.search(r'\w+{}.'.format('CT_'), file_moving, re.IGNORECASE) and file_moving.endswith('.nii'): print('\tNo second run for CT-MRI registrations possible.') return else: allruns = [ re.search(r'\w+(run)([\d.]+)', x).group(2) for x in prev_reg ] lastrun = int(sorted(allruns)[-1]) file_moving = os.path.join( input_folder, self.cfg["preprocess"]["registration"]["prefix"] + 'run' + str(lastrun) + '_' + os.path.basename(file_moving)) # update for second run run = lastrun + 1 n4prefix = self.cfg['preprocess']['ANTsN4']['prefix'] basename = '{}{}'.format(n4prefix, file_moving.split(n4prefix)[1]) filename_save = os.path.join( input_folder, self.cfg['preprocess'][flag]['prefix'] + 'run' + str(run) + '_' + basename) print(filename_save) log_filename = os.path.join( ROOTDIR, 'logs', "log_Registration_using_ANTs_{}_run_{}_".format(subj, str(run)) + time.strftime("%Y%m%d-%H%M%S") + '.txt') imaging = dict() for idx, file_id in enumerate([file_fixed, file_moving]): sequence = ants.image_read( file_id) # load data and resample images if necessary imaging[idx] = Imaging.resampleANTs( mm_spacing=self.cfg['preprocess']['registration'] ['resample_spacing'], ANTsImageObject=sequence, file_id=file_id, method=int( self.cfg['preprocess']['registration']['resample_method'])) if run == 1: metric = self.cfg['preprocess']['registration']['metric'][0] else: self.cfg['preprocess']['registration'][ 'default_registration'] = 'yes' # TODO: must be changed if non-default works metric = self.cfg['preprocess']['registration']['metric'][0] if self.cfg['preprocess']['registration'][ 'default_registration'] == 'yes': registered_images = self.default_registration(imaging, file_fixed, file_moving, log_filename, metric=metric, step=step) else: registered_images = self.custom_registration( imaging, file_fixed, file_moving, input_folder + '/', log_filename, run) key2rename = { 'fwdtransforms': ['{}_0GenericAffineRegistration.mat'.format(step), 1], 'invtransforms': ['{}_1InvWarpMatrix.mat'.format(step), 0] } for key, value in key2rename.items(): Transform = ants.read_transform(registered_images[key][value[1]]) ants.write_transform(Transform, os.path.join(input_folder, value[0])) ants.image_write(registered_images['warpedmovout'], filename=filename_save) FileOperations.create_folder(os.path.join(input_folder, "debug")) if run > 1: # Previous registrations are moved to debug-folder prev_reg = ''.join(prev_reg) if type( prev_reg) == list else prev_reg filename_dest = re.sub( r'({}run[0-9]_)+({})'.format( self.cfg['preprocess']['registration']['prefix'], self.cfg['preprocess']['ANTsN4']['prefix']), '{}RUNPREV{}_{}'.format( self.cfg['preprocess']['registration']['prefix'], lastrun, self.cfg['preprocess']['ANTsN4']['prefix']), os.path.basename(prev_reg)) shutil.move(prev_reg, os.path.join(input_folder, 'debug', filename_dest)) create_mask = True if create_mask and 't1' in file_moving and not os.path.isfile( os.path.join(input_folder, 'brainmask_T1.nii')): Imaging.create_brainmask( input_folder, subj=subj, registered_images=registered_images['warpedmovout'])
def PaCER_script(subjects, inputfolder=''): """wrapper script for all steps included in the PaCER algorithm""" print("\nLead detection of {} subject(s)".format(len(subjects))) inputfolder = cfg['folders']['nifti'] if not inputfolder else inputfolder # select default input folder LW = LeadWorks() # load the class including necessary functions # Look for data files containing CT imaging including the brainMask and load this into workspace available_files = FileOperations.get_filelist_as_tuple(inputdir=inputfolder, subjects=subjects) regex2lookfor = 'reg_' + 'run[0-9]', 'brainmask_' file_id_CTimaging = [file_tuple for file_tuple in available_files if re.search(r'\w.({}).'.format(regex2lookfor[0]), file_tuple[0], re.IGNORECASE) and file_tuple[0].endswith('.nii') and 'CT' in file_tuple[0]] file_id_brainMask = [file_tuple for file_tuple in available_files if re.search(r'\w.({}).'.format(regex2lookfor[1]), file_tuple[0], re.IGNORECASE) and file_tuple[0].endswith('.nii')] if any(t > 2 for t in [len(k) for k in file_id_CTimaging]): print("More than one files for imaging or brainmask available. Please double-check!") return if not file_id_brainMask: warnings.warn(message="\tNo brain mask was found, trying to obtain a mask using ANTSpyNET routines") regex2lookforT1 = cfg['preprocess']['normalisation']['prefix'] + 'run' file_id_T1 = [file_tuple for file_tuple in available_files if re.search(r'\w.({}).'.format(regex2lookforT1), file_tuple[0], re.IGNORECASE) and 't1' in file_tuple[0] and file_tuple[0].endswith('.nii')] if not file_id_T1: Output.msg_box(text='No T1-sequence imaging available. BrainMask extraction impossible.', title='T1 sequences missing")') return else: T1imaging = ants.image_read(file_id_T1[0][0]) file_id_brainMask = Imaging.create_brainmask(input_folder=inputfolder, subj=''.join(subjects), registered_images=T1imaging) file_id_brainMask = [file_id_brainMask] if type(file_id_brainMask) == tuple else file_id_brainMask fileID = list(FileOperations.inner_join(file_id_brainMask, file_id_CTimaging)) # joins all to single list metal_threshold = int(cfg['lead_detection']['PaCER']['metal_threshold']) elecModels, intensityProfiles, skelSkalms = LW.electrodeEstimation(fileID[0], threshold=metal_threshold) elecModels, skelSkalms, intensityProfiles, _ = \ LeadProperties.estimate_hemisphere(elecModels, intensityProfiles, skelSkalms) # returns hemisphere from coords. filename_save = os.path.join(os.path.join(inputfolder, subjects[0]), 'elecModels_' + subjects[0] + '.pkl') with open(filename_save, "wb") as f: pickle.dump(elecModels, f) pickle.dump(intensityProfiles, f) pickle.dump(skelSkalms, f) sides = ['left', 'right'] rotation_default, rotation_mod = [{k: [] for k in sides} for _ in range(2)] for s in sides: rotation_default[s] = function_wrapper(subj=subjects[0], side=s) rotation_mod[s] = Configuration.rotation_dict_mod() # creates an empty array to save modified data later filename_save = os.path.join(os.path.join(inputfolder, subjects[0]), 'rotation_' + subjects[0] + '.pkl') with open(filename_save, "wb") as f: pickle.dump(rotation_default, f) pickle.dump(rotation_mod, f) print("Finished with lead detection!") # TODO: it does not return to the empty command line. return
def N4BiasCorrection(self, subjects): """N4BiasCorrection according to N.J. Tustison, ..., and J.C. Gee. "N4ITK: Improved N3 Bias Correction" IEEE Transactions on Medical Imaging, 29(6):1310-1320, June 2010.""" print('\nDebiasing imaging of {} subject(s)'.format(len(subjects))) allfiles = FileOperations.get_filelist_as_tuple(inputdir=self.cfg['folders']['nifti'], subjects=subjects) strings2exclude = ['CT', self.cfg['preprocess']['ANTsN4']['prefix'], 'reg_run', 'Mask', 'Warp', 'Registration'] allfiles = [x for x in allfiles if x[0].endswith('.nii') and not any(re.search(r'\w+(?!_).({})|^({})\w+.'.format(z, z), os.path.basename(x[0]), re.IGNORECASE) for z in strings2exclude)] file_id_DTI = [x for x in allfiles if (x[0].endswith('.nii') and self.cfg['preprocess']['ANTsN4']['dti_prefix'] in x[0]) and not any(re.search(r'\w+(?!_).({})|^({})\w+.'.format(z, z), os.path.basename(x[0]), re.IGNORECASE) for z in strings2exclude)] file_id_noDTI = [x for x in allfiles if (x[0].endswith('.nii') and not self.cfg['preprocess']['ANTsN4']['dti_prefix'] in x[0]) and not any(re.search(r'\w+(?!_).({})|^({})\w+.'.format(z, z), os.path.basename(x[0]), re.IGNORECASE) for z in strings2exclude)] seq = 'struct' # For debugging purposes if not seq: fileIDs = allfiles elif seq == 'struct': fileIDs = list(set(allfiles) - set(file_id_DTI)) elif seq == 'dwi': fileIDs = list(set(allfiles) - set(file_id_noDTI)) start_multi = time.time() status = mp.Queue() processes = [mp.Process(target=self.N4BiasCorrection_multiprocessing, args=(name_file, no_subj, os.path.join(self.cfg['folders']['nifti'], no_subj), status)) for name_file, no_subj in fileIDs] for p in processes: p.start() while any([p.is_alive() for p in processes]): while not status.empty(): process, no_subj, filename = status.get() print("Process: {}; Debiasing {}, filename: {}".format(process, no_subj, filename)) time.sleep(0.1) for p in processes: p.join() # Functions creating/updating pipeline log, which document individually all steps along with settings for subjID in subjects: allfiles_subj = [os.path.split(files_subj)[1] for files_subj, subj_no in fileIDs if subj_no == subjID] log_text = "{} files successfully processed (@{}): {}, \n\n Mean Duration per subject: {:.2f} secs" \ .format(len(set(allfiles_subj)), time.strftime("%Y%m%d-%H%M%S"), '\n\t{}'.format('\n\t'.join(os.path.split(x)[1] for x in sorted(set(allfiles_subj)))), (time.time() - start_multi) / len(subjects)) Output.logging_routine(text=Output.split_lines(log_text), cfg=self.cfg, subject=str(subjID), module='N4BiasCorrection', opt=self.cfg['preprocess']['ANTsN4'], project="") print('\nIn total, a list of {} subject(s) was processed \nOverall, bias correction took ' '{:.1f} secs.'.format(len(subjects), time.time() - start_multi))
def __init__(self, parent=None): super(GuiTabTemplate, self).__init__(parent) self.selected_subj_Gen = '' self.wdirTemplate = os.path.join(ROOTDIR, 'ext', 'templates') # General settings/variables/helper files needed needed at some point self.cfg = Configuration.load_config(ROOTDIR) if os.path.isdir(self.cfg['folders']['nifti']): self.niftidir = self.cfg['folders']['nifti'] else: self.niftidir = FileOperations.set_wdir_in_config(self.cfg, foldername='nifti', init=True) self.cfg['folders']['rootdir'] = ROOTDIR Configuration.save_config(ROOTDIR, self.cfg) self.lay = QHBoxLayout(self) self.tab = QWidget() # Customize tab # ============================== Tab 1 - General ============================== self.tab.layout = QHBoxLayout() self.tab.setLayout(self.tab.layout) # ------------------------- Upper left part (Folder) ------------------------- # self.FolderboxTab = QGroupBox("Directory (Templates)") self.HBoxUpperLeftTab = QVBoxLayout(self.FolderboxTab) self.dirTemplates = QLabel('wDIR: {}'.format(self.wdirTemplate)) self.HBoxUpperLeftTab.addWidget(self.dirTemplates) self.btnChangeWdir = QPushButton('Change working directory') self.btnChangeWdir.setDisabled(True) self.btnReloadFilesTab = QPushButton('Reload files') self.btnReloadFilesTab.clicked.connect(self.run_reload_files) self.HBoxUpperLeftTab.addWidget(self.btnChangeWdir) self.HBoxUpperLeftTab.addWidget(self.btnReloadFilesTab) # ------------------------- Lower left part (Processing) ------------------------- # self.ActionsTab = QGroupBox("Functions") self.HBoxLowerLeftTab = QVBoxLayout(self.ActionsTab) self.btn_new_default = QPushButton('Set new \ndefault') # self.btn_subj_details.setToolTip(setToolTips.subjectDetails()) TODO: new ToolTip needed self.btn_new_default.clicked.connect(self.redefineDefault) self.btn_viewer = QPushButton('View selected \nTemplate in viewer') # self.btn_viewer.setToolTip(setToolTips.displayFolderContent()) TODO: new ToolTip needed self.btn_viewer.clicked.connect(self.view_template) self.create_SST = QPushButton('Create Study-specific\ntemplate') # self.btn_renaming.setToolTip(setToolTips.renameFolders()) TODO: new ToolTip needed self.create_SST.clicked.connect(self.create_StudySpecificTemplate) self.HBoxLowerLeftTab.addWidget(self.btn_viewer) self.HBoxLowerLeftTab.addWidget(self.btn_new_default) self.HBoxLowerLeftTab.addWidget(self.create_SST) # -------------------- Right part (Subject list) ----------------------- # self.listbox = QGroupBox('Available subjects') self.HBoxUpperRightTab = QVBoxLayout(self.listbox) self.availableTemplates = QListWidget() self.availableTemplates.setSelectionMode(QAbstractItemView.ExtendedSelection) self.availableTemplates.itemSelectionChanged.connect(self.change_list_item) itemsTab = set(FileOperations.list_files_in_folder(self.wdirTemplate)) self.add_available_templates(self.availableTemplates, itemsTab) self.HBoxUpperRightTab.addWidget(self.availableTemplates) # Combine all Boxes for General Tab Layout self.LeftboxTab = QGroupBox() self.HBoxTabLeft = QVBoxLayout(self.LeftboxTab) self.HBoxTabLeft.addWidget(self.FolderboxTab) self.HBoxTabLeft.addStretch() self.HBoxTabLeft.addWidget(self.ActionsTab) self.tab.layout.addWidget(self.LeftboxTab) self.tab.layout.addWidget(self.listbox) self.lay.addWidget(self.tab)
def __init__(self, working_directory, _option_gui, parent=None): super(QWidget, self).__init__(parent) self.cfg = Configuration.load_config(ROOTDIR) self.option_gui = _option_gui # ============================ Different options available ============================ if self.option_gui == 'dcm2niix': working_dir = self.cfg['folders']['dicom'] self.working_dir = working_dir if os.path.isdir( working_dir) else os.getcwd() options = { 'folderbox_title': "Directory (DICOM-files)", 'str_labelDir': 'DICOM DIR: {}'.format(self.working_dir), 'runBTN_label': 'Run processing' } elif self.option_gui == 'displayNiftiFiles': if not working_directory: Output.msg_box( text="Please provide a valid folder. Terminating this GUI.", title="No folder provided") self.close() return else: self.working_dir = working_directory options = { 'folderbox_title': "Directory (nifti-files)", 'str_labelDir': 'subjects\' DIR: {}'.format(self.working_dir), 'runBTN_label': 'View files' } else: Output.msg_box( text= "Please provide a valid option such as 'dcm2niix' or 'displayNiftiFiles'. " "Terminating the GUI", title="Wrong input as option") self.close() return # ============================ Start creating layout ============================ # Create general layout self.tot_layout = QVBoxLayout(self) self.mid_layout = QHBoxLayout(self) # ============================ Create upper of GUI, i.e. working directory ============================ self.label_folderbox = QGroupBox(options["folderbox_title"]) self.HBoxUpperTwoListGUI = QVBoxLayout(self.label_folderbox) self.label_workingdir = QLabel(options["str_labelDir"]) self.HBoxUpperTwoListGUI.addWidget(self.label_workingdir) self.btn_workingdir = QPushButton('Change working \ndirectory') self.btn_workingdir.setFixedSize(150, 40) self.btn_workingdir.setDisabled(True) if self.option_gui == 'dcm2niix': self.btn_workingdir.setEnabled(True) self.btn_workingdir.clicked.connect(self.change_workingdir) self.btn_savedir = QPushButton('Save directory \nto config file') self.btn_savedir.setFixedSize(150, 40) self.btn_savedir.setDisabled(True) if self.option_gui == 'dcm2niix': self.btn_savedir.setEnabled(True) self.btn_savedir.setToolTip( Output.split_lines(setToolTips.saveDirButton())) self.btn_savedir.clicked.connect(self.save_cfg_dicomdir) hlay_upper = QHBoxLayout() hlay_upper.addWidget(self.btn_workingdir) hlay_upper.addWidget(self.btn_savedir) hlay_upper.addStretch(1) self.HBoxUpperTwoListGUI.addLayout(hlay_upper) # ==================== Create Content for Lists, i.e. input/output ==================== self.listboxInputGUITwoList = QGroupBox( 'Available items in working directory') self.listboxInput = QVBoxLayout(self.listboxInputGUITwoList) self.mInput = QListWidget() self.listboxInput.addWidget(self.mInput) self.mButtonToAvailable = QPushButton("<<") self.mBtnMoveToAvailable = QPushButton(">") self.mBtnMoveToSelected = QPushButton("<") self.mButtonToSelected = QPushButton(">>") self.mBtnUp = QPushButton("Up") self.mBtnDown = QPushButton("Down") self.listboxOutputGUITwoLIst = QGroupBox('Items to process') self.listboxOutput = QVBoxLayout(self.listboxOutputGUITwoLIst) self.mOutput = QListWidget() self.listboxOutput.addWidget(self.mOutput) # First column (Left side) vlay = QVBoxLayout() vlay.addStretch() vlay.addWidget(self.mBtnMoveToAvailable) vlay.addWidget(self.mBtnMoveToSelected) vlay.addStretch() vlay.addWidget(self.mButtonToAvailable) vlay.addWidget(self.mButtonToSelected) vlay.addStretch() # Second column (Right side) vlay2 = QVBoxLayout() vlay2.addStretch() vlay2.addWidget(self.mBtnUp) vlay2.addWidget(self.mBtnDown) vlay2.addStretch() # ==================== Lower part of GUI, i.e. Preferences/Start estimation ==================== self.btn_preferences = QPushButton("Preferences") self.btn_preferences.setDisabled(True) self.btn_preferences.clicked.connect(self.settings_show) if self.option_gui == 'dcm2niix': self.btn_preferences.setEnabled(True) self.btn_run_command = QPushButton(options["runBTN_label"]) if self.option_gui == 'dcm2niix': self.btn_run_command.setToolTip(setToolTips.run_dcm2niix()) else: self.btn_run_command.setToolTip( setToolTips.run_CheckRegistration()) self.btn_run_command.clicked.connect(self.start_process) hlay_bottom = QHBoxLayout() hlay_bottom.addStretch(1) hlay_bottom.addWidget(self.btn_preferences) hlay_bottom.addWidget(self.btn_run_command) hlay_bottom.addStretch() # ==================== Set all contents to general Layout ======================= self.mid_layout.addWidget(self.listboxInputGUITwoList) self.mid_layout.addLayout(vlay) self.mid_layout.addWidget(self.listboxOutputGUITwoLIst) self.mid_layout.addLayout(vlay2) self.tot_layout.addWidget(self.label_folderbox) self.tot_layout.addLayout(self.mid_layout) self.tot_layout.addLayout(hlay_bottom) try: self.mInput.clear() if self.option_gui == 'dcm2niix': items = FileOperations.list_folders(self.working_dir, prefix='') else: items = FileOperations.list_files_in_folder( inputdir=self.working_dir, contains='', suffix='nii') self.addAvailableItems(items) except FileExistsError: print('{} without any valid files/folders, continuing ...'.format( self.working_dir)) self.update_buttons_status() self.connections()
def __init__(self, parent=None): super(GuiTabPreprocessANTs, self).__init__(parent) self.selected_subj_ANT = '' self.cfg = Configuration.load_config(ROOTDIR) if os.path.isdir(self.cfg['folders']['nifti']): self.niftidir = self.cfg['folders']['nifti'] else: self.niftidir = FileOperations.set_wdir_in_config(self.cfg, foldername='nifti', init=True) self.cfg['folders']['nifti'] = self.niftidir self.cfg['folders']['rootdir'] = ROOTDIR Configuration.save_config(ROOTDIR, self.cfg) # Customize tab self.lay = QHBoxLayout(self) self.tab = QWidget() self.tab.layout = QHBoxLayout() self.tab.setLayout(self.tab.layout) # ------------------------- Upper left part (Folder) ------------------------- # self.FolderboxTab = QGroupBox('Directory (NIFTI-files)') self.HBoxUpperLeftTab = QVBoxLayout(self.FolderboxTab) self.lblWdirTab = QLabel('wDIR: {}'.format(self.niftidir)) self.HBoxUpperLeftTab.addWidget(self.lblWdirTab) self.btnChangeWdir = QPushButton('Change working directory') self.btnChangeWdir.setToolTip(setToolTips.ChangeWdirNIFTI()) self.btnChangeWdir.clicked.connect(self.change_wdir) self.btnReloadFilesTab = QPushButton('Reload files') self.btnReloadFilesTab.clicked.connect(self.run_reload_files) self.HBoxUpperLeftTab.addWidget(self.btnChangeWdir) self.HBoxUpperLeftTab.addWidget(self.btnReloadFilesTab) # ------------------------- Middle left part (Preferences) ------------------------- # self.SettingsTabANTs = QGroupBox('Preferences') self.HBoxMiddleLeftTabExt = QVBoxLayout(self.SettingsTabANTs) self.btn_ANTsettings = QPushButton('ANT Settings') self.btn_ANTsettings.clicked.connect(self.run_ANTsPreferences) self.btn_ANTsettings.setToolTip(setToolTips.ANTsSettings()) self.HBoxMiddleLeftTabExt.addWidget(self.btn_ANTsettings) # ------------------------- Middle left part (ANTs routines) ------------------------- # self.ActionsTabANTs = QGroupBox('ANTs routines') self.HBoxMiddleLeftTab = QVBoxLayout(self.ActionsTabANTs) self.btn_N4BiasCorr = QPushButton('N4BiasCorrect') self.btn_N4BiasCorr.setToolTip(setToolTips.N4BiasCorrection()) self.btn_N4BiasCorr.clicked.connect(self.run_n4Bias_corr) self.btn_MRIreg = QPushButton('MR-Registration') self.btn_MRIreg.setToolTip(setToolTips.RegisterMRI2template()) self.btn_MRIreg.clicked.connect(self.run_RegisterMRI2template) self.btn_CTreg = QPushButton('CT-Registration') self.btn_CTreg.setToolTip(setToolTips.RegisterCT2MRI()) self.btn_CTreg.clicked.connect(self.run_RegisterCT2MRI) self.HBoxMiddleLeftTab.addWidget(self.btn_N4BiasCorr) self.HBoxMiddleLeftTab.addWidget(self.btn_MRIreg) self.HBoxMiddleLeftTab.addWidget(self.btn_CTreg) # ------------------------- Lower left part (Processing) ------------------------- # self.QualityTabANTs = QGroupBox('Quality checks for ANTs preprocessing') self.HBoxLowerLeftTab = QVBoxLayout(self.QualityTabANTs) self.btn_QC_ANTsPreproc = QPushButton('View available \nNIFTI-files in viewer') self.btn_QC_ANTsPreproc.setToolTip(setToolTips.compareNIFTIfiles()) self.btn_QC_ANTsPreproc.clicked.connect(self.display_nifti_files) self.HBoxLowerLeftTab.addWidget(self.btn_QC_ANTsPreproc) # -------------------- Right part (Subject list) ----------------------- # self.listbox = QGroupBox('Available subjects') self.HBoxUpperRightTab = QVBoxLayout(self.listbox) self.availableNiftiTab = QListWidget() self.availableNiftiTab.setSelectionMode(QAbstractItemView.ExtendedSelection) itemsTab = FileOperations.list_folders(self.niftidir, prefix=self.cfg['folders']['prefix']) self.add_available_items(self.availableNiftiTab, itemsTab, msg='no') self.availableNiftiTab.itemSelectionChanged.connect(self.change_list_item) self.HBoxUpperRightTab.addWidget(self.availableNiftiTab) # Combine all Boxes for Tab 2 Layout self.LeftboxTabANTs = QGroupBox() self.HBoxTabANTsLeft = QVBoxLayout(self.LeftboxTabANTs) self.HBoxTabANTsLeft.addWidget(self.FolderboxTab) self.HBoxTabANTsLeft.addStretch(1) self.HBoxTabANTsLeft.addWidget(self.SettingsTabANTs) self.HBoxTabANTsLeft.addWidget(self.ActionsTabANTs) self.HBoxTabANTsLeft.addWidget(self.QualityTabANTs) self.tab.layout.addWidget(self.LeftboxTabANTs) self.tab.layout.addWidget(self.listbox) self.lay.addWidget(self.tab)
def dcm2niix_multiprocessing(self, name_subj, no_subj, dcm2niix_bin, last_idx, total_subj, status): """function intended to provide multiprocessing approach to speed up extraction of DICOM data to nifti files""" modalities = ['CT', 'MRI'] if self.logfile: log_filename = os.path.join( ROOTDIR, 'logs', 'log_DCM2NII_' + str(no_subj + last_idx) + time.strftime("%Y%m%d-%H%M%S")) else: log_filename = os.devnull subj_outdir = os.path.join( self.outdir, self.cfg['folders']['prefix'] + str(no_subj + last_idx)) FileOperations.create_folder(subj_outdir) start_time_subject = time.time() keptfiles, deletedfiles = ([] for _ in range(2)) for mod in modalities: status.put((name_subj, mod, no_subj, total_subj)) input_folder_name = os.path.join(self.inputdir, name_subj + mod) # input_folder_files = [f.path for f in os.scandir(input_folder_name) # if (f.is_dir() and ('100' in f.path or 'DICOM' in f.path or '001' in f.path))] input_folder_files = [] [ input_folder_files.append(item) for item in os.listdir(input_folder_name) if (os.path.isdir(os.path.join(input_folder_name, item)) and ( '100' in item or 'DICOM' in item or '001' in item)) ] orig_stdout = sys.stdout sys.stdout = open(log_filename, 'w') for folder in input_folder_files: subprocess.call( [ dcm2niix_bin, '-a', 'y', # anonimisation of DICOM data '-b', self.cfg['preprocess']['dcm2nii']['BIDSsidecar'][0], '-z', self.cfg['preprocess']['dcm2nii']['OutputCompression'] [0], '-f', self.cfg['preprocess']['dcm2nii']['OutputFileStruct'], '-o', subj_outdir, '-w', str(self.cfg['preprocess']['dcm2nii'] ['NameConflicts']), '-v', str(self.cfg['preprocess']['dcm2nii']['Verbosity']), '-x', str(self.cfg['preprocess']['dcm2nii']['ReorientCrop']), folder ], stdout=sys.stdout, stderr=subprocess.STDOUT) sys.stdout.close() sys.stdout = orig_stdout files_kept, files_deleted = self.select_sequences(subj_outdir) keptfiles.extend(files_kept) deletedfiles.extend(files_deleted) # Functions creating/updating pipeline log, which document individually all steps along with settings log_text = "{} files successfully converted: {}, \n\nand {} deleted: {}.\nDuration: {:.2f} secs" \ .format(len(set(keptfiles)), '\n\t{}'.format('\n\t'.join(os.path.split(x)[1] for x in sorted(set(keptfiles)))), len(set(deletedfiles)), '\n\t{}'.format('\n\t'.join(os.path.split(x)[1] for x in sorted(set(deletedfiles)))), time.time() - start_time_subject) Output.logging_routine(text=Output.split_lines(log_text), cfg=self.cfg, subject=self.cfg['folders']['prefix'] + str(no_subj), module='dcm2nii', opt=self.cfg['preprocess']['dcm2nii'], project="")
def __init__(self, parent=None): super(GuiTabDetectLeads, self).__init__(parent) self.selected_subj_ANT = '' # General settings/variables/helper files needed at some point self.cfg = Configuration.load_config(ROOTDIR) if os.path.isdir(self.cfg['folders']['nifti']): self.niftidir = self.cfg['folders']['nifti'] else: self.niftidir = FileOperations.set_wdir_in_config( self.cfg, foldername='nifti', init=True) self.cfg['folders']['nifti'] = self.niftidir self.cfg['folders']['rootdir'] = ROOTDIR Configuration.save_config(ROOTDIR, self.cfg) self.lay = QHBoxLayout(self) self.tab = QWidget() # Customize tab # ============================== Tab 3 - Lead detection routines ============================== self.tab.layout = QHBoxLayout() self.tab.setLayout(self.tab.layout) # ------------------------- Upper left part (Folder) ------------------------- # self.FolderboxTab = QGroupBox("Directory") self.HBoxUpperLeftTab = QVBoxLayout(self.FolderboxTab) self.lblWdirTab = QLabel('wDIR: {}'.format(self.niftidir)) self.HBoxUpperLeftTab.addWidget(self.lblWdirTab) self.btnChangeWdir = QPushButton('Change working directory') self.btnChangeWdir.clicked.connect(self.change_wdir) self.btnReloadFilesTab = QPushButton('Reload files') self.btnReloadFilesTab.clicked.connect(self.run_reload_files) self.HBoxUpperLeftTab.addWidget(self.btnChangeWdir) self.HBoxUpperLeftTab.addWidget(self.btnReloadFilesTab) # ------------------------- Middle left part (Settings) ------------------------- # self.SettingsTabLeadDetect = QGroupBox("Preferences") self.HBoxMiddleLeftTabExt = QVBoxLayout(self.SettingsTabLeadDetect) self.btn_LeadDetectSettings = QPushButton('Settings \nLead detection') self.btn_LeadDetectSettings.clicked.connect( self.run_PreferencesLeadDetection) self.btn_LeadDetectSettings.setToolTip(setToolTips.ANTsSettings()) self.HBoxMiddleLeftTabExt.addWidget(self.btn_LeadDetectSettings) # ------------------------- Middle left part (Processing) ------------------------- # self.ActionsTabANTs = QGroupBox("Lead detection routines") self.HBoxMiddleLeftTab = QVBoxLayout(self.ActionsTabANTs) self.btn_LeadDetectPacer = QPushButton('PaCER algorithm') self.btn_LeadDetectPacer.clicked.connect(self.run_LeadDetectionPaCER) self.btn_RefineDetectedLeads = QPushButton('Refine detected leads') self.btn_RefineDetectedLeads.clicked.connect(self.run_ManualCorrection) self.HBoxMiddleLeftTab.addWidget(self.btn_LeadDetectPacer) self.HBoxMiddleLeftTab.addWidget(self.btn_RefineDetectedLeads) # ------------------------- Lower left part (Processing) ------------------------- # self.QualityTabLeadDetect = QGroupBox( "Quality checks for Lead detection") self.HBoxLowerLeftTab = QVBoxLayout(self.QualityTabLeadDetect) self.btn_QC_LeadDetect = QPushButton( 'Check lead detection \nin viewer') self.btn_QC_LeadDetect.setToolTip(setToolTips.compareNIFTIfiles()) self.btn_QC_LeadDetect.clicked.connect(self.VisualiseLeadDetection) self.HBoxLowerLeftTab.addWidget(self.btn_QC_LeadDetect) # -------------------- Right part (Subject list) ----------------------- # self.listbox = QGroupBox('Available subjects') self.HBoxUpperRightTab = QVBoxLayout(self.listbox) self.availableNiftiTab = QListWidget() self.availableNiftiTab.setSelectionMode( QAbstractItemView.ExtendedSelection) itemsTab = FileOperations.list_folders( self.niftidir, prefix=self.cfg['folders']['prefix']) self.add_available_items(self.availableNiftiTab, itemsTab, msg='no') self.availableNiftiTab.itemSelectionChanged.connect( self.change_list_item) self.HBoxUpperRightTab.addWidget(self.availableNiftiTab) # Combine all Boxes for Tab 2 Layout self.LeftboxTabANTs = QGroupBox() self.HBoxTabLeadDetectLeft = QVBoxLayout(self.LeftboxTabANTs) self.HBoxTabLeadDetectLeft.addWidget(self.FolderboxTab) self.HBoxTabLeadDetectLeft.addStretch(1) self.HBoxTabLeadDetectLeft.addWidget(self.SettingsTabLeadDetect) self.HBoxTabLeadDetectLeft.addWidget(self.ActionsTabANTs) self.HBoxTabLeadDetectLeft.addWidget(self.QualityTabLeadDetect) self.tab.layout.addWidget(self.LeftboxTabANTs) self.tab.layout.addWidget(self.listbox) self.lay.addWidget(self.tab)
def __init__(self, parent=None): super(QWidget, self).__init__(parent) # Load configuration files and general settings self.cfg = Configuration.load_config(ROOTDIR) if os.path.isdir(self.cfg['folders']['dicom']): self.dicomdir = self.cfg['folders']['dicom'] else: self.dicomdir = FileOperations.set_wdir_in_config(self.cfg, foldername='dicom', init=True) self.cfg['folders']['dicom'] = self.dicomdir self.cfg['folders']['rootdir'] = ROOTDIR Configuration.save_config(ROOTDIR, self.cfg) # Create general layout self.tot_layout = QVBoxLayout(self) self.mid_layout = QHBoxLayout(self) # ============================ Create upper of GUI, i.e. working directory ============================ self.folderboxDcm2nii = QGroupBox("Directory (DICOM-files)") self.HBoxUpperDcm2nii = QVBoxLayout(self.folderboxDcm2nii) self.label_dicomdir = QLabel('dicom DIR: {}'.format(self.dicomdir)) self.HBoxUpperDcm2nii.addWidget(self.label_dicomdir) self.btn_dicomdir = QPushButton('Change working \ndirectory') self.btn_dicomdir.setFixedSize(150, 40) self.btn_dicomdir.clicked.connect(self.change_dicomdir) self.btn_savedir = QPushButton('Save directory \nto config file') self.btn_savedir.setFixedSize(150, 40) self.btn_savedir.setToolTip(Output.split_lines(setToolTips.saveDirButton())) self.btn_savedir.clicked.connect(self.save_cfg) hlay_upper = QHBoxLayout() hlay_upper.addWidget(self.btn_dicomdir) hlay_upper.addWidget(self.btn_savedir) hlay_upper.addStretch(1) self.HBoxUpperDcm2nii.addLayout(hlay_upper) # ==================== Create Content for Lists, i.e. input/output ==================== self.listboxInputDcm2nii = QGroupBox('Available subjects in working directory') self.listboxInput = QVBoxLayout(self.listboxInputDcm2nii) self.mInput = QListWidget() self.listboxInput.addWidget(self.mInput) self.mButtonToAvailable = QPushButton("<<") self.mBtnMoveToAvailable = QPushButton(">") self.mBtnMoveToSelected = QPushButton("<") self.mButtonToSelected = QPushButton(">>") self.mBtnUp = QPushButton("Up") self.mBtnDown = QPushButton("Down") self.listboxOutputDcm2nii = QGroupBox('Subjects to process') self.listboxOutput = QVBoxLayout(self.listboxOutputDcm2nii) self.mOutput = QListWidget() self.listboxOutput.addWidget(self.mOutput) # First column on the left side vlay = QVBoxLayout() vlay.addStretch() vlay.addWidget(self.mBtnMoveToAvailable) vlay.addWidget(self.mBtnMoveToSelected) vlay.addStretch() vlay.addWidget(self.mButtonToAvailable) vlay.addWidget(self.mButtonToSelected) vlay.addStretch() # Second column on the right side vlay2 = QVBoxLayout() vlay2.addStretch() vlay2.addWidget(self.mBtnUp) vlay2.addWidget(self.mBtnDown) vlay2.addStretch() # ==================== Lower part of GUI, i.e. Preferences/Start estimation ==================== self.btn_preferences = QPushButton("Preferences") self.btn_preferences.clicked.connect(self.settings_show) self.btn_run_dcm2niix = QPushButton("Run dcm2niix") self.btn_run_dcm2niix.setToolTip(setToolTips.run_dcm2niix()) self.btn_run_dcm2niix.clicked.connect(self.start_converting) hlay_bottom = QHBoxLayout() hlay_bottom.addStretch(1) hlay_bottom.addWidget(self.btn_preferences) hlay_bottom.addWidget(self.btn_run_dcm2niix) hlay_bottom.addStretch() # ==================== Set all contents to general Layout ======================= self.mid_layout.addWidget(self.listboxInputDcm2nii) self.mid_layout.addLayout(vlay) self.mid_layout.addWidget(self.listboxOutputDcm2nii) self.mid_layout.addLayout(vlay2) self.tot_layout.addWidget(self.folderboxDcm2nii) self.tot_layout.addLayout(self.mid_layout) self.tot_layout.addLayout(hlay_bottom) try: self.mInput.clear() items = FileOperations.list_folders(inputdir=self.cfg['folders']['dicom'], prefix='', files2lookfor='') items = self.check_for_complete_input(list(items)) self.add_available_subj(items) except FileExistsError: print('{} without any valid files/folders, continuing ...'.format(self.dicomdir)) self.update_buttons_status() self.connections()
def __init__(self, parent=None): super(GuiTabGeneral, self).__init__(parent) self.selected_subj_Gen = '' # General settings/variables/helper files needed needed at some point self.cfg = Configuration.load_config(ROOTDIR) if os.path.isdir(self.cfg['folders']['nifti']): self.niftidir = self.cfg['folders']['nifti'] else: self.niftidir = FileOperations.set_wdir_in_config( self.cfg, foldername='nifti', init=True) # self.cfg['folders']['rootdir'] = ROOTDIR ## changed 2021/02/23 Configuration.save_config(ROOTDIR, self.cfg) self.lay = QHBoxLayout(self) self.tab = QWidget() # Customize tab # ============================== Tab 1 - General ============================== self.tab.layout = QHBoxLayout() self.tab.setLayout(self.tab.layout) # ------------------------- Upper left part (Folder) ------------------------- # self.FolderboxTab = QGroupBox("Directory (NIFTI-files)") self.HBoxUpperLeftTab = QVBoxLayout(self.FolderboxTab) self.lblWdirTab = QLabel('wDIR: {}'.format(self.niftidir)) self.HBoxUpperLeftTab.addWidget(self.lblWdirTab) self.btnChangeWdir = QPushButton('Change working directory') self.btnChangeWdir.setToolTip(setToolTips.ChangeWdirDICOM()) self.btnChangeWdir.clicked.connect(self.change_wdir) self.btnReloadFilesTab = QPushButton('Reload files') self.btnReloadFilesTab.clicked.connect(self.run_reload_files) self.HBoxUpperLeftTab.addWidget(self.btnChangeWdir) self.HBoxUpperLeftTab.addWidget(self.btnReloadFilesTab) # ------------------------- Lower left part (Processing) ------------------------- # self.ActionsTab = QGroupBox("Functions") self.HBoxLowerLeftTab = QVBoxLayout(self.ActionsTab) self.btn_subj_details = QPushButton('Subject details') self.btn_subj_details.setToolTip(setToolTips.subjectDetails()) self.btn_subj_details.clicked.connect(self.openDetails) self.btn_dcm2nii = QPushButton('Dcm2niix') self.btn_dcm2nii.setToolTip(setToolTips.runDCM2NII()) self.btn_dcm2nii.clicked.connect(self.run_DCM2NII) self.btn_viewer = QPushButton('View available \nNIFTI-files in viewer') self.btn_viewer.setToolTip(setToolTips.displayFolderContent()) self.btn_viewer.clicked.connect(self.show_nifti_files) self.btn_renaming = QPushButton('Rename\nfolders') self.btn_renaming.setToolTip(setToolTips.renameFolders()) self.btn_renaming.clicked.connect(self.run_rename_folders) self.HBoxLowerLeftTab.addWidget(self.btn_subj_details) self.HBoxLowerLeftTab.addWidget(self.btn_dcm2nii) self.HBoxLowerLeftTab.addWidget(self.btn_viewer) self.HBoxLowerLeftTab.addWidget(self.btn_renaming) # -------------------- Right part (Subject list) ----------------------- # self.listbox = QGroupBox('Available subjects') self.HBoxUpperRightTab = QVBoxLayout(self.listbox) self.availableNiftiTab = QListWidget() self.availableNiftiTab.setSelectionMode( QAbstractItemView.ExtendedSelection) self.availableNiftiTab.itemSelectionChanged.connect( self.change_list_item) itemsTab = FileOperations.list_folders( self.niftidir, prefix=self.cfg['folders']['prefix']) self.add_available_items(self.availableNiftiTab, itemsTab) self.HBoxUpperRightTab.addWidget(self.availableNiftiTab) # Combine all Boxes for General Tab Layout self.LeftboxTab = QGroupBox() self.HBoxTabLeft = QVBoxLayout(self.LeftboxTab) self.HBoxTabLeft.addWidget(self.FolderboxTab) self.HBoxTabLeft.addStretch() self.HBoxTabLeft.addWidget(self.ActionsTab) self.tab.layout.addWidget(self.LeftboxTab) self.tab.layout.addWidget(self.listbox) self.lay.addWidget(self.tab)