Beispiel #1
0
    def setup_ui(self, main_window_instance):
        self.main_window_instance = main_window_instance
        self.call_class = MainPageCallClass()
        self.add_on_options_controller = AddOptions(self)

        ##########################################
        #  IMPLEMENTATION OF THE MAIN PAGE VIEW  #
        ##########################################
        if platform.system() == 'Darwin':
            self.stylesheet_path = "res/stylesheet.qss"
        else:
            self.stylesheet_path = "res/stylesheet-win-linux.qss"
        self.stylesheet = open(resource_path(self.stylesheet_path)).read()
        window_icon = QIcon()
        window_icon.addPixmap(QPixmap(resource_path(
            "res/images/icon.ico")), QIcon.Normal, QIcon.Off)
        self.main_window_instance.setMinimumSize(1080, 700)
        self.main_window_instance.setObjectName("MainOnkoDicomWindowInstance")
        self.main_window_instance.setWindowIcon(window_icon)
        self.main_window_instance.setStyleSheet(self.stylesheet)

        self.setup_central_widget()
        self.setup_actions()

        # Create SUV2ROI object and connect signals
        self.suv2roi = SUV2ROI()
        self.suv2roi_progress_window = \
            ProgressWindow(self.main_window_instance,
                           QtCore.Qt.WindowTitleHint |
                           QtCore.Qt.WindowCloseButtonHint)
        self.suv2roi_progress_window.signal_loaded.connect(
            self.on_loaded_suv2roi)
Beispiel #2
0
    def setup_ui(self, main_window_instance):
        self.main_window_instance = main_window_instance
        self.call_class = MainPageCallClass()
        self.add_on_options_controller = AddOptions(self)

        ##########################################
        #  IMPLEMENTATION OF THE MAIN PAGE VIEW  #
        ##########################################
        if platform.system() == 'Darwin':
            self.stylesheet_path = "res/stylesheet.qss"
        else:
            self.stylesheet_path = "res/stylesheet-win-linux.qss"
        stylesheet = open(resource_path(self.stylesheet_path)).read()
        window_icon = QIcon()
        window_icon.addPixmap(QPixmap(resource_path("res/images/icon.ico")), QIcon.Normal, QIcon.Off)
        self.main_window_instance.setMinimumSize(1080, 700)
        self.main_window_instance.setObjectName("MainOnkoDicomWindowInstance")
        self.main_window_instance.setWindowIcon(window_icon)
        self.main_window_instance.setStyleSheet(stylesheet)

        self.setup_central_widget()

        # Create actions and set menu and tool bars
        self.action_handler = ActionHandler(self)
        self.menubar = MenuBar(self.action_handler)
        self.main_window_instance.setMenuBar(self.menubar)
        self.toolbar = Toolbar(self.action_handler)
        self.main_window_instance.addToolBar(QtCore.Qt.TopToolBarArea, self.toolbar)
        self.main_window_instance.setWindowTitle("OnkoDICOM")
    def transect_handler(self):
        """
        Function triggered when the Transect button is pressed from the menu.
        """

        pixmaps = self.patient_dict_container.get("pixmaps_axial")
        id = self.current_slice
        dt = self.patient_dict_container.dataset[id]
        rowS = dt.PixelSpacing[0]
        colS = dt.PixelSpacing[1]
        dt.convert_pixel_data()
        MainPageCallClass().run_transect(
            self.draw_roi_window_instance,
            self.dicom_view.view,
            pixmaps[id],
            dt._pixel_array.transpose(),
            rowS,
            colS,
            is_roi_draw=True,
        )
