Example #1
0
    def __init__(self, window: QMainWindow):
        self._logger = getLogger('WindowContent')

        # Set key private attributes
        self._window = window
        self._project = CADProject()

        # Setup basic UI - from design.py
        self.setupUi(self._window)

        # Init GLWidget: self.work_plane - QOpenGLWidget that was created into
        # setupUi in design.py
        super().__init__(self.work_plane)

        # Set additional private attributes
        self._setup_useful_aliases()

        # Setup special UI - method _setup_ui
        self._setup_ui()

        # Setup handlers (only for ui, because handlers for glwindow are
        # default methods)
        self._setup_handlers()

        # States
        self.controller_st = ControllerSt.NOTHING  # Left buttons controllers
        self.creation_st = CreationSt.NOTHING  # Stages of figures creation
        self.action_st = ActionSt.NOTHING  # Moving and changing figures

        # Special attributes
        self._mouse_xy = (0, 0)
        self._selected_figure_name = None  # Name of figure that selected now
        self._selected_restriction_name = None
        self._created_figure = None  # Figure that is created at this moment
        self._highlighted_figures = []
        self._moved_binding = None  # Binding used to move figure
        self._restriction_bindings = []  # Selected bindings for restriction
        self._current_bindings = []
        self._filename = None
Example #2
0
    def test_changing_parameters(self):
        project = CADProject()

        # Add figures
        point1 = Point((1, 1))
        point1_name = project.add_figure(point1)
        point2 = Point((5, 6))
        point2_name = project.add_figure(point2)
        segment1 = Segment((0, 0), 0, 10)
        segment1_name = project.add_figure(segment1)

        project.change_figure(point1_name, 'y', 2)
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (0, 0, 10, 0),
        }
        assert self._is_figures_correct(project.figures, correct_figures)

        new_len = 7
        project.change_figure(segment1_name, 'length', new_len)
        answer_1 = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: ((10 - new_len) / 2, 0, 10 - (10 - new_len) / 2, 0),
        }
        answer_2 = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (0, 0, new_len, 0),
        }
        answer_3 = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (10 - new_len, 0, 10, 0),
        }
        assert (
            self._is_figures_correct(project.figures, answer_1)
            or self._is_figures_correct(project.figures, answer_2)
            or self._is_figures_correct(project.figures, answer_3)
        )
Example #3
0
measure = DEFAULT_CONTEXT.measure
measure_total = DEFAULT_CONTEXT_TOTAL.measure


def measured_total(_func=None):
    return measured(_func, diagnostic_context=DEFAULT_CONTEXT_TOTAL)


