예제 #1
0
    def on_load_ascii_from_file(self, filename):
        try:
            file = open(filename, 'r')
            str = file.readlines()
            startpos = Pos(0, 0)
            pos = Pos(0, 0)
            for line in str:
                # create a TEXT instance for each line
                # fill selected_objects...
                symbol = Text(pos, line)
                selection = SelectedObjects(startpos=startpos, symbol=symbol)
                pos += Pos(0, 1)
                self.selected_objects.append(selection)
            file.close()
            pub.sendMessage('OBJECTS_SELECTED', objects=self.selected_objects)
            return True

        except IOError as e:
            msg = _(
                "Unable to open file for reading: {} error({}): {}").format(
                    filename, e.errno, e.strerror)
            pub.sendMessage('STATUS_MESSAGE', msg=msg, type=WARNING)
            return False

        except UnicodeDecodeError as e:
            msg = _(
                "Unable to open file for reading: {} error({}): {}").format(
                    filename, e.encoding, e.reason)
            pub.sendMessage('STATUS_MESSAGE', msg=msg, type=WARNING)
            return False
예제 #2
0
 def __init__(self,
              id=0,
              grid=None,
              ori=None,
              mirrored=None,
              startpos=None,
              endpos=None):
     self._id = id
     self._has_pickpoint = True
     if ori is None:
         self._ori = 0
     else:
         self._ori = ori
     if mirrored is None:
         self._mirrored = 0
     else:
         self._mirrored = mirrored
     if grid is None:
         self._grid = self.default
     else:
         self._grid = grid
     if startpos is None:
         self._startpos = Pos(0, 0)
     else:
         self._startpos = startpos
     if endpos is None:
         self._endpos = Pos(0, 0)
     else:
         self._endpos = endpos
     self._is_symbol = True
     self._is_text = False
     self._is_line = False
예제 #3
0
 def previous_char():
     # move to the previous character or the previous line
     if grid_pos.x > 0:
         if grid_pos.x <= 2:
             self._hover_pos -= Pos(1, 0).view_xy()
     elif grid_pos.y > 0:
         self._hover_pos += Pos(2, -1).view_xy()
예제 #4
0
    def _line_match(self, idx, ori, pos):
        """
        Match a character in the grid against a Magic Line pattern.

        :param idx: number of the line matching pattern to be used
        :param ori: orientation of the line to be drawn
        :param pos: character position (col, row) coordinates

        :return result: True if a match was found, otherwise False
        :return ori: magic line orientation
        :return char: terminal character

        """
        lmd = MagicLineSettings.LMD[idx]
        result = True
        m_ori = None
        m_char = None
        if pos > Pos(0, 0) and (ori is None or ori == lmd.ori):
            for j, row in enumerate(lmd.pattern):
                for i, char in enumerate(row):
                    if char != 'x':
                        m_pos = pos + Pos(i - 1, j - 1)
                        m_cell = self.cell(m_pos)
                        if m_cell != char:
                            result = False
                            break
            if result:
                m_ori = lmd.ori
                m_char = lmd.char
                # print("line_match, idx:", idx, " m_ori:", m_ori, " m_char:", m_char)
        else:
            result = False
        return result, m_ori, m_char
예제 #5
0
 def calc_offset(self):
     """Calculate the upper left coordinate where the matrix will be drawn."""
     grid_w = Preferences.values['GRIDSIZE_W']
     grid_h = Preferences.values['GRIDSIZE_H']
     x_offset = round((self._surface.get_width() - 3 * grid_w) / 2)
     y_offset = round((self._surface.get_height() - 3 * grid_h) / 2)
     self._offset = Pos(x_offset, y_offset)
     self._offset.snap_to_grid()
예제 #6
0
 def _representation(self):
     self._repr = dict()
     pos = self._startpos
     incr = Pos(1, 0)
     for row in range(self._size[1]):
         pos.x = self._startpos.x
         for col in range(self._size[0]):
             self._repr[pos] = CELL_ERASE
             pos += incr
         pos += Pos(0, 1)