Beispiel #4
0
class UIMainWindow:
    """
    The central class responsible for initializing most of the values stored in the PatientDictContainer model and
    defining the visual layout of the main window of OnkoDICOM.
    No class has access to the attributes belonging to this class, except for the class's ActionHandler, which is used
    to trigger actions within the main window. Components of this class (i.e. QWidget child classes such as
    StructureTab, DicomView, DicomTree, etc.) should not be able to reference this class, and rather should exist
    independently and only be able to communicate with the PatientDictContainer model. If a component needs to
    communicate with another component, that should be accomplished by emitting signals within that components, and
    having the slots for those signals within this class (as demonstrated by the update_views() method of this class).
    If a class needs to trigger one of the actions defined in the ActionHandler, then the instance of the ActionHandler
    itself can safely be passed into the class.
    """
    pyradi_trigger = QtCore.Signal(str, dict, str)

    def setup_ui(self, main_window_instance):
        self.main_window_instance = main_window_instance
        self.call_class = MainPageCallClass()
        self.add_on_options_controller = AddOptions(self)

        ##########################################
        #  IMPLEMENTATION OF THE MAIN PAGE VIEW  #
        ##########################################
        if platform.system() == 'Darwin':
            self.stylesheet_path = "res/stylesheet.qss"
        else:
            self.stylesheet_path = "res/stylesheet-win-linux.qss"
        stylesheet = open(resource_path(self.stylesheet_path)).read()
        window_icon = QIcon()
        window_icon.addPixmap(QPixmap(resource_path("res/images/icon.ico")), QIcon.Normal, QIcon.Off)
        self.main_window_instance.setMinimumSize(1080, 700)
        self.main_window_instance.setObjectName("MainOnkoDicomWindowInstance")
        self.main_window_instance.setWindowIcon(window_icon)
        self.main_window_instance.setStyleSheet(stylesheet)

        self.setup_central_widget()

        # Create actions and set menu and tool bars
        self.action_handler = ActionHandler(self)
        self.menubar = MenuBar(self.action_handler)
        self.main_window_instance.setMenuBar(self.menubar)
        self.toolbar = Toolbar(self.action_handler)
        self.main_window_instance.addToolBar(QtCore.Qt.TopToolBarArea, self.toolbar)
        self.main_window_instance.setWindowTitle("OnkoDICOM")

    def setup_central_widget(self):
        patient_dict_container = PatientDictContainer()
        self.central_widget = QtWidgets.QWidget()
        self.central_widget_layout = QtWidgets.QVBoxLayout()

        self.patient_bar = PatientBar()

        splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)

        # Left panel contains stuctures tab, isodoses tab, and structure information
        self.left_panel = QtWidgets.QTabWidget()
        self.left_panel.setMinimumWidth(300)
        self.left_panel.setMaximumWidth(500)

        # Add structures tab to left panel
        if patient_dict_container.has_modality("rtss"):
            self.structures_tab = StructureTab()
            self.structures_tab.request_update_structures.connect(self.update_views)
            self.left_panel.addTab(self.structures_tab, "Structures")

        if patient_dict_container.has_modality("rtdose"):
            self.isodoses_tab = IsodoseTab()
            self.isodoses_tab.request_update_isodoses.connect(self.update_views)
            self.left_panel.addTab(self.isodoses_tab, "Isodoses")

        # Hide left panel if no rtss or rtdose
        if not patient_dict_container.has_modality("rtss") and not patient_dict_container.has_modality("rtdose"):
            self.left_panel.hide()

        # Right panel contains the different tabs of DICOM view, DVH, clinical data, DICOM tree
        self.right_panel = QtWidgets.QTabWidget()

        # Add DICOM view to right panel as a tab
        roi_color_dict = self.structures_tab.color_dict if hasattr(self, 'structures_tab') else None
        iso_color_dict = self.isodoses_tab.color_dict if hasattr(self, 'isodoses_tab') else None
        self.dicom_view = DicomView(roi_color=roi_color_dict, iso_color=iso_color_dict)
        self.right_panel.addTab(self.dicom_view, "DICOM View")

        # Add DVH tab to right panel as a tab
        if patient_dict_container.has_modality("rtss") and patient_dict_container.has_modality("rtdose"):
            self.dvh_tab = DVHTab()
            self.right_panel.addTab(self.dvh_tab, "DVH")

        self.dicom_tree = DicomTreeView()
        self.right_panel.addTab(self.dicom_tree, "DICOM Tree")

        # Create Clinical Data tab
        # TODO refactor the entire Clinical Data form/display class
        # As they currently stand, they are given the right tab widget, and make direct modifications to the tab.
        # This class should be refactored in the same way as the rest of the main window's components, i.e. the Clinical
        # Data should be a child of QWidget that can exist independently of OnkoDICOM. This class would differ from most
        # other main window components in that rather than interacting with the PatientDictContainer as its model, it
        # would use the patient's ClinicalData csv file as the model (which means that the QWidget would theoretically
        # easily exist outside OnkoDICOM).
        # There are two classes: one for displaying the clinical data, and another for modifying the clinical data.
        # The check below determines whether there already exists a clinical data csv for the patient, and loads either
        # the data display or the data form depending on what exists.
        reg = '/CSV/ClinicalData*[.csv]'
        if not glob.glob(patient_dict_container.path + reg):
            self.call_class.display_cd_form(self.right_panel, patient_dict_container.path)
        else:
            self.call_class.display_cd_dat(self.right_panel, patient_dict_container.path)

        splitter.addWidget(self.left_panel)
        splitter.addWidget(self.right_panel)

        # Create footer
        self.footer = QtWidgets.QWidget()
        self.create_footer()

        # Set layout
        self.central_widget_layout.addWidget(self.patient_bar)
        self.central_widget_layout.addWidget(splitter)
        self.central_widget_layout.addWidget(self.footer)

        self.central_widget.setLayout(self.central_widget_layout)
        self.main_window_instance.setCentralWidget(self.central_widget)

    def create_footer(self):
        self.footer.setFixedHeight(15)
        layout_footer = QtWidgets.QHBoxLayout(self.footer)
        layout_footer.setContentsMargins(0, 0, 0, 0)

        label_footer = QtWidgets.QLabel("@OnkoDICOM 2019-20")
        label_footer.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignRight)

        layout_footer.addWidget(label_footer)

    def update_views(self):
        """
        This function is a slot for signals to request the updating of the DICOM View and DVH tabs in order to reflect
        changes made by other components of the main window (for example, when a structure in the structures tab is
        selected, this method needs to be called in order for the DICOM view window to be updated to show the new
        region of interest.
        """
        self.dicom_view.update_view()
        if hasattr(self, 'dvh_tab'):
            self.dvh_tab.update_plot()

    def zoom_in(self):
        self.dicom_view.zoom_in()

    def zoom_out(self):
        self.dicom_view.zoom_out()
