def UI_Setup_AxesParms_Subcontainer(self):
     self.subcont_axesparms = QWidget()
     self.axes_widget = True
     self.vlay_axesparms.addWidget(self.subcont_axesparms)
     self.formlay_axesparms = QFormLayout(self.subcont_axesparms)
     if system() == "Darwin":
         set_formlay_options(self.formlay_axesparms)
    def __init__(self, config):
        super().__init__()
        # Load in the master config file if it exists; otherwise, make it
        self.config = config
        with open(
                Path(self.config["ProjectDir"]) / "JSON_LOGIC" /
                "ErrorsListing.json") as mainwin_err_reader:
            self.mwin_errs = json.load(
                mainwin_err_reader)  # Window Size and initial visual setup
        # self.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.resize(self.config["ScreenSize"][0] // 2.5,
                    self.config["ScreenSize"][1] // 2.25)
        self.cw = QWidget(self)
        self.setCentralWidget(self.cw)
        # Main Icon setup
        self.icon_main = QIcon(
            str(
                Path(self.config["ProjectDir"]) / "media" /
                "ExploreASL_logo.png"))
        self.setWindowIcon(self.icon_main)
        # Main Layout Setup
        self.mainlay = QVBoxLayout(self.cw)
        self.mainlay.setContentsMargins(0, 0, 0, 0)
        self.setWindowTitle("Explore ASL GUI")

        # Misc Players
        self.file_explorer = xASL_FileExplorer(self)

        # Set up each of the subcomponents of the main window program
        self.UI_Setup_Navigator()
        self.le_defaultdir.setCompleter(
            self.file_explorer.completer_current_dir)

        # Pre-initialize the main players
        self.parmsmaker = xASL_Parms(self)
        self.executor = xASL_Executor(self)
        self.plotter = xASL_Plotting(self)
        self.importer = xASL_GUI_Importer(self)

        # Menubar has to come after, as it references the main widgets
        self.UI_Setup_MenuBar()
        self.UI_Setup_ToolBar()

        # Save the configuration
        self.save_config()

        # Additional MacOS actions:
        if system() == "Darwin":
            set_formlay_options(self.formlay_defaultdir)
            self.vlay_navigator.setSpacing(5)
            self.file_explorer.mainlay.setSpacing(5)
    def __init__(self, parent_win=None):
        # Parent window is fed into the constructor to allow for communication with parent window devices
        super().__init__(parent=parent_win)
        self.config = self.parent().config

        with open(Path(self.config["ProjectDir"]) / "JSON_LOGIC" / "ToolTips.json") as plot_tips_reader:
            self.plot_tips = load(plot_tips_reader)["Plotting"]
        with open(Path(self.config["ProjectDir"]) / "JSON_LOGIC" / "ErrorsListing.json") as plot_errs_reader:
            self.plot_errs = load(plot_errs_reader)

        # Window Size and initial visual setup
        self.setMinimumSize(1920, 1000)
        self.cw = QWidget(self)
        self.setCentralWidget(self.cw)
        self.mainlay = QVBoxLayout(self.cw)
        self.setWindowTitle("Explore ASL - Post Processing Visualization")

        # Central Classes and their connections
        self.dtype_indicator = xASL_GUI_Datatype_Indicator(self)
        self.subsetter = xASL_GUI_Subsetter(self)
        self.loader = xASL_GUI_Data_Loader(self)
        self.loader.signal_dtype_was_changed.connect(self.subsetter.add_or_remove_subsetable_field)

        # Initialize blank givens
        self.fig_manager = None
        self.fig_artist = None
        self.spacer = QSpacerItem(0, 1, QSizePolicy.Preferred, QSizePolicy.Expanding)

        # Main Widgets setup
        self.UI_Setup_Docker()

        # QMenu
        self.menubar_main = QMenuBar(self)
        self.setMenuBar(self.menubar_main)
        self.menu_dock = self.menubar_main.addMenu("Dock")
        self.menu_dock.addAction("Show Dock", partial(self.dock.setVisible, True))

        # MacOS addtional actions
        if system() == "Darwin":
            set_formlay_options(self.formlay_directories, vertical_spacing=0)
            self.hlay_analysis_dir.setContentsMargins(5, 0, 5, 0)
            self.hlay_metadata.setContentsMargins(5, 0, 5, 0)
            self.vlay_directories.setSpacing(0)
            self.vlay_directories.addStretch(1)
            for widget in [self.cmb_figuretypeselection, self.cmb_stats_selection, self.cmb_atlas_selection,
                           self.cmb_pvc_selection]:
                widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