if __name__ == '__main__':
    from restrictions import SegmentsNormal, SegmentsSpotsJoint
    from project import CADProject
    from figures import Segment
    from bindings import choose_best_bindings

    with measure('create project'):
        project = CADProject()

    with measure('create figures'):
        segment1 = Segment((0, 0), 0, 10)
        segment1_name = project.add_figure(segment1)
        segment2 = Segment((1, 1), 1, 10)
        segment2_name = project.add_figure(segment2)
        # segment3 = Segment((2, 5), -1, 10)
        # segment3_name = project.add_figure(segment3)

    with measure('choose binding'):
        bb = choose_best_bindings(project.bindings, 10,
                                  0)[0]  # end of segment 1

    with measure('add restrictions'):
        project.add_restriction(SegmentsNormal(),
Example #4
0
 def new(self, _=None):
     self.reset()
     self._project = CADProject()
     self._filename = None
     self.update()
Example #5
0
class WindowContent(QOpenGLWidget, Ui_window):
    def __init__(self, window: QMainWindow):
        self._logger = getLogger('WindowContent')

        # Set key private attributes
        self._window = window
        self._project = CADProject()

        # Setup basic UI - from design.py
        self.setupUi(self._window)

        # Init GLWidget: self.work_plane - QOpenGLWidget that was created into
        # setupUi in design.py
        super().__init__(self.work_plane)

        # Set additional private attributes
        self._setup_useful_aliases()

        # Setup special UI - method _setup_ui
        self._setup_ui()

        # Setup handlers (only for ui, because handlers for glwindow are
        # default methods)
        self._setup_handlers()

        # States
        self.controller_st = ControllerSt.NOTHING  # Left buttons controllers
        self.creation_st = CreationSt.NOTHING  # Stages of figures creation
        self.action_st = ActionSt.NOTHING  # Moving and changing figures

        # Special attributes
        self._mouse_xy = (0, 0)
        self._selected_figure_name = None  # Name of figure that selected now
        self._selected_restriction_name = None
        self._created_figure = None  # Figure that is created at this moment
        self._highlighted_figures = []
        self._moved_binding = None  # Binding used to move figure
        self._restriction_bindings = []  # Selected bindings for restriction
        self._current_bindings = []
        self._filename = None

    def _setup_useful_aliases(self):
        self._footer_widgets = dict()
        self._left_buttons = dict()
        self._footer_checkboxes = dict()
        self._footer_fields = dict()

        for name in dir(self):
            if re.match(r'^checkbox_restr_', name):
                self._footer_checkboxes[name] = getattr(self, name)
            elif re.match(r'^button_(add|restr)_', name):
                self._left_buttons[name] = getattr(self, name)
            elif re.match(r'^widget_(add|restr)_', name):
                self._footer_widgets[name] = getattr(self, name)
            elif re.match(r'^field_.*_add', name):
                self._footer_fields[name] = getattr(self, name)

    def _setup_ui(self):
        self._logger.debug('setup_ui start')

        self.widget_elements_table.show()
        self._reset_footer_widgets()
        self.action_show_elements_table.triggered['bool'].connect(
            lambda ev: self.widget_elements_table.show()
            if ev else self.widget_elements_table.hide())

        self.widget_elements_table.setHeaderHidden(True)
        self.widget_elements_table_figures = QTreeWidgetItem(['Figures'])
        self.widget_elements_table_restrictions = QTreeWidgetItem(
            ['Restrictions'])
        self.widget_elements_table.addTopLevelItems([
            self.widget_elements_table_figures,
            self.widget_elements_table_restrictions,
        ])

        # Setting tab order. Can do it into designer and remove from here

        # Add point
        self.setTabOrder(self.field_x_add_point, self.field_y_add_point)
        # Add segment
        self.setTabOrder(self.field_x1_add_segment, self.field_y1_add_segment)
        self.setTabOrder(self.field_y1_add_segment, self.field_x2_add_segment)
        self.setTabOrder(self.field_x2_add_segment, self.field_y2_add_segment)
        self.setTabOrder(self.field_y2_add_segment,
                         self.field_length_add_segment)
        self.setTabOrder(self.field_length_add_segment,
                         self.field_angle_add_segment)

        # GLWidget settings
        self.elapsed = 0
        self.setAutoFillBackground(True)
        self.setMouseTracking(True)
        self.setGeometry(
            QRect(0, 0, self.work_plane.width(), self.work_plane.height()))
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)
        # Set focus on window for keyPressEvent
        self.setFocusPolicy(Qt.StrongFocus)

    def _setup_handlers(self):
        # Left buttons
        self.button_add_point.clicked['bool'].connect(
            lambda ev: self.controller_add_point(ControllerCmd.SHOW))
        self.button_add_segment.clicked['bool'].connect(
            lambda ev: self.controller_add_segment(ControllerCmd.SHOW))
        self.button_restr_joint.clicked['bool'].connect(
            lambda ev: self.controller_restr_joint(ControllerCmd.SHOW))
        self.button_restr_point_on_segment_line.clicked['bool'].connect(
            lambda ev: self.controller_restr_point_on_segment_line(
                ControllerCmd.SHOW))
        self.button_restr_segments_parallel.clicked['bool'].connect(
            lambda ev: self.controller_restr_segments_parallel(ControllerCmd.
                                                               SHOW))
        self.button_restr_segments_normal.clicked['bool'].connect(
            lambda ev: self.controller_restr_segments_normal(ControllerCmd.SHOW
                                                             ))
        self.button_restr_segment_vertical.clicked['bool'].connect(
            lambda ev: self.controller_restr_segment_vertical(ControllerCmd.
                                                              SHOW))
        self.button_restr_segment_horizontal.clicked['bool'].connect(
            lambda ev: self.controller_restr_segment_horizontal(ControllerCmd.
                                                                SHOW))
        self.button_restr_fixed.clicked['bool'].connect(
            lambda ev: self.controller_restr_fixed(ControllerCmd.SHOW))
        self.button_restr_segment_length_fixed.clicked['bool'].connect(
            lambda ev: self.controller_restr_segment_length_fixed(ControllerCmd
                                                                  .SHOW))
        self.button_restr_segment_angle_fixed.clicked['bool'].connect(
            lambda ev: self.controller_restr_segment_angle_fixed(ControllerCmd.
                                                                 SHOW))
        self.button_restr_segments_angle_between_fixed.clicked['bool'].connect(
            lambda ev: self.controller_restr_segments_angle_between_fixed(
                ControllerCmd.SHOW))

        # Fields
        self.field_x_add_point.valueChanged.connect(
            lambda new_value: self.change_created_or_selected_figure(
                'x', new_value))
        self.field_y_add_point.valueChanged.connect(
            lambda new_value: self.change_created_or_selected_figure(
                'y', new_value))
        self.field_x1_add_segment.valueChanged.connect(
            lambda new_value: self.change_created_or_selected_figure(
                'x1', new_value))
        self.field_y1_add_segment.valueChanged.connect(
            lambda new_value: self.change_created_or_selected_figure(
                'y1', new_value))
        self.field_x2_add_segment.valueChanged.connect(
            lambda new_value: self.change_created_or_selected_figure(
                'x2', new_value))
        self.field_y2_add_segment.valueChanged.connect(
            lambda new_value: self.change_created_or_selected_figure(
                'y2', new_value))
        self.field_length_add_segment.valueChanged.connect(
            lambda new_value: self.change_created_or_selected_figure(
                'length', new_value))
        self.field_angle_add_segment.valueChanged.connect(
            lambda new_value: self.change_created_or_selected_figure(
                'angle', new_value * np_pi / 180))

        # Actions
        self.action_undo.triggered['bool'].connect(self.undo)
        self.action_redo.triggered['bool'].connect(self.redo)
        self.action_save.triggered['bool'].connect(self.save)
        self.action_save_as.triggered['bool'].connect(self.save_as)
        self.action_open.triggered['bool'].connect(self.open)
        self.action_new.triggered['bool'].connect(self.new)
        self.action_exit.triggered['bool'].connect(self.exit)
        self.action_delete.triggered['bool'].connect(self.delete)

        # List views
        self.widget_elements_table.clicked.connect(
            self.handle_elements_table_click)

    @property
    def _center(self) -> tuple:
        return self.width() // 2, self.height() // 2

    def _to_real_xy(self, x, y) -> tuple:
        return x - self._center[0], -(y - self._center[1])

    def _update_fields(self):
        self._logger.debug('update fields')
        if self._created_figure is not None:
            figure = self._created_figure
        elif self._selected_figure_name is not None:
            figure = self._project.figures[self._selected_figure_name]
        else:
            return

        def set_with_block(field, value):
            field.blockSignals(True)
            field.setValue(value)
            field.blockSignals(False)

        if isinstance(figure, Point):
            params = figure.get_params()
            set_with_block(self.field_x_add_point, params['x'])
            set_with_block(self.field_y_add_point, params['y'])

            # Select field with focus
            if self.field_x_add_point.hasFocus():
                self.field_x_add_point.selectAll()
            elif self.field_y_add_point.hasFocus():
                self.field_y_add_point.selectAll()

        elif isinstance(figure, Segment):
            params = figure.get_params()
            set_with_block(self.field_x1_add_segment, params['x1'])
            set_with_block(self.field_y1_add_segment, params['y1'])
            set_with_block(self.field_x2_add_segment, params['x2'])
            set_with_block(self.field_y2_add_segment, params['y2'])
            set_with_block(self.field_length_add_segment, params['length'])
            set_with_block(self.field_angle_add_segment,
                           params['angle'] * 180 / np_pi)

            # Select field with focus
            if self.field_x1_add_segment.hasFocus():
                self.field_x1_add_segment.selectAll()
            elif self.field_y1_add_segment.hasFocus():
                self.field_y1_add_segment.selectAll()
            elif self.field_x2_add_segment.hasFocus():
                self.field_x2_add_segment.selectAll()
            elif self.field_y2_add_segment.hasFocus():
                self.field_y2_add_segment.selectAll()
            elif self.field_length_add_segment.hasFocus():
                self.field_length_add_segment.selectAll()
            elif self.field_angle_add_segment.hasFocus():
                self.field_angle_add_segment.selectAll()

    def handle_elements_table_click(self, item_idx):
        # I don't know why [0]
        object_name = self.widget_elements_table.model().itemData(item_idx)[0]
        self._reset_behind_statuses()

        if object_name in self._project.figures:
            self._selected_figure_name = object_name
            self.begin_figure_selection()
        elif object_name in self._project.restrictions:
            self._selected_restriction_name = object_name
            restr = self._project.restrictions[object_name]
            self._highlighted_figures = [
                self._project.figures[f_name]
                for f_name in restr.get_object_names()
            ]
            self.update()

    def _select_element_on_tree(self):
        self.widget_elements_table.clearSelection()

        if self._selected_figure_name is not None:
            element_name = self._selected_figure_name
        elif self._selected_restriction_name is not None:
            element_name = self._selected_restriction_name
        else:
            return

        element_to_select = self.widget_elements_table.findItems(
            element_name,
            # Qt.MatchExactly,
            Qt.MatchRecursive,
        )[0]

        self.widget_elements_table.setCurrentItem(element_to_select)

    def handle_selecting_element_on_plane(self):
        self._select_element_on_tree()
        self.begin_figure_selection()

    def change_created_or_selected_figure(self, field: str, value: float):
        # For angle value in radians
        if self._created_figure is not None:
            self._created_figure.set_param(field, value)
        elif self._selected_figure_name is not None:
            try:
                self._project.change_figure(self._selected_figure_name, field,
                                            value)
            except CannotSolveSystemError:
                pass

        self.update()

    def begin_figure_selection(self):
        if self._selected_figure_name is None:
            return
        selected_figure_name = self._selected_figure_name
        self.reset()
        self._selected_figure_name = selected_figure_name
        self.action_st = ActionSt.SELECTED
        self._update_fields()
        figure = self._project.figures[self._selected_figure_name]
        if isinstance(figure, Point):
            self.controller_add_point(ControllerCmd.SHOW)
        elif isinstance(figure, Segment):
            self.controller_add_segment(ControllerCmd.SHOW)
        self.update()

    # ======================== Controllers ==================

    def controller_add_point(self, cmd):
        self._logger.debug(f'controller_add_point start with status {cmd}')

        if cmd == ControllerCmd.SUBMIT:
            figure_coo = self._created_figure.get_base_representation()
            self._project.add_figure(Point.from_coordinates(*figure_coo))
            self.reset()

            self.controller_add_point(ControllerCmd.SHOW)

        elif cmd == ControllerCmd.SHOW:
            if self.action_st == ActionSt.NOTHING:
                self._reset_behind_statuses()
                self.controller_st = ControllerSt.ADD_POINT
                self.creation_st = CreationSt.POINT_SET
                self._created_figure = Point.from_coordinates(
                    self.field_x_add_point.value(),
                    self.field_y_add_point.value(),
                )
                self.button_add_point.setChecked(True)

            self.widget_add_point.show()
            self.field_x_add_point.setFocus()
            self.field_x_add_point.selectAll()

        elif cmd == ControllerCmd.HIDE:
            self.reset()

        self.update()

    def controller_add_segment(self, cmd):
        self._logger.debug(f'controller_add_segment start with status {cmd}')

        if cmd == ControllerCmd.SUBMIT:
            figure_coo = self._created_figure.get_base_representation()
            s = Segment.from_coordinates(*figure_coo)
            self._project.add_figure(s)
            self.reset()

            self.controller_add_segment(ControllerCmd.SHOW)

        elif cmd == ControllerCmd.SHOW:
            if self.action_st == ActionSt.NOTHING:
                self._reset_behind_statuses()
                self.controller_st = ControllerSt.ADD_SEGMENT
                self.creation_st = CreationSt.SEGMENT_START_SET

                self._created_figure = Segment.from_coordinates(
                    self.field_x1_add_segment.value(),
                    self.field_y1_add_segment.value(),
                    self.field_x2_add_segment.value(),
                    self.field_y2_add_segment.value(),
                )

                self.button_add_segment.setChecked(True)

            self.widget_add_segment.show()
            self.field_x1_add_segment.setFocus()
            self.field_x1_add_segment.selectAll()

        elif cmd == ControllerCmd.HIDE:
            self.reset()

        self.update()

    def controller_restr_joint(self, cmd, bindings: list = None):
        def get_restr_fun(b1, b2):
            if isinstance(b1, PointBinding):
                if isinstance(b2, PointBinding):
                    restr = PointsJoint()
                else:
                    restr = PointAndSegmentSpotJoint(b2.spot_type)
            else:
                if isinstance(b2, PointBinding):
                    restr = SegmentSpotAndPointJoint(b1.spot_type)
                else:
                    restr = SegmentsSpotsJoint(b1.spot_type, b2.spot_type)
            return restr

        self._controller_restr_two_objects(
            'joint',
            cmd,
            bindings,
            get_restr_fun,
            (is_normal_point_binding, is_normal_point_binding),
        )

    def controller_restr_point_on_segment_line(self,
                                               cmd,
                                               bindings: list = None):
        def get_restr_fun(_b1, _b2):
            return PointOnSegmentLine()

        self._controller_restr_two_objects(
            'point_on_segment_line',
            cmd,
            bindings,
            get_restr_fun,
            (lambda b: isinstance(b, PointBinding), is_any_segment_binding),
        )

    def controller_restr_segments_parallel(self, cmd, bindings: list = None):
        def get_restr_fun(_b1, _b2):
            return SegmentsParallel()

        self._controller_restr_two_objects(
            'segments_parallel',
            cmd,
            bindings,
            get_restr_fun,
            (is_any_segment_binding, is_any_segment_binding),
        )

    def controller_restr_segments_normal(self, cmd, bindings: list = None):
        def get_restr_fun(_b1, _b2):
            return SegmentsNormal()

        self._controller_restr_two_objects(
            'segments_normal',
            cmd,
            bindings,
            get_restr_fun,
            (is_any_segment_binding, is_any_segment_binding),
        )

    def controller_restr_segment_vertical(self, cmd, bindings: list = None):
        def get_restr_fun(_binding):
            return SegmentVertical()

        self._controller_restr_single_object(
            'segment_vertical',
            cmd,
            bindings,
            get_restr_fun,
            is_any_segment_binding,
        )

    def controller_restr_segment_horizontal(self, cmd, bindings: list = None):
        def get_restr_fun(_binding):
            return SegmentHorizontal()

        self._controller_restr_single_object(
            'segment_horizontal',
            cmd,
            bindings,
            get_restr_fun,
            is_any_segment_binding,
        )

    def controller_restr_fixed(self, cmd, bindings: list = None):
        def get_restr_fun(binding):
            figure_name = binding.get_object_names()[0]
            coo = self._project.figures[figure_name].get_base_representation()
            if isinstance(binding, PointBinding):
                restr = PointFixed(*coo)
            elif isinstance(binding, FullSegmentBinding):
                restr = SegmentFixed(*coo)
            elif isinstance(binding, SegmentSpotBinding):
                binding_spot_type = binding.spot_type
                if binding_spot_type == 'start':
                    coo = coo[:2]
                elif binding_spot_type == 'end':
                    coo = coo[2:]
                else:  # center
                    coo = (coo[0] + coo[2]) / 2, (coo[1] + coo[3]) / 2
                restr = SegmentSpotFixed(*coo, binding_spot_type)
            else:
                raise RuntimeError(f'Unexpected binding type {type(binding)}')
            return restr

        self._controller_restr_single_object('fixed', cmd, bindings,
                                             get_restr_fun,
                                             is_any_normal_binding)

    def controller_restr_segment_length_fixed(self,
                                              cmd,
                                              bindings: list = None):
        def get_restr_fun(binding):
            segment_name = binding.get_object_names()[0]
            length = self._project.figures[segment_name].get_params()['length']
            return SegmentLengthFixed(length)

        self._controller_restr_single_object(
            'segment_length_fixed',
            cmd,
            bindings,
            get_restr_fun,
            is_any_segment_binding,
        )

    def controller_restr_segment_angle_fixed(self, cmd, bindings: list = None):
        def get_restr_fun(binding):
            segment_name = binding.get_object_names()[0]
            angle = self._project.figures[segment_name].get_params()['angle']
            return SegmentAngleFixed(angle)

        self._controller_restr_single_object(
            'segment_angle_fixed',
            cmd,
            bindings,
            get_restr_fun,
            is_any_segment_binding,
        )

    def controller_restr_segments_angle_between_fixed(self,
                                                      cmd,
                                                      bindings: list = None):
        def get_restr_fun(_b1, _b2):
            s1_name = _b1.get_object_names()[0]
            s2_name = _b2.get_object_names()[0]
            s1_params = self._project.figures[s1_name].get_params()
            s2_params = self._project.figures[s2_name].get_params()
            s1_x1, s1_y1 = s1_params['x1'], s1_params['y1']
            s1_x2, s1_y2 = s1_params['x2'], s1_params['y2']
            s2_x1, s2_y1 = s2_params['x1'], s2_params['y1']
            s2_x2, s2_y2 = s2_params['x2'], s2_params['y2']

            s1_dx, s1_dy = s1_x2 - s1_x1, s1_y2 - s1_y1
            s2_dx, s2_dy = s2_x2 - s2_x1, s2_y2 - s2_y1

            scalar_prod = s1_dx * s2_dx + s1_dy * s2_dy
            l1 = (s1_dx**2 + s1_dy**2)**0.5
            l2 = (s2_dx**2 + s2_dy**2)**0.5

            angle_cos = scalar_prod / (l1 * l2)
            return SegmentsAngleBetweenFixed(np_arccos(angle_cos))

        self._controller_restr_two_objects(
            'segments_angle_between_fixed',
            cmd,
            bindings,
            get_restr_fun,
            (is_any_segment_binding, is_any_segment_binding),
        )

    def _controller_restr_single_object(self, name, cmd, bindings,
                                        get_restr_fun, check_binding_func):
        if cmd == ControllerCmd.STEP:
            binding = find_first(bindings, check_binding_func)
            if binding:
                if len(self._restriction_bindings) == 0:
                    self._restriction_bindings.append(binding)
                    getattr(self, f'checkbox_restr_{name}').toggle()

        elif cmd == ControllerCmd.SUBMIT:
            if len(self._restriction_bindings) != 1:
                return
            binding = self._restriction_bindings[0]
            figure_name = binding.get_object_names()[0]
            restr = get_restr_fun(binding)
            try:
                self._project.add_restriction(restr, (figure_name, ))
            except CannotSolveSystemError:
                pass
            self.reset()

            controller_name = f'controller_restr_{name}'
            getattr(self, controller_name)(ControllerCmd.SHOW)

        elif cmd == ControllerCmd.SHOW:
            self._reset_behind_statuses()
            status = getattr(ControllerSt, f'restr_{name}'.upper())
            widget = getattr(self, f'widget_restr_{name}')
            widget.show()
            button = getattr(self, f'button_restr_{name}')
            button.setChecked(True)
            self.controller_st = status

        elif cmd == ControllerCmd.HIDE:
            self.reset()

        self.update()

    def _controller_restr_two_objects(self, name, cmd, bindings, get_restr_fun,
                                      check_binding_funcs):
        if cmd == ControllerCmd.STEP:
            if len(self._restriction_bindings) == 0:
                binding = find_first(bindings, check_binding_funcs[0])
                if binding:
                    self._restriction_bindings.append(binding)
                    getattr(self, f'checkbox_restr_{name}_1').toggle()
            elif len(self._restriction_bindings) == 1:
                binding = find_first(bindings, check_binding_funcs[1])
                if binding and binding is not self._restriction_bindings[0]:
                    self._restriction_bindings.append(binding)
                    getattr(self, f'checkbox_restr_{name}_2').toggle()

        elif cmd == ControllerCmd.SUBMIT:
            if len(self._restriction_bindings) != 2:
                return
            b1, b2 = self._restriction_bindings
            f1_name = b1.get_object_names()[0]
            f2_name = b2.get_object_names()[0]
            restr = get_restr_fun(b1, b2)
            try:
                self._project.add_restriction(restr, (f1_name, f2_name))
            except CannotSolveSystemError:
                pass
            self.reset()

            controller_name = f'controller_restr_{name}'
            getattr(self, controller_name)(ControllerCmd.SHOW)

        elif cmd == ControllerCmd.SHOW:
            self._reset_behind_statuses()
            status = getattr(ControllerSt, f'restr_{name}'.upper())
            widget = getattr(self, f'widget_restr_{name}')
            widget.show()
            button = getattr(self, f'button_restr_{name}')
            button.setChecked(True)
            self.controller_st = status

        elif cmd == ControllerCmd.HIDE:
            self.reset()

        self.update()

    # ====================================== Events ========================
    def paintEvent(self, event):
        # self._logger.info('paintEvent')

        self._update_current_bindings()

        selected_figures = []
        if self._selected_figure_name is not None:
            selected_figures.append(
                self._project.figures[self._selected_figure_name])
        selected_figures.extend(self._highlighted_figures)

        self.paint_all(
            event,
            self._current_bindings,
            self._project.figures,
            selected_figures,
            self._created_figure,
        )

        self._update_list_view()

    def mousePressEvent(self, event):
        self._logger.debug('mousePressEvent: start')
        if event.button() == Qt.LeftButton:
            x, y = self._to_real_xy(event.x(), event.y())

            if self.controller_st == ControllerSt.ADD_POINT:
                if self.creation_st == CreationSt.POINT_SET:
                    self.controller_add_point(ControllerCmd.SUBMIT)

            elif self.controller_st == ControllerSt.ADD_SEGMENT:
                if self.creation_st == CreationSt.SEGMENT_START_SET:
                    self._created_figure.set_param('x1', x).set_param('y1', y)
                    self.creation_st = CreationSt.SEGMENT_END_SET
                    self.field_x2_add_segment.setFocus()
                    self.field_x2_add_segment.selectAll()
                elif self.creation_st == CreationSt.SEGMENT_END_SET:
                    self.controller_add_segment(ControllerCmd.SUBMIT)

            elif ControllerSt.is_restr(self.controller_st):
                # Make restriction step
                bindings = choose_best_bindings(self._project.bindings, x, y)
                for name in dir(ControllerSt):
                    if (re.match(r'^RESTR_', name) and getattr(
                            ControllerSt, name) == self.controller_st):
                        controller = getattr(self,
                                             f'controller_{name.lower()}')
                        controller(ControllerCmd.STEP, bindings)

            else:  # self.controller_st == ControllerSt.NOTHING:
                bindings = choose_best_bindings(self._project.bindings, x, y)
                if len(bindings) > 0:
                    if self.action_st == ActionSt.NOTHING:
                        self._moved_binding = bindings[0]
                        self.action_st = ActionSt.BINDING_PRESSED
                    if self.action_st == ActionSt.SELECTED:
                        self._moved_binding = bindings[0]
                        self.action_st = (
                            ActionSt.BINDING_PRESSED_WHILE_SELECTED)
                else:
                    self.reset()

        self.update()

    def mouseMoveEvent(self, event):
        # self._logger.debug('mouseMoveEvent: start')
        x, y = self._to_real_xy(event.x(), event.y())
        self._mouse_xy = (x, y)

        if self.action_st == ActionSt.BINDING_PRESSED:
            self.action_st = ActionSt.MOVE

        elif self.action_st == ActionSt.BINDING_PRESSED_WHILE_SELECTED:
            self.action_st = ActionSt.MOVE_WHILE_SELECTED

        if (self.action_st == ActionSt.MOVE
                or self.action_st == ActionSt.MOVE_WHILE_SELECTED
                and self._moved_binding.get_object_names()[0]
                == self._selected_figure_name):

            try:
                self._project.move_figure(self._moved_binding, x, y)
                self._update_fields()
            except CannotSolveSystemError:
                self._project.rollback()

        if self.controller_st == ControllerSt.ADD_POINT:
            if self.creation_st == CreationSt.POINT_SET:
                self._created_figure.set_param('x', x).set_param('y', y)
                self._update_fields()

        elif self.controller_st == ControllerSt.ADD_SEGMENT:
            if self.creation_st == CreationSt.SEGMENT_START_SET:
                self._created_figure.set_param('x1', x).set_param('y1', y)
                self._update_fields()
            elif self.creation_st == CreationSt.SEGMENT_END_SET:
                self._created_figure.set_param('x2', x).set_param('y2', y)
                self._update_fields()

        self.update()

    def mouseReleaseEvent(self, event):
        self._logger.debug('mouseReleaseEvent: start')
        if event.button() == Qt.LeftButton:

            if self.action_st == ActionSt.MOVE:
                self._project.commit()
                self.action_st = ActionSt.NOTHING

            elif self.action_st == ActionSt.MOVE_WHILE_SELECTED:
                self._project.commit()
                self.action_st = ActionSt.SELECTED

            elif (self.action_st == ActionSt.BINDING_PRESSED or self.action_st
                  == ActionSt.BINDING_PRESSED_WHILE_SELECTED):
                selected_figures = self._moved_binding.get_object_names()
                self._moved_binding = None
                if len(selected_figures) == 1:
                    self._selected_figure_name = selected_figures[0]
                    # Also start changing and set action_st = SELECTED
                    self.handle_selecting_element_on_plane()
                self.action_st = ActionSt.SELECTED

        self.update()

    def keyPressEvent(self, event):
        key = event.key()
        modifiers = event.modifiers()
        if key == Qt.Key_Enter or key == Qt.Key_Return:
            if self.controller_st == ControllerSt.ADD_POINT:
                self.controller_add_point(ControllerCmd.SUBMIT)
            elif self.controller_st == ControllerSt.ADD_SEGMENT:
                self.controller_add_segment(ControllerCmd.SUBMIT)
            elif ControllerSt.is_restr(self.controller_st):
                name = None
                for name in dir(ControllerSt):
                    if (re.match('^RESTR_', name) and getattr(
                            ControllerSt, name) == self.controller_st):
                        break
                if name:
                    controller_name = f'controller_{name.lower()}'
                    getattr(self, controller_name)(ControllerCmd.SUBMIT)
        elif key == Qt.Key_Escape:
            self.reset()
        elif modifiers & Qt.ControlModifier:
            if event.key() == Qt.Key_Y:
                self.delete()

    def _reset_footer_widgets(self):
        for w_name, widget in self._footer_widgets.items():
            widget.hide()

        for box in self._footer_checkboxes.values():
            if box.checkState() == Qt.Checked:
                box.toggle()

        for field in self._footer_fields.values():
            field.setValue(0)

    def _uncheck_left_buttons(self):
        for _, button in self._left_buttons.items():
            button.setChecked(False)

    def delete(self, _=None):
        self._logger.debug('delete: start')
        if self._selected_restriction_name is not None:
            self._project.remove_restriction(self._selected_restriction_name)
            self.reset()

        elif self._selected_figure_name is not None:
            self._project.remove_figure(self._selected_figure_name)
            self.reset()

        self.update()

    def new(self, _=None):
        self.reset()
        self._project = CADProject()
        self._filename = None
        self.update()

    def exit(self, _=None):
        self._window.close()

    def reset(self, _=None):
        self._logger.debug('reset: start')
        self._reset_behind_statuses()
        self._reset_statuses()
        self.update()

    def _reset_behind_statuses(self):
        self._selected_figure_name = None  # Name of figure that selected now
        self._selected_restriction_name = None
        self._created_figure = None  # Figure that is created at this moment
        self._highlighted_figures = []
        self._moved_binding = None  # Binding used to move figure
        self._restriction_bindings = []  # Selected bindings for restriction
        self._filename = None

        self._reset_footer_widgets()
        self._uncheck_left_buttons()

    def _reset_statuses(self):
        self.controller_st = ControllerSt.NOTHING
        self.creation_st = CreationSt.NOTHING
        self.action_st = ActionSt.NOTHING

    def save(self, _=None):
        if self._filename is None:
            self.save_as()
        else:
            self._project.save(self._filename)

    def save_as(self, _=None):
        filename, _ = QFileDialog.getSaveFileName(self, 'Сохранить', '',
                                                  'SCAD Files (*.scad)')
        if filename:
            self._filename = filename
            self.save()

    def open(self, _=None):
        filename, _ = QFileDialog.getOpenFileName(self, 'Открыть', '',
                                                  'SCAD Files (*.scad)')
        if filename:
            self._filename = filename
            self._project.load(self._filename)
        self.update()

    def undo(self, ev):
        self._logger.debug(f'Undo: ev = {ev}')
        try:
            self._project.undo()
        except ActionImpossible:
            pass
        self.update()

    def redo(self, ev):
        self._logger.debug(f'Redo: ev = {ev}')
        try:
            self._project.redo()
        except ActionImpossible:
            pass
        self.update()

    def _update_list_view(self):
        updateble_types = [
            [self.widget_elements_table_figures, self._project.figures],
            [
                self.widget_elements_table_restrictions,
                self._project.restrictions,
            ],
        ]

        for updateble_type_tree, updateble_type in updateble_types:
            for i in reversed(range(updateble_type_tree.childCount())):
                updateble_type_tree.removeChild(updateble_type_tree.child(i))

            for name in updateble_type.keys():
                element = QTreeWidgetItem([name])
                updateble_type_tree.addChild(element)

        self._select_element_on_tree()

    def _update_current_bindings(self):
        # Work with bindings
        if self.controller_st == ControllerSt.RESTR_JOINT:
            allowed_bindings_types = (PointBinding, SegmentSpotBinding)
            # TODO: check all variants
        else:
            allowed_bindings_types = None

        x, y = self._mouse_xy
        best_bindings = choose_best_bindings(self._project.bindings, x, y)
        self._current_bindings = []
        for binding in best_bindings:
            if allowed_bindings_types is None or isinstance(
                    binding, allowed_bindings_types):
                self._current_bindings.append(binding)

    def paint_all(
        self,
        event: QPaintEvent,
        bindings,
        figures: Dict[str, Figure],
        selected_figures: list,
        created_figure: Optional[Figure] = None,
    ):
        painter = QPainter()
        painter.begin(self)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.fillRect(event.rect(), QBrush(QColor(255, 255, 255)))
        painter.save()
        painter.translate(*self._center)

        # Paint all
        paint.write_coordinates_near_pointer(painter, self._mouse_xy)

        for figure in figures.values():
            paint.paint_figure(painter, figure, 'basic')

        paint.paint_bindings(painter, figures, bindings)

        # Paint painted figure
        if created_figure is not None:
            paint.paint_figure(painter, created_figure, 'created')

        # Paint selected figure
        for selected_figure in selected_figures:
            paint.paint_figure(painter, selected_figure, 'selected')

        # Finish painting
        painter.restore()
        painter.end()
