def _create_main_widget(self) -> QWidget:
        self.__current_left_boundary: Optional[QColor] = None
        self.__current_right_boundary: Optional[QColor] = None
        self.__supported_color_spaces = {0: 'HSV'}
        self.__chosen_color_space_index: int = 0

        self.__main_widget = QWidget()
        self.__main_widget_layout = QGridLayout(self.__main_widget)

        self.__color_space_widget = ChooseOneOfWidget(
            'Color-space',
            self.__supported_color_spaces,
            checked_index=self.__chosen_color_space_index)
        self.__main_widget_layout.addWidget(self.__color_space_widget, 0, 0)
        self.__show_im_cb = QCheckBox('Show result immediately', self)
        self.__main_widget_layout.addWidget(self.__show_im_cb, 0, 1)
        self.__left_boundary_picker = MinimalColorPickerWidget(
            SupportedColorSpaces.HSV, 'Select left boundary')
        self.__left_boundary_picker.color_changed.connect(
            self.__set_left_boundary)
        self.__right_boundary_picker = MinimalColorPickerWidget(
            SupportedColorSpaces.HSV, 'Select right boundary')
        self.__right_boundary_picker.color_changed.connect(
            self.__set_right_boundary)
        self.__main_widget_layout.addWidget(self.__left_boundary_picker, 1, 0)
        self.__main_widget_layout.addWidget(self.__right_boundary_picker, 1, 1)

        return self.__main_widget
    def _create_main_widget(self) -> QWidget:
        self.__main_widget = QWidget()
        self.__layout = QVBoxLayout(self.__main_widget)

        self.__method_widget = ChooseOneOfWidget('Method', {
            cv.HOUGH_GRADIENT: 'HOUGH_GRADIENT',
            cv.HOUGH_GRADIENT_ALT: 'HOUGH_GRADIENT_ALT'
        }, checked_index=cv.HOUGH_GRADIENT_ALT)

        self.__layout.addWidget(self.__method_widget)

        params_layout = QFormLayout()
        self.__dp_line_edit = QLineEdit()
        params_layout.addRow(QLabel('dp'), self.__dp_line_edit)
        self.__min_dist_line_edit = QLineEdit()
        params_layout.addRow(QLabel('minDist'), self.__min_dist_line_edit)
        self.__param1_line_edit = QLineEdit()
        params_layout.addRow(QLabel('param1'), self.__param1_line_edit)
        self.__param2_line_edit = QLineEdit()
        params_layout.addRow(QLabel('param2'), self.__param2_line_edit)
        self.__min_radius_line_edit = QLineEdit()
        params_layout.addRow(QLabel('minRadius'), self.__min_radius_line_edit)
        self.__max_radius_line_edit = QLineEdit()
        params_layout.addRow(QLabel('maxRadius'), self.__max_radius_line_edit)

        self.__layout.addLayout(params_layout)

        self.__detect_button = QPushButton('Detect')
        self.__detect_button.clicked.connect(self.__on_detect_button_clicked)
        self.__layout.addWidget(self.__detect_button)

        return self.__main_widget
    def _create_main_widget(self) -> QWidget:
        self.__main_widget = QWidget()
        self.__main_widget_layout = QGridLayout(self.__main_widget)
        self.__morph_op = ChooseOneOfWidget(
            'Morphological operation', {
                0: 'Erode',
                1: 'Dilate',
                2: 'Morphological gradient',
                3: 'Opening: dilate(erode(src))',
                4: 'Closing: erode(dilate(src))'
            })
        self.__morph_op.toggled.connect(self.__apply_morph)
        self.__main_widget_layout.addWidget(self.__morph_op, 0, 0)
        self.__morph_type = ChooseOneOfWidget(
            'Shape', {
                cv.MORPH_RECT: 'Rect',
                cv.MORPH_CROSS: 'Cross',
                cv.MORPH_ELLIPSE: 'Ellipse'
            })
        self.__morph_type.toggled.connect(self.__apply_morph)
        self.__main_widget_layout.addWidget(self.__morph_type, 1, 0)
        self.__kernel_size_slider = SliderWidget(
            'Anchor (Kernel size = 2*Anchor+1)', 0, 40)
        self.__kernel_size_slider.value_changed.connect(
            self.__apply_kernel_size)
        self.__main_widget_layout.addWidget(self.__kernel_size_slider, 0, 1)

        return self.__main_widget
    def _create_main_widget(self) -> QWidget:
        self.__main_widget = QWidget(self)
        self.__layout = QHBoxLayout(self.__main_widget)
        self.__mode_widget = ChooseOneOfWidget('Contour retrieval mode', {
            cv.RETR_EXTERNAL: 'RETR_EXTERNAL',
            cv.RETR_LIST: 'RETR_LIST',
            cv.RETR_CCOMP: 'RETR_CCOMP',
            cv.RETR_TREE: 'RETR_TREE',
            cv.RETR_FLOODFILL: 'RETR_FLOODFILL'
        }, checked_index=cv.RETR_TREE)
        self.__layout.addWidget(self.__mode_widget)

        self.__method_widget = ChooseOneOfWidget('Contour approximation method', {
            cv.CHAIN_APPROX_NONE: 'CHAIN_APPROX_NONE',
            cv.CHAIN_APPROX_SIMPLE: 'CHAIN_APPROX_SIMPLE',
            cv.CHAIN_APPROX_TC89_L1: 'CHAIN_APPROX_TC89_L1',
            cv.CHAIN_APPROX_TC89_KCOS: 'CHAIN_APPROX_TC89_KCOS'
        }, checked_index=cv.CHAIN_APPROX_SIMPLE)

        return self.__main_widget