Example #4
0
    def __init__(self, parent):
        super().__init__(parent=parent)
        self.parent_cw = parent
        self.artist: xASL_GUI_MRIViewArtist = self.parent_cw.fig_artist
        self.mainlay = QVBoxLayout(self)
        self.spacer = QSpacerItem(0, 1, QSizePolicy.Preferred, QSizePolicy.Expanding)

        with open(Path(self.parent_cw.config["ProjectDir"]) / "JSON_LOGIC" / "GraphingParameters.json") as freader:
            parms = load(freader)
            self.all_dtypes = parms["all_dtypes"]
            self.numeric_dtypes = parms["numeric_dtypes"]
            self.categorical_dtypes = ["object", "category"]
            self.palettenames = parms["palettenames"]
            self.default_palette_idx = self.palettenames.index("Set1")

        # Key Variables
        analysis_dir = Path(self.parent_cw.le_analysis_dir.text())
        self.analysis_dir = self.parent_cw.le_analysis_dir.text()
        try:
            with open(next(analysis_dir.glob("DataPar*.json"))) as parms_reader:
                self.subject_regex = load(parms_reader)["subject_regexp"].strip("$^")

            self.axes_arg_x = ''  # Set to '' instead of None because the latter is not supported by lineedits
            self.axes_arg_y = ''
            self.axes_arg_hue = ''
            self.axes_widget = None

            self.subject_sessions = self.parent_cw.loader.loaded_wide_data["SUBJECT"].tolist()

            self.UI_Setup_Tabs()
            self.UI_Setup_MRIViewParameters()
            self.error_init = False

            # Additional MacOS actions
            if system() == "Darwin":
                set_formlay_options(self.formlay_mriparms, vertical_spacing=5)

        except (FileNotFoundError, StopIteration):
            QMessageBox.warning(self.parent_cw, self.parent_cw.plot_errs["MRIPlotNoDataPar"][0],
                                self.parent_cw.plot_errs["MRIPlotNoDataPar"][1], QMessageBox.Ok)
            self.error_init = True
        except KeyError:
            QMessageBox.warning(self.parent_cw, self.parent_cw.plot_errs["MRIPlotBadDataPar"][0],
                                self.parent_cw.plot_errs["MRIPlotBadDataPar"][1], QMessageBox.Ok)
            self.error_init = True