예제 #7
0
    def _corner_line(self, ori):
        startpos = self._startpos
        endpos = self._endpos
        dx = endpos.x - startpos.x
        dy = endpos.y - startpos.y
        if (dy >= 0) ^ (ori != VERTICAL):
            corner_char = Preferences.values['LOWER_CORNER']
        else:
            corner_char = Preferences.values['UPPER_CORNER']
        top = 0
        left = 0
        if ori == HORIZONTAL:
            top = startpos.y
            left = endpos.x
        elif ori == VERTICAL:
            top = endpos.y
            left = startpos.x

        # horizontal line
        if dx > 0:
            startv = startpos.x + 1  # don't overwrite the terminal char (in the startposition)
            endv = endpos.x
        else:
            startv = endpos.x + 1  # don't overwrite the terminal char (in the startposition)
            endv = startpos.x
        if abs(dx) > 1:
            for temp in range(endv - startv):
                pos = Pos(startv + temp, top)
                if self.cell(pos) == Preferences.values['LINE_VERT']:
                    char = Preferences.values['CROSSING']
                else:
                    char = Preferences.values['LINE_HOR']
                self._repr[pos] = char

        # vertical line
        if dy > 0:
            startv = startpos.y + 1  # don't overwrite the terminal char (in the startposition)
            endv = endpos.y
        else:
            startv = endpos.y + 1  # don't overwrite the terminal char (in the startposition)
            endv = startpos.y
        if abs(dy) > 1:
            for temp in range(endv - startv):
                pos = Pos(left, startv + temp)
                if self.cell(pos) == Preferences.values['LINE_HOR']:
                    char = Preferences.values['CROSSING']
                else:
                    char = Preferences.values['LINE_VERT']
                self._repr[pos] = char

        # corner character, if there is a corner
        if (ori == HORIZONTAL and abs(dy) > 0) or (ori == VERTICAL
                                                   and abs(dx) > 0):
            self._repr[Pos(left, top)] = corner_char
예제 #8
0
 def _representation(self):
     self._repr = dict()
     pos = self._startpos
     incr = Pos(1, 0)
     for row in self.grid:
         pos.x = self._startpos.x
         for char in row:
             if char != ' ':
                 self._repr[pos] = char
             pos += incr
         pos += Pos(0, 1)
예제 #9
0
    def test_rect(self):

        c = Controller()
        c.on_new()

        start = Pos(5, 5)
        end = Pos(15, 15)

        c.on_paste_rect(start, end)

        filename = 'tmp/test_rect.aac'
        self.assertTrue(c.on_write_to_file(filename))
예제 #10
0
    def test_comparison(self):

        a = Pos(10, 15)
        b = Pos(12, 21)
        c = Pos(12, 21)

        self.assertTrue(a < b)
        self.assertFalse(a > b)

        self.assertTrue(a <= b)
        self.assertFalse(a >= b)

        self.assertTrue(b == c)
        self.assertFalse(a == b)
예제 #11
0
    def test_erase(self):

        c = Controller()
        c.on_new()

        # NB test should be run against the default (en_US) component library
        c.on_component_changed('AND gate')
        c.on_paste_objects(Pos(4, 2))

        c.on_eraser_selected((5, 5))
        c.on_paste_objects(Pos(5, 2))

        filename = 'tmp/test_edit_erase.aac'
        self.assertTrue(c.on_write_to_file(filename))
예제 #12
0
    def on_key_press(self, widget, event):

        # TODO Will this work in other locale too?
        def filter_non_printable(ascii):
            char = ''
            if (ascii > 31 and ascii < 255) or ascii == 9:
                char = chr(ascii)
            return char

        def valid_index(pos):
            if pos.x >= 0 and pos.x < 3 and pos.y >= 0 and pos.y < 3:
                return True
            else:
                return False

        def next_char():
            # move to the next character or the next line
            if grid_pos.x < 2:
                self._hover_pos += Pos(1, 0).view_xy()
            elif grid_pos.y < 2:
                self._hover_pos += Pos(-2, 1).view_xy()

        def previous_char():
            # move to the previous character or the previous line
            if grid_pos.x > 0:
                if grid_pos.x <= 2:
                    self._hover_pos -= Pos(1, 0).view_xy()
            elif grid_pos.y > 0:
                self._hover_pos += Pos(2, -1).view_xy()

        grid_pos = self._hover_pos - self._offset
        grid_pos.snap_to_grid()
        grid_pos = grid_pos.grid_cr()
        value = event.keyval
        if value in (Gdk.KEY_Shift_L, Gdk.KEY_Shift_R):
            pass
        elif value == Gdk.KEY_Left or value == Gdk.KEY_BackSpace:
            previous_char()
        elif value == Gdk.KEY_Right:
            next_char()
        elif value == Gdk.KEY_Up:
            self._hover_pos -= Pos(0, 1).view_xy()
        elif value == Gdk.KEY_Down:
            self._hover_pos += Pos(0, 1).view_xy()
        elif value & 255 != 13:  # enter
            if valid_index(grid_pos):
                str = filter_non_printable(value)
                self._matrix[grid_pos.y][grid_pos.x] = str
                next_char()
        return True
