示例#1
0
    def initUI(self):
        box = QComboBox(self)
        box.resize(box.sizeHint())
        box.setStyleSheet("""
QComboBox::drop-down {border-width: 0px;}
QComboBox::down-arrow {image: url(noimg); border-width: 0px;}
""")
        box.move(50, 50)

        #Using the palette doesn't work:
        pal = box.palette()
        pal.setColor(box.backgroundRole(), QColor())
        box.setAutoFillBackground(True)
        box.setPalette(pal)

        self.setGeometry(300, 300, 250, 150)
        self.show()
示例#2
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()
示例#3
0
class UIManipulateROIWindow:
    def setup_ui(self, manipulate_roi_window_instance, rois, dataset_rtss,
                 roi_color, signal_roi_manipulated):

        self.patient_dict_container = PatientDictContainer()
        self.rois = rois
        self.dataset_rtss = dataset_rtss
        self.signal_roi_manipulated = signal_roi_manipulated
        self.roi_color = roi_color

        self.roi_names = []  # Names of selected ROIs
        self.all_roi_names = []  # Names of all existing ROIs
        for roi_id, roi_dict in self.rois.items():
            self.all_roi_names.append(roi_dict['name'])

        # Operation names
        self.single_roi_operation_names = [
            "Expand", "Contract", "Inner Rind (annulus)",
            "Outer Rind (annulus)"
        ]
        self.multiple_roi_operation_names = [
            "Union", "Intersection", "Difference"
        ]
        self.operation_names = self.multiple_roi_operation_names + \
                               self.single_roi_operation_names

        self.new_ROI_contours = None
        self.manipulate_roi_window_instance = manipulate_roi_window_instance

        self.dicom_view = DicomAxialView(metadata_formatted=True,
                                         is_four_view=True)
        self.dicom_preview = DicomAxialView(metadata_formatted=True,
                                            is_four_view=True)
        self.dicom_view.slider.valueChanged.connect(
            self.dicom_view_slider_value_changed)
        self.dicom_preview.slider.valueChanged.connect(
            self.dicom_preview_slider_value_changed)
        self.init_layout()

        QtCore.QMetaObject.connectSlotsByName(manipulate_roi_window_instance)

    def retranslate_ui(self, manipulate_roi_window_instance):
        _translate = QtCore.QCoreApplication.translate
        manipulate_roi_window_instance.setWindowTitle(
            _translate("ManipulateRoiWindowInstance",
                       "OnkoDICOM - Draw Region Of Interest"))
        self.first_roi_name_label.setText(
            _translate("FirstROINameLabel", "ROI 1: "))
        self.first_roi_name_dropdown_list.setPlaceholderText("ROI 1")
        self.first_roi_name_dropdown_list.addItems(self.all_roi_names)
        self.operation_name_label.setText(
            _translate("OperationNameLabel", "Operation"))
        self.operation_name_dropdown_list.setPlaceholderText("Operation")
        self.operation_name_dropdown_list.addItems(self.operation_names)
        self.second_roi_name_label.setText(
            _translate("SecondROINameLabel", "ROI 2: "))
        self.second_roi_name_dropdown_list.setPlaceholderText("ROI 2")
        self.second_roi_name_dropdown_list.addItems(self.all_roi_names)
        self.manipulate_roi_window_instance_draw_button.setText(
            _translate("ManipulateRoiWindowInstanceDrawButton", "Draw"))
        self.manipulate_roi_window_instance_save_button.setText(
            _translate("ManipulateRoiWindowInstanceSaveButton", "Save"))
        self.manipulate_roi_window_instance_cancel_button.setText(
            _translate("ManipulateRoiWindowInstanceCancelButton", "Cancel"))
        self.margin_label.setText(_translate("MarginLabel", "Margin (mm): "))
        self.new_roi_name_label.setText(
            _translate("NewROINameLabel", "New ROI Name"))
        self.ROI_view_box_label.setText("ROI")
        self.preview_box_label.setText("Preview")

    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 ManipulateROIWindow
        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.manipulate_roi_window_instance.setObjectName(
            "ManipulateRoiWindowInstance")
        self.manipulate_roi_window_instance.setWindowIcon(window_icon)

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

        # Create a label for denoting the first ROI name
        self.first_roi_name_label = QLabel()
        self.first_roi_name_label.setObjectName("FirstROINameLabel")
        self.first_roi_name_dropdown_list = QComboBox()
        # Create an dropdown list for ROI name
        self.first_roi_name_dropdown_list.setObjectName(
            "FirstROINameDropdownList")
        self.first_roi_name_dropdown_list.setSizePolicy(
            QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.first_roi_name_dropdown_list.resize(
            self.first_roi_name_dropdown_list.sizeHint().width(),
            self.first_roi_name_dropdown_list.sizeHint().height())
        self.first_roi_name_dropdown_list.activated.connect(
            self.update_selected_rois)
        self.manipulate_roi_window_input_container_box.addRow(
            self.first_roi_name_label, self.first_roi_name_dropdown_list)

        # Create a label for denoting the operation
        self.operation_name_label = QLabel()
        self.operation_name_label.setObjectName("OperationNameLabel")
        self.operation_name_dropdown_list = QComboBox()
        # Create an dropdown list for operation name
        self.operation_name_dropdown_list.setObjectName(
            "OperationNameDropdownList")
        self.operation_name_dropdown_list.setSizePolicy(
            QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.operation_name_dropdown_list.resize(
            self.operation_name_dropdown_list.sizeHint().width(),
            self.operation_name_dropdown_list.sizeHint().height())
        self.operation_name_dropdown_list.activated.connect(
            self.operation_changed)
        self.manipulate_roi_window_input_container_box.addRow(
            self.operation_name_label, self.operation_name_dropdown_list)

        # Create a label for denoting the second ROI name
        self.second_roi_name_label = QLabel()
        self.second_roi_name_label.setObjectName("SecondROINameLabel")
        self.second_roi_name_label.setVisible(False)
        self.second_roi_name_dropdown_list = QComboBox()
        # Create an dropdown list for ROI name
        self.second_roi_name_dropdown_list.setObjectName(
            "SecondROINameDropdownList")
        self.second_roi_name_dropdown_list.setSizePolicy(
            QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.second_roi_name_dropdown_list.resize(
            self.second_roi_name_dropdown_list.sizeHint().width(),
            self.second_roi_name_dropdown_list.sizeHint().height())
        self.second_roi_name_dropdown_list.setVisible(False)
        self.second_roi_name_dropdown_list.activated.connect(
            self.update_selected_rois)
        self.manipulate_roi_window_input_container_box.addRow(
            self.second_roi_name_label, self.second_roi_name_dropdown_list)

        # Create a label for denoting the margin
        self.margin_label = QLabel()
        self.margin_label.setObjectName("MarginLabel")
        self.margin_label.setVisible(False)
        # Create input for the new ROI name
        self.margin_line_edit = QLineEdit()
        self.margin_line_edit.setObjectName("MarginInput")
        self.margin_line_edit.setSizePolicy(QSizePolicy.MinimumExpanding,
                                            QSizePolicy.Minimum)
        self.margin_line_edit.resize(self.margin_line_edit.sizeHint().width(),
                                     self.margin_line_edit.sizeHint().height())
        self.margin_line_edit.setVisible(False)
        self.margin_line_edit.setValidator(
            QRegularExpressionValidator(
                QRegularExpression("^[0-9]*[.]?[0-9]*$")))
        self.manipulate_roi_window_input_container_box.addRow(
            self.margin_label, self.margin_line_edit)

        # Create a label for denoting the new ROI name
        self.new_roi_name_label = QLabel()
        self.new_roi_name_label.setObjectName("NewROINameLabel")
        # Create input for the new ROI name
        self.new_roi_name_line_edit = QLineEdit()
        self.new_roi_name_line_edit.setObjectName("NewROINameInput")
        self.new_roi_name_line_edit.setSizePolicy(QSizePolicy.MinimumExpanding,
                                                  QSizePolicy.Minimum)
        self.new_roi_name_line_edit.resize(
            self.new_roi_name_line_edit.sizeHint().width(),
            self.new_roi_name_line_edit.sizeHint().height())
        self.manipulate_roi_window_input_container_box.addRow(
            self.new_roi_name_label, self.new_roi_name_line_edit)

        # Create a spacer between inputs and buttons
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        spacer.setFocusPolicy(Qt.NoFocus)
        self.manipulate_roi_window_input_container_box.addRow(spacer)

        # Create a warning message when missing inputs
        self.warning_message = QWidget()
        self.warning_message.setContentsMargins(8, 5, 8, 5)
        warning_message_layout = QHBoxLayout()
        warning_message_layout.setAlignment(QtCore.Qt.AlignLeft
                                            | QtCore.Qt.AlignLeft)

        warning_message_icon = QLabel()
        warning_message_icon.setPixmap(
            QtGui.QPixmap(
                resource_path("res/images/btn-icons/alert_icon.png")))
        warning_message_layout.addWidget(warning_message_icon)

        self.warning_message_text = QLabel()
        self.warning_message_text.setStyleSheet("color: red")
        warning_message_layout.addWidget(self.warning_message_text)
        self.warning_message.setLayout(warning_message_layout)
        self.warning_message.setVisible(False)
        self.manipulate_roi_window_input_container_box.addRow(
            self.warning_message)

        # Create a draw button
        self.manipulate_roi_window_instance_draw_button = QPushButton()
        self.manipulate_roi_window_instance_draw_button.setObjectName(
            "ManipulateRoiWindowInstanceDrawButton")
        self.manipulate_roi_window_instance_draw_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))
        self.manipulate_roi_window_instance_draw_button.resize(
            self.manipulate_roi_window_instance_draw_button.sizeHint().width(),
            self.manipulate_roi_window_instance_draw_button.sizeHint().height(
            ))
        self.manipulate_roi_window_instance_draw_button.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.manipulate_roi_window_instance_draw_button.clicked.connect(
            self.onDrawButtonClicked)
        self.manipulate_roi_window_input_container_box.addRow(
            self.manipulate_roi_window_instance_draw_button)

        # Create a horizontal box for saving and cancel the drawing
        self.manipulate_roi_window_cancel_save_box = QHBoxLayout()
        self.manipulate_roi_window_cancel_save_box.setObjectName(
            "ManipulateRoiWindowCancelSaveBox")
        # Create an exit button to cancel the drawing
        # Add a button to go back/exit from the application
        self.manipulate_roi_window_instance_cancel_button = QPushButton()
        self.manipulate_roi_window_instance_cancel_button.setObjectName(
            "ManipulateRoiWindowInstanceCancelButton")
        self.manipulate_roi_window_instance_cancel_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))
        self.manipulate_roi_window_instance_cancel_button.resize(
            self.manipulate_roi_window_instance_cancel_button.sizeHint().width(
            ),
            self.manipulate_roi_window_instance_cancel_button.sizeHint().
            height())
        self.manipulate_roi_window_instance_cancel_button.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.manipulate_roi_window_instance_cancel_button.clicked.connect(
            self.onCancelButtonClicked)
        self.manipulate_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.manipulate_roi_window_instance_cancel_button.setIcon(icon_cancel)
        self.manipulate_roi_window_cancel_save_box.addWidget(
            self.manipulate_roi_window_instance_cancel_button)
        # Create a save button to save all the changes
        self.manipulate_roi_window_instance_save_button = QPushButton()
        self.manipulate_roi_window_instance_save_button.setObjectName(
            "ManipulateRoiWindowInstanceSaveButton")
        self.manipulate_roi_window_instance_save_button.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum))
        self.manipulate_roi_window_instance_save_button.resize(
            self.manipulate_roi_window_instance_save_button.sizeHint().width(),
            self.manipulate_roi_window_instance_save_button.sizeHint().height(
            ))
        self.manipulate_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.manipulate_roi_window_instance_save_button.setIcon(icon_save)
        self.manipulate_roi_window_instance_save_button.clicked.connect(
            self.onSaveClicked)
        self.manipulate_roi_window_cancel_save_box.addWidget(
            self.manipulate_roi_window_instance_save_button)
        self.manipulate_roi_window_input_container_box.addRow(
            self.manipulate_roi_window_cancel_save_box)

        # Creating a horizontal box to hold the ROI view and the preview
        self.manipulate_roi_window_instance_view_box = QHBoxLayout()
        self.manipulate_roi_window_instance_view_box.setObjectName(
            "ManipulateRoiWindowInstanceViewBoxes")
        # Font for the ROI view and preview's labels
        font = QFont()
        font.setBold(True)
        font.setPixelSize(20)
        # Creating the ROI view
        self.ROI_view_box_layout = QVBoxLayout()
        self.ROI_view_box_label = QLabel()
        self.ROI_view_box_label.setFont(font)
        self.ROI_view_box_label.setAlignment(Qt.AlignHCenter)
        self.ROI_view_box_layout.addWidget(self.ROI_view_box_label)
        self.ROI_view_box_layout.addWidget(self.dicom_view)
        self.ROI_view_box_widget = QWidget()
        self.ROI_view_box_widget.setLayout(self.ROI_view_box_layout)
        # Creating the preview
        self.preview_box_layout = QVBoxLayout()
        self.preview_box_label = QLabel()
        self.preview_box_label.setFont(font)
        self.preview_box_label.setAlignment(Qt.AlignHCenter)
        self.preview_box_layout.addWidget(self.preview_box_label)
        self.preview_box_layout.addWidget(self.dicom_preview)
        self.preview_box_widget = QWidget()
        self.preview_box_widget.setLayout(self.preview_box_layout)

        # Add View and Slider into horizontal box
        self.manipulate_roi_window_instance_view_box.addWidget(
            self.ROI_view_box_widget)
        self.manipulate_roi_window_instance_view_box.addWidget(
            self.preview_box_widget)
        # Create a widget to hold the image slice box
        self.manipulate_roi_window_instance_view_widget = QWidget()
        self.manipulate_roi_window_instance_view_widget.setObjectName(
            "ManipulateRoiWindowInstanceActionWidget")
        self.manipulate_roi_window_instance_view_widget.setLayout(
            self.manipulate_roi_window_instance_view_box)

        # Create a horizontal box for containing the input fields and the
        # viewports
        self.manipulate_roi_window_main_box = QHBoxLayout()
        self.manipulate_roi_window_main_box.setObjectName(
            "ManipulateRoiWindowMainBox")
        self.manipulate_roi_window_main_box.addLayout(
            self.manipulate_roi_window_input_container_box, 1)
        self.manipulate_roi_window_main_box.addWidget(
            self.manipulate_roi_window_instance_view_widget, 11)

        # Create a new central widget to hold the horizontal box layout
        self.manipulate_roi_window_instance_central_widget = QWidget()
        self.manipulate_roi_window_instance_central_widget.setObjectName(
            "ManipulateRoiWindowInstanceCentralWidget")
        self.manipulate_roi_window_instance_central_widget.setLayout(
            self.manipulate_roi_window_main_box)

        self.retranslate_ui(self.manipulate_roi_window_instance)
        self.manipulate_roi_window_instance.setStyleSheet(stylesheet)
        self.manipulate_roi_window_instance.setCentralWidget(
            self.manipulate_roi_window_instance_central_widget)
        QtCore.QMetaObject.connectSlotsByName(
            self.manipulate_roi_window_instance)

    def dicom_view_slider_value_changed(self):
        """
        Display selected ROIs in dropbox when moving to another image slice
        """
        self.display_selected_roi()
        if self.dicom_preview.slider.value() != self.dicom_view.slider.value():
            self.dicom_preview.slider.setValue(self.dicom_view.slider.value())

    def dicom_preview_slider_value_changed(self):
        """
        Display generated ROI when moving to another image slice
        """
        self.draw_roi()
        if self.dicom_preview.slider.value() != self.dicom_view.slider.value():
            self.dicom_view.slider.setValue(self.dicom_preview.slider.value())

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

    def onDrawButtonClicked(self):
        """
        Function triggered when the Draw button is pressed from the menu.
        """
        # Hide warning message
        self.warning_message.setVisible(False)

        # Check inputs
        selected_operation = self.operation_name_dropdown_list.currentText()
        roi_1 = self.first_roi_name_dropdown_list.currentText()
        roi_2 = self.second_roi_name_dropdown_list.currentText()
        new_roi_name = self.new_roi_name_line_edit.text()

        # Check the selected inputs and execute the operations
        if roi_1 != "" and new_roi_name != "" and \
                self.margin_line_edit.text() != "" and \
                selected_operation in self.single_roi_operation_names:
            # Single ROI operations
            dict_rois_contours = ROI.get_roi_contour_pixel(
                self.patient_dict_container.get("raw_contour"), [roi_1],
                self.patient_dict_container.get("pixluts"))
            roi_geometry = ROI.roi_to_geometry(dict_rois_contours[roi_1])
            margin = float(self.margin_line_edit.text())

            if selected_operation == self.single_roi_operation_names[0]:
                new_geometry = ROI.scale_roi(roi_geometry, margin)
            elif selected_operation == self.single_roi_operation_names[1]:
                new_geometry = ROI.scale_roi(roi_geometry, -margin)
            elif selected_operation == self.single_roi_operation_names[2]:
                new_geometry = ROI.rind_roi(roi_geometry, -margin)
            else:
                new_geometry = ROI.rind_roi(roi_geometry, margin)
            self.new_ROI_contours = ROI.geometry_to_roi(new_geometry)

            self.draw_roi()
            return True
        elif roi_1 != "" and roi_2 != "" and new_roi_name != "" and \
                selected_operation in self.multiple_roi_operation_names:
            # Multiple ROI operations
            dict_rois_contours = ROI.get_roi_contour_pixel(
                self.patient_dict_container.get("raw_contour"), [roi_1, roi_2],
                self.patient_dict_container.get("pixluts"))
            roi_1_geometry = ROI.roi_to_geometry(dict_rois_contours[roi_1])
            roi_2_geometry = ROI.roi_to_geometry(dict_rois_contours[roi_2])

            # Execute the selected operation
            new_geometry = ROI.manipulate_rois(roi_1_geometry, roi_2_geometry,
                                               selected_operation.upper())
            self.new_ROI_contours = ROI.geometry_to_roi(new_geometry)

            self.draw_roi()
            return True

        self.warning_message_text.setText("Not all values are specified.")
        self.warning_message.setVisible(True)
        return False

    def onSaveClicked(self):
        """ Save the new ROI """
        # Get the name of the new ROI
        new_roi_name = self.new_roi_name_line_edit.text()

        # If the new ROI hasn't been drawn, draw the new ROI. Then if the new
        # ROI is drawn successfully, proceed to save the new ROI.
        if self.new_ROI_contours is None:
            if not self.onDrawButtonClicked():
                return

        # Get a dict to convert SOPInstanceUID to slice id
        slice_ids_dict = get_dict_slice_to_uid(PatientDictContainer())

        # Transform new_ROI_contours to a list of roi information
        rois_to_save = {}

        for uid, contour_sequence in self.new_ROI_contours.items():
            slider_id = slice_ids_dict[uid]
            location = self.patient_dict_container.filepaths[slider_id]
            ds = pydicom.dcmread(location)
            slice_info = {'coords': contour_sequence, 'ds': ds}
            rois_to_save[slider_id] = slice_info

        roi_list = ROI.convert_hull_list_to_contours_data(
            rois_to_save, self.patient_dict_container)

        connectSaveROIProgress(self, roi_list, self.dataset_rtss, new_roi_name,
                               self.roi_saved)

    def draw_roi(self):
        """ Draw the new ROI """
        # Get the new ROI's name
        new_roi_name = self.new_roi_name_line_edit.text()

        # Check if the new ROI contour is None
        if self.new_ROI_contours is None:
            return

        # Get the info required to draw the new ROI
        slider_id = self.dicom_preview.slider.value()
        curr_slice = self.patient_dict_container.get("dict_uid")[slider_id]

        # Calculate the new ROI's polygon
        dict_ROI_contours = {}
        dict_ROI_contours[new_roi_name] = self.new_ROI_contours
        polygons = ROI.calc_roi_polygon(new_roi_name, curr_slice,
                                        dict_ROI_contours)

        # Set the new ROI color
        color = QtGui.QColor()
        color.setRgb(90, 250, 175, 200)
        pen_color = QtGui.QColor(color.red(), color.green(), color.blue())
        pen = QtGui.QPen(pen_color)
        pen.setStyle(QtCore.Qt.PenStyle(1))
        pen.setWidthF(2.0)

        # Draw the new ROI
        self.dicom_preview.update_view()
        for i in range(len(polygons)):
            self.dicom_preview.scene.addPolygon(polygons[i], pen,
                                                QtGui.QBrush(color))

    def update_selected_rois(self):
        """ Get the names of selected ROIs """
        # Hide warning message
        self.warning_message.setVisible(False)

        self.roi_names = []
        if self.first_roi_name_dropdown_list.currentText() != "":
            self.roi_names.append(
                self.first_roi_name_dropdown_list.currentText())
        if self.second_roi_name_dropdown_list.currentText() != "" and \
                self.second_roi_name_dropdown_list.isVisible():
            self.roi_names.append(
                self.second_roi_name_dropdown_list.currentText())

        self.dict_rois_contours_axial = ROI.get_roi_contour_pixel(
            self.patient_dict_container.get("raw_contour"), self.roi_names,
            self.patient_dict_container.get("pixluts"))

        self.display_selected_roi()

    def display_selected_roi(self):
        """ Display selected ROIs """
        # Get the info required to display the selected ROIs
        slider_id = self.dicom_view.slider.value()
        curr_slice = self.patient_dict_container.get("dict_uid")[slider_id]
        self.rois = self.patient_dict_container.get("rois")

        # Display the selected ROIs
        self.dicom_view.update_view()
        for roi_id, roi_dict in self.rois.items():
            roi_name = roi_dict['name']
            if roi_name in self.roi_names:
                polygons = ROI.calc_roi_polygon(roi_name, curr_slice,
                                                self.dict_rois_contours_axial)
                self.dicom_view.draw_roi_polygons(roi_id, polygons,
                                                  self.roi_color)

    def operation_changed(self):
        """ Change the form when users select different operations """
        # Hide warning message
        self.warning_message.setVisible(False)

        selected_operation = self.operation_name_dropdown_list.currentText()
        if selected_operation in self.single_roi_operation_names:
            self.second_roi_name_label.setVisible(False)
            self.second_roi_name_dropdown_list.setVisible(False)
            self.margin_label.setVisible(True)
            self.margin_line_edit.setVisible(True)
            self.update_selected_rois()
        else:
            self.second_roi_name_label.setVisible(True)
            self.second_roi_name_dropdown_list.setVisible(True)
            self.margin_label.setVisible(False)
            self.margin_line_edit.setVisible(False)
            self.update_selected_rois()

    def roi_saved(self, new_rtss):
        """ Create a new ROI in Structure Tab and notify user """
        new_roi_name = self.new_roi_name_line_edit.text()
        self.signal_roi_manipulated.emit((new_rtss, {"draw": new_roi_name}))
        QMessageBox.about(self.manipulate_roi_window_instance, "Saved",
                          "New contour successfully created!")
        self.close()