Example #6
0
    def test_addition_and_deletion_figures(self):
        project = CADProject()

        # Add figures
        point1 = Point((1, 2))
        point1_name = project.add_figure(point1)
        correct_types = {point1_name: Point}
        assert check_objects_types(project.figures, correct_types)

        point2 = Point((5, 6))
        point2_name = project.add_figure(point2)
        correct_types = {point1_name: Point, point2_name: Point}
        assert check_objects_types(project.figures, correct_types)

        segment1 = Segment((0, 0), 0, 10)
        segment1_name = project.add_figure(segment1)
        correct_types = {
            point1_name: Point,
            point2_name: Point,
            segment1_name: Segment,
        }
        assert check_objects_types(project.figures, correct_types)

        # Remove figures
        project.remove_figure(point2_name)
        correct_types = {point1_name: Point, segment1_name: Segment}
        assert check_objects_types(project.figures, correct_types)

        with pytest.raises(IncorrectParamValue):
            project.remove_figure(point2_name)

        project.remove_figure(segment1_name)
        correct_types = {point1_name: Point}
        assert check_objects_types(project.figures, correct_types)

        project.remove_figure(point1_name)
        correct_types = {}
        assert check_objects_types(project.figures, correct_types)
Example #7
0
    def test_save_and_load(self):
        project1 = CADProject()

        # Add figures
        point1 = Point((1, 2))
        point1_name = project1.add_figure(point1, 'p1')
        assert point1_name in project1.figures

        # Save
        filename = 'test_save_and_load.scad'
        project1.save(filename)

        # Load 1
        project2 = CADProject()
        project2.load(filename)
        assert point1_name in project2.figures

        # One more project
        project3 = CADProject()
        point2 = Point((0, 0))
        point2_name = project3.add_figure(point2, 'p2')
        assert point2_name in project3.figures
        assert point1_name not in project3.figures

        project3.load(filename)
        assert point2_name not in project3.figures
        assert point1_name in project3.figures

        os.remove(filename)