예제 #13
0
 def _representation(self):
     ul = self._startpos
     ur = Pos(self._endpos.x, self._startpos.y)
     bl = Pos(self._startpos.x, self._endpos.y)
     br = self._endpos
     line1 = Line(ul, ur, Line.LINE1)
     line2 = Line(ur, br, Line.LINE4)
     line3 = Line(bl, br, Line.LINE1)
     line4 = Line(ul, bl, Line.LINE4)
     self._repr = dict()
     self._repr.update(line1.repr)
     self._repr.update(line3.repr)
     self._repr.update(line2.repr)
     self._repr.update(line4.repr)
예제 #14
0
    def pointer_dir_avg(self):
        """Return the pointer direction in relation to the previous position."""
        (x, y) = self._drag_currentpos.xy
        length = len(self._drag_prevpos)
        assert length > 0

        x_sum = 0
        y_sum = 0
        for pos in self._drag_prevpos:
            x_sum += pos.x
            y_sum += pos.y
        x_avg = x_sum / length
        y_avg = y_sum / length

        dx = abs(x - x_avg)
        dy = abs(y - y_avg)
        if dx > dy:
            dir = HORIZONTAL
        else:
            dir = VERTICAL

        # previous position keeps a list of the last n pointer locations
        self._drag_prevpos.append(Pos(x, y))
        if len(self._drag_prevpos) > 5:
            self._drag_prevpos.pop(0)
        return dir
예제 #15
0
 def rotate(self):
     w = self._endpos.x - self._startpos.x
     h = self._endpos.y - self._startpos.y
     ul = self._startpos
     br = Pos(self._startpos.x + h, self._startpos.y + w)
     self._startpos = ul
     self._endpos = br
예제 #16
0
    def selecting_state(self, pos, event):
        if self._selection.item == ROW:
            row = pos.grid_cr().y
            pub.sendMessage('GRID_ROW', row=row, action=self._selection.action)

        elif self._selection.item == COL:
            col = pos.grid_cr().x
            pub.sendMessage('GRID_COL', col=col, action=self._selection.action)

        elif self._selection.item in (TEXT, TEXT_BLOCK):
            button = event.button
            if button == 1:
                # left button
                self._selection.state = SELECTED
                self._symbol.startpos = pos.grid_cr()
                pub.sendMessage('PASTE_TEXT', symbol=self._symbol)
            elif button == 3:
                # right button
                self._symbol.rotate()
                # FIXME more elegant options? otoh grid_view() is owner of the Text Symbol
                pub.sendMessage('ORIENTATION_CHANGED',
                                ori=self._symbol.ori_as_str)

        elif self._selection.item == OBJECT:
            self._selection.state = SELECTED
            # select the object within the cursor rect
            ul = pos
            br = ul + Pos(Preferences.values['GRIDSIZE_W'],
                          Preferences.values['GRIDSIZE_H'])
            self._drag_startpos = ul
            self._drag_endpos = br
            self._selection.startpos = ul
            self._selection.endpos = br
            self._selection.maxpos = self.max_pos_grid
            pub.sendMessage('SELECTION_CHANGED', selected=True)
예제 #17
0
 def on_paste_grid(self):
     """
     Copy the content of the clipboard to the grid.
     ASCII lines, terminated by CR, are interpreted as rows.
     """
     selected = []
     pos = Pos(0, 0)
     relative_pos = Pos(0, 0)
     content = xerox.paste().splitlines()
     for line in content:
         symbol = Text(relative_pos, line)
         selection = SelectedObjects(startpos=pos, symbol=symbol)
         selected.append(selection)
         relative_pos += Pos(0, 1)
     self.selected_objects = selected
     pub.sendMessage('OBJECTS_SELECTED', objects=self.selected_objects)
예제 #18
0
    def on_hover(self, widget, event):
        if not self.has_focus():
            self.grab_focus()
        width = Preferences.values['GRIDSIZE_W']
        height = Preferences.values['GRIDSIZE_H']
        self._hover_pos = self.calc_position(event.x, event.y)
        delta = self._hover_previous_pos - self._hover_pos
        if abs(delta.x) > width / 2 or abs(delta.y) > height / 2:
            moved_enough = True
            self._hover_previous_pos = self._hover_pos
        else:
            # reduce message flooding and superfluous drawing updates
            moved_enough = False

        if moved_enough:
            pub.sendMessage('POINTER_MOVED', pos=self._hover_pos.grid_cr())

        if self._selection.state == SELECTING and \
                self._selection.item == OBJECT:
            if moved_enough:
                pub.sendMessage('SELECTOR_MOVED',
                                pos=self._hover_pos.grid_cr())

        if not Preferences.values['SELECTION_DRAG'] \
                and self._selection.state == SELECTING \
                and self._selection.item in (DRAW_RECT, ARROW, RECT, ERASER, LINE, MAG_LINE, DIR_LINE):
            offset = Pos(event.x, event.y) - self._drag_startpos
            if moved_enough:
                self.on_drag_update(None, offset.x, offset.y)