Beispiel #5
0
    def setup_ui(self, main_window_instance):
        self.main_window_instance = main_window_instance
        self.call_class = MainPageCallClass()
        self.add_on_options_controller = AddOptions(self)
        patient_dict_container = PatientDictContainer()

        ##########################################
        #  IMPLEMENTATION OF THE MAIN PAGE VIEW  #
        ##########################################
        if platform.system() == 'Darwin':
            self.stylesheet_path = "src/res/stylesheet.qss"
        else:
            self.stylesheet_path = "src/res/stylesheet-win-linux.qss"
        stylesheet = open(resource_path(self.stylesheet_path)).read()
        window_icon = QIcon()
        window_icon.addPixmap(
            QPixmap(resource_path("src/res/images/icon.ico")), QIcon.Normal,
            QIcon.Off)
        self.main_window_instance.setMinimumSize(1080, 700)
        self.main_window_instance.setObjectName("MainOnkoDicomWindowInstance")
        self.main_window_instance.setWindowIcon(window_icon)
        self.main_window_instance.setStyleSheet(stylesheet)

        self.central_widget = QtWidgets.QWidget()
        self.central_widget_layout = QtWidgets.QVBoxLayout()

        self.patient_bar = PatientBar()

        splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)

        # Left panel contains stuctures tab, isodoses tab, and structure information
        self.left_panel = QtWidgets.QTabWidget()
        self.left_panel.setMinimumWidth(300)
        self.left_panel.setMaximumWidth(500)

        # Add structures tab to left panel
        if patient_dict_container.has_modality("rtss"):
            self.structures_tab = StructureTab()
            self.structures_tab.request_update_structures.connect(
                self.update_views)
            self.left_panel.addTab(self.structures_tab, "Structures")

        if patient_dict_container.has_modality("rtdose"):
            self.isodoses_tab = IsodoseTab()
            self.isodoses_tab.request_update_isodoses.connect(
                self.update_views)
            self.left_panel.addTab(self.isodoses_tab, "Isodoses")

        # Hide left panel if no rtss or rtdose
        if not patient_dict_container.has_modality(
                "rtss") and not patient_dict_container.has_modality("rtdose"):
            self.left_panel.hide()

        # Right panel contains the different tabs of DICOM view, DVH, clinical data, DICOM tree
        self.right_panel = QtWidgets.QTabWidget()

        # Add DICOM view to right panel as a tab
        roi_color_dict = self.structures_tab.color_dict if hasattr(
            self, 'structures_tab') else None
        iso_color_dict = self.isodoses_tab.color_dict if hasattr(
            self, 'isodoses_tab') else None
        self.dicom_view = DicomView(roi_color=roi_color_dict,
                                    iso_color=iso_color_dict)
        self.right_panel.addTab(self.dicom_view, "DICOM View")

        # Add DVH tab to right panel as a tab
        if patient_dict_container.has_modality(
                "rtss") and patient_dict_container.has_modality("rtdose"):
            self.dvh_tab = DVHTab()
            self.right_panel.addTab(self.dvh_tab, "DVH")

        self.dicom_tree = DicomTreeView()
        self.right_panel.addTab(self.dicom_tree, "DICOM Tree")

        # Create Clinical Data tab
        # TODO refactor the entire Clinical Data form/display class
        # As they currently stand, they are given the right tab widget, and make direct modifications to the tab.
        # This class should be refactored in the same way as the rest of the main window's components, i.e. the Clinical
        # Data should be a child of QWidget that can exist independently of OnkoDICOM. This class would differ from most
        # other main window components in that rather than interacting with the PatientDictContainer as its model, it
        # would use the patient's ClinicalData csv file as the model (which means that the QWidget would theoretically
        # easily exist outside OnkoDICOM).
        # There are two classes: one for displaying the clinical data, and another for modifying the clinical data.
        # The check below determines whether there already exists a clinical data csv for the patient, and loads either
        # the data display or the data form depending on what exists.
        reg = '/CSV/ClinicalData*[.csv]'
        if not glob.glob(patient_dict_container.path + reg):
            self.call_class.display_cd_form(self.right_panel,
                                            patient_dict_container.path)
        else:
            self.call_class.display_cd_dat(self.right_panel,
                                           patient_dict_container.path)

        splitter.addWidget(self.left_panel)
        splitter.addWidget(self.right_panel)

        # Create footer
        self.footer = QtWidgets.QWidget()
        self.create_footer()

        # Set layout
        self.central_widget_layout.addWidget(self.patient_bar)
        self.central_widget_layout.addWidget(splitter)
        self.central_widget_layout.addWidget(self.footer)

        self.central_widget.setLayout(self.central_widget_layout)
        self.main_window_instance.setCentralWidget(self.central_widget)

        # Create actions and set menu and tool bars
        self.action_handler = ActionHandler(self)
        self.menubar = MenuBar(self.action_handler)
        self.main_window_instance.setMenuBar(self.menubar)
        self.toolbar = Toolbar(self.action_handler)
        self.main_window_instance.addToolBar(QtCore.Qt.TopToolBarArea,
                                             self.toolbar)
        self.main_window_instance.setWindowTitle("OnkoDICOM")