Example #8
0
    def test_undo_redo_commit_rollback(self):
        project = CADProject()

        with pytest.raises(ActionImpossible):
            project.undo()

        # Add figures
        point1 = Point((1, 2))
        _ = project.add_figure(point1)
        point2 = Point((5, 6))
        point2_name = project.add_figure(point2)

        # Undo, redo, _commit inside
        with pytest.raises(ActionImpossible):
            project.redo()

        project.undo()
        assert point2_name not in project.figures
        project.redo()
        assert point2_name in project.figures

        project.undo()
        assert point2_name not in project.figures
        segment1 = Segment((0, 0), 0, 10)
        segment1_name = project.add_figure(segment1)
        with pytest.raises(ActionImpossible):
            project.redo()

        project.undo()
        assert segment1_name not in project.figures
        project.undo()
        assert not project.figures
        assert not project.bindings
        with pytest.raises(ActionImpossible):
            project.undo()

        # Commit & rollback
        point3 = Point((1, 1))
        point3_name = project.add_figure(point3)
        bb = choose_best_bindings(project.bindings, 1, 1)[0]
        project.move_figure(bb, 5, 5)
        assert project.figures[point3_name].get_params()['x'] == 5
        project.commit()
        assert project.figures[point3_name].get_params()['x'] == 5

        bb = choose_best_bindings(project.bindings, 5, 5)[0]
        project.move_figure(bb, 7, 7)
        assert project.figures[point3_name].get_params()['x'] == 7
        project.rollback()
        assert project.figures[point3_name].get_params()['x'] == 5
        project.rollback()
        assert project.figures[point3_name].get_params()['x'] == 5
