Exemple #1
0
class PathLine(QWidget):
    """The PathLine class provides file or directory path display."""
    def __init__(self):
        super().__init__()
        self._line_edit = QLineEdit()
        self._line_edit.setReadOnly(True)

        # setup layout
        h_layout = AHBoxLayout(self)
        h_layout.addWidget(self._line_edit)
        h_layout.setContentsMargins(QMargins(0, 0, 0, 0))

    def update_path(self, path_name: str):
        """Update path of this widget. This method changes the display of the gui.

        Parameters
        ----------
        path_name : str
            The name of path.
        """
        self._line_edit.setText(path_name)
        # Moves the cursor position to the beginning of the line.
        self._line_edit.setCursorPosition(0)
class UIDrawROIWindow:
    def setup_ui(self, draw_roi_window_instance, rois, dataset_rtss,
                 signal_roi_drawn):
        """
        this function is responsible for setting up the UI
        for DrawROIWindow
        param draw_roi_window_instance: the current drawing
        window instance.
        :param rois: the rois to be drawn
        :param dataset_rtss: the rtss to be written to
        :param signal_roi_drawn: the signal to be triggered
        when roi is drawn
        """
        self.patient_dict_container = PatientDictContainer()

        self.rois = rois
        self.dataset_rtss = dataset_rtss
        self.signal_roi_drawn = signal_roi_drawn
        self.drawn_roi_list = {}
        self.standard_organ_names = []
        self.standard_volume_names = []
        self.standard_names = []  # Combination of organ and volume
        self.ROI_name = None  # Selected ROI name
        self.target_pixel_coords = []  # This will contain the new pixel
        # coordinates specified by the min and max
        self.drawingROI = None
        self.slice_changed = False
        self.drawing_tool_radius = INITIAL_DRAWING_TOOL_RADIUS
        self.keep_empty_pixel = False
        # pixel density
        self.target_pixel_coords_single_array = []  # 1D array
        self.draw_roi_window_instance = draw_roi_window_instance
        self.colour = None
        self.ds = None
        self.zoom = 1.0

        self.upper_limit = None
        self.lower_limit = None

        # is_four_view is set to True to stop the SUV2ROI button from appearing
        self.dicom_view = DicomAxialView(is_four_view=True)
        self.current_slice = self.dicom_view.slider.value()
        self.dicom_view.slider.valueChanged.connect(self.slider_value_changed)
        self.init_layout()

        QtCore.QMetaObject.connectSlotsByName(draw_roi_window_instance)

    def retranslate_ui(self, draw_roi_window_instance):
        """
            this function retranslate the ui for draw roi window

            :param draw_roi_window_instance: the current drawing
            window instance.
            """
        _translate = QtCore.QCoreApplication.translate
        draw_roi_window_instance.setWindowTitle(
            _translate("DrawRoiWindowInstance",
                       "OnkoDICOM - Draw Region Of Interest"))
        self.roi_name_label.setText(
            _translate("ROINameLabel", "Region of Interest: "))
        self.roi_name_line_edit.setText(_translate("ROINameLineEdit", ""))
        self.image_slice_number_label.setText(
            _translate("ImageSliceNumberLabel", "Slice Number: "))
        self.image_slice_number_line_edit.setText(
            _translate("ImageSliceNumberLineEdit",
                       str(self.dicom_view.current_slice_number)))
        self.image_slice_number_transect_button.setText(
            _translate("ImageSliceNumberTransectButton", "Transect"))
        self.image_slice_number_box_draw_button.setText(
            _translate("ImageSliceNumberBoxDrawButton", "Set Bounds"))
        self.image_slice_number_draw_button.setText(
            _translate("ImageSliceNumberDrawButton", "Draw"))
        self.image_slice_number_move_forward_button.setText(
            _translate("ImageSliceNumberMoveForwardButton", ""))
        self.image_slice_number_move_backward_button.setText(
            _translate("ImageSliceNumberMoveBackwardButton", ""))
        self.draw_roi_window_instance_save_button.setText(
            _translate("DrawRoiWindowInstanceSaveButton", "Save"))
        self.draw_roi_window_instance_cancel_button.setText(
            _translate("DrawRoiWindowInstanceCancelButton", "Cancel"))
        self.internal_hole_max_label.setText(
            _translate("InternalHoleLabel",
                       "Maximum internal hole size (pixels): "))
        self.internal_hole_max_line_edit.setText(
            _translate("InternalHoleInput", "9"))
        self.isthmus_width_max_label.setText(
            _translate("IsthmusWidthLabel",
                       "Maximum isthmus width size (pixels): "))
        self.isthmus_width_max_line_edit.setText(
            _translate("IsthmusWidthInput", "5"))
        self.min_pixel_density_label.setText(
            _translate("MinPixelDensityLabel", "Minimum density (pixels): "))
        self.min_pixel_density_line_edit.setText(
            _translate("MinPixelDensityInput", ""))
        self.max_pixel_density_label.setText(
            _translate("MaxPixelDensityLabel", "Maximum density (pixels): "))
        self.max_pixel_density_line_edit.setText(
            _translate("MaxPixelDensityInput", ""))
        self.toggle_keep_empty_pixel_label.setText(
            _translate("ToggleKeepEmptyPixelLabel", "Keep empty pixel: "))

        self.draw_roi_window_viewport_zoom_label.setText(
            _translate("DrawRoiWindowViewportZoomLabel", "Zoom: "))
        self.draw_roi_window_cursor_radius_change_label.setText(
            _translate("DrawRoiWindowCursorRadiusChangeLabel",
                       "Cursor Radius: "))

        self.draw_roi_window_instance_action_reset_button.setText(
            _translate("DrawRoiWindowInstanceActionClearButton", "Reset"))

    def init_layout(self):
        """
        Initialize the layout for the DICOM View tab.
        Add the view widget and the slider in the layout.
        Add the whole container 'tab2_view' as a tab in the main page.
        """

        # Initialise a DrawROIWindow
        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.draw_roi_window_instance.setObjectName("DrawRoiWindowInstance")
        self.draw_roi_window_instance.setWindowIcon(window_icon)

        # Creating a form box to hold all buttons and input fields
        self.draw_roi_window_input_container_box = QFormLayout()
        self.draw_roi_window_input_container_box. \
            setObjectName("DrawRoiWindowInputContainerBox")
        self.draw_roi_window_input_container_box. \
            setLabelAlignment(Qt.AlignLeft)

        # Create a label for denoting the ROI name
        self.roi_name_label = QLabel()
        self.roi_name_label.setObjectName("ROINameLabel")
        self.roi_name_line_edit = QLineEdit()
        # Create an input box for ROI name
        self.roi_name_line_edit.setObjectName("ROINameLineEdit")
        self.roi_name_line_edit.setSizePolicy(QSizePolicy.Minimum,
                                              QSizePolicy.Minimum)
        self.roi_name_line_edit.resize(
            self.roi_name_line_edit.sizeHint().width(),
            self.roi_name_line_edit.sizeHint().height())
        self.roi_name_line_edit.setEnabled(False)
        self.draw_roi_window_input_container_box. \
            addRow(self.roi_name_label, self.roi_name_line_edit)

        # Create horizontal box to store image slice number and backward,
        # forward buttons
        self.image_slice_number_box = QHBoxLayout()
        self.image_slice_number_box.setObjectName("ImageSliceNumberBox")

        # Create a label for denoting the Image Slice Number
        self.image_slice_number_label = QLabel()
        self.image_slice_number_label.setObjectName("ImageSliceNumberLabel")
        self.image_slice_number_box.addWidget(self.image_slice_number_label)
        # Create a line edit for containing the image slice number
        self.image_slice_number_line_edit = QLineEdit()
        self.image_slice_number_line_edit. \
            setObjectName("ImageSliceNumberLineEdit")
        self.image_slice_number_line_edit. \
            setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.image_slice_number_line_edit.resize(
            self.image_slice_number_line_edit.sizeHint().width(),
            self.image_slice_number_line_edit.sizeHint().height())

        self.image_slice_number_line_edit.setCursorPosition(0)
        self.image_slice_number_line_edit.setEnabled(False)
        self.image_slice_number_box. \
            addWidget(self.image_slice_number_line_edit)
        # Create a button to move backward to the previous image
        self.image_slice_number_move_backward_button = QPushButton()
        self.image_slice_number_move_backward_button. \
            setObjectName("ImageSliceNumberMoveBackwardButton")
        self.image_slice_number_move_backward_button.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.image_slice_number_move_backward_button.resize(QSize(24, 24))
        self.image_slice_number_move_backward_button.clicked. \
            connect(self.onBackwardClicked)
        icon_move_backward = QtGui.QIcon()
        icon_move_backward.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/backward_slide_icon.png')))
        self.image_slice_number_move_backward_button.setIcon(
            icon_move_backward)
        self.image_slice_number_box. \
            addWidget(self.image_slice_number_move_backward_button)
        # Create a button to move forward to the next image
        self.image_slice_number_move_forward_button = QPushButton()
        self.image_slice_number_move_forward_button. \
            setObjectName("ImageSliceNumberMoveForwardButton")
        self.image_slice_number_move_forward_button.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.image_slice_number_move_forward_button.resize(QSize(24, 24))
        self.image_slice_number_move_forward_button.clicked. \
            connect(self.onForwardClicked)
        icon_move_forward = QtGui.QIcon()
        icon_move_forward.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/forward_slide_icon.png')))
        self.image_slice_number_move_forward_button.setIcon(icon_move_forward)
        self.image_slice_number_box. \
            addWidget(self.image_slice_number_move_forward_button)

        self.draw_roi_window_input_container_box. \
            addRow(self.image_slice_number_box)

        # Create a horizontal box for containing the zoom function
        self.draw_roi_window_viewport_zoom_box = QHBoxLayout()
        self.draw_roi_window_viewport_zoom_box.setObjectName(
            "DrawRoiWindowViewportZoomBox")
        # Create a label for zooming
        self.draw_roi_window_viewport_zoom_label = QLabel()
        self.draw_roi_window_viewport_zoom_label. \
            setObjectName("DrawRoiWindowViewportZoomLabel")
        # Create an input box for zoom factor
        self.draw_roi_window_viewport_zoom_input = QLineEdit()
        self.draw_roi_window_viewport_zoom_input. \
            setObjectName("DrawRoiWindowViewportZoomInput")
        self.draw_roi_window_viewport_zoom_input. \
            setText("{:.2f}".format(self.zoom * 100) + "%")
        self.draw_roi_window_viewport_zoom_input.setCursorPosition(0)
        self.draw_roi_window_viewport_zoom_input.setEnabled(False)
        self.draw_roi_window_viewport_zoom_input. \
            setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.draw_roi_window_viewport_zoom_input.resize(
            self.draw_roi_window_viewport_zoom_input.sizeHint().width(),
            self.draw_roi_window_viewport_zoom_input.sizeHint().height())
        # Create 2 buttons for zooming in and out
        # Zoom In Button
        self.draw_roi_window_viewport_zoom_in_button = QPushButton()
        self.draw_roi_window_viewport_zoom_in_button. \
            setObjectName("DrawRoiWindowViewportZoomInButton")
        self.draw_roi_window_viewport_zoom_in_button.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.draw_roi_window_viewport_zoom_in_button.resize(QSize(24, 24))
        self.draw_roi_window_viewport_zoom_in_button. \
            setProperty("QPushButtonClass", "zoom-button")
        icon_zoom_in = QtGui.QIcon()
        icon_zoom_in.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/zoom_in_icon.png')))
        self.draw_roi_window_viewport_zoom_in_button.setIcon(icon_zoom_in)
        self.draw_roi_window_viewport_zoom_in_button.clicked. \
            connect(self.onZoomInClicked)
        # Zoom Out Button
        self.draw_roi_window_viewport_zoom_out_button = QPushButton()
        self.draw_roi_window_viewport_zoom_out_button. \
            setObjectName("DrawRoiWindowViewportZoomOutButton")
        self.draw_roi_window_viewport_zoom_out_button.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.draw_roi_window_viewport_zoom_out_button.resize(QSize(24, 24))
        self.draw_roi_window_viewport_zoom_out_button. \
            setProperty("QPushButtonClass", "zoom-button")
        icon_zoom_out = QtGui.QIcon()
        icon_zoom_out.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/zoom_out_icon.png')))
        self.draw_roi_window_viewport_zoom_out_button.setIcon(icon_zoom_out)
        self.draw_roi_window_viewport_zoom_out_button.clicked. \
            connect(self.onZoomOutClicked)
        self.draw_roi_window_viewport_zoom_box. \
            addWidget(self.draw_roi_window_viewport_zoom_label)
        self.draw_roi_window_viewport_zoom_box. \
            addWidget(self.draw_roi_window_viewport_zoom_input)
        self.draw_roi_window_viewport_zoom_box. \
            addWidget(self.draw_roi_window_viewport_zoom_out_button)
        self.draw_roi_window_viewport_zoom_box. \
            addWidget(self.draw_roi_window_viewport_zoom_in_button)
        self.draw_roi_window_input_container_box. \
            addRow(self.draw_roi_window_viewport_zoom_box)

        self.init_cursor_radius_change_box()
        # Create field to toggle two options: Keep empty pixel or fill empty
        # pixel when using draw cursor
        self.toggle_keep_empty_pixel_box = QHBoxLayout()
        self.toggle_keep_empty_pixel_label = QLabel()
        self.toggle_keep_empty_pixel_label. \
            setObjectName("ToggleKeepEmptyPixelLabel")
        # Create input for min pixel size
        self.toggle_keep_empty_pixel_combo_box = QComboBox()
        self.toggle_keep_empty_pixel_combo_box.addItems(["Off", "On"])
        self.toggle_keep_empty_pixel_combo_box.setCurrentIndex(0)
        self.toggle_keep_empty_pixel_combo_box.setEnabled(False)
        self.toggle_keep_empty_pixel_combo_box. \
            setObjectName("ToggleKeepEmptyPixelComboBox")
        self.toggle_keep_empty_pixel_combo_box. \
            setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.toggle_keep_empty_pixel_combo_box.resize(
            self.toggle_keep_empty_pixel_combo_box.sizeHint().width(),
            self.toggle_keep_empty_pixel_combo_box.sizeHint().height())
        self.toggle_keep_empty_pixel_combo_box.currentIndexChanged.connect(
            self.toggle_keep_empty_pixel_box_index_changed)
        self.toggle_keep_empty_pixel_box. \
            addWidget(self.toggle_keep_empty_pixel_label)
        self.toggle_keep_empty_pixel_box. \
            addWidget(self.toggle_keep_empty_pixel_combo_box)
        self.draw_roi_window_input_container_box. \
            addRow(self.toggle_keep_empty_pixel_box)

        # Create a horizontal box for transect and draw button
        self.draw_roi_window_transect_draw_box = QHBoxLayout()
        self.draw_roi_window_transect_draw_box. \
            setObjectName("DrawRoiWindowTransectDrawBox")
        # Create a transect button
        self.image_slice_number_transect_button = QPushButton()
        self.image_slice_number_transect_button. \
            setObjectName("ImageSliceNumberTransectButton")
        self.image_slice_number_transect_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))
        self.image_slice_number_transect_button.resize(
            self.image_slice_number_transect_button.sizeHint().width(),
            self.image_slice_number_transect_button.sizeHint().height())
        self.image_slice_number_transect_button.clicked. \
            connect(self.transect_handler)
        icon_transect = QtGui.QIcon()
        icon_transect.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/transect_icon.png')))
        self.image_slice_number_transect_button.setIcon(icon_transect)
        self.draw_roi_window_transect_draw_box. \
            addWidget(self.image_slice_number_transect_button)
        # Create a bounding box button
        self.image_slice_number_box_draw_button = QPushButton()
        self.image_slice_number_box_draw_button. \
            setObjectName("ImageSliceNumberBoxDrawButton")
        self.image_slice_number_box_draw_button.setSizePolicy(
            QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        self.image_slice_number_box_draw_button.resize(
            self.image_slice_number_box_draw_button.sizeHint().width(),
            self.image_slice_number_box_draw_button.sizeHint().height())
        self.image_slice_number_box_draw_button.clicked. \
            connect(self.onBoxDrawClicked)
        icon_box_draw = QtGui.QIcon()
        icon_box_draw.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/draw_bound_icon.png')))
        self.image_slice_number_box_draw_button.setIcon(icon_box_draw)
        self.draw_roi_window_transect_draw_box. \
            addWidget(self.image_slice_number_box_draw_button)
        # Create a draw button
        self.image_slice_number_draw_button = QPushButton()
        self.image_slice_number_draw_button. \
            setObjectName("ImageSliceNumberDrawButton")
        self.image_slice_number_draw_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))
        self.image_slice_number_draw_button.resize(
            self.image_slice_number_draw_button.sizeHint().width(),
            self.image_slice_number_draw_button.sizeHint().height())
        self.image_slice_number_draw_button.clicked.connect(self.onDrawClicked)
        icon_draw = QtGui.QIcon()
        icon_draw.addPixmap(
            QtGui.QPixmap(resource_path('res/images/btn-icons/draw_icon.png')))
        self.image_slice_number_draw_button.setIcon(icon_draw)
        self.draw_roi_window_transect_draw_box. \
            addWidget(self.image_slice_number_draw_button)
        self.draw_roi_window_input_container_box. \
            addRow(self.draw_roi_window_transect_draw_box)

        # Create a contour preview button
        self.row_preview_layout = QtWidgets.QHBoxLayout()
        self.button_contour_preview = QtWidgets.QPushButton("Preview contour")
        self.button_contour_preview.clicked.connect(self.onPreviewClicked)
        self.row_preview_layout.addWidget(self.button_contour_preview)
        self.draw_roi_window_input_container_box. \
            addRow(self.row_preview_layout)
        icon_preview = QtGui.QIcon()
        icon_preview.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/preview_icon.png')))
        self.button_contour_preview.setIcon(icon_preview)

        # Create input line edit for alpha value
        self.label_alpha_value = QtWidgets.QLabel("Alpha value:")
        self.input_alpha_value = QtWidgets.QLineEdit("0.2")
        self.input_alpha_value. \
            setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        self.input_alpha_value.resize(
            self.input_alpha_value.sizeHint().width(),
            self.input_alpha_value.sizeHint().height())
        self.input_alpha_value.setValidator(
            QRegularExpressionValidator(
                QRegularExpression("^[0-9]*[.]?[0-9]*$")))
        self.draw_roi_window_input_container_box. \
            addRow(self.label_alpha_value, self.input_alpha_value)

        # Create a label for denoting the max internal hole size
        self.internal_hole_max_label = QLabel()
        self.internal_hole_max_label.setObjectName("InternalHoleLabel")

        # Create input for max internal hole size
        self.internal_hole_max_line_edit = QLineEdit()
        self.internal_hole_max_line_edit.setObjectName("InternalHoleInput")
        self.internal_hole_max_line_edit. \
            setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        self.internal_hole_max_line_edit.resize(
            self.internal_hole_max_line_edit.sizeHint().width(),
            self.internal_hole_max_line_edit.sizeHint().height())
        self.internal_hole_max_line_edit.setValidator(
            QRegularExpressionValidator(
                QRegularExpression("^[0-9]*[.]?[0-9]*$")))
        self.draw_roi_window_input_container_box.addRow(
            self.internal_hole_max_label, self.internal_hole_max_line_edit)

        # Create a label for denoting the isthmus width size
        self.isthmus_width_max_label = QLabel()
        self.isthmus_width_max_label.setObjectName("IsthmusWidthLabel")
        # Create input for max isthmus width size
        self.isthmus_width_max_line_edit = QLineEdit()
        self.isthmus_width_max_line_edit.setObjectName("IsthmusWidthInput")
        self.isthmus_width_max_line_edit.setSizePolicy(
            QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        self.isthmus_width_max_line_edit.resize(
            self.isthmus_width_max_line_edit.sizeHint().width(),
            self.isthmus_width_max_line_edit.sizeHint().height())
        self.isthmus_width_max_line_edit.setValidator(
            QRegularExpressionValidator(
                QRegularExpression("^[0-9]*[.]?[0-9]*$")))
        self.draw_roi_window_input_container_box.addRow(
            self.isthmus_width_max_label, self.isthmus_width_max_line_edit)

        # Create a label for denoting the minimum pixel density
        self.min_pixel_density_label = QLabel()
        self.min_pixel_density_label.setObjectName("MinPixelDensityLabel")
        # Create input for min pixel size
        self.min_pixel_density_line_edit = QLineEdit()
        self.min_pixel_density_line_edit.setObjectName("MinPixelDensityInput")
        self.min_pixel_density_line_edit.setSizePolicy(
            QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        self.min_pixel_density_line_edit.resize(
            self.min_pixel_density_line_edit.sizeHint().width(),
            self.min_pixel_density_line_edit.sizeHint().height())
        self.min_pixel_density_line_edit.setValidator(
            QRegularExpressionValidator(
                QRegularExpression("^[0-9]*[.]?[0-9]*$")))
        self.draw_roi_window_input_container_box.addRow(
            self.min_pixel_density_label, self.min_pixel_density_line_edit)

        # Create a label for denoting the minimum pixel density
        self.max_pixel_density_label = QLabel()
        self.max_pixel_density_label.setObjectName("MaxPixelDensityLabel")
        # Create input for min pixel size
        self.max_pixel_density_line_edit = QLineEdit()
        self.max_pixel_density_line_edit.setObjectName("MaxPixelDensityInput")
        self.max_pixel_density_line_edit. \
            setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
        self.max_pixel_density_line_edit.resize(
            self.max_pixel_density_line_edit.sizeHint().width(),
            self.max_pixel_density_line_edit.sizeHint().height())
        self.max_pixel_density_line_edit.setValidator(
            QRegularExpressionValidator(
                QRegularExpression("^[0-9]*[.]?[0-9]*$")))
        self.draw_roi_window_input_container_box.addRow(
            self.max_pixel_density_label, self.max_pixel_density_line_edit)

        # Create a button to clear the draw
        self.draw_roi_window_instance_action_reset_button = QPushButton()
        self.draw_roi_window_instance_action_reset_button. \
            setObjectName("DrawRoiWindowInstanceActionClearButton")
        self.draw_roi_window_instance_action_reset_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))

        reset_button = self.draw_roi_window_instance_action_reset_button
        self.draw_roi_window_instance_action_reset_button.resize(
            reset_button.sizeHint().width(),
            reset_button.sizeHint().height())
        self.draw_roi_window_instance_action_reset_button.clicked. \
            connect(self.onResetClicked)
        self.draw_roi_window_instance_action_reset_button. \
            setProperty("QPushButtonClass", "fail-button")
        icon_clear_roi_draw = QtGui.QIcon()
        icon_clear_roi_draw.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/reset_roi_draw_icon.png')))
        self.draw_roi_window_instance_action_reset_button. \
            setIcon(icon_clear_roi_draw)
        self.draw_roi_window_input_container_box. \
            addRow(self.draw_roi_window_instance_action_reset_button)

        # Create a horizontal box for saving and cancel the drawing
        self.draw_roi_window_cancel_save_box = QHBoxLayout()
        self.draw_roi_window_cancel_save_box. \
            setObjectName("DrawRoiWindowCancelSaveBox")
        # Create an exit button to cancel the drawing
        # Add a button to go back/exit from the application
        self.draw_roi_window_instance_cancel_button = QPushButton()
        self.draw_roi_window_instance_cancel_button. \
            setObjectName("DrawRoiWindowInstanceCancelButton")
        self.draw_roi_window_instance_cancel_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))
        self.draw_roi_window_instance_cancel_button.resize(
            self.draw_roi_window_instance_cancel_button.sizeHint().width(),
            self.draw_roi_window_instance_cancel_button.sizeHint().height())
        self.draw_roi_window_instance_cancel_button. \
            setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.draw_roi_window_instance_cancel_button.clicked. \
            connect(self.onCancelButtonClicked)
        self.draw_roi_window_instance_cancel_button. \
            setProperty("QPushButtonClass", "fail-button")
        icon_cancel = QtGui.QIcon()
        icon_cancel.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/cancel_icon.png')))
        self.draw_roi_window_instance_cancel_button.setIcon(icon_cancel)
        self.draw_roi_window_cancel_save_box. \
            addWidget(self.draw_roi_window_instance_cancel_button)
        # Create a save button to save all the changes
        self.draw_roi_window_instance_save_button = QPushButton()
        self.draw_roi_window_instance_save_button. \
            setObjectName("DrawRoiWindowInstanceSaveButton")
        self.draw_roi_window_instance_save_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))
        self.draw_roi_window_instance_save_button.resize(
            self.draw_roi_window_instance_save_button.sizeHint().width(),
            self.draw_roi_window_instance_save_button.sizeHint().height())
        self.draw_roi_window_instance_save_button. \
            setProperty("QPushButtonClass", "success-button")
        icon_save = QtGui.QIcon()
        icon_save.addPixmap(
            QtGui.QPixmap(resource_path('res/images/btn-icons/save_icon.png')))
        self.draw_roi_window_instance_save_button.setIcon(icon_save)
        self.draw_roi_window_instance_save_button.clicked. \
            connect(self.onSaveClicked)
        self.draw_roi_window_cancel_save_box. \
            addWidget(self.draw_roi_window_instance_save_button)

        self.draw_roi_window_input_container_box. \
            addRow(self.draw_roi_window_cancel_save_box)

        # Creating a horizontal box to hold the ROI view and slider
        self.draw_roi_window_instance_view_box = QHBoxLayout()
        self.draw_roi_window_instance_view_box. \
            setObjectName("DrawRoiWindowInstanceViewBox")
        # Add View and Slider into horizontal box
        self.draw_roi_window_instance_view_box.addWidget(self.dicom_view)
        # Create a widget to hold the image slice box
        self.draw_roi_window_instance_view_widget = QWidget()
        self.draw_roi_window_instance_view_widget.setObjectName(
            "DrawRoiWindowInstanceActionWidget")
        self.draw_roi_window_instance_view_widget.setLayout(
            self.draw_roi_window_instance_view_box)

        # Create a horizontal box for containing the input fields and the
        # viewport
        self.draw_roi_window_main_box = QHBoxLayout()
        self.draw_roi_window_main_box.setObjectName("DrawRoiWindowMainBox")
        self.draw_roi_window_main_box. \
            addLayout(self.draw_roi_window_input_container_box, 1)
        self.draw_roi_window_main_box. \
            addWidget(self.draw_roi_window_instance_view_widget, 11)

        # Create a new central widget to hold the vertical box layout
        self.draw_roi_window_instance_central_widget = QWidget()
        self.draw_roi_window_instance_central_widget. \
            setObjectName("DrawRoiWindowInstanceCentralWidget")
        self.draw_roi_window_instance_central_widget.setLayout(
            self.draw_roi_window_main_box)

        self.retranslate_ui(self.draw_roi_window_instance)
        self.draw_roi_window_instance.setStyleSheet(stylesheet)
        self.draw_roi_window_instance. \
            setCentralWidget(self.draw_roi_window_instance_central_widget)
        QtCore.QMetaObject.connectSlotsByName(self.draw_roi_window_instance)

    def slider_value_changed(self):
        """
        actions to be taken when slider value changes

        """
        image_slice_number = self.current_slice
        # save progress
        self.save_drawing_progress(image_slice_number)
        self.set_current_slice(self.dicom_view.slider.value())

    def set_current_slice(self, slice_number):
        """
            set the current slice
            :param slice_number: the slice number to be set
        """
        self.image_slice_number_line_edit.setText(str(slice_number + 1))
        self.current_slice = slice_number
        self.dicom_view.update_view()

        # check if this slice has any drawings before
        if self.drawn_roi_list.get(self.current_slice) is not None:
            self.drawingROI = self.drawn_roi_list[
                self.current_slice]['drawingROI']
            self.ds = self.drawn_roi_list[self.current_slice]['ds']
            self.dicom_view.view.setScene(self.drawingROI)
            self.enable_cursor_radius_change_box()
            self.drawingROI.clear_cursor(self.drawing_tool_radius)

        else:
            self.disable_cursor_radius_change_box()
            self.ds = None

    def onZoomInClicked(self):
        """
        This function is used for zooming in button
        """
        self.dicom_view.zoom *= 1.05
        self.dicom_view.update_view(zoom_change=True)
        if self.drawingROI \
                and self.drawingROI.current_slice == self.current_slice:
            self.dicom_view.view.setScene(self.drawingROI)
        self.draw_roi_window_viewport_zoom_input.setText(
            "{:.2f}".format(self.dicom_view.zoom * 100) + "%")
        self.draw_roi_window_viewport_zoom_input.setCursorPosition(0)

    def onZoomOutClicked(self):
        """
        This function is used for zooming out button
        """
        self.dicom_view.zoom /= 1.05
        self.dicom_view.update_view(zoom_change=True)
        if self.drawingROI \
                and self.drawingROI.current_slice == self.current_slice:
            self.dicom_view.view.setScene(self.drawingROI)
        self.draw_roi_window_viewport_zoom_input. \
            setText("{:.2f}".format(self.dicom_view.zoom * 100) + "%")
        self.draw_roi_window_viewport_zoom_input.setCursorPosition(0)

    def toggle_keep_empty_pixel_box_index_changed(self):
        self.keep_empty_pixel = self.toggle_keep_empty_pixel_combo_box. \
                                    currentText() == "On"
        self.drawingROI.keep_empty_pixel = self.keep_empty_pixel

    def onCancelButtonClicked(self):
        """
        This function is used for canceling the drawing
        """
        self.closeWindow()

    def onBackwardClicked(self):
        """
        This function is used when backward button is clicked
        """
        image_slice_number = self.current_slice
        # save progress
        if self.save_drawing_progress(image_slice_number):
            # Backward will only execute if current image slice is above 0.
            if int(image_slice_number) > 0:
                # decrements slice by 1 and update slider to move to correct
                # position
                self.dicom_view.slider.setValue(image_slice_number - 1)

    def onForwardClicked(self):
        """
        This function is used when forward button is clicked
        """
        image_slice_number = self.current_slice
        # save progress
        if self.save_drawing_progress(image_slice_number):
            pixmaps = self.patient_dict_container.get("pixmaps_axial")
            total_slices = len(pixmaps)

            # Forward will only execute if current image slice is below the
            # total number of slices.
            if int(image_slice_number) < total_slices:
                # increments slice by 1 and update slider to move to correct
                # position
                self.dicom_view.slider.setValue(image_slice_number + 1)

    def onResetClicked(self):
        """
        This function is used when reset button is clicked
        """
        self.dicom_view.image_display()
        self.dicom_view.update_view()
        self.isthmus_width_max_line_edit.setText("5")
        self.internal_hole_max_line_edit.setText("9")
        self.min_pixel_density_line_edit.setText("")
        self.max_pixel_density_line_edit.setText("")
        if hasattr(self, 'bounds_box_draw'):
            delattr(self, 'bounds_box_draw')
        if hasattr(self, 'drawingROI'):
            delattr(self, 'drawingROI')
        self.ds = None

    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,
        )

    def save_drawing_progress(self, image_slice_number):
        """
        this function saves the drawing progress on current slice
        :param image_slice_number: the slice number to be saved
        """
        if self.slice_changed:
            if hasattr(self, 'drawingROI') and self.drawingROI \
                    and self.ds is not None \
                    and len(self.drawingROI.target_pixel_coords) != 0:
                alpha = float(self.input_alpha_value.text())
                pixel_hull_list = calculate_concave_hull_of_points(
                    self.drawingROI.target_pixel_coords, alpha)
                coord_list = []
                for pixel_hull in pixel_hull_list:
                    coord_list.append(pixel_hull)
                self.drawn_roi_list[image_slice_number] = {
                    'coords': coord_list,
                    'ds': self.ds,
                    'drawingROI': self.drawingROI
                }
                self.slice_changed = False
                return True
        else:
            return True

        return True

    def on_transect_close(self):
        """
        Function triggered when transect is closed
        """
        if self.upper_limit and self.lower_limit:
            self.min_pixel_density_line_edit.setText(str(self.lower_limit))
            self.max_pixel_density_line_edit.setText(str(self.upper_limit))

        self.dicom_view.update_view()

    def onDrawClicked(self):
        """
        Function triggered when the Draw button is pressed from the menu.
        """
        pixmaps = self.patient_dict_container.get("pixmaps_axial")

        if self.min_pixel_density_line_edit.text() == "" \
                or self.max_pixel_density_line_edit.text() == "":
            QMessageBox.about(self.draw_roi_window_instance, "Not Enough Data",
                              "Not all values are specified or correct.")
        else:
            # Getting most updated selected slice
            id = self.current_slice

            dt = self.patient_dict_container.dataset[id]
            dt.convert_pixel_data()

            # Path to the selected .dcm file
            location = self.patient_dict_container.filepaths[id]
            self.ds = pydicom.dcmread(location)

            min_pixel = self.min_pixel_density_line_edit.text()
            max_pixel = self.max_pixel_density_line_edit.text()

            # If they are number inputs
            if min_pixel.isdecimal() and max_pixel.isdecimal():

                min_pixel = int(min_pixel)
                max_pixel = int(max_pixel)

                if min_pixel >= max_pixel:
                    QMessageBox.about(
                        self.draw_roi_window_instance, "Incorrect Input",
                        "Please ensure maximum density is "
                        "atleast higher than minimum density.")

                self.drawingROI = Drawing(
                    pixmaps[id], dt._pixel_array.transpose(), min_pixel,
                    max_pixel, self.patient_dict_container.dataset[id],
                    self.draw_roi_window_instance, self.slice_changed,
                    self.current_slice, self.drawing_tool_radius,
                    self.keep_empty_pixel, set())
                self.slice_changed = True
                self.dicom_view.view.setScene(self.drawingROI)
                self.enable_cursor_radius_change_box()
            else:
                QMessageBox.about(self.draw_roi_window_instance,
                                  "Not Enough Data",
                                  "Not all values are specified or correct.")

    def onBoxDrawClicked(self):
        """
        Function triggered when bounding box button is pressed
        """
        id = self.current_slice
        dt = self.patient_dict_container.dataset[id]
        dt.convert_pixel_data()
        pixmaps = self.patient_dict_container.get("pixmaps_axial")

        self.bounds_box_draw = DrawBoundingBox(pixmaps[id], dt)
        self.dicom_view.view.setScene(self.bounds_box_draw)
        self.disable_cursor_radius_change_box()

    def onSaveClicked(self):
        """
            Function triggered when Save button is clicked
        """
        # Make sure the user has clicked Draw first
        if self.save_drawing_progress(image_slice_number=self.current_slice):
            self.saveROIList()

    def saveROIList(self):
        """
            Function triggered when saving ROI list
        """
        roi_list = ROI.convert_hull_list_to_contours_data(
            self.drawn_roi_list, self.patient_dict_container)
        if len(roi_list) == 0:
            QMessageBox.about(self.draw_roi_window_instance, "No ROI Detected",
                              "Please ensure you have drawn your ROI first.")
            return

        # The list of points will need to be converted into a
        # single-dimensional array, as RTSTRUCT contour data is stored in
        # such a way. i.e. [x, y, z, x, y, z, x, y, z, ..., ...] Create a
        # popup window that modifies the RTSTRUCT and tells the user that
        # processing is happening.
        connectSaveROIProgress(self, roi_list, self.dataset_rtss,
                               self.ROI_name, self.roi_saved)

    def roi_saved(self, new_rtss):
        """
            Function to call save ROI and display progress
        """
        self.signal_roi_drawn.emit((new_rtss, {"draw": self.ROI_name}))
        QMessageBox.about(self.draw_roi_window_instance, "Saved",
                          "New contour successfully created!")
        self.closeWindow()

    def onPreviewClicked(self):
        """
        function triggered when Preview button is clicked
        """
        if hasattr(self, 'drawingROI') and self.drawingROI and len(
                self.drawingROI.target_pixel_coords) > 0:
            alpha = float(self.input_alpha_value.text())
            polygon_list = calculate_concave_hull_of_points(
                self.drawingROI.target_pixel_coords, alpha)
            self.drawingROI.draw_contour_preview(polygon_list)
        else:
            QMessageBox.about(self.draw_roi_window_instance, "Not Enough Data",
                              "Please ensure you have drawn your ROI first.")

    def set_selected_roi_name(self, roi_name):
        """
        function to set selected roi name
        :param roi_name: roi name selected
        """
        roi_exists = False

        patient_dict_container = PatientDictContainer()
        existing_rois = patient_dict_container.get("rois")
        number_of_rois = len(existing_rois)

        # Check to see if the ROI already exists
        for key, value in existing_rois.items():
            if roi_name in value['name']:
                roi_exists = True

        if roi_exists:
            QMessageBox.about(self.draw_roi_window_instance,
                              "ROI already exists in RTSS",
                              "Would you like to continue?")

        self.ROI_name = roi_name
        self.roi_name_line_edit.setText(self.ROI_name)

    def onRadiusReduceClicked(self):
        """
        function triggered when user reduce cursor radius
        """
        self.drawing_tool_radius = max(self.drawing_tool_radius - 1, 4)
        self.draw_roi_window_cursor_radius_change_input.setText(
            str(self.drawing_tool_radius))
        self.draw_roi_window_cursor_radius_change_input.setCursorPosition(0)
        self.draw_cursor_when_radius_changed()

    def onRadiusIncreaseClicked(self):
        """
        function triggered when user increase cursor radius
        """
        self.drawing_tool_radius = min(self.drawing_tool_radius + 1, 25)
        self.draw_roi_window_cursor_radius_change_input.setText(
            str(self.drawing_tool_radius))
        self.draw_cursor_when_radius_changed()

    def draw_cursor_when_radius_changed(self):
        """
        function to update drawing cursor when radius changed
        """
        if self.drawingROI.cursor:
            self.drawingROI.draw_cursor(
                self.drawingROI.current_cursor_x + self.drawing_tool_radius,
                self.drawingROI.current_cursor_y + self.drawing_tool_radius,
                self.drawing_tool_radius)
        else:
            self.drawingROI.draw_cursor(
                (self.drawingROI.min_x + self.drawingROI.max_x) / 2,
                (self.drawingROI.min_y + self.drawingROI.max_y) / 2,
                self.drawing_tool_radius, True)

    def init_cursor_radius_change_box(self):
        """
        function to init cursor radius change box
        """
        # Create a horizontal box for containing the cursor radius changing
        # function
        self.draw_roi_window_cursor_radius_change_box = QHBoxLayout()
        self.draw_roi_window_cursor_radius_change_box.setObjectName(
            "DrawRoiWindowCursorRadiusChangeBox")
        # Create a label for cursor radius change
        self.draw_roi_window_cursor_radius_change_label = QLabel()
        self.draw_roi_window_cursor_radius_change_label.setObjectName(
            "DrawRoiWindowCursorRadiusChangeLabel")
        # Create an input box for cursor radius
        self.draw_roi_window_cursor_radius_change_input = QLineEdit()
        self.draw_roi_window_cursor_radius_change_input.setObjectName(
            "DrawRoiWindowCursorRadiusChangeInput")
        self.draw_roi_window_cursor_radius_change_input.setText(str(19))
        self.draw_roi_window_cursor_radius_change_input.setCursorPosition(0)
        self.draw_roi_window_cursor_radius_change_input.setEnabled(False)
        self.draw_roi_window_cursor_radius_change_input.setSizePolicy(
            QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.draw_roi_window_cursor_radius_change_input.resize(
            self.draw_roi_window_cursor_radius_change_input.sizeHint().width(),
            self.draw_roi_window_cursor_radius_change_input.sizeHint().height(
            ))
        # Create 2 buttons for increasing and reducing cursor radius
        # Increase Button
        self.draw_roi_window_cursor_radius_change_increase_button = \
            QPushButton()
        self.draw_roi_window_cursor_radius_change_increase_button. \
            setObjectName("DrawRoiWindowCursorRadiusIncreaseButton")
        self.draw_roi_window_cursor_radius_change_increase_button. \
            setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.draw_roi_window_cursor_radius_change_increase_button.resize(
            QSize(24, 24))
        self.draw_roi_window_cursor_radius_change_increase_button.setProperty(
            "QPushButtonClass", "zoom-button")
        icon_zoom_in = QtGui.QIcon()
        icon_zoom_in.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/zoom_in_icon.png')))
        self.draw_roi_window_cursor_radius_change_increase_button.setIcon(
            icon_zoom_in)
        self.draw_roi_window_cursor_radius_change_increase_button.clicked. \
            connect(self.onRadiusIncreaseClicked)
        # Reduce Button
        self.draw_roi_window_cursor_radius_change_reduce_button = QPushButton()
        self.draw_roi_window_cursor_radius_change_reduce_button.setObjectName(
            "DrawRoiWindowCursorRadiusReduceButton")
        self.draw_roi_window_cursor_radius_change_reduce_button.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.draw_roi_window_cursor_radius_change_reduce_button.resize(
            QSize(24, 24))
        self.draw_roi_window_cursor_radius_change_reduce_button.setProperty(
            "QPushButtonClass", "zoom-button")
        icon_zoom_out = QtGui.QIcon()
        icon_zoom_out.addPixmap(
            QtGui.QPixmap(
                resource_path('res/images/btn-icons/zoom_out_icon.png')))
        self.draw_roi_window_cursor_radius_change_reduce_button.setIcon(
            icon_zoom_out)
        self.draw_roi_window_cursor_radius_change_reduce_button.clicked. \
            connect(self.onRadiusReduceClicked)
        self.draw_roi_window_cursor_radius_change_box.addWidget(
            self.draw_roi_window_cursor_radius_change_label)
        self.draw_roi_window_cursor_radius_change_box.addWidget(
            self.draw_roi_window_cursor_radius_change_input)
        self.draw_roi_window_cursor_radius_change_box.addWidget(
            self.draw_roi_window_cursor_radius_change_reduce_button)
        self.draw_roi_window_cursor_radius_change_box.addWidget(
            self.draw_roi_window_cursor_radius_change_increase_button)
        self.draw_roi_window_input_container_box.addRow(
            self.draw_roi_window_cursor_radius_change_box)
        self.draw_roi_window_cursor_radius_change_increase_button.setEnabled(
            False)
        self.draw_roi_window_cursor_radius_change_reduce_button.setEnabled(
            False)

    def disable_cursor_radius_change_box(self):
        """
        function  to disable cursor radius change box
        """
        self.draw_roi_window_cursor_radius_change_reduce_button.setEnabled(
            False)
        self.draw_roi_window_cursor_radius_change_increase_button.setEnabled(
            False)
        self.toggle_keep_empty_pixel_combo_box.setEnabled(False)

    def enable_cursor_radius_change_box(self):
        """
        function  to enable cursor radius change box
        """
        self.draw_roi_window_cursor_radius_change_reduce_button.setEnabled(
            True)
        self.draw_roi_window_cursor_radius_change_increase_button.setEnabled(
            True)
        self.toggle_keep_empty_pixel_combo_box.setEnabled(True)

    def closeWindow(self):
        """
        function to close draw roi window
        """
        self.drawn_roi_list = {}
        if hasattr(self, 'bounds_box_draw'):
            delattr(self, 'bounds_box_draw')
        if hasattr(self, 'drawingROI'):
            delattr(self, 'drawingROI')
        self.ds = None
        self.close()