Beispiel #6
0
class UIMainWindow:
    """
    The central class responsible for initializing most of the values stored
    in the PatientDictContainer model and defining the visual layout of the
    main window of OnkoDICOM. No class has access to the attributes
    belonging to this class, except for the class's ActionHandler, which is
    used to trigger actions within the main window. Components of this class
    (i.e. QWidget child classes such as StructureTab, DicomView, DicomTree,
    etc.) should not be able to reference this class, and rather should
    exist independently and only be able to communicate with the
    PatientDictContainer model. If a component needs to communicate with
    another component, that should be accomplished by emitting signals
    within that components, and having the slots for those signals within
    this class (as demonstrated by the update_views() method of this class).
    If a class needs to trigger one of the actions defined in the
    ActionHandler, then the instance of the ActionHandler itself can safely
    be passed into the class.
    """
    pyradi_trigger = QtCore.Signal(str, dict, str)

    # Connect to GUIController
    image_fusion_main_window = QtCore.Signal()

    def setup_ui(self, main_window_instance):
        self.main_window_instance = main_window_instance
        self.call_class = MainPageCallClass()
        self.add_on_options_controller = AddOptions(self)

        ##########################################
        #  IMPLEMENTATION OF THE MAIN PAGE VIEW  #
        ##########################################
        if platform.system() == 'Darwin':
            self.stylesheet_path = "res/stylesheet.qss"
        else:
            self.stylesheet_path = "res/stylesheet-win-linux.qss"
        self.stylesheet = open(resource_path(self.stylesheet_path)).read()
        window_icon = QIcon()
        window_icon.addPixmap(QPixmap(resource_path(
            "res/images/icon.ico")), QIcon.Normal, QIcon.Off)
        self.main_window_instance.setMinimumSize(1080, 700)
        self.main_window_instance.setObjectName("MainOnkoDicomWindowInstance")
        self.main_window_instance.setWindowIcon(window_icon)
        self.main_window_instance.setStyleSheet(self.stylesheet)

        self.setup_central_widget()
        self.setup_actions()

        # Create SUV2ROI object and connect signals
        self.suv2roi = SUV2ROI()
        self.suv2roi_progress_window = \
            ProgressWindow(self.main_window_instance,
                           QtCore.Qt.WindowTitleHint |
                           QtCore.Qt.WindowCloseButtonHint)
        self.suv2roi_progress_window.signal_loaded.connect(
            self.on_loaded_suv2roi)

    def setup_actions(self):
        if hasattr(self, 'toolbar'):
            self.main_window_instance.removeToolBar(self.toolbar)
        self.action_handler = ActionHandler(self)
        self.menubar = MenuBar(self.action_handler)
        self.main_window_instance.setMenuBar(self.menubar)
        self.toolbar = Toolbar(self.action_handler)
        self.main_window_instance.addToolBar(
            QtCore.Qt.TopToolBarArea, self.toolbar)
        self.main_window_instance.setWindowTitle("OnkoDICOM")

    def setup_central_widget(self):
        patient_dict_container = PatientDictContainer()
        self.central_widget = QtWidgets.QWidget()
        self.central_widget_layout = QVBoxLayout()

        self.patient_bar = PatientBar()

        splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)

        # Left panel contains stuctures tab, isodoses tab,
        # and structure information
        self.left_panel = QtWidgets.QTabWidget()
        self.left_panel.setMinimumWidth(300)
        self.left_panel.setMaximumWidth(500)

        # Add structures tab to left panel
        if not hasattr(self, 'structures_tab'):
            self.structures_tab = StructureTab()
            self.structures_tab.request_update_structures.connect(
                self.update_views)
        else:
            self.structures_tab.update_ui()
        self.left_panel.addTab(self.structures_tab, "Structures")

        if patient_dict_container.has_modality("rtdose"):
            self.isodoses_tab = IsodoseTab()
            self.isodoses_tab.request_update_isodoses.connect(
                self.update_views)
            self.isodoses_tab.request_update_ui.connect(
                self.structures_tab.fixed_container_structure_modified)
            self.left_panel.addTab(self.isodoses_tab, "Isodoses")
        elif hasattr(self, 'isodoses_tab'):
            del self.isodoses_tab

        # Right panel contains the different tabs of DICOM view, DVH,
        # clinical data, DICOM tree
        self.right_panel = QtWidgets.QTabWidget()

        # Create a Dicom View containing single-slice and 3-slice views
        self.dicom_view = DicomStackedWidget(self.format_data)

        roi_color_dict = self.structures_tab.color_dict if hasattr(
            self, 'structures_tab') else None
        iso_color_dict = self.isodoses_tab.color_dict if hasattr(
            self, 'isodoses_tab') else None
        self.dicom_single_view = DicomAxialView(
            roi_color=roi_color_dict, iso_color=iso_color_dict)
        self.dicom_axial_view = DicomAxialView(
            is_four_view=True, roi_color=roi_color_dict, iso_color=iso_color_dict,
            metadata_formatted=True, cut_line_color=QtGui.QColor(255, 0, 0))
        self.dicom_sagittal_view = DicomSagittalView(
            roi_color=roi_color_dict, iso_color=iso_color_dict,
            cut_line_color=QtGui.QColor(0, 255, 0))
        self.dicom_coronal_view = DicomCoronalView(
            roi_color=roi_color_dict, iso_color=iso_color_dict,
            cut_line_color=QtGui.QColor(0, 0, 255))
        self.three_dimension_view = DicomView3D()

        # Rescale the size of the scenes inside the 3-slice views
        self.dicom_axial_view.zoom = INITIAL_FOUR_VIEW_ZOOM
        self.dicom_sagittal_view.zoom = INITIAL_FOUR_VIEW_ZOOM
        self.dicom_coronal_view.zoom = INITIAL_FOUR_VIEW_ZOOM
        self.dicom_axial_view.update_view(zoom_change=True)
        self.dicom_sagittal_view.update_view(zoom_change=True)
        self.dicom_coronal_view.update_view(zoom_change=True)

        self.dicom_four_views = QWidget()
        self.dicom_four_views_layout = QGridLayout()
        for i in range(2):
            self.dicom_four_views_layout.setColumnStretch(i, 1)
            self.dicom_four_views_layout.setRowStretch(i, 1)
        self.dicom_four_views_layout.addWidget(self.dicom_axial_view, 0, 0)
        self.dicom_four_views_layout.addWidget(self.dicom_sagittal_view, 0, 1)
        self.dicom_four_views_layout.addWidget(self.dicom_coronal_view, 1, 0)
        self.dicom_four_views_layout.addWidget(self.three_dimension_view, 1, 1)
        self.dicom_four_views.setLayout(self.dicom_four_views_layout)

        self.dicom_view.addWidget(self.dicom_four_views)
        self.dicom_view.addWidget(self.dicom_single_view)
        self.dicom_view.setCurrentWidget(self.dicom_single_view)

        # Add DICOM View to right panel as a tab
        self.right_panel.addTab(self.dicom_view, "DICOM View")

        # Add PETVT View to right panel as a tab
        self.pet_ct_tab = PetCtView()
        self.right_panel.addTab(self.pet_ct_tab, "PET/CT View")

        # Add DVH tab to right panel as a tab
        if patient_dict_container.has_modality("rtdose"):
            self.dvh_tab = DVHTab()
            self.right_panel.addTab(self.dvh_tab, "DVH")
        elif hasattr(self, 'dvh_tab'):
            del self.dvh_tab

        # Add DICOM Tree View tab
        self.dicom_tree = DicomTreeView()
        self.right_panel.addTab(self.dicom_tree, "DICOM Tree")

        # Connect SUV2ROI signal to handler function
        self.dicom_single_view.suv2roi_signal.connect(self.perform_suv2roi)

        # Add clinical data tab
        self.call_class.display_clinical_data(self.right_panel)

        splitter.addWidget(self.left_panel)
        splitter.addWidget(self.right_panel)

        # Create footer
        self.footer = QtWidgets.QWidget()
        self.create_footer()

        # Set layout
        self.central_widget_layout.addWidget(self.patient_bar)
        self.central_widget_layout.addWidget(splitter)
        self.central_widget_layout.addWidget(self.footer)

        self.central_widget.setLayout(self.central_widget_layout)
        self.main_window_instance.setCentralWidget(self.central_widget)

    def create_footer(self):
        self.footer.setFixedHeight(15)
        layout_footer = QtWidgets.QHBoxLayout(self.footer)
        layout_footer.setContentsMargins(0, 0, 0, 0)

        label_footer = QtWidgets.QLabel("@OnkoDICOM2021")
        label_footer.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignRight)

        layout_footer.addWidget(label_footer)

    def update_views(self, update_3d_window=False):
        """
        This function is a slot for signals to request the updating of the
        DICOM View and DVH tabs in order to reflect changes made by other
        components of the main window (for example, when a structure in the
        structures tab is selected, this method needs to be called in order
        for the DICOM view window to be updated to show the new region of
        interest.

        :param update_3d_window: a boolean to mark if 3d model
        needs to be updated
        """

        self.dicom_single_view.update_view()
        self.dicom_axial_view.update_view()
        self.dicom_coronal_view.update_view()
        self.dicom_sagittal_view.update_view()

        if update_3d_window:
            self.three_dimension_view.update_view()

        if hasattr(self, 'dvh_tab'):
            self.dvh_tab.update_plot()

        if hasattr(self, 'pet_ct_tab'):
            if self.pet_ct_tab.initialised:
                self.pet_ct_tab.update_view()

        if hasattr(self, 'image_fusion_view'):
            if self.image_fusion_view_axial is not None:
                self.image_fusion_single_view.update_view()
                self.image_fusion_view_axial.update_view()
                self.image_fusion_view_coronal.update_view()
                self.image_fusion_view_sagittal.update_view()

    def toggle_cut_lines(self):
        if self.dicom_axial_view.horizontal_view is None or \
                self.dicom_axial_view.vertical_view is None or \
                self.dicom_coronal_view.horizontal_view is None or \
                self.dicom_coronal_view.vertical_view is None or \
                self.dicom_sagittal_view.horizontal_view is None or \
                self.dicom_sagittal_view.vertical_view is None:
            self.dicom_axial_view.set_views(self.dicom_coronal_view,
                                            self.dicom_sagittal_view)
            self.dicom_coronal_view.set_views(self.dicom_axial_view,
                                              self.dicom_sagittal_view)
            self.dicom_sagittal_view.set_views(self.dicom_axial_view,
                                               self.dicom_coronal_view)
        else:
            self.dicom_axial_view.set_views(None, None)
            self.dicom_coronal_view.set_views(None, None)
            self.dicom_sagittal_view.set_views(None, None)

        if hasattr(self, 'image_fusion_view'):
            if self.image_fusion_view is not None:
                if self.image_fusion_view_axial.horizontal_view is None or \
                        self.image_fusion_view_axial.vertical_view is None or \
                        self.image_fusion_view_coronal.horizontal_view is None \
                        or self.image_fusion_view_coronal.vertical_view is None \
                        or \
                        self.image_fusion_view_sagittal.horizontal_view is None \
                        or \
                        self.image_fusion_view_sagittal.vertical_view is None:
                    self.image_fusion_view_axial.set_views(
                        self.image_fusion_view_coronal,
                        self.image_fusion_view_sagittal)
                    self.image_fusion_view_coronal.set_views(
                        self.image_fusion_view_axial,
                        self.image_fusion_view_sagittal)

                    self.image_fusion_view_sagittal.set_views(
                        self.image_fusion_view_axial,
                        self.image_fusion_view_coronal)
                else:
                    self.image_fusion_view_axial.set_views(None, None)
                    self.image_fusion_view_coronal.set_views(None, None)
                    self.image_fusion_view_sagittal.set_views(None, None)

    def zoom_in(self, is_four_view, image_reg_single, image_reg_four):
        """
        This function calls the zooming in function on the four view's views
        or the single view depending on what view is showing on screen.
        is_four_view: Whether the four view is showing
        """
        if is_four_view:
            self.dicom_axial_view.zoom_in()
            self.dicom_coronal_view.zoom_in()
            self.dicom_sagittal_view.zoom_in()
        else:
            self.dicom_single_view.zoom_in()

        if image_reg_single:
            self.image_fusion_single_view.zoom_in()

        if image_reg_four:
            self.image_fusion_view_axial.zoom_in()
            self.image_fusion_view_coronal.zoom_in()
            self.image_fusion_view_sagittal.zoom_in()

        if self.pet_ct_tab.initialised:
            self.pet_ct_tab.zoom_in()

    def zoom_out(self, is_four_view, image_reg_single, image_reg_four):
        """
        This function calls the zooming out function on the four view's
        views or the single view depending on what view is showing on screen.
        is_four_view: Whether the four view is showing
        """
        if is_four_view:
            self.dicom_axial_view.zoom_out()
            self.dicom_coronal_view.zoom_out()
            self.dicom_sagittal_view.zoom_out()
        else:
            self.dicom_single_view.zoom_out()

        if image_reg_single:
            self.image_fusion_single_view.zoom_out()

        if image_reg_four:
            self.image_fusion_view_axial.zoom_out()
            self.image_fusion_view_coronal.zoom_out()
            self.image_fusion_view_sagittal.zoom_out()

        if self.pet_ct_tab.initialised:
            self.pet_ct_tab.zoom_out()

    def format_data(self, size):
        """
        This function is used to update the meta data's font size and margin
        based on the height and width of the viewports.
        size: The size of the DicomStackedWidget
        """
        self.dicom_axial_view.format_metadata(size)

    def create_image_fusion_tab(self):
        """
        This function is used to create the tab for image fusion.
        Function checks if the moving dict container contains rtss to
        load rtss. Views are created and stacked into three window view.
        """
        # Set a flag for Zooming
        self.action_handler.has_image_registration_four = True

        # Instance of Moving Model
        moving_dict_container = MovingDictContainer()

        if moving_dict_container.has_modality("rtss"):
            if len(self.structures_tab.rois.items()) == 0:
                self.structures_tab.update_ui(moving=True)
            # else:
            # TODO: Display both ROIs in the same tab

        self.image_fusion_single_view \
            = ImageFusionAxialView()

        self.image_fusion_view = QStackedWidget()
        self.image_fusion_view_axial = ImageFusionAxialView(
            metadata_formatted=False,
            cut_line_color=QtGui.QColor(255, 0, 0))
        self.image_fusion_view_sagittal = ImageFusionSagittalView(
            cut_line_color=QtGui.QColor(0, 255, 0))
        self.image_fusion_view_coronal = ImageFusionCoronalView(
            cut_line_color=QtGui.QColor(0, 0, 255))
        self.image_fusion_roi_transfer_option_view = ROITransferOptionView(
            self.structures_tab.fixed_container_structure_modified,
            self.structures_tab.moving_container_structure_modified)

        # Rescale the size of the scenes inside the 3-slice views
        self.image_fusion_view_axial.zoom = INITIAL_FOUR_VIEW_ZOOM
        self.image_fusion_view_sagittal.zoom = INITIAL_FOUR_VIEW_ZOOM
        self.image_fusion_view_coronal.zoom = INITIAL_FOUR_VIEW_ZOOM
        self.image_fusion_view_axial.update_view(zoom_change=True)
        self.image_fusion_view_sagittal.update_view(zoom_change=True)
        self.image_fusion_view_coronal.update_view(zoom_change=True)

        self.image_fusion_four_views = QWidget()
        self.image_fusion_four_views_layout = QGridLayout()
        for i in range(2):
            self.image_fusion_four_views_layout.setColumnStretch(i, 1)
            self.image_fusion_four_views_layout.setRowStretch(i, 1)
        self.image_fusion_four_views_layout.addWidget(
            self.image_fusion_view_axial, 0, 0)
        self.image_fusion_four_views_layout.addWidget(
            self.image_fusion_view_sagittal, 0, 1)
        self.image_fusion_four_views_layout.addWidget(
            self.image_fusion_view_coronal, 1, 0)

        self.image_fusion_four_views_layout.addWidget(
            self.image_fusion_roi_transfer_option_view, 1, 1
        )
        self.image_fusion_four_views.setLayout(
            self.image_fusion_four_views_layout)

        self.image_fusion_view.addWidget(self.image_fusion_four_views)
        self.image_fusion_view.addWidget(self.image_fusion_single_view)
        self.image_fusion_view.setCurrentWidget(self.image_fusion_four_views)

        # Add Image Fusion Tab
        self.right_panel.addTab(self.image_fusion_view, "Image Fusion")
        self.right_panel.setCurrentWidget(self.image_fusion_view)

        # Update the Add On Option GUI
        self.add_on_options_controller.update_ui()

    def perform_suv2roi(self):
        """
        Performs the SUV2ROI process.
        """
        # Get patient weight - needs to run first as GUI cannot run in
        # threads, like the ProgressBar
        patient_dict_container = PatientDictContainer()
        dataset = patient_dict_container.dataset[0]
        self.suv2roi.get_patient_weight(dataset)
        if self.suv2roi.patient_weight is None:
            return

        # Start the SUV2ROI process
        self.suv2roi_progress_window.start(self.suv2roi.start_conversion)

    def on_loaded_suv2roi(self):
        """
        Called when progress bar has finished. Closes the progress
        window and refreshes the main screen.
        """
        if self.suv2roi.suv2roi_status:
            patient_dict_container = PatientDictContainer()
            self.structures_tab.fixed_container_structure_modified((
                patient_dict_container.get('dataset_rtss'), {"draw": None}))
        else:
            # Alert user that SUV2ROI failed and for what reason
            if self.suv2roi.failure_reason == "UNIT":
                failure_reason = \
                    "PET units are not Bq/mL. OnkoDICOM can currently only\n" \
                    "perform SUV2ROI on PET images stored in these units."
            elif self.suv2roi.failure_reason == "DECY":
                failure_reason = \
                    "PET is not decay corrected. OnkoDICOM can currently " \
                    "only\nperform SUV2ROI on PET images that are decay " \
                    "corrected."
            else:
                failure_reason = "The SUV2ROI process has failed."
            button_reply = \
                QtWidgets.QMessageBox(
                    QtWidgets.QMessageBox.Icon.Warning,
                    "SUV2ROI Failed",
                    failure_reason,
                    QtWidgets.QMessageBox.StandardButton.Ok, self)
            button_reply.button(
                QtWidgets.QMessageBox.StandardButton.Ok).setStyleSheet(
                self.stylesheet)
            button_reply.exec_()

        # Close progress window
        self.suv2roi_progress_window.close()