예제 #19
0
 def _representation(self):
     x, y = (self._endpos - self._startpos).xy
     # TODO better representation of straight line (using ASCII chars)?
     if abs(x) > 0:
         angle = atan(y / x)
     else:
         angle = pi / 2
     if angle < radians(-75):
         linechar = '|'
     if angle >= radians(-75) and angle <= radians(-52):
         linechar = '.'
     elif angle > radians(-52) and angle <= radians(-37):
         linechar = '/'
     elif angle > radians(-37) and angle <= radians(-15):
         linechar = '.'
     elif angle > radians(-15) and angle <= radians(15):
         linechar = '-'
     elif angle > radians(15) and angle <= radians(37):
         linechar = '.'
     elif angle > radians(37) and angle <= radians(52):
         linechar = '\\'
     elif angle > radians(52) and angle <= radians(75):
         linechar = '.'
     elif angle > radians(75):
         linechar = '|'
     else:
         linechar = '?'
     repr = dict()
     line = bresenham(self._startpos.x, self._startpos.y, self._endpos.x,
                      self._endpos.y)
     for coord in line:
         pos = Pos(coord[0], coord[1])
         repr[pos] = linechar
     self._repr = repr
예제 #20
0
 def endpos_capped(self):
     """Return end position capped by the coordinates maximum."""
     x, y = self._endpos.xy
     if self._endpos.x > self._maxpos.x:
         x = self._maxpos.x
     if self._endpos.y > self._maxpos.y:
         y = self._maxpos.y
     return Pos(x, y)
예제 #21
0
    def test_duplicate(self):

        c = Controller()
        c.on_new()

        # NB test should be run against the default (en_US) component library
        c.on_component_changed('AND gate')
        c.on_paste_objects(Pos(4, 2))

        c.on_component_changed('NAND gate')
        c.on_paste_objects(Pos(4, 2))

        rect = (Pos(4, 2), Pos(5, 3))
        c.on_cut(rect)

        filename = 'tmp/test_edit_duplicate.aac'
        self.assertTrue(c.on_write_to_file(filename))
예제 #22
0
    def _repr_hor(self):
        """
        Horizontal arrow representation:

             d  E
             \
        b----c\
        |      /e
        a----g/
             /
        S    f

        S: start position
        E: end position
        a-g: arrow vertices
        """
        startpos = self._startpos
        endpos = self._endpos
        h = startpos.y - endpos.y
        if h == 0:
            h = 3
        h = h - h % 3
        h2 = h / 2
        h3 = h / 3
        my = (startpos.y + endpos.y) / 2
        a = Pos(startpos.x, startpos.y - h3)
        b = Pos(startpos.x, endpos.y + h3)
        c = Pos(endpos.x - h2, endpos.y + h3)
        d = Pos(endpos.x - h2, endpos.y)
        e = Pos(endpos.x, my)
        f = Pos(endpos.x - h2, startpos.y)
        g = Pos(endpos.x - h2, startpos.y - h3)
        self._repr_poly(a, b, c, d, e, f, g)
        self._pickpoint = c
예제 #23
0
    def _repr_vert(self):
        """
        Vertical arrow representation:

             e
             \\  E
            / \
           /   \
         d/-c g-\f
            | |
            | |
          S b-a

        S: start position
        E: end position
        a-g: arrow vertices
        """
        startpos = self._startpos
        endpos = self._endpos
        w = endpos.x - startpos.x
        if w == 0:
            w = 3
        w = w - w % 3
        w2 = w / 2
        w3 = w / 3
        mx = (startpos.x + endpos.x) / 2
        a = Pos(endpos.x - w3, startpos.y)
        b = Pos(startpos.x + w3, startpos.y)
        c = Pos(startpos.x + w3, endpos.y + w2)
        d = Pos(startpos.x, endpos.y + w2)
        e = Pos(mx, endpos.y)
        f = Pos(endpos.x, endpos.y + w2)
        g = Pos(endpos.x - w3, endpos.y + w2)
        self._repr_poly(a, b, c, d, e, f, g)
        self._pickpoint = c
