Source code for resomapper.core.utils

import os
import re
import shutil
import zipfile
import tkinter as tk
from pathlib import Path
from tkinter import filedialog

from resomapper.core.misc import auto_innited_logger as lggr

import warnings

warnings.filterwarnings("ignore")

#### GUI UTILITIES ####


[docs] def select_directory(): """Allows selection of root directory showing a file explorer window. Returns: Path: full path to the selected directory.""" root = tk.Tk() root.withdraw() file_path = filedialog.askdirectory() root.destroy() # root.mainloop() return Path(file_path)
[docs] def select_file(): """Allows selection of a file showing a file explorer window. Returns: Path: full path to the selected file. """ root = tk.Tk() root.withdraw() file_path = filedialog.askopenfilename() root.destroy() # root.mainloop() return Path(file_path)
[docs] def select_modalities_to_process(): """Select from a checklist those modalities to be processed. Returns: list: list of str with the selected modalities. """ print(f"\n{lggr.ask}Select the modalities to process in the pop-up window.") # init tkinter root = tk.Tk() root.title("resomapper") row = 1 selected_modals = [] modalities = ["T1 map", "T2 map", "T2star map", "DTI", "MTR map"] # add label tk.Label(root, text="Select the modalities to process.").grid(row=0, sticky="w") # create checklist for modal in modalities: var = tk.IntVar() selected_modals.append(var) tk.Checkbutton(root, text=modal, variable=var).grid(row=row, sticky="w") row += 1 # create button to close the window tk.Button(root, text="OK", command=root.destroy).grid(row=6, sticky="w", pady=4) root.mainloop() return [ modalities[idx] for idx in range(len(modalities)) if selected_modals[idx].get() == 1 ]
[docs] def close_window(root): print("Closing window...") root.withdraw() root.destroy() root.quit()
[docs] def ask_user_parameters(parameter_dict): """Select values for different parameters in an emergent window. If a new value is selected it has to be of the same class as the predetermined value. Args: parameter_dict (dict): Dictionary containing the name of the different parameters as keys, along with a list containing the predetermined value for each one and a brief description. Returns: dict: Dictionary containing the selected values for each parameter name. """ root = tk.Tk() root.title("resomapper") values = {} def submit(): nonlocal values for parameter, info in parameter_dict.items(): value = entry_boxes[parameter].get() predetermined_value = info[0] value_type = type(predetermined_value) try: if value_type is bool: # For boolean types, check if the input is 'True' or 'False' value = str(value).lower() if value in ["true", "1"]: value = True elif value in ["false", "0"]: value = False else: raise ValueError else: value = value_type(value) if value_type is str and not value: raise ValueError values[parameter] = value except (ValueError, TypeError): error_label.config(text=f"Invalid input for {parameter}!") return root.destroy() root.quit() entry_boxes = {} for parameter, info in parameter_dict.items(): label_text = f"[{parameter}] {info[1]}" label = tk.Label(root, text=label_text) label.pack(padx=50, pady=(10, 0)) entry_box = tk.Entry(root) entry_box.insert(0, info[0]) # Set predetermined value as default entry_box.pack() entry_boxes[parameter] = entry_box error_label = tk.Label(root, text="", fg="red") error_label.pack() submit_button = tk.Button(root, text="OK", command=submit) submit_button.pack(pady=20) root.mainloop() try: return values except NameError: # TODO: change to raise specific error or check if this is neccesary print( f"\n\n{lggr.error}You have not selected any parameters. Exiting the program." ) exit()
#### CLI UTILITIES ####
[docs] def ask_user(question): """Prompts the user with a question and expects a 'y' or 'n' answer. Args: question (str): The question to be displayed to the user. Returns: bool: True if the user answers 'y', False if the user answers 'n'. """ while True: answer = input("\n" + lggr.ask + question + " [y/n]\n" + lggr.pointer).lower() if answer == "y": return True elif answer == "n": return False else: print(f"\n{lggr.error}Please, select one of the two options. [y/n]\n")
[docs] def ask_user_options(question, options): """Prompt a question to the user, display options with meanings, and return the selected option. Args: question (str): The question to ask the user. options (dict): A dictionary containing the available options as keys and their meanings as values. Returns: str: The selected option. """ while True: print("\n" + lggr.ask + question) print("Please, select one of the following options:") for option, meaning in options.items(): print(f"- [{option}]: {meaning}") user_input = input(lggr.pointer).lower() if user_input in options: return user_input else: print(f"\n{lggr.error}Please, select one of the specified otpions.\n")
#### FILE UTILITIES ####
[docs] def is_folder_and_not_occult(path): return ( (os.path.isdir(path)) and not os.path.basename(path).startswith(".") and os.path.basename(path) != "resomapper_output" )
[docs] def is_folder_or_zip_and_not_occult(path): return ( (os.path.isdir(path) or zipfile.is_zipfile(path)) and not os.path.basename(path).startswith(".") and os.path.basename(path) != "resomapper_output" )
[docs] def is_nii(filename): return filename.endswith(".nii") or filename.endswith(".nii.gz")
[docs] def get_modality_nii_acq(nii_file): if "_T2map_acq" in nii_file: return "T2 map" elif "_T2starmap_acq" in nii_file: return "T2star map" elif "_T1map_acq" in nii_file: return "T1 map" elif "_dwi" in nii_file: # TODO: allow choice between DTI or ADC return "DTI" elif "_MTon" in nii_file: return "MTR map" else: return None
[docs] def get_modality_nii_map(nii_file): if "_T2map" in nii_file: return "T2 map" elif "_T2starmap" in nii_file: return "T2star map" elif "_T1map" in nii_file: return "T1 map" elif "_DTI" in nii_file: return "DTI" elif "_MTR" in nii_file: return "MTR map" else: return None
[docs] def get_acq(filename): pattern = r"acq-[^_]+_" result = re.search(pattern, filename) return result.group().rstrip("_")
[docs] def get_nifti_filenames(sourcedata_dir, study, acq): matching_files = [] regex_pattern = rf"{re.escape(study)}.*{re.escape(acq)}.*\.nii" pattern = re.compile(regex_pattern) for root, dirs, files in os.walk(sourcedata_dir): matching_files.extend( os.path.join(root, filename) for filename in files if pattern.search(filename) ) return matching_files
[docs] def rename_associated_files(nifti_filename): if "_preproc" in nifti_filename: json_file = nifti_filename.replace("_preproc.nii.gz", ".json") bval_file = nifti_filename.replace("_preproc.nii.gz", ".bval") bvec_file = nifti_filename.replace("_preproc.nii.gz", ".bvec") for associated_file in [json_file, bval_file, bvec_file]: if os.path.exists(associated_file): shutil.copy( associated_file, nifti_filename.replace("nii.gz", associated_file.split(".")[-1]), )
[docs] def create_readme(output_dir): # TODO pass