Пример #1
0
    def __init__(self, parent, dicomDatabase=None, detailsPopup=None):
        if dicomDatabase:
            self.dicomDatabase = dicomDatabase
        else:
            self.dicomDatabase = slicer.dicomDatabase
        self.detailsPopup = detailsPopup
        self.recentSeries = []
        self.widget = qt.QWidget(parent)
        self.widget.name = 'recentActivityWidget'
        self.layout = qt.QVBoxLayout()
        self.widget.setLayout(self.layout)

        self.statusLabel = qt.QLabel(self.widget)
        self.layout.addWidget(self.statusLabel)
        self.statusLabel.text = 'No inserts in the past hour'

        self.scrollArea = qt.QScrollArea()
        self.layout.addWidget(self.scrollArea)
        self.listWidget = qt.QListWidget()
        self.listWidget.name = 'recentActivityListWidget'
        self.scrollArea.setWidget(self.listWidget)
        self.scrollArea.setWidgetResizable(True)
        self.listWidget.setProperty('SH_ItemView_ActivateItemOnSingleClick', 1)
        self.listWidget.connect('activated(QModelIndex)', self.onActivated)

        self.refreshButton = qt.QPushButton(self.widget)
        self.layout.addWidget(self.refreshButton)
        self.refreshButton.text = 'Refresh'
        self.refreshButton.connect('clicked()', self.update)

        self.tags = {}
        self.tags['seriesDescription'] = "0008,103e"
        self.tags['patientName'] = "0010,0010"
Пример #2
0
    def createMainMenuDockWidget(self):
        # Set up main frame
        self.mainMenuDockWidget = qt.QDockWidget(self.parent)
        self.mainMenuDockWidget.setTitleBarWidget(
            qt.QWidget())  # hide title bar
        self.mainMenuDockWidget.setObjectName('FirstTaskPanel')
        self.mainMenuDockWidget.setWindowTitle('First Task')

        # self.mainMenuDockWidget.setStyleSheet(self.styleSheet)
        self.mainMenuDockWidget.setFeatures(
            qt.QDockWidget.DockWidgetMovable
            | qt.QDockWidget.DockWidgetFloatable)  # not closable

        mainWindow = slicer.util.mainWindow()
        self.mainMenuDockWidget.setParent(mainWindow)

        # Setup scroll area
        self.mainFrame = qt.QFrame(self.mainMenuDockWidget)
        self.mainLayout = qt.QVBoxLayout(self.mainFrame)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.spacing = 0

        self.mainScrollArea = qt.QScrollArea(self.mainFrame)
        self.mainScrollArea.widgetResizable = True
        self.mainScrollArea.sizeAdjustPolicy = qt.QAbstractScrollArea.AdjustToContentsOnFirstShow
        self.mainScrollArea.horizontalScrollBarPolicy = qt.Qt.ScrollBarAlwaysOff
        self.mainScrollArea.setSizePolicy(
            qt.QSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding))
        self.mainScrollArea.frameShape = qt.QFrame.NoFrame

        # Slicelet panel
        self.sliceletPanel = qt.QFrame(self.mainFrame)
        self.sliceletPanelLayout = qt.QVBoxLayout(self.sliceletPanel)
        self.sliceletPanelLayout.spacing = 0
        self.mainScrollArea.setWidget(self.sliceletPanel)

        self.mainLayout.addWidget(self.mainScrollArea)
        self.mainMenuDockWidget.setWidget(self.mainFrame)

        # Setup feature panel
        self.setupUi()

        self.sliceletPanelLayout.addStretch(1)

        self.setupSettingsPanel()

        self.mainScrollArea.minimumWidth = self.sliceletPanel.sizeHint.width()