Example #9
0
    def test_complex(self):
        project = CADProject()

        # Add figures
        point1 = Point((1, 2))
        point1_name = project.add_figure(point1)
        point2 = Point((5, 6))
        point2_name = project.add_figure(point2)
        segment1 = Segment.from_coordinates(0, 0, 10, 1)
        segment1_name = project.add_figure(segment1)

        # Fix point1
        r = PointFixed(1, 2)
        _ = project.add_restriction(r, (point1_name,))
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (0, 0, 10, 1),
        }
        assert self._is_figures_correct(project.figures, correct_figures)

        # Join point1 and start of segment1
        r = PointAndSegmentSpotJoint(spot_type='start')
        _ = project.add_restriction(r, (point1_name, segment1_name))
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (1, 2, 10, 1),
        }
        assert self._is_figures_correct(project.figures, correct_figures)

        # Move segment end
        bb = choose_best_bindings(project.bindings, 10, 0)[0]
        project.move_figure(bb, 12, 2)
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (1, 2, 12, 2),  # Segment set. save length and angle
        }
        assert self._is_figures_correct(project.figures, correct_figures)
        project.commit()

        # Change length
        project.change_figure(segment1_name, 'length', 11)
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (1, 2, 12, 2),
        }
        assert self._is_figures_correct(project.figures, correct_figures)