Example #5
0
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowFlag(Qt.Window)
        self.setWindowTitle("Subset the data")
        self.font_format = QFont()
        self.font_format.setPointSize(16)
        self.parent_cw = parent
        self.setMinimumSize(400, 200)

        # This will be used to keep track of columns that have already been added to the subset form layout
        self.current_subsettable_fields = {}
        self.do_not_add = ['', np.nan, 'nan']
        # Main Setup
        self.Setup_UI_MainWidgets()

        # Additional MacOS actions
        if system() == "Darwin":
            set_formlay_options(self.formlay_subsets)
    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.parent: xASL_GUI_FacetManager = parent  # The Facet Grid Manager
        self.setWindowFlag(Qt.Window)
        self.setWindowTitle("FacetGrid Ticklabels Settings")
        self.formlay_ticks = QFormLayout(self)
        if system() == "Darwin":
            set_formlay_options(self.formlay_ticks)

        # Widgets
        self.cmb_hfontsize = QComboBox()
        self.cmb_hfontsize.addItems(["xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"])
        self.cmb_hfontsize.setCurrentIndex(2)
        self.cmb_hfontweight = QComboBox()
        self.cmb_hfontweight.addItems(["normal", "bold", "extra bold"])
        self.cmb_hfontweight.setCurrentIndex(0)
        self.cmb_hfontstyle = QComboBox()
        self.cmb_hfontstyle.addItems(["normal", "italic"])
        self.cmb_hfontstyle.setCurrentIndex(0)
        self.cmb_halign = QComboBox()
        self.cmb_halign.addItems(["center", "left", "right"])
        self.cmb_halign.setCurrentIndex(2)
        self.spinbox_hrot = QSpinBox(maximum=90, minimum=0, value=0, singleStep=1)
        self.spinbox_halpha = QDoubleSpinBox(maximum=1, minimum=0, value=1, singleStep=0.01)

        for widget, description in zip([self.cmb_hfontsize, self.cmb_hfontweight, self.cmb_hfontstyle, self.cmb_halign,
                                        self.spinbox_hrot, self.spinbox_halpha],
                                       ["Font Size", "Font Weight", "Font Style", "Alignment", "Rotation", "Opaqueness"
                                        ]):
            self.formlay_ticks.addRow(description, widget)
            connect_widget_to_signal(widget, self.sendSignal_tickparms_updateplot)
            if system() == "Darwin":
                widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

        self.xtick_kwargs = {
            "fontsize": self.cmb_hfontsize.currentText,
            "fontweight": self.cmb_hfontweight.currentText,
            "fontstyle": self.cmb_hfontstyle.currentText,
            "horizontalalignment": self.cmb_halign.currentText,
            "rotation": self.spinbox_hrot.value,
            "alpha": self.spinbox_halpha.value
        }
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent = parent  # The Facet Grid Manager
        self.setWindowFlag(Qt.Window)
        self.setWindowTitle("Axis Labels Settings")
        self.setMinimumSize(300, 480)
        self.mainlay = QVBoxLayout(self)
        self.grp_xaxislabel = QGroupBox(title="X-Axis Label Settings")
        self.grp_yaxislabel = QGroupBox(title="Y-Axis Label Settings")
        self.grp_title = QGroupBox(title="Title Settings")
        self.formlay_xaxislabel = QFormLayout(self.grp_xaxislabel)
        self.formlay_yaxislabel = QFormLayout(self.grp_yaxislabel)
        self.formlay_title = QFormLayout(self.grp_title)

        self.Setup_UI_XAxisLabelParms()
        self.Setup_UI_YAxisLabelParms()
        self.Setup_UI_TitleParms()

        self.mainlay.addWidget(self.grp_title)
        self.mainlay.addWidget(self.grp_xaxislabel)
        self.mainlay.addWidget(self.grp_yaxislabel)

        # Additional MacOS actions
        if system() == "Darwin":
            set_formlay_options(self.formlay_title)
            set_formlay_options(self.formlay_xaxislabel)
            set_formlay_options(self.formlay_yaxislabel)
    def __init__(self, parent):
        super().__init__(parent=parent)
        self.parent_cw = parent
        self.artist: xASL_GUI_FacetArtist = self.parent_cw.fig_artist
        self.mainlay = QVBoxLayout(self)

        ##########################################
        # Widget Classes for more complex settings
        ##########################################
        # Prepare arguments for legend widget
        self.legend_widget = xASL_GUI_FacetLegend(self)
        self.ticklabels_widget = xASL_GUI_FacetTickWidget(self)
        self.labels_widget = xASL_GUI_FacetLabels(self)

        # Set up variables that the artist will reference when updating the plot
        self.axes_arg_x = ''  # Set to '' instead of None because the latter is not supported by lineedits
        self.axes_arg_y = ''
        self.axes_arg_hue = ''
        self.legend_kwargs = {}
        self.padding_kwargs = {}
        self.axes_widget = None

        with open(Path(self.parent_cw.config["ProjectDir"]) / "JSON_LOGIC" / "GraphingParameters.json") as freader:
            parms = load(freader)
            self.all_dtypes = parms["all_dtypes"]
            self.numeric_dtypes = parms["numeric_dtypes"]
            self.categorical_dtypes = ["object", "category"]
            self.palettenames = parms["palettenames"]
            self.default_palette_idx = self.palettenames.index("Set1")

        # Setup the widget
        self.UI_Setup_Tabs()
        self.UI_Setup_CommonParameters()
        self.UI_Setup_FigureParms()

        # Additional MacOS actions:
        if system() == "Darwin":
            set_formlay_options(self.formlay_commonparms)
            set_formlay_options(self.formlay_figparms)
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        # Main Widget attributes
        self.parent = parent
        self.config = parent.config
        self.target_dir: str = self.parent.le_modjob.text()
        self.setWindowFlag(Qt.Window)
        self.setWindowTitle("Alter participants.tsv contents")
        self.resize(600, 600)

        # Misc attributes
        self.default_tsvcols = []
        self.default_metacols = []
        self.df_metadata = pd.DataFrame()
        self.df_parttsv = pd.DataFrame()
        self.df_backupmetadata = pd.DataFrame()
        self.df_backupparttsv = pd.DataFrame()
        self.metadatafile_duringalter: str = ""

        # Create the Widget
        self.Setup_UI_TSValter()

        # Followup
        self.btn_reset.setEnabled(False)
        self.btn_altertsv.setEnabled(False)
        if not (Path(self.target_dir) / "participants_orig.tsv").exists():
            self.btn_revert.setEnabled(False)
        self.load_parttsvfile()

        # Addtional MacOS actions
        if system() == "Darwin":
            set_formlay_options(self.formlay_metadatafile, vertical_spacing=5)
            for le in [self.le_metadatafile, self.le_metadata_subjectcol, self.le_parttsv_subjectcol]:
                le.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            self.mainlay.setSpacing(3)