Пример #3
0
    def remakeWidget(self):
        """ Ideally, this would be unncessary.  But, since QScrollArea doesn't
            dynamically update, we have to update this ourselves.
        """

        #-------------------
        # Clear all of the inner widgets
        #-------------------
        if self.innerWidget:
            del self.innerWidget
        if self.innerWidgetLayout:
            del self.innerWidgetLayout
        if self.scrollWidget:
            del self.scrollWidget

        #-------------------
        # Reset the inner widget layout
        #-------------------
        self.innerWidgetLayout = qt.QFormLayout()
        self.innerWidgetLayout.setVerticalSpacing(10)

        #-------------------
        # Sort download rows by their queue positions,
        # add them to the innerWidgetLayout.
        #-------------------
        sortedRows = [None] * len(self.downloadRows)
        for key, item in self.downloadRows.iteritems():
            #print len(sortedRows), item['queuePosition']
            sortedRows[item['queuePosition']] = key
        for key in sortedRows:
            self.innerWidgetLayout.addRow(self.downloadRows[key]['widget'])

        #-------------------
        # Remake the inner widget
        #-------------------
        self.innerWidget = qt.QWidget()
        self.innerWidget.setLayout(self.innerWidgetLayout)
        self.innerWidget.setObjectName('innerWidget')
        self.innerWidget.setStyleSheet('#innerWidget {width: 100%;}')
        self.innerWidget.setSizePolicy(qt.QSizePolicy.MinimumExpanding,
                                       qt.QSizePolicy.MinimumExpanding)

        #-------------------
        # Remake the scroll widget
        #-------------------
        self.scrollWidget = qt.QScrollArea()
        self.scrollWidget.setWidget(self.innerWidget)
        self.scrollWidget.verticalScrollBar().setStyleSheet('width: 15px')
        self.scrollWidget.setObjectName('scrollWidget')
        self.scrollWidget.setStyleSheet('#scrollWidget {border: none}')
        self.scrollWidget.setSizePolicy(qt.QSizePolicy.MinimumExpanding,
                                        qt.QSizePolicy.MinimumExpanding)
        self.scrollWidget.setWidgetResizable(True)

        #-------------------
        # Clear the master widget and add the new contents.
        #-------------------
        delWidget = self.masterLayout.itemAt(0)
        while (delWidget):
            self.masterLayout.removeItem(delWidget)
            del delWidget
            delWidget = self.masterLayout.itemAt(0)

        self.innerWidget.update()
        self.masterLayout.addRow(self.scrollWidget)
        self.setSizePolicy(qt.QSizePolicy.MinimumExpanding,
                           qt.QSizePolicy.MinimumExpanding)

        calcHeight = (self.rowWidgetHeight + 12) * len(self.downloadRows)
        if calcHeight < 800:
            self.setMinimumHeight(calcHeight)
        else:
            self.setMinimumHeight(800)

        self.update()
Пример #4
0
    def setup(self):
        # Instantiate and connect widgets ...

        # reload button
        # (use this during development, but remove it when delivering
        #  your module to users)
        self.reloadButton = qt.QPushButton("Reload")
        self.reloadButton.toolTip = "Reload this module."
        self.reloadButton.name = "MurineTrial Reload"
        self.layout.addWidget(self.reloadButton)
        self.reloadButton.connect('clicked()', self.onReload)

        # reload and test button
        # (use this during development, but remove it when delivering
        #  your module to users)
        self.reloadAndTestButton = qt.QPushButton("Reload and Test")
        self.reloadAndTestButton.toolTip = "Reload this module and then run the self tests."
        self.layout.addWidget(self.reloadAndTestButton)
        self.reloadAndTestButton.connect('clicked()', self.onReloadAndTest)

        # Collapsible button
        measurementsCollapsibleButton = ctk.ctkCollapsibleButton()
        measurementsCollapsibleButton.text = "Materials"
        self.layout.addWidget(measurementsCollapsibleButton)

        # Layout within the measurements collapsible button
        measurementsFormLayout = qt.QFormLayout(measurementsCollapsibleButton)

        # list of materials in the trial
        measurementsFormLayout.addWidget(qt.QLabel("Individual Segmentations"))
        self.materialsScrollArea = qt.QScrollArea()
        measurementsFormLayout.addWidget(self.materialsScrollArea)
        self.materialsListWidget = qt.QListWidget()
        self.materialsScrollArea.setWidget(self.materialsListWidget)
        self.materialsScrollArea.setWidgetResizable(True)
        self.materialsListWidget.setProperty(
            'SH_ItemView_ActivateItemOnSingleClick', 1)
        self.materialsListWidget.connect('activated(QModelIndex)',
                                         self.onMaterialActivated)
        # populate it
        materialKeys = self.logic.materials.keys()
        materialKeys.sort()
        for materialKey in materialKeys:
            self.materialsListWidget.addItem(materialKey)

        # list of sampleIDs in the trial
        measurementsFormLayout.addWidget(
            qt.QLabel("Segmentation Comparisions"))
        self.sampleIDsScrollArea = qt.QScrollArea()
        measurementsFormLayout.addWidget(self.sampleIDsScrollArea)
        self.sampleIDsListWidget = qt.QListWidget()
        self.sampleIDsScrollArea.setWidget(self.sampleIDsListWidget)
        self.sampleIDsScrollArea.setWidgetResizable(True)
        self.sampleIDsListWidget.setProperty(
            'SH_ItemView_ActivateItemOnSingleClick', 1)
        self.sampleIDsListWidget.connect('activated(QModelIndex)',
                                         self.onSampleIDActivated)
        # populate it
        for sampleID in self.logic.gigSegComparisonSampleIDs():
            self.sampleIDsListWidget.addItem(sampleID)

        # list of retestIDs in the trial
        measurementsFormLayout.addWidget(qt.QLabel("Repeatability Tests"))
        self.retestIDsScrollArea = qt.QScrollArea()
        measurementsFormLayout.addWidget(self.retestIDsScrollArea)
        self.retestIDsListWidget = qt.QListWidget()
        self.retestIDsScrollArea.setWidget(self.retestIDsListWidget)
        self.retestIDsScrollArea.setWidgetResizable(True)
        self.retestIDsListWidget.setProperty(
            'SH_ItemView_ActivateItemOnSingleClick', 1)
        self.retestIDsListWidget.connect('activated(QModelIndex)',
                                         self.onRetestActivated)
        # populate it
        for retestID in self.logic.retestComparisonSampleIDs():
            self.retestIDsListWidget.addItem(retestID)

        # process all  button
        processAllButton = qt.QPushButton("Process All")
        processAllButton.toolTip = "Loads all subjecs at all timepoints."
        measurementsFormLayout.addWidget(processAllButton)
        processAllButton.connect('clicked(bool)', self.logic.processAll)

        # results area
        self.resultsView = qt.QWebView()
        self.resultsView.minimumSize = qt.QSize(100, 100)
        policy = qt.QSizePolicy()
        policy.setHorizontalPolicy(qt.QSizePolicy.Ignored)
        self.resultsView.setSizePolicy(policy)
        self.layout.addWidget(self.resultsView)

        # Add vertical spacer
        self.layout.addStretch(1)