예제 #24
0
    def test_lines(self):

        c = Controller()
        c.on_new()

        start = Pos(5, 5)
        end = Pos(15, 5)

        line_types = (Line.MLINE, Line.LINE1, Line.LINE2, Line.LINE3,
                      Line.LINE4)

        for type in line_types:
            c.on_paste_line(start, end, type)
            start += Pos(0, 5)
            end += Pos(0, 5)

        filename = 'tmp/test_lines.aac'
        self.assertTrue(c.on_write_to_file(filename))
예제 #25
0
    def _representation(self):
        """Compose the line elements."""
        self._direction()
        self._repr = dict()
        terminal = self._terminal
        start_terminal = terminal
        start = self._startpos
        end = self._endpos
        pos = start
        if self._dir == HORIZONTAL:
            line_char = Preferences.values['LINE_HOR']
            incr = Pos(1, 0)
            if start > end:
                # line drawn from right-to-left
                pos = Pos(end.x, start.y)
                end = start

        elif self._dir == VERTICAL:
            if self._type == self.LINE4:
                start_terminal = Preferences.values['TERMINAL4_VERT']
            line_char = Preferences.values['LINE_VERT']
            incr = Pos(0, 1)
            if start > end:
                # line drawn from right-to-left
                pos = Pos(start.x, end.y)
                end = start
        else:
            line_char = "?"
            incr = Pos(1, 1)

        # startpoint terminal
        if start_terminal is None:
            self._repr[pos] = line_char
        else:
            self._repr[pos] = start_terminal
        pos += incr
        while pos < end:
            self._repr[pos] = line_char
            pos += incr
        # endpoint terminal
        if self._terminal is None:
            self._repr[pos] = line_char
        else:
            self._repr[pos] = self._terminal
예제 #26
0
    def test_in_rect(self):

        a = Pos(10, 15)
        b = Pos(12, 21)

        ul = Pos(9, 14)
        br = Pos(12, 20)
        r = (ul, br)

        self.assertTrue(a.in_rect(r))
        self.assertFalse(b.in_rect(r))
예제 #27
0
 def _representation(self):
     self._repr = dict()
     startpos = self._startpos
     pos = self._startpos
     str = self._text.split('\n')
     if self._ori == 0 or self._ori == 2:
         for line in str:
             pos.x = startpos.x
             for char in line:
                 if char != ' ':
                     self._repr[pos] = char
                 pos += Pos(1, 0)
             pos += Pos(0, 1)
     elif self._ori == 1 or self._ori == 3:
         for line in str:
             pos.y = startpos.y
             for char in line:
                 if char != ' ':
                     self._repr[pos] = char
                 pos += Pos(0, 1)
             pos += Pos(1, 0)
예제 #28
0
 def __init__(self, lmd):
     super(MatrixView, self).__init__()
     self._surface = None
     self._hover_pos = Pos(0, 0)
     self.set_can_focus(True)
     self.set_focus_on_click(True)
     self.connect('draw', self.on_draw)
     self.connect('configure-event', self.on_configure)
     self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
     self.connect('button-press-event', self.on_button_press)
     # https://stackoverflow.com/questions/44098084/how-do-i-handle-keyboard-events-in-gtk3
     self.add_events(Gdk.EventMask.KEY_PRESS_MASK)
     self.connect('key-press-event', self.on_key_press)
     self.add_events(Gdk.EventMask.POINTER_MOTION_MASK)
     self.connect('motion-notify-event', self.on_hover)
     self._cursor_on = True
     self._hover_pos = Pos(0, 0)
     self.init_line_matching_data(lmd)
     # https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-add-tick-callback
     self.start_time = time.time()
     self.cursor_callback = self.add_tick_callback(self.toggle_cursor)
     pub.subscribe(self.on_matching_data_changed, 'MATCHING_DATA_CHANGED')
예제 #29
0
    def test_read_ascii(self):

        c = Controller()
        c.on_new()

        filename = 'tests/files/test_ascii.txt'
        self.assertTrue(c.on_load_ascii_from_file(filename))

        pos = Pos(0, 0)
        c.on_paste_objects(pos)

        filename = 'tmp/test_ascii.aac'
        self.assertTrue(c.on_write_to_file(filename))
예제 #30
0
 def draw(self, ctx, pos=None):
     """
     :param ctx: the Cairo context
     :param pos: target position in grid canvas (x,y) coordinates
     """
     ctx.save()
     ctx.set_source_rgb(0.75, 0.75, 0.75)
     x_start, y_start = pos.xy
     # size from grid (col,row) to view (x,y) coordinates
     size = Pos(self._size[0], self._size[1]).view_xy()
     width = size.x
     height = size.y
     ctx.rectangle(x_start, y_start, width, height)
     ctx.fill()
     ctx.restore()