Example #10
0
    def __init__(self, parent_win=None):
        # Parent window is fed into the constructor to allow for communication with parent window devices
        super().__init__(parent=parent_win)
        self.config = parent_win.config
        with open(
                Path(self.config["ProjectDir"]) / "JSON_LOGIC" /
                "ErrorsListing.json") as import_err_reader:
            self.import_errs = json.load(import_err_reader)
        with open(
                Path(self.config["ProjectDir"]) / "JSON_LOGIC" /
                "ToolTips.json") as import_tooltips_reader:
            self.import_tips = json.load(import_tooltips_reader)["Importer"]
        del import_err_reader, import_tooltips_reader

        # Misc and Default Attributes
        self.labfont = QFont()
        self.labfont.setPointSize(16)
        self.lefont = QFont()
        self.lefont.setPointSize(12)
        self.rawdir = ''
        self.delim = "\\" if system() == "Windows" else "/"
        self.subject_regex = None
        self.visit_regex = None
        self.run_regex = None
        self.scan_regex = None
        self.run_aliases = OrderedDict()
        self.scan_aliases = dict.fromkeys(["ASL4D", "T1", "T2" "M0", "FLAIR"])
        self.cmb_runaliases_dict = {}
        self.threadpool = QThreadPool()
        self.import_summaries = []
        self.failed_runs = []
        self.import_workers = []

        # Window Size and initial visual setup
        self.setWindowTitle("ExploreASL - DICOM to NIFTI Import")
        self.cw = QWidget(self)
        self.setCentralWidget(self.cw)
        self.mainlay = QVBoxLayout(self.cw)
        self.mainlay.setContentsMargins(0, 5, 0, 0)
        self.resize(self.config["ScreenSize"][0] // 3.5,
                    self.height())  # Hack, window a bit too wide without this

        # The central tab widget and its setup
        self.central_tab_widget = QTabWidget()
        self.cont_import = QWidget()
        self.vlay_import = QVBoxLayout(self.cont_import)
        self.cont_dehybridizer = QWidget()
        self.vlay_dehybridizer = QVBoxLayout(self.cont_dehybridizer)
        self.vlay_dehybridizer.setContentsMargins(0, 0, 0, 0)
        self.central_tab_widget.addTab(self.cont_import, "Importer")
        self.central_tab_widget.addTab(self.cont_dehybridizer,
                                       "Expand Directories")
        self.mainlay.addWidget(self.central_tab_widget)

        # The importer UI setup
        self.mainsplit = QSplitter(Qt.Vertical)
        handle_path = str(
            Path(self.config["ProjectDir"]) / "media" /
            "3_dots_horizontal.svg")
        if system() == "Windows":
            handle_path = handle_path.replace("\\", "/")
        handle_style = 'QSplitter::handle {image: url(' + handle_path + ');}'
        self.mainsplit.setStyleSheet(handle_style)
        self.mainsplit.setHandleWidth(20)

        self.Setup_UI_UserSpecifyDirStuct()
        self.Setup_UI_UserSpecifyScanAliases()
        self.Setup_UI_UserSpecifyRunAliases()

        # Img_vars
        icon_import = QIcon(
            str(
                Path(self.config["ProjectDir"]) / "media" /
                "import_win10_64x64.png"))
        icon_terminate = QIcon(
            str(
                Path(self.config["ProjectDir"]) / "media" /
                "stop_processing.png"))

        # Bottom split: the progressbar and Run/Stop buttons
        self.cont_runbtns = QWidget()
        self.mainsplit.addWidget(self.cont_runbtns)
        self.vlay_runbtns = QVBoxLayout(self.cont_runbtns)
        self.progbar_import = QProgressBar(orientation=Qt.Horizontal,
                                           minimum=0,
                                           value=0,
                                           maximum=1,
                                           textVisible=True)
        if system() == "Darwin":
            self.progbar_import.setSizePolicy(QSizePolicy.Expanding,
                                              QSizePolicy.Preferred)
        self.btn_run_importer = xASL_PushButton(text="Convert DICOM to NIFTI",
                                                func=self.run_importer,
                                                font=self.labfont,
                                                fixed_height=50,
                                                enabled=False,
                                                icon=icon_import,
                                                icon_size=QSize(40, 40))
        self.btn_terminate_importer = xASL_PushButton(
            enabled=False,
            icon=icon_terminate,
            icon_size=QSize(40, 40),
            func=self.signal_stop_import.emit)
        self.vlay_runbtns.addStretch(1)
        for widget in [
                self.progbar_import, self.btn_run_importer,
                self.btn_terminate_importer
        ]:
            self.vlay_runbtns.addWidget(widget)
        self.vlay_import.addWidget(self.mainsplit)

        # The dehybridizer UI setup
        self.dehybridizer = xASL_GUI_Dehybridizer(self)
        self.vlay_dehybridizer.addWidget(self.dehybridizer)

        # Additional MacOS options
        if system() == "Darwin":
            set_formlay_options(self.formlay_rootdir, vertical_spacing=3)
            set_formlay_options(self.formlay_scanaliases)
            set_formlay_options(self.formlay_runaliases)
            self.vlay_dirstruct.setSpacing(5)
    def __init__(self, parent=None, default_mergedir=""):
        super().__init__(parent=parent)
        self.parent = parent
        self.setWindowFlag(Qt.Window)
        self.setWindowTitle("Explore ASL - Merge Study Directories")
        self.setMinimumSize(512, 512)
        self.mainlay = QVBoxLayout(self)

        # Misc Vars
        self.list_le_srcdirs: List[QLineEdit] = []
        self.list_hlay_srcdirs: List[QHBoxLayout] = []
        self.list_btn_srcdirs: List[QPushButton] = []

        # Grep 3 - Buttoms
        # Although these are the finishing widgets, they must be initialized first due to the automatic connections
        # made during instantiation
        self.btn_mergedirs = QPushButton("Merge Directories", clicked=self.merge_dirs)
        self.btn_mergedirs.setEnabled(False)
        btn_font = QFont()
        btn_font.setPointSize(16)
        self.btn_mergedirs.setFont(btn_font)
        self.btn_mergedirs.setMinimumHeight(50)
        merge_icon = QIcon(str(Path(self.parent.config["ProjectDir"]) / "media" / "merge_ios_100ax100.png"))
        self.btn_mergedirs.setIcon(merge_icon)
        self.btn_mergedirs.setIconSize(QSize(50, 50))

        # Group 1 - Overall settings
        self.grp_settings = QGroupBox(title="Merge Settings")
        self.formlay_settings = QFormLayout(self.grp_settings)
        (self.hlay_mergedst, self.le_mergedst,
         self.btn_mergedst) = self.make_le_btn_pair(le_placetxt="Specify the path to the merge directory",
                                                    btnfunc=self.select_dir, lefunc=self.can_merge)
        self.le_mergedst.setText(default_mergedir)
        self.spin_nsrcdirs = QSpinBox(minimum=2, singleStep=1)
        self.spin_nsrcdirs.valueChanged.connect(self.add_remove_srcdirs)
        self.chk_symlinks = QCheckBox(checked=True)
        self.chk_overwrite = QCheckBox(checked=False)
        self.formlay_settings.addRow(self.hlay_mergedst)
        for widget, desc, tipkey in zip([self.spin_nsrcdirs, self.chk_symlinks, self.chk_overwrite],
                                        ["Number of Studies to Merge", "Create Symlinks?", "Overwrite Existing?"],
                                        ["spin_nsrcdirs", "chk_symlinks", "chk_overwrite"]):
            self.formlay_settings.addRow(desc, widget)
            widget.setToolTip(self.parent.exec_tips["Modjob_MergeDirs"][tipkey])
            if system() == "Darwin":
                widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

        # Group 2 - Source Directories
        self.grp_srcdirs = QGroupBox(title="Directories to Merge")
        self.vlay_srcdirs_lvl1 = QVBoxLayout(self.grp_srcdirs)
        self.vlay_srcdirs_lvl1.setContentsMargins(0, 0, 0, 0)
        self.scroll_srcdirs = QScrollArea()
        self.cont_srcdirs = QWidget()
        self.vlay_srcdirs_lvl1.addWidget(self.scroll_srcdirs)
        self.scroll_srcdirs.setWidget(self.cont_srcdirs)
        self.scroll_srcdirs.setWidgetResizable(True)
        self.vlay_srcdirs_lvl2 = QVBoxLayout(self.cont_srcdirs)
        self.vlay_srcdirs_lvl2.addStretch(1)

        self.add_remove_srcdirs(2)

        # Adding groups to main layout and adding tooltips
        self.mainlay.addWidget(self.grp_settings)
        self.mainlay.addWidget(self.grp_srcdirs)
        self.mainlay.addWidget(self.btn_mergedirs)
        for widget, tipkey in zip([self.le_mergedst, self.btn_mergedirs],
                                  ["le_mergedst", "btn_mergedirs"]):
            widget.setToolTip(self.parent.exec_tips["Modjob_MergeDirs"][tipkey])
            if system() == "Darwin":
                widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

        # Additional MacOS actions
        if system() == "Darwin":
            set_formlay_options(self.formlay_settings, vertical_spacing=5)
            self.vlay_srcdirs_lvl2.setSpacing(3)
    def __init__(self, parent=None):
        # Main Standard Setup
        super(xASL_GUI_ModSidecars, self).__init__(parent=parent)
        self.parent = parent
        self.setWindowFlag(Qt.Window)
        self.setWindowTitle("Explore ASL - Modify JSON sidecars")
        self.setMinimumSize(400, 720)
        self.root_dir = Path(self.parent.le_modjob.text())
        self.mainlay = QVBoxLayout(self)

        self.log_file = self.root_dir / "Logs" / "JSON Sidecar Modlogs" / "JSON Sidecar Modification.log"
        self.log_file.parent.mkdir(exist_ok=True)
        if not self.log_file.exists():
            with open(self.log_file, "w") as toucher:
                toucher.write("")
        self.logger = logging.Logger(name="ModSidecarsLogger", level=logging.DEBUG)
        self.handler = logging.FileHandler(filename=str(self.log_file))
        fmt = logging.Formatter(fmt="%(message)s")
        self.handler.setFormatter(fmt)
        self.logger.addHandler(self.handler)

        # Grp 1 - Editing Jsons from a csv config file
        self.grp_fromfile = QGroupBox(title="Specify config from a CSV file", checkable=True, checked=True)
        self.grp_fromfile.clicked.connect(partial(self.ctrl_which_option, widget=self.grp_fromfile))
        self.grp_fromfile.clicked.connect(self.is_ready)
        self.formlay_fromfile = QFormLayout(self.grp_fromfile)
        self.hlay_fromfile = QHBoxLayout()
        self.le_fromfile = DandD_FileExplorer2LineEdit(acceptable_path_type="File",
                                                       supported_extensions=[".csv", ".tsv"])
        self.le_fromfile.setClearButtonEnabled(True)
        self.le_fromfile.textChanged.connect(self.is_ready)
        self.btn_fromfile = QPushButton("...", clicked=self.select_file)
        self.hlay_fromfile.addWidget(self.le_fromfile)
        self.hlay_fromfile.addWidget(self.btn_fromfile)
        self.formlay_fromfile.addRow(self.hlay_fromfile)

        # Grp 2 - Editing Jsons from a list of subjects and the indicated key + value
        self.grp_fromlist = QGroupBox(title="Specify subject list", checkable=True, checked=False)
        self.grp_fromlist.clicked.connect(partial(self.ctrl_which_option, widget=self.grp_fromlist))
        self.grp_fromlist.clicked.connect(self.is_ready)
        self.formlay_fromlist = QFormLayout(self.grp_fromlist)
        self.lab_subs = QLabel(text="Drag and drop the directories of the subjects\n"
                                    "whose json sidecars should be altered")
        self.lst_subs = DandD_FileExplorer2ListWidget()
        self.lst_subs.itemsAdded.connect(self.le_fromfile.clear)
        self.lst_subs.itemsAdded.connect(self.is_ready)
        self.btn_clearsubjects = QPushButton("Clear the above list", clicked=self.lst_subs.clear)
        self.btn_clearsubjects.clicked.connect(self.is_ready)
        self.le_key = QLineEdit(placeholderText="Specify the name of the field to be changed", clearButtonEnabled=True)
        self.le_key.textChanged.connect(self.is_ready)
        self.le_value = QLineEdit(placeholderText="Specify the value, if applicable", clearButtonEnabled=True)
        for widget in [self.lab_subs, self.lst_subs, self.btn_clearsubjects, self.le_key, self.le_value]:
            self.formlay_fromlist.addRow(widget)

        # Grp 3 - Other Settings
        self.grp_runsettings = QGroupBox(title="Run Settings")
        self.formlay_runsettings = QFormLayout(self.grp_runsettings)
        self.cmb_actiontype = QComboBox()
        self.cmb_actiontype.addItems(["Add/Edit a field", "Remove a field"])
        self.cmb_actiontype.currentIndexChanged.connect(self.is_ready)
        self.chk_asl = QCheckBox(checked=True)
        self.chk_asl.stateChanged.connect(self.is_ready)
        self.chk_m0 = QCheckBox(checked=False)
        self.chk_m0.stateChanged.connect(self.is_ready)
        self.chk_t1 = QCheckBox(checked=False)
        self.chk_t1.stateChanged.connect(self.is_ready)
        self.btn_run = QPushButton("Alter JSON sidecars", clicked=self.alter_json_sidecars)
        self.btn_run.setEnabled(False)
        for widget, desc in zip([self.cmb_actiontype,
                                 self.chk_asl, self.chk_m0, self.chk_t1, self.btn_run],
                                ["Which action to perform",
                                 "Do this for ASL JSONs", "Do this for M0 JSONs", "Do this for T1 JSONs", ""]):
            self.formlay_runsettings.addRow(widget) if desc == "" else self.formlay_runsettings.addRow(desc, widget)
            if system() == "Darwin":
                widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

        # Put it all together
        for grp in [self.grp_fromfile, self.grp_fromlist, self.grp_runsettings]:
            self.mainlay.addWidget(grp)

        # Add tooltips
        for tipkey, tiptext in self.parent.exec_tips["Modjob_ModSidecars"].items():
            getattr(self, tipkey).setToolTip(tiptext)

        if system() == "Darwin":
            set_formlay_options(self.formlay_fromfile)
            set_formlay_options(self.formlay_fromlist, vertical_spacing=3)
            set_formlay_options(self.formlay_runsettings)
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowFlag(Qt.Window)
        self.parent_cw = parent
        self.mainlay = QVBoxLayout(self)
        if parent is not None:
            self.config = parent.config

        # Misc Variables
        self.leftside_name2le = {}
        self.rightside_name2le = {}
        self.basenames = {}
        self.documented_rootdir = ""
        self.documented_exampledir = ""
        self.glob_path = ""
        self.threadpool = QThreadPool()
        self.paths = []
        self.translator = {}
        self.global_debt = 0

        # The setup
        with open(
                Path(self.config["ProjectDir"]) / "JSON_LOGIC" /
                "ToolTips.json") as dehyb_tips_reader:
            self.dehyb_tips = load(dehyb_tips_reader)["Dehybridizer"]
        with open(
                Path(self.config["ProjectDir"]) / "JSON_LOGIC" /
                "ErrorsListing.json") as dehyb_errs_reader:
            self.dehyb_errs = load(dehyb_errs_reader)
        self.SetupUI_UserSpecifyHybridDir()
        self.SetupUI_LeftandRightSections()
        movieplayer_path = Path(
            self.config["ProjectDir"]) / "media" / "processing.gif"
        self.expanddirs_movieplayer = xASL_ImagePlayer(
            movieplayer_path,
            alignment=Qt.AlignCenter,
            initial_movie_size=(self.config["ScreenSize"][0] // 51,
                                self.config["ScreenSize"][1] // 28))
        self.expanddirs_movieplayer.setMaximumHeight(
            self.config["ScreenSize"][1] // 24)

        self.chk_makebackup = QCheckBox(
            text="(STRONGLY RECOMMENDED) Make a backup of the source directory "
            "before folder restructuring?",
            checked=True)

        self.btn_run_dehybridizer = QPushButton("Expand Directory Level",
                                                clicked=self.run_dehybridizer)
        self.btnfont = QFont()
        self.btnfont.setPointSize(16)
        self.btn_run_dehybridizer.setFont(self.btnfont)
        self.btn_run_dehybridizer.setFixedHeight(50)
        self.btn_run_dehybridizer.setEnabled(False)
        icon = QIcon(
            str(
                Path(self.config["ProjectDir"]) / "media" /
                "unpack_win10_64x64.png"))
        self.btn_run_dehybridizer.setIcon(icon)
        self.btn_run_dehybridizer.setIconSize(QSize(40, 40))

        self.mainlay.addWidget(self.chk_makebackup)
        self.mainlay.addWidget(self.btn_run_dehybridizer)

        # Connect signals
        self.signal_startexpanding.connect(self.expand_directories)

        # MacOS additional actions
        if system() == "Darwin":
            set_formlay_options(self.formlay_basedirs, vertical_spacing=3)
        self.mainlay.setContentsMargins(5, 0, 5, 5)