Beispiel #7
0
    def setupUi(self, MainWindow, patient_dict_container):

        ##############################
        #  LOAD PATIENT INFORMATION  #
        ##############################
        self.dataset = patient_dict_container.dataset
        self.raw_dvh = patient_dict_container.get("raw_dvh")
        self.dvh_x_y = patient_dict_container.get("dvh_x_y")

        self.has_rtss = True if patient_dict_container.has('rtss') else False
        self.has_rtdose = True if patient_dict_container.has(
            'rtdose') else False

        # Dictionary whose key is the ROI number as specified in the RTSS file
        # and whose value is a dictionary with keys 'uid', 'name' and 'algorithm'
        self.rois = patient_dict_container.get("rois")
        # Contain the ordered list of ROI numbers.
        # Used to manage the case of missing integers in an ordered series of ROI numbers (for example 1 2 4 7)
        if self.has_rtss:
            self.list_roi_numbers = self.ordered_list_rois()

        self.filepaths = patient_dict_container.filepaths
        self.path = patient_dict_container.path
        if self.has_rtdose:
            self.dose_pixluts = get_dose_pixluts(self.dataset)
        self.hashed_path = ''  # Path to hashed patient directory

        self.rxdose = 1
        if patient_dict_container.has("rtplan"):
            # the TargetPrescriptionDose is type 3 (optional), so it may not be there
            # However, it is preferable to the sum of the beam doses
            # DoseReferenceStructureType is type 1 (value is mandatory),
            # but it can have a value of ORGAN_AT_RISK rather than TARGET
            # in which case there will *not* be a TargetPrescriptionDose
            # and even if it is TARGET, that's no guarantee that TargetPrescriptionDose
            # will be encoded and have a value
            if ('DoseReferenceSequence' in self.dataset['rtplan']
                    and self.dataset['rtplan'].DoseReferenceSequence[0].
                    DoseReferenceStructureType and self.dataset['rtplan'].
                    DoseReferenceSequence[0].TargetPrescriptionDose):
                self.rxdose = self.dataset['rtplan'].DoseReferenceSequence[
                    0].TargetPrescriptionDose * 100
            # beam doses are to a point, not necessarily to the same point
            # and don't necessarily add up to the prescribed dose to the target
            # which is frequently to a SITE rather than to a POINT
            elif self.dataset['rtplan'].FractionGroupSequence:
                fraction_group = self.dataset['rtplan'].FractionGroupSequence[
                    0]
                if ("NumberOfFractionsPlanned" in fraction_group) and \
                        ("ReferencedBeamSequence" in fraction_group):
                    beams = fraction_group.ReferencedBeamSequence
                    number_of_fractions = fraction_group.NumberOfFractionsPlanned
                    for beam in beams:
                        if "BeamDose" in beam:
                            self.rxdose += beam.BeamDose * number_of_fractions * 100
        self.rxdose = round(self.rxdose)

        # Commented out as rx dose dialogue no longer needed.
        #dose_check_dialog = Rxdose_Check(self.rxdose)
        #dose_check_dialog.exec()
        #self.rxdose = dose_check_dialog.get_dose()

        # WindowWidth and WindowCenter values in the DICOM file can be either
        # a list or a float. The following lines of code check what instance
        # the values belong to and sets the window and level values accordingly
        # The values are converted from the type pydicom.valuerep.DSfloat to
        # int for processing later on in the program
        if isinstance(self.dataset[0].WindowWidth, pydicom.valuerep.DSfloat):
            self.window = int(self.dataset[0].WindowWidth)
        elif isinstance(self.dataset[0].WindowWidth,
                        pydicom.multival.MultiValue):
            self.window = int(self.dataset[0].WindowWidth[1])

        if isinstance(self.dataset[0].WindowCenter, pydicom.valuerep.DSfloat):
            self.level = int(self.dataset[0].WindowCenter)
        elif isinstance(self.dataset[0].WindowCenter,
                        pydicom.multival.MultiValue):
            self.level = int(self.dataset[0].WindowCenter[1])

        # Variables to check for the mouse position when altering the window and
        # level values
        self.x1, self.y1 = 256, 256

        # Check to see if the imageWindowing.csv file exists
        if os.path.exists('src/data/csv/imageWindowing.csv'):
            # If it exists, read data from file into the self.dict_windowing variable
            self.dict_windowing = {}
            with open('src/data/csv/imageWindowing.csv', "r") as fileInput:
                next(fileInput)
                self.dict_windowing["Normal"] = [self.window, self.level]
                for row in fileInput:
                    # Format: Organ - Scan - Window - Level
                    items = [item for item in row.split(',')]
                    self.dict_windowing[items[0]] = [
                        int(items[2]), int(items[3])
                    ]
        else:
            # If csv does not exist, initialize dictionary with default values
            self.dict_windowing = {
                "Normal": [self.window, self.level],
                "Lung": [1600, -300],
                "Bone": [1400, 700],
                "Brain": [160, 950],
                "Soft Tissue": [400, 800],
                "Head and Neck": [275, 900]
            }

        self.pixel_values = convert_raw_data(self.dataset)
        self.pixmaps = get_pixmaps(self.pixel_values, self.window, self.level)

        if self.has_rtss:
            self.file_rtss = self.filepaths['rtss']
            self.dataset_rtss = pydicom.dcmread(self.file_rtss, force=True)

        self.rtss_modified = False  # Flag that changes the first time the rtss file is modified.

        if self.has_rtdose:
            self.file_rtdose = self.filepaths['rtdose']
            self.dataset_rtdose = pydicom.dcmread(self.file_rtdose, force=True)

        self.dict_UID = dict_instanceUID(self.dataset)
        self.selected_rois = []
        self.selected_doses = []

        self.basicInfo = get_basic_info(self.dataset[0])
        self.dict_pixluts = patient_dict_container.get("pixluts")
        self.dict_raw_ContourData = patient_dict_container.get("raw_contour")
        self.dict_NumPoints = patient_dict_container.get("num_points")

        self.dict_polygons = {}

        self.zoom = 1

        # DICOM Tree for RTSS file
        if self.has_rtss:
            self.dicomTree_rtss = DicomTree(self.file_rtss)
            self.dictDicomTree_rtss = self.dicomTree_rtss.dict

        # DICOM Tree for RT Dose file
        if self.has_rtdose:
            self.dicomTree_rtdose = DicomTree(self.file_rtdose)
            self.dictDicomTree_rtdose = self.dicomTree_rtdose.dict

        # Patient Position
        filename_CT0 = self.filepaths[0]
        dicomTreeSlice_CT0 = DicomTree(filename_CT0)
        dictSlice_CT0 = dicomTreeSlice_CT0.dict
        self.patient_HFS = dictSlice_CT0['Patient Position'][0][:2] == 'HF'

        self.callClass = MainPageCallClass(self.path, self.dataset,
                                           self.filepaths, self.raw_dvh)
        self.callManager = AddOptions(self)

        ##########################################
        #  IMPLEMENTATION OF THE MAIN PAGE VIEW  #
        ##########################################

        # Main Window
        MainWindow.setMinimumSize(1080, 700)
        MainWindow.setWindowTitle("OnkoDICOM")
        MainWindow.setWindowIcon(QtGui.QIcon("src/Icon/DONE.png"))

        # Main Container and Layout
        self.main_widget = QtWidgets.QWidget(MainWindow)
        self.main_widget.setFocusPolicy(QtCore.Qt.NoFocus)
        self.main_layout = QtWidgets.QVBoxLayout(self.main_widget)

        # Patient Bar
        self.patient_bar = PatientBar(self)

        # Functionalities Container and Layout
        # (Structure/Isodoses tab, Structure Information tab, DICOM View / DVH / DICOM Tree / Clinical Data tab)
        self.function_widget = QtWidgets.QWidget(MainWindow)
        self.function_widget.setFocusPolicy(QtCore.Qt.NoFocus)
        self.function_layout = QtWidgets.QHBoxLayout(self.function_widget)
        self.function_layout.setContentsMargins(0, 0, 0, 0)

        splitter = QtWidgets.QSplitter(Qt.Horizontal)

        # Left Column
        self.left_widget = QtWidgets.QWidget(self.main_widget)
        self.left_layout = QtWidgets.QVBoxLayout(self.left_widget)
        self.left_layout.setContentsMargins(0, 0, 0, 0)
        self.left_widget.setMinimumWidth(230)
        self.left_widget.setMaximumWidth(500)

        if not self.has_rtss and not self.has_rtdose:
            self.left_widget.hide()

        # Left Top Column: Structure and Isodoses Tabs
        self.tab1 = QtWidgets.QTabWidget(self.left_widget)
        self.tab1.setFocusPolicy(QtCore.Qt.NoFocus)
        self.tab1.setGeometry(QtCore.QRect(0, 40, 200, 361))

        # Left Column: Structures tab
        if self.has_rtss:
            self.structures_tab = StructureTab(self)
        # Left Column: Isodoses tab
        if self.has_rtdose:
            self.isodoses_tab = IsodosesTab(self)

        # Structure Information (bottom left of the window)
        if self.has_rtss and self.has_rtdose:
            self.struct_info = StructureInformation(self)

        if self.has_rtss or self.has_rtdose:
            self.left_layout.addWidget(self.tab1)

        if self.has_rtss and self.has_rtdose:
            self.left_layout.addWidget(self.struct_info.widget)

        # Main view
        self.tab2 = QtWidgets.QTabWidget(self.main_widget)
        self.tab2.setGeometry(QtCore.QRect(200, 40, 880, 561))
        self.tab2.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))

        # Main view: DICOM View
        self.dicom_view = DicomView(self)

        # Main view: DVH
        if self.has_rtss and self.has_rtdose:
            self.dvh = DVH(self)

        # Main view: DICOM Tree
        self.dicom_tree = DicomTreeUI(self)

        # Main view: Clinical Data
        self.tab2_clinical_data = QtWidgets.QWidget()
        self.tab2_clinical_data.setFocusPolicy(QtCore.Qt.NoFocus)
        # check for csv data
        reg = '/CSV/ClinicalData*[.csv]'
        if not glob.glob(self.path + reg):
            self.callClass.display_cd_form(self.tab2, self.path)
        else:
            self.callClass.display_cd_dat(self.tab2, self.path)
        self.tab2.setFocusPolicy(QtCore.Qt.NoFocus)

        splitter.addWidget(self.left_widget)
        splitter.addWidget(self.tab2)
        self.function_layout.addWidget(splitter)

        self.main_layout.addWidget(self.function_widget)

        self.tab1.raise_()
        self.tab2.raise_()

        self.create_footer()

        MainWindow.setCentralWidget(self.main_widget)
        clinical_tab_index = 3 if self.has_rtss and self.has_rtdose else 2
        self.tab2.setTabText(clinical_tab_index, "Clinical Data")
        # self.tab2.setTabText(self.tab2.indexOf(self.tab2_clinical_data), _translate("MainWindow", "Clinical Data"))

        # Menu and Tool bars
        self.menu_bar = MenuBar(self)

        self.tab1.setCurrentIndex(0)
        self.tab2.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        if self.has_rtss:
            self.callROI = ROIDelOption(self)