class HoughCircleDialog(AbstractMatActionDialog):

    def _create_main_widget(self) -> QWidget:
        self.__main_widget = QWidget()
        self.__layout = QVBoxLayout(self.__main_widget)

        self.__method_widget = ChooseOneOfWidget('Method', {
            cv.HOUGH_GRADIENT: 'HOUGH_GRADIENT',
            cv.HOUGH_GRADIENT_ALT: 'HOUGH_GRADIENT_ALT'
        }, checked_index=cv.HOUGH_GRADIENT_ALT)

        self.__layout.addWidget(self.__method_widget)

        params_layout = QFormLayout()
        self.__dp_line_edit = QLineEdit()
        params_layout.addRow(QLabel('dp'), self.__dp_line_edit)
        self.__min_dist_line_edit = QLineEdit()
        params_layout.addRow(QLabel('minDist'), self.__min_dist_line_edit)
        self.__param1_line_edit = QLineEdit()
        params_layout.addRow(QLabel('param1'), self.__param1_line_edit)
        self.__param2_line_edit = QLineEdit()
        params_layout.addRow(QLabel('param2'), self.__param2_line_edit)
        self.__min_radius_line_edit = QLineEdit()
        params_layout.addRow(QLabel('minRadius'), self.__min_radius_line_edit)
        self.__max_radius_line_edit = QLineEdit()
        params_layout.addRow(QLabel('maxRadius'), self.__max_radius_line_edit)

        self.__layout.addLayout(params_layout)

        self.__detect_button = QPushButton('Detect')
        self.__detect_button.clicked.connect(self.__on_detect_button_clicked)
        self.__layout.addWidget(self.__detect_button)

        return self.__main_widget

    @Slot()
    def __on_detect_button_clicked(self):
        wrong_fields = []
        dp, success = self.__to_float(self.__dp_line_edit.text())
        if not success:
            wrong_fields.append('dp')
        min_dist, success = self.__to_float(self.__min_dist_line_edit.text())
        if not success:
            wrong_fields.append('minDist')
        param1, success = self.__to_float(self.__param1_line_edit.text())
        if not success:
            wrong_fields.append('param1')
        param2, success = self.__to_float(self.__param2_line_edit.text())
        if not success:
            wrong_fields.append('param2')
        min_radius, success = self.__to_int(self.__min_radius_line_edit.text())
        if not success:
            wrong_fields.append('min_radius')
        max_radius, success = self.__to_int(self.__max_radius_line_edit.text())
        if not success:
            wrong_fields.append('max_radius')

        if len(wrong_fields) > 0:
            QMessageBox.critical(self, 'Error',
                                 'Following fields has empty or wrong values: ' + ', '.join(
                                     wrong_fields),
                                 QMessageBox.Ok, QMessageBox.NoButton)
            return
        action = ActionFactory.create_hough_circle_action(
            method=self.__method_widget.get_checked(),
            dp=dp,
            min_dist=min_dist,
            param1=param1,
            param2=param2,
            min_radius=min_radius,
            max_radius=max_radius)
        self.display_action_result.emit(action)

    @staticmethod
    def __to_float(value: str) -> Tuple[float, bool]:
        try:
            return float(value), True
        except ValueError:
            return 0, False

    @staticmethod
    def __to_int(value: str) -> Tuple[int, bool]:
        try:
            return int(value), True
        except ValueError:
            return 0, False