Example #10
0
    def test_addition_and_deletion_restrictions(self):
        project = CADProject()

        # Add figures
        point1 = Point((1, 2))
        point1_name = project.add_figure(point1)
        point2 = Point((5, 6))
        point2_name = project.add_figure(point2)
        segment1 = Segment((0, 0), 0, 10)
        segment1_name = project.add_figure(segment1)

        # Fix point1
        r = PointFixed(1, 2)
        p1_fixed_name = project.add_restriction(r, (point1_name,))
        correct_types = {p1_fixed_name: PointFixed}
        assert check_objects_types(project.restrictions, correct_types)
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (0, 0, 10, 0),
        }
        assert self._is_figures_correct(project.figures, correct_figures)

        # Join point1 and start of segment1
        r = PointAndSegmentSpotJoint(spot_type='start')
        p1_s1s_joint_name = project.add_restriction(
            r, (point1_name, segment1_name)
        )
        correct_types = {
            p1_fixed_name: PointFixed,
            p1_s1s_joint_name: PointAndSegmentSpotJoint,
        }
        assert check_objects_types(project.restrictions, correct_types)
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (1, 2, 10, 0),  # Segment set. save length and angle
        }
        assert self._is_figures_correct(project.figures, correct_figures)

        # Fix end of segment1
        r = SegmentSpotFixed(1, 7, spot_type='end')
        s1e_fixed_name = project.add_restriction(r, (segment1_name,))
        correct_types = {
            p1_fixed_name: PointFixed,
            p1_s1s_joint_name: PointAndSegmentSpotJoint,
            s1e_fixed_name: SegmentSpotFixed,
        }
        assert check_objects_types(project.restrictions, correct_types)
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (1, 2, 1, 7),
        }
        assert self._is_figures_correct(project.figures, correct_figures)

        # Remove fix end of segment1
        project.remove_restriction(s1e_fixed_name)
        correct_types = {
            p1_fixed_name: PointFixed,
            p1_s1s_joint_name: PointAndSegmentSpotJoint,
        }
        assert check_objects_types(project.restrictions, correct_types)
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (1, 2, 1, 7),
        }
        assert self._is_figures_correct(project.figures, correct_figures)

        # Fix length of segment1
        r = SegmentLengthFixed(3)
        s1_fixed_length_name = project.add_restriction(r, (segment1_name,))
        correct_types = {
            p1_fixed_name: PointFixed,
            p1_s1s_joint_name: PointAndSegmentSpotJoint,
            s1_fixed_length_name: SegmentLengthFixed,
        }
        assert check_objects_types(project.restrictions, correct_types)
        correct_figures = {
            point1_name: (1, 2),
            point2_name: (5, 6),
            segment1_name: (1, 2, 1, 5),
        }
        assert self._is_figures_correct(project.figures, correct_figures)
Example #11
0
    def test_moving(self):
        project = CADProject()

        # Add figures
        point1 = Point((1, 1))
        point1_name = project.add_figure(point1)
        point2 = Point((5, 6))
        point2_name = project.add_figure(point2)
        segment1 = Segment((0, 0), 0, 10)
        segment1_name = project.add_figure(segment1)

        # Move point to (3, 4)
        bb = choose_best_bindings(project.bindings, 1.1, 1)[0]
        project.move_figure(bb, 3, 4)
        correct_figures = {
            point1_name: (3, 4),
            point2_name: (5, 6),
            segment1_name: (0, 0, 10, 0),
        }
        assert self._is_figures_correct(project.figures, correct_figures)
        project.commit()

        # Move segment end to (7, 7)
        bb = choose_best_bindings(project.bindings, 10, 0)[0]
        project.move_figure(bb, 7, 7)
        correct_figures = {
            point1_name: (3, 4),
            point2_name: (5, 6),
            segment1_name: (0, 0, 7, 7),
        }
        assert self._is_figures_correct(project.figures, correct_figures)
        project.commit()