Пример #5
0
    def setup(self):
        #Instantiate and Connect Widgets
        #################################################
        #HeterogeneityCAD Inputs Collapsible Button

        self.inputHeterogeneityCADCollapsibleButton = ctk.ctkCollapsibleButton(
        )
        self.inputHeterogeneityCADCollapsibleButton.text = "HeterogeneityCAD Input"
        self.layout.addWidget(self.inputHeterogeneityCADCollapsibleButton)
        self.inputHeterogeneityCADLayout = qt.QFormLayout(
            self.inputHeterogeneityCADCollapsibleButton)

        ##Input Volume as a PET/CT/MRI image or parameter map converted to a volume
        self.inputVolHetFrame = qt.QFrame(
            self.inputHeterogeneityCADCollapsibleButton)
        self.inputVolHetFrame.setLayout(qt.QHBoxLayout())
        self.inputHeterogeneityCADLayout.addRow(self.inputVolHetFrame)
        # label for selecting individual node
        self.inputVolHet = qt.QLabel("Input Node: ", self.inputVolHetFrame)
        self.inputVolHetFrame.layout().addWidget(self.inputVolHet)
        # select individual nodes
        self.inputSelectorVolHet = slicer.qMRMLNodeComboBox(
            self.inputVolHetFrame)
        self.inputSelectorVolHet.nodeTypes = (("vtkMRMLScalarVolumeNode"), "")
        self.inputSelectorVolHet.selectNodeUponCreation = False
        self.inputSelectorVolHet.addEnabled = False
        self.inputSelectorVolHet.removeEnabled = False
        self.inputSelectorVolHet.setMRMLScene(slicer.mrmlScene)
        self.inputVolHetFrame.layout().addWidget(self.inputSelectorVolHet)
        # add Data Node button
        self.addDataNodeButton = qt.QPushButton("Add Node",
                                                self.inputVolHetFrame)
        self.addDataNodeButton.objectName = 'AddDataNodeButton'
        self.addDataNodeButton.setToolTip("Add a Node to Queue")
        self.addDataNodeButton.connect('clicked()',
                                       self.onAddDataNodeButtonClicked)
        self.inputVolHetFrame.layout().addWidget(self.addDataNodeButton)

        ## data nodes Frame
        self.dataNodesFrame = ctk.ctkCollapsibleGroupBox(
            self.inputHeterogeneityCADCollapsibleButton)
        self.dataNodesFrame.title = "Nodes List"
        self.dataNodesFrame.collapsed = False
        self.dataNodesFrame.setLayout(qt.QVBoxLayout())
        # all buttons frame
        self.allButtonsFrame = qt.QFrame(
            self.inputHeterogeneityCADCollapsibleButton)
        self.allButtonsFrame.objectName = 'AllButtonsFrameButton'
        self.allButtonsFrame.setLayout(qt.QVBoxLayout())
        self.inputHeterogeneityCADLayout.addRow(self.dataNodesFrame,
                                                self.allButtonsFrame)
        # Data Nodes view
        # Use list view here with scroll area widget.
        self.dataScrollArea = qt.QScrollArea()
        self.dataNodesListWidget = qt.QListWidget()
        self.dataNodesListWidget.name = 'dataNodesListWidget'
        self.dataScrollArea.setWidget(self.dataNodesListWidget)
        self.dataNodesListWidget.resize(350, 100)
        self.dataNodesFrame.layout().addWidget(self.dataScrollArea)
        #self.listWidget.setProperty('SH_ItemView_ActivateItemOnSingleClick', 1)
        #self.listWidget.connect('activated(QModelIndex)', self.onActivated)
        # add all Data Nodes from scene button
        self.addAllDataNodesButton = qt.QPushButton("Add All Nodes From Scene",
                                                    self.allButtonsFrame)
        self.addAllDataNodesButton.objectName = 'AddAllDataNodesButton'
        self.addAllDataNodesButton.setToolTip(
            "Add all Nodes from the Scene to Queue")
        self.addAllDataNodesButton.connect('clicked()',
                                           self.onAddAllDataNodesButtonClicked)
        self.allButtonsFrame.layout().addWidget(self.addAllDataNodesButton)
        # remove single Data Node
        self.removeDataNodeButton = qt.QPushButton("Remove Node",
                                                   self.allButtonsFrame)
        self.removeDataNodeButton.objectName = 'RemoveDataNodeButton'
        self.removeDataNodeButton.setToolTip(
            "Removes Selected Node from the Queue.")
        self.removeDataNodeButton.connect('clicked()',
                                          self.onRemoveDataNodeButtonClicked)
        self.allButtonsFrame.layout().addWidget(self.removeDataNodeButton)
        # remove all Data Nodes button
        self.removeAllDataNodesButton = qt.QPushButton("Remove All Nodes",
                                                       self.allButtonsFrame)
        self.removeAllDataNodesButton.objectName = 'RemoveAllDataNodesButton'
        self.removeAllDataNodesButton.setToolTip(
            "Removes All Nodes from the Queue.")
        self.removeAllDataNodesButton.connect(
            'clicked()', self.onRemoveAllDataNodesButtonClicked)
        self.allButtonsFrame.layout().addWidget(self.removeAllDataNodesButton)

        # Use Label Map as ROI(segmentation output or user-selected ROI)
        self.inputLabelROIFrame = qt.QFrame(
            self.inputHeterogeneityCADCollapsibleButton)
        self.inputLabelROIFrame.setLayout(qt.QHBoxLayout())
        self.inputHeterogeneityCADLayout.addRow(self.inputLabelROIFrame)
        # Enable Input Label Map as ROI
        self.inputLabelROI = qt.QLabel("Label Map ROI: ",
                                       self.inputLabelROIFrame)
        self.inputLabelROIFrame.layout().addWidget(self.inputLabelROI)
        # Select Input Label Map as ROI
        self.inputSelectorLabel = slicer.qMRMLNodeComboBox(
            self.inputLabelROIFrame)
        self.inputSelectorLabel.nodeTypes = (("vtkMRMLLabelMapVolumeNode"), "")
        self.inputSelectorLabel.selectNodeUponCreation = False
        self.inputSelectorLabel.renameEnabled = True
        self.inputSelectorLabel.removeEnabled = False
        self.inputSelectorLabel.noneEnabled = True
        self.inputSelectorLabel.addEnabled = False
        self.inputSelectorLabel.setMRMLScene(slicer.mrmlScene)
        self.inputLabelROIFrame.layout().addWidget(self.inputSelectorLabel)

        #End HeterogeneityCAD Inputs Collapsible Button
        #################################################
        #HeterogeneityCAD Features Collapsible Button

        self.HeterogeneityCADCollapsibleButton = ctk.ctkCollapsibleButton()
        self.HeterogeneityCADCollapsibleButton.text = "HeterogeneityCAD Features Selection"
        self.layout.addWidget(self.HeterogeneityCADCollapsibleButton)
        self.featuresHeterogeneityCADLayout = qt.QFormLayout(
            self.HeterogeneityCADCollapsibleButton)

        # auto-generate QTabWidget Tabs and QCheckBoxes (subclassed in FeatureWidgetHelperLib)
        self.tabsFeatureClasses = FeatureWidgetHelperLib.CheckableTabWidget()
        self.featuresHeterogeneityCADLayout.addRow(self.tabsFeatureClasses)

        gridWidth, gridHeight = 3, 9
        for featureClass in self.featureClassKeys:
            # by default, features from the following features classes are checked:
            if featureClass in [
                    "Node Information", "First-Order Statistics",
                    "Morphology and Shape", "Texture: GLCM", "Texture: GLRL"
            ]:
                check = True
            else:
                check = False
            tabFeatureClass = qt.QWidget()
            tabFeatureClass.setLayout(qt.QGridLayout())
            #featureList = (feature for feature in self.featureClassKeys[featureClass])
            gridLayoutCoordinates = ((row, col) for col in range(gridWidth)
                                     for row in range(gridHeight))
            for featureName in self.featureClassKeys[featureClass]:
                row, col = next(gridLayoutCoordinates, None)
                if featureName is None or row is None or col is None:
                    break
                featureCheckboxWidget = FeatureWidgetHelperLib.FeatureWidget()
                featureCheckboxWidget.Setup(featureName=featureName,
                                            checkStatus=check)

                tabFeatureClass.layout().addWidget(featureCheckboxWidget, row,
                                                   col)
                self.featureWidgets[featureClass].append(featureCheckboxWidget)
            self.tabsFeatureClasses.addTab(tabFeatureClass,
                                           featureClass,
                                           self.featureWidgets[featureClass],
                                           checkStatus=check)

        self.tabsFeatureClasses.setCurrentIndex(1)

        # note: try using itertools list merging with lists of GLRL diagonal
        self.heterogeneityFeatureWidgets = list(
            itertools.chain.from_iterable(self.featureWidgets.values()))
        self.classes = list(self.featureWidgets.keys())
        # or reduce(lambda x,y: x+y, self.featureWidgets.values())

        ########## Parameter options
        # add parameters for top-level feature classes
        self.tabsFeatureClasses.addParameter("Geometrical Measures",
                                             "Extrusion Parameter 1")
        self.tabsFeatureClasses.addParameter("Texture: GLCM",
                                             "GLCM Matrix Parameter 1")
        self.tabsFeatureClasses.addParameter("Texture: GLRL",
                                             "GLRL Matrix Parameter 1")

        # compile dict of feature classes with parameter names and values
        self.featureClassParametersDict = collections.OrderedDict()
        for featureClassWidget in self.tabsFeatureClasses.getFeatureClassWidgets(
        ):
            featureClassName = featureClassWidget.getName()
            self.featureClassParametersDict[
                featureClassName] = collections.OrderedDict()
            self.updateFeatureClassParameterDict(0, featureClassWidget)
            for parameterName in featureClassWidget.widgetMenu.parameters:
                featureClassWidget.getParameterEditWindow(
                    parameterName).connect(
                        'intValueChanged(int)',
                        lambda intValue, featureClassWidget=featureClassWidget:
                        self.updateFeatureClassParameterDict(
                            intValue, featureClassWidget))

        # add parameters for individual features
        for featureWidget in self.heterogeneityFeatureWidgets:
            if featureWidget.getName() == "Voxel Count":
                featureWidget.addParameter("Example Parameter 1")
                featureWidget.addParameter("Example Parameter 2")
            if featureWidget.getName() == "Gray Levels":
                featureWidget.addParameter("Example Parameter 1-GL")
                featureWidget.addParameter("Example Parameter 2-GL")

        # compile dict of features with parameter names and values
        self.featureParametersDict = collections.OrderedDict()
        for featureWidget in self.heterogeneityFeatureWidgets:
            featureName = featureWidget.getName()
            self.featureParametersDict[featureName] = collections.OrderedDict()
            self.updateFeatureParameterDict(0, featureWidget)
            for parameterName in featureWidget.widgetMenu.parameters:
                featureWidget.getParameterEditWindow(parameterName).connect(
                    'intValueChanged(int)',
                    lambda intValue, featureWidget=featureWidget: self.
                    updateFeatureParameterDict(intValue, featureWidget)
                )  #connect intvaluechanged signals to updateParamaterDict function
        ##########

        # Feature Buttons Frame and Layout
        self.featureButtonFrame = qt.QFrame(
            self.HeterogeneityCADCollapsibleButton)
        self.featureButtonFrame.setLayout(qt.QHBoxLayout())
        self.featuresHeterogeneityCADLayout.addRow(self.featureButtonFrame)

        # HeterogeneityCAD Apply Button
        self.HeterogeneityCADButton = qt.QPushButton("Apply HeterogeneityCAD",
                                                     self.featureButtonFrame)
        self.HeterogeneityCADButton.toolTip = "Analyze input volume using selected Heterogeneity Features."
        self.featureButtonFrame.layout().addWidget(self.HeterogeneityCADButton)
        self.HeterogeneityCADButton.connect(
            'clicked()', self.onHeterogeneityCADButtonClicked)

        # Save Button
        self.saveButton = qt.QPushButton("Save to File",
                                         self.featureButtonFrame)
        self.saveButton.toolTip = "Save analyses to CSV file"
        self.saveButton.enabled = False
        self.featureButtonFrame.layout().addWidget(self.saveButton)
        self.saveButton.connect('clicked()', self.onSave)

        #End HeterogeneityCAD Features Collapsible Button
        #################################################
        #Feature Summary Chart

        #Complete chart options, export list of user-selected options identified via connections to labelstatistics module
        self.chartOptions = ("Count", "Volume mm^3", "Volume cc", "Min", "Max",
                             "Mean", "StdDev")
        self.StatisticsChartCollapsibleButton = ctk.ctkCollapsibleButton()
        self.StatisticsChartCollapsibleButton.text = "HeterogeneityCAD Features Summary"
        self.layout.addWidget(self.StatisticsChartCollapsibleButton)
        self.StatisticsChartLayout = qt.QFormLayout(
            self.StatisticsChartCollapsibleButton)
        self.StatisticsChartCollapsibleButton.collapsed = False

        #Table View to display Label statistics
        self.view = qt.QTableView(self.StatisticsChartCollapsibleButton)
        self.view.sortingEnabled = True
        self.StatisticsChartLayout.addWidget(self.view)
        self.view.minimumHeight = 175
    def setup(self):
        #---------------------------------------------------------
        # Batch Covert DICOM to NRRD
        self.BatchConvertCollapsibleButton = ctk.ctkCollapsibleButton()
        self.BatchConvertCollapsibleButton.text = "Batch convert DICOM to NRRD or NIFTI"
        self.layout.addWidget(self.BatchConvertCollapsibleButton)
        self.BatchConvertCollapsibleButton.collapsed = False
        self.BatchConvertFormLayout = qt.QFormLayout(
            self.BatchConvertCollapsibleButton)

        # Input 1: Input Directory selector
        self.input1Selector = qt.QLabel("Input Directory (DICOM):  ",
                                        self.BatchConvertCollapsibleButton)
        self.input1Button = qt.QPushButton(
            "Select Main Input Directory of DICOM files")
        self.input1Button.toolTip = "Select main directory with DICOM files (folder names are patient names)"
        self.input1Button.enabled = True
        self.BatchConvertFormLayout.addRow(self.input1Selector,
                                           self.input1Button)

        # Input 2: Output Directory selector
        self.input2Selector = qt.QLabel("Output Directory:  ",
                                        self.BatchConvertCollapsibleButton)
        self.input2Button = qt.QPushButton("Select Main Output Directory")
        self.input2Button.toolTip = "Select main directory for output NRRD or NIFTI files (folder names are patient names)"
        self.input2Button.enabled = True
        self.BatchConvertFormLayout.addRow(self.input2Selector,
                                           self.input2Button)

        # RTStruct Conversion
        self.contourConvertLabel = qt.QLabel(
            "Convert DICOM-RT Contours:  ", self.BatchConvertCollapsibleButton)

        self.contourConvertSelectFrame = qt.QFrame(
            self.BatchConvertCollapsibleButton)
        self.contourConvertSelectFrame.setLayout(qt.QHBoxLayout())
        self.contourConvertGroup = qt.QButtonGroup(
            self.contourConvertSelectFrame)
        self.noConvertButton = qt.QRadioButton("None")
        self.noConvertButton.checked = True
        self.allConvertButton = qt.QRadioButton("All")
        self.selectConvertButton = qt.QRadioButton("Select")
        self.contourConvertGroup.addButton(self.noConvertButton)
        self.contourConvertGroup.addButton(self.allConvertButton)
        self.contourConvertGroup.addButton(self.selectConvertButton)
        self.contourConvertSelectFrame.layout().addWidget(self.noConvertButton)
        self.contourConvertSelectFrame.layout().addWidget(
            self.allConvertButton)
        self.contourConvertSelectFrame.layout().addWidget(
            self.selectConvertButton)
        self.BatchConvertFormLayout.layout().addRow(
            self.contourConvertLabel, self.contourConvertSelectFrame)

        self.contourConvertCollapsibleButton = ctk.ctkCollapsibleButton(
            self.BatchConvertCollapsibleButton)
        self.contourConvertCollapsibleButton.text = "Select Contours to Convert"
        self.BatchConvertFormLayout.addRow(
            self.contourConvertCollapsibleButton)
        self.contourConvertCollapsibleButton.enabled = False
        self.contourConvertCollapsibleButton.collapsed = True
        self.contourConvertFormLayout = qt.QFormLayout(
            self.contourConvertCollapsibleButton)

        # Keywords to catch RTStruct Structures
        self.contoursFrame = qt.QFrame(self.contourConvertCollapsibleButton)
        self.contoursFrame.setLayout(qt.QVBoxLayout())
        self.contoursFrame.setFrameStyle(2)
        self.contourConvertFormLayout.addWidget(self.contoursFrame)

        self.addContourButton = qt.QPushButton(
            "Add Contour to convert from RTStruct (separate keywords by comma)",
            self.contoursFrame)
        self.keywordsScrollWidget = qt.QWidget()
        self.keywordsScrollWidget.setLayout(qt.QFormLayout())
        self.keywordsScroll = qt.QScrollArea()
        self.keywordsScroll.setWidgetResizable(True)
        self.keywordsScroll.setWidget(self.keywordsScrollWidget)
        self.contoursFrame.layout().addWidget(self.keywordsScroll)
        self.contoursFrame.layout().addWidget(self.addContourButton)

        # Settings Collapsible Button
        self.settingsCollapsibleButton = ctk.ctkCollapsibleButton()
        self.settingsCollapsibleButton.text = "Settings"
        self.settingsCollapsibleButton.setLayout(qt.QFormLayout())
        self.layout.addWidget(self.settingsCollapsibleButton)

        # NRRD or NIFTI Radio Buttons
        self.fileFormatLabel = qt.QLabel("Output File Format:  ",
                                         self.settingsCollapsibleButton)

        self.fileFormatSelectFrame = qt.QFrame(self.settingsCollapsibleButton)
        self.fileFormatSelectFrame.setLayout(qt.QFormLayout())
        self.fileFormatGroup = qt.QButtonGroup(self.fileFormatSelectFrame)
        self.nrrdButton = qt.QRadioButton("NRRD")
        self.nrrdButton.checked = True
        self.niftiButton = qt.QRadioButton("NIFTI")
        self.fileFormatGroup.addButton(self.nrrdButton)
        self.fileFormatGroup.addButton(self.niftiButton)
        self.fileFormatSelectFrame.layout().addRow(self.nrrdButton,
                                                   self.niftiButton)
        self.settingsCollapsibleButton.layout().addRow(
            self.fileFormatLabel, self.fileFormatSelectFrame)

        # Use input DICOM Patient Directory names as PatientID or infer from DICOM Metadata
        self.patientIDLabel = qt.QLabel("Infer Patient IDs from:  ",
                                        self.settingsCollapsibleButton)
        self.patientIDLabel.toolTip = "Use input DICOM Patient Directory names as PatientID or infer from DICOM Metadata"

        self.patientIDSelectFrame = qt.QFrame(self.settingsCollapsibleButton)
        self.patientIDSelectFrame.setLayout(qt.QFormLayout())
        self.patientIDGroup = qt.QButtonGroup(self.patientIDSelectFrame)
        self.metadataButton = qt.QRadioButton("Series DICOM Metadata")
        self.metadataButton.checked = True
        self.inputDirButton = qt.QRadioButton("Input Patient Subdirectories")
        self.patientIDGroup.addButton(self.metadataButton)
        self.patientIDGroup.addButton(self.inputDirButton)
        self.patientIDSelectFrame.layout().addRow(self.metadataButton,
                                                  self.inputDirButton)
        self.settingsCollapsibleButton.layout().addRow(
            self.patientIDLabel, self.patientIDSelectFrame)

        # Center Images option
        self.centerImagesLabel = qt.QLabel("Center Images:  ",
                                           self.settingsCollapsibleButton)

        self.centerImagesSelectFrame = qt.QFrame(
            self.settingsCollapsibleButton)
        self.centerImagesSelectFrame.setLayout(qt.QFormLayout())
        self.centerImagesGroup = qt.QButtonGroup(self.centerImagesSelectFrame)
        self.centerImagesButton = qt.QRadioButton("Yes")
        self.noCenterImagesButton = qt.QRadioButton("No")
        self.noCenterImagesButton.checked = True
        self.centerImagesGroup.addButton(self.centerImagesButton)
        self.centerImagesGroup.addButton(self.noCenterImagesButton)
        self.centerImagesSelectFrame.layout().addRow(self.centerImagesButton,
                                                     self.noCenterImagesButton)
        self.settingsCollapsibleButton.layout().addRow(
            self.centerImagesLabel, self.centerImagesSelectFrame)

        # Center Labels option
        self.centerLabelsLabel = qt.QLabel("Center Labels:  ",
                                           self.settingsCollapsibleButton)

        self.centerLabelsSelectFrame = qt.QFrame(
            self.settingsCollapsibleButton)
        self.centerLabelsSelectFrame.setLayout(qt.QFormLayout())
        self.centerLabelsGroup = qt.QButtonGroup(self.centerLabelsSelectFrame)
        self.centerLabelsButton = qt.QRadioButton("Yes")
        self.noCenterLabelsButton = qt.QRadioButton("No")
        self.noCenterLabelsButton.checked = True
        self.centerLabelsGroup.addButton(self.centerLabelsButton)
        self.centerLabelsGroup.addButton(self.noCenterLabelsButton)
        self.centerLabelsSelectFrame.layout().addRow(self.centerLabelsButton,
                                                     self.noCenterLabelsButton)
        self.settingsCollapsibleButton.layout().addRow(
            self.centerLabelsLabel, self.centerLabelsSelectFrame)

        # Parse and Save DICOM Metadata to CSV
        self.metadataExtractLabel = qt.QLabel("DICOM Metadata Extraction",
                                              self.settingsCollapsibleButton)
        self.metadataExtractLabel.toolTip = "Extract and Save all DICOM Metadata to a CSV file"

        self.metadataExtractSelectFrame = qt.QFrame(
            self.settingsCollapsibleButton)
        self.metadataExtractSelectFrame.setLayout(qt.QFormLayout())
        self.metadataExtractGroup = qt.QButtonGroup(
            self.metadataExtractSelectFrame)
        self.extractCSVButton = qt.QRadioButton("CSV")
        self.extractCSVButton.checked = True
        #self.extractJSONButton = qt.QRadioButton("JSON")
        self.doNotExtractButton = qt.QRadioButton("None")
        self.metadataExtractGroup.addButton(self.extractCSVButton)
        self.metadataExtractGroup.addButton(self.doNotExtractButton)
        self.metadataExtractSelectFrame.layout().addRow(
            self.extractCSVButton, self.doNotExtractButton)
        self.settingsCollapsibleButton.layout().addRow(
            self.metadataExtractLabel, self.metadataExtractSelectFrame)

        # Apply Batch Convert button
        self.applyBatchButton = qt.QPushButton("Apply Batch Convert")
        self.applyBatchButton.toolTip = "Batch convert DICOM to NRRD or NIFTI files"
        self.layout.addWidget(self.applyBatchButton)
        self.applyBatchButton.enabled = False

        #---------------------------------------------------------
        # Connections
        self.input1Button.connect('clicked(bool)', self.onInput1Button)
        self.input2Button.connect('clicked(bool)', self.onInput2Button)
        self.selectConvertButton.toggled.connect(self.selectConvert)
        self.addContourButton.connect('clicked(bool)',
                                      self.addContourFilterWidget)
        self.applyBatchButton.connect('clicked(bool)', self.onBatchApply)