class InRangeDialog(AbstractMatActionDialog):
    def _create_main_widget(self) -> QWidget:
        self.__current_left_boundary: Optional[QColor] = None
        self.__current_right_boundary: Optional[QColor] = None
        self.__supported_color_spaces = {0: 'HSV'}
        self.__chosen_color_space_index: int = 0

        self.__main_widget = QWidget()
        self.__main_widget_layout = QGridLayout(self.__main_widget)

        self.__color_space_widget = ChooseOneOfWidget(
            'Color-space',
            self.__supported_color_spaces,
            checked_index=self.__chosen_color_space_index)
        self.__main_widget_layout.addWidget(self.__color_space_widget, 0, 0)
        self.__show_im_cb = QCheckBox('Show result immediately', self)
        self.__main_widget_layout.addWidget(self.__show_im_cb, 0, 1)
        self.__left_boundary_picker = MinimalColorPickerWidget(
            SupportedColorSpaces.HSV, 'Select left boundary')
        self.__left_boundary_picker.color_changed.connect(
            self.__set_left_boundary)
        self.__right_boundary_picker = MinimalColorPickerWidget(
            SupportedColorSpaces.HSV, 'Select right boundary')
        self.__right_boundary_picker.color_changed.connect(
            self.__set_right_boundary)
        self.__main_widget_layout.addWidget(self.__left_boundary_picker, 1, 0)
        self.__main_widget_layout.addWidget(self.__right_boundary_picker, 1, 1)

        return self.__main_widget

    def __apply_in_range(self):
        if self.__current_right_boundary is None or self.__current_left_boundary is None:
            return None

        lower_boundary = []
        upper_boundary = []
        if self.__color_space_widget.get_checked() == 0:
            h, s, v = self.__current_left_boundary.hue(), \
                      self.__current_left_boundary.saturation(), \
                      self.__current_left_boundary.value()
            lower_boundary = [int(h / 2), s, v]
            h, s, v = self.__current_right_boundary.hue(), \
                      self.__current_right_boundary.saturation(), \
                      self.__current_right_boundary.value()
            upper_boundary = [int(h / 2), s, v]

        action = ActionFactory.create_in_range_action(
            self.__supported_color_spaces[
                self.__color_space_widget.get_checked()].lower(),
            lower_boundary, upper_boundary)
        self._current_action = action
        self.display_action_result.emit(action)

    @Slot(QColor)
    def __set_left_boundary(self, color: QColor):
        self.__current_left_boundary = color
        if self.__show_im_cb.isChecked():
            self.__apply_in_range()

    @Slot(QColor)
    def __set_right_boundary(self, color: QColor):
        self.__current_right_boundary = color
        if self.__show_im_cb.isChecked():
            self.__apply_in_range()

    @Slot(int, bool)
    def __select_bound_toggled(self, index: int, checked: bool):
        if checked:
            self.__chosen_color_space_index = index
class ErosionAndDilationDialog(AbstractMatActionDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle('Erosion and Dilation parameters')

    def _create_main_widget(self) -> QWidget:
        self.__main_widget = QWidget()
        self.__main_widget_layout = QGridLayout(self.__main_widget)
        self.__morph_op = ChooseOneOfWidget(
            'Morphological operation', {
                0: 'Erode',
                1: 'Dilate',
                2: 'Morphological gradient',
                3: 'Opening: dilate(erode(src))',
                4: 'Closing: erode(dilate(src))'
            })
        self.__morph_op.toggled.connect(self.__apply_morph)
        self.__main_widget_layout.addWidget(self.__morph_op, 0, 0)
        self.__morph_type = ChooseOneOfWidget(
            'Shape', {
                cv.MORPH_RECT: 'Rect',
                cv.MORPH_CROSS: 'Cross',
                cv.MORPH_ELLIPSE: 'Ellipse'
            })
        self.__morph_type.toggled.connect(self.__apply_morph)
        self.__main_widget_layout.addWidget(self.__morph_type, 1, 0)
        self.__kernel_size_slider = SliderWidget(
            'Anchor (Kernel size = 2*Anchor+1)', 0, 40)
        self.__kernel_size_slider.value_changed.connect(
            self.__apply_kernel_size)
        self.__main_widget_layout.addWidget(self.__kernel_size_slider, 0, 1)

        return self.__main_widget

    @Slot(int, bool)
    def __apply_morph(self, index, is_checked):
        if is_checked:
            self.__transform_and_emit()

    @Slot(int)
    def __apply_kernel_size(self):
        self.__transform_and_emit()

    def __transform_and_emit(self):
        morph_op = self.__morph_op.get_checked()
        shape = self.__morph_type.get_checked()
        anchor = self.__kernel_size_slider.get_current_value()
        if shape == -1 or morph_op == -1:
            return
        action = None
        if morph_op == 0:
            action = ActionFactory.create_erosion_action(shape, anchor)
        elif morph_op == 1:
            action = ActionFactory.create_dilation_action(shape, anchor)
        elif morph_op == 2:
            action = ActionFactory.create_morph_gradient_action(shape, anchor)
        elif morph_op == 3:
            action = ActionFactory.create_morph_opening_action(shape, anchor)
        elif morph_op == 4:
            action = ActionFactory.create_morph_closing_action(shape, anchor)

        if action is not None:
            self._current_action = action
            self.display_action_result.emit(action)