Exemple #1
0
class SelectDots(object):
    """
    classdocs
    """


    ###@profile    
    def __init__(self, frame, mw=None,
                  display_game=True,
                  image_hash=None,
                  nrows=10,
                  ncols=None,
                  width=None, height=None, tbmove=.1,
                  stroke_checking=False,
                  do_corners=True,
                  do_edges=True,
                  do_regions=True,
                  check_mod=None,
                  down_click_call=None,
                  highlight_limit=None,
                  highlighting=True,
                  corner_visible=True,
                  corner_width=10,
                  region_on_color='',
                  ###region_off_color='light slate gray',
                  region_off_color='',
                  region_visible=False,
                  region_width=0,
                  edge_width=8,
                  edge_visible=True):
        """
        :frame: - frame within we are placed
        :image_hash: file image access
        :display_game: display game updates default: True
                       False - skip display updates, where possible
        :nrows: number of rows of squares default: 10
        :ncols: number of columns of squares default: rows
        :width: window width
        :height: window height
        :stroke_checking: Check for mouse/hand stroking
        :tbmove: minimum time(seconds) between move detection
        :check_mod: routine called, if present, before & after
                part modification
        :corner_visible: corner is visible default: True
        :corner_width: corner size
        :region_on_color: color when region is "on"
        :region_off_color: color when region if "off"
        :region_visible: region (off) is visible default: False
        :highlight_limit: limit highlighting (seconds)
                default: None (None - no limit)
        :highlighting: True - highlight part under mouse
                    Default: True
        :edge_width: edge width in pixels
                        default:1
        :edge_visible: edge visible default: True
        """
        self.display_game = display_game
        self.frame = frame
        self.image_hash = image_hash
        self.canvas = None
        self.mw = mw
        self.nrows = nrows
        if ncols is None:
            ncols = nrows
        self.ncols = ncols
        self.nrows = nrows
        self.ncols = ncols
        self.do_corners = do_corners
        self.do_edges = do_edges
        self.do_regions = do_regions
        if width is None:
            width = frame.winfo_width()
        self.width = width
        if height is None:
            height = frame.winfo_height()
        self.height = height
        self.drawn_lines = []           # lines drawn
            
        self. min_xlen = 10
        self.min_ylen = self.min_xlen
        self.check_mod = check_mod
        self.tbmove = tbmove
        self.highlight_limit = highlight_limit
        self.highlighting = highlighting
        
        self.down_click_call = down_click_call
        self.region_width=region_width
        self.region_on_color = region_on_color
        self.region_off_color = region_off_color
        self.region_visible = region_visible
        self.corner_visible = corner_visible
        self.corner_width = corner_width
        self.edge_width = edge_width
        self.edge_visible = edge_visible
        self.stroke_checking = stroke_checking
        self.area = None        # Set non None when created
        self.display_tracking = DisplayTracking(self)
        self.setup_area()


    def setup_shadow(self):
        """ Setup shadow which can be used to speedup testing
        if display or immediate display is not required
        """
        self.shadow = DotsShadow(self, nrows=self.nrows, ncols=self.ncols)
            
        
            
    def setup_area(self):
        """ Setup / resetup board setting
        """
        self.setup_shadow()           # Shadow used to speed testing
        new_area = False            # Indicate reset
        if self.canvas is not None:
            self.canvas.destroy()
            self.canvas = None 
            new_area = True
        self.canvas = CanvasTracked(self.frame,
                 width=self.width, height=self.height,
                 bg="white")
        self.canvas.set_parts_control(self)     # connect the dots - for info :)
        self.canvas.pack(expand=YES, fill=BOTH)
        
        self.area = SelectArea(self.canvas, mw=self.mw,
                               board=self,
                               image_hash=self.image_hash,
                               display_game=self.display_game,
                               tbmove=self.tbmove,
                               stroke_checking=self.stroke_checking,
                               check_mod=self.check_mod,
                               down_click_call=self.down_click_call,
                               highlight_limit=self.highlight_limit,
                               highlighting=self.highlighting)
        
        rects =  []
        rects_rows = []         # So we can pass row, col
        rects_cols = []
        
        def rn(val):
            return int(round(val))
        
        xmin = .1*float(self.width)
        xmax = .9*float(self.width)
        xlen = (xmax-xmin)/float(self.ncols)
        min_xlen = float(self.min_xlen)
        if xlen < min_xlen:
            SlTrace.lg("xlen(%.0f) set to %.0f" % (xlen, min_xlen))
            xlen = min_xlen
        ymin = .1*float(self.height)
        ymax = .9*float(self.height)
        ylen = (ymax-ymin)/float(self.nrows)
        min_ylen = float(self.min_ylen)
        if ylen < min_ylen:
            SlTrace.lg("ylen(%.0f) set to %.0f" % (ylen, min_ylen))
            ylen = min_ylen
        for i in range(int(self.ncols)):
            col = i+1
            x1 = xmin + i*xlen
            x2 = x1 + xlen
            for j in range(int(self.nrows)):
                row = j+1
                y1 = ymin + j*ylen
                y2 = y1 + ylen
                rect = ((rn(x1), rn(y1)), (rn(x2), rn(y2)))
                rects.append(rect)
                rects_rows.append(row)
                rects_cols.append(col)
       
        
        for i, rect in enumerate(rects):
            row = rects_rows[i]
            col = rects_cols[i]
            self.area.add_rect(rect, row=row, col=col,
                            color=self.region_off_color,
                            do_corners=self.do_corners,
                            do_edges=self.do_edges,
                            draggable_edge=False,
                            draggable_corner=False,
                            draggable_region=False,
                            on_color=self.region_on_color,
                            off_color=self.region_off_color,   
                            invisible_region=not self.region_visible,
                            invisible_edge=not self.edge_visible,
                            region_width=self.region_width,
                            edge_width=self.edge_width)
        ####if not self.display_game:
        ####    return
        
        for part in self.area.get_parts():
            self.shadow.set_part(part)                  # Add to shadow data
            if self.do_corners and part.is_corner():
                part.set(display_shape="circle",
                           display_size=self.corner_width,
                           color="blue")
                if new_area:
                    if self.display_game:
                        part.display()
            elif self.do_edges and part.is_edge():
                part.set(edge_width_select=50,
                           edge_width_display=self.edge_width,
                           on_highlighting=True,
                           off_highlighting=True,
                           color="lightgreen")
            elif self.do_regions and part.is_region():
                part.set(color=self.region_off_color)
                top_edge = part.get_top_edge()
                if top_edge is not None:
                    top_edge.row = part.row 
                    top_edge.col = part.col
                right_edge = part.get_right_edge()
                if right_edge is not None:
                    right_edge.row = part.row 
                    right_edge.col = part.col + 1
                botom_edge = part.get_botom_edge()
                if botom_edge is not None:
                    botom_edge.row = part.row + 1 
                    botom_edge.col = part.col
                left_edge = part.get_left_edge()
                if left_edge is not None:
                    left_edge.row = part.row 
                    left_edge.col = part.col
        self.complete_square_call = None                # Setup for complete square call
        self.new_edge_call = None                       # Setup for new edge call


    def get_mvpart(self, mvpart=None):
        """ Get MVP part
        :mvpart: unique move part specification (row,col,hv)
        :returns: part, None if not found
        """
        return self.shadow.get_mvpart(mvpart=mvpart)

    def get_part(self, id=None, type=None, sub_type=None, row=None, col=None):
        """ Get basic part
        :mvpart: shadow (MoveList) id
        :id: unique part id
        :type: part type e.g., edge, region, corner
        :row:  part row
        :col: part column
        :returns: part, None if not found
        """
        
        return self.area.get_part(id=id, type=type, sub_type=sub_type, row=row, col=col)

 
    def get_parts(self, pt_type=None):
        """ Get parts in figure
        :pt_type: part type, default: all parts
                "corner", "edge", "region"
        """
        return self.area.get_parts(pt_type=pt_type)
    
    def get_legal_list(self):
        """  Get MoveList of edges that would be legal moves
        """
        return self.shadow.get_legal_list()
    
    def get_legal_moves(self):
        """  Get list of edges that would be legal moves
        """
        return self.shadow.get_legal_moves()


    def get_square_moves(self, moves):
        """ Get, from moves, those which would complete a square
        :moves: move list default: all legal moves
        """
        return self.shadow.get_square_moves(moves)


    def get_num_legal_moves(self):
        return self.shadow.get_num_legal_moves()


    def get_square_distance_list(self, min_dist=2, move_list=None):
        """ Get moves which provide a minimum distance to sqaree completion
        """
        return self.shadow.get_square_distance_list(min_dist=min_dist, move_list=move_list)
    
    
    def get_selects(self):
        """ GEt list of selected part ids
        :returns: list, empty if none
        """
        return self.area.get_selects()


    def get_selected_part(self):
        """ Get selected part
        :returns: selected part, None if none selected
        """
        return self.area.get_selected_part()
                
    
    def get_parts_at(self, x, y, sz_type=SelectPart.SZ_SELECT):
        """ Check if any part is at canvas location provided
        If found list of parts
        :Returns: SelectPart[]
        """
        return self.area.get_parts_at(x,y,sz_type=sz_type)



    def get_xy(self):
        """ get current mouse position (or last one recongnized
        :returns: x,y on area canvas, None if never been anywhere
        """
        return self.area.get_xy()

    
    def add_complete_square_call(self, call_back):
        """ Add function to be called upon completed square
        :call_back: call back (edge, region) - None - remove call back
        """
        self.complete_square_call = call_back


    def add_down_click_call(self, call):
        """ Add down click processing function
        :call: down click processing function
        """
        self.down_click_call = call             # For reestablishing call
        self.area.add_down_click_call(call)
        
    
    def add_new_edge_call(self, call_back):
        """ Add function to be called upon newly added edge
        :call_back: call back (edge) - None - remove call back
        """
        self.new_edge_call = call_back
        
    def complete_square(self, edge, regions):
        """ Report completed square
        :edge: - edge that completed the region
        :regions: - completed region(s)
        """
        SlTrace.lg("Completed region edge=%s" % (edge), "complete_square")
        if self.complete_square_call is not None:
            self.complete_square_call(edge, regions)


    def set_down_click_call(self, down_click_call):
        """ Direct down_click processing
        """
        self.down_click_call = down_click_call


    def is_square_complete(self, edge, squares=None, ifadd=False):
        """ Determine if this edge completes a square(s)
        :edge: - potential completing edge
        :squares: list, to which any completed squares(regions) are added
                Default: no regions are added
        :returns: True iff one or more squares are completed
        """
        return self.area.is_square_complete(edge, squares=squares, ifadd=ifadd)




    def square_complete_distance(self, edge,
                                  squares_distances=None):
        """ Determine minimum number of moves, including this
        move to complete a square
        :edge: - potential completing edge
        :squares_distances: list, of (distance, square) pairs
                Default: no entries returned
                if no connected squares - empty list returned
        :returns: closest distance, NOT_CLOSE if no squares
        """
        return self.area.square_complete_distance(edge, squares_distances=squares_distances)
        
        
    def new_edge(self, edge):
        """ Report new edge added
        :edge: - edge that was added
        """
        SlTrace.lg("SelectDots.new_edge: edge=%s" % (edge), "new_edge")
        if self.new_edge_call is not None:
            self.new_edge_call(edge)
        self.area.stroke_info.setup()      # Reset stroke search


    def disable_moves(self):
        """ Disable(ignore) moves by user
        """
        self.area.disable_moves()
        
        
    def enable_moves(self):
        """ Enable moves by user
        """
        self.area.enable_moves()


        
    def down_click(self, part, event=None):
        """ Process down click over highlighted part
        All highlighted parts elicit a call
        :part: highlighted part
        :event: event if available
        :Returns: True if processing is complete
        """
        if self.down_click_call is not None:
            return self.down_click_call(part, event=event)
        
        """ Self processing
        """
        if part.is_edge() and not part.is_turned_on():
            SlTrace.lg("turning on %s" % part, "turning_on")
            self.drawn_lines.append(part)
            part.turn_on()
            regions = part.get_adjacents()      # Look if we completed any squares
            for square in regions:
                if square.is_complete():
                    self.complete_square(part, square)
                    
            return True             # Indicate processing is done
    
        return False
        

    def stroke_call(self, part=None, x=None, y=None):
        """ Call back from sel_area.add_stroke_call
        """
        
        self.down_click(part)
        
            
    def display(self):
        
        if not self.area.display_game:
            return

        self.area.display()

    def add_display_objects(self, part, objects):
        """ Add newly displayed objects on canvas
        :part: displaying part
        :objects: objects, or lists of objects, or lists of...
        """
        self.display_tracking.add_display_objects(part, objects)

    def add_display_tags(self, part, tags):
        """ Add tags of newly displayed canvas objects
        :part: displaying part
        :tags: tag, or lists of tags, or lists of...
        """
        self.display_tracking.add_display_tags(part, tags)


    def clear_blinking(self):
        self.area.clear_blinking()

    def display_clear(self, part, display=False):
        """ Clear out display of current edge
        """
        self.display_tracking.display_clear(part, display=display)


    def destroy(self):
        if self.area is not None:
            self.area.destroy()
            self.area = None
        if self.canvas is not None:
            self.canvas.destroy()
            self.canvas = None



    def draw_line(self, p1, p2, color=None, width=None, **kwargs):
        """ Draw line between two points on canvas
        :p1: point x,y canvas coordinates
        :p2: point x,y canvas coordinates
        :color: line color default: red
        :width: line width in pixels default:1
        :kwargs: keyword args passed to tk
        :returns: canvas line object
        """
        return self.area.draw_line(p1, p2, color=color, width=width, **kwargs)

 
    def draw_outline(self, sq, color=None, width=None):
        self.area.draw_outline(sq=sq, color=color, width=width)
        

    def remove_parts(self, parts):
        """ Remove deleted or changed parts
        Only clears display, leaving part in place
        :parts: list of parts to be removed
        """
        for part in parts:
            if part.is_region():
                part.clear_centered_texts()
                pass

    def reset(self):
        """ Set board to beginning of game
        """
        self.setup_area()
        '''
        regions = self.area.get_parts(pt_type="region")
        for region in regions:
            region.display_clear()
        edges = self.area.get_parts(pt_type="edge")
        for edge in edges:
            edge.turn_off()
            edge.select_clear()
            edge.highlight_clear()
            edge.display_clear()
        '''
            
    def insert_parts(self, parts):
        """ Add new or changed parts
        Replaces part of same id, redisplaying
        :parts: list of parts to be env_added
        """
        for part in parts:
            d_part = self.area.get_part(id=part.part_id)
            if d_part is None:
                if self.display_game:
                    raise SelectError("insert_parts: No part(id=%d) found %s"
                                   % (part.part_id, part))
                continue
            self.set_part(part)
        
        
    def select_clear(self, parts=None):
        """ Select part(s)
        :parts: part or list of parts
                default: all selected
        """
        self.area.select_clear(parts=parts)


    def select_set(self, parts, keep=False):
        """ Select part(s)
        :parts: part(s) to select/deselect
        """
        self.area.select_set(parts, keep=keep)
        
        
        

    def set_part(self, part):
        """ Set base part.contents to values of Part
        
        :part: part structure with new values
        """
        pt = self.area.parts_by_id[part.part_id]
        if pt is None:
            SlTrace.lg("part %s(%d) is not in area - skipped"
                       % (part, part.part_id))
            return

        pt.__dict__ = part.__dict__.copy()
        
        
    
    
    def set_stroke_move(self, use_stroke=True):
        """ Enable/Disable use of stroke moves
        Generally for use in touch screens
        """
        self.area.set_stroke_move(use_stroke)
class SelectSquares(object):
    """
    classdocs
    """

    ###@profile
    def __init__(self,
                 canvas,
                 mw=None,
                 nrows=10,
                 ncols=None,
                 width=None,
                 height=None,
                 tbmove=.1,
                 check_mod=None,
                 down_click_call=None,
                 highlight_limit=None):
        """
        :canvas: - canvas within we are placed
        :nrows: number of rows of squares default: 10
        :ncols: number of columns of squares default: rows
        :width: window width
        :height: window height
        :tbmove: minimum time(seconds) between move detection
        :check_mod: routine called, if present, before & after
                part modification
        :highlight_limit: limit highlighting (seconds)
                default: None (None - no limit)
        """
        if ncols is None:
            ncols = nrows
        self.canvas = canvas
        self.nrows = nrows
        self.ncols = ncols
        if width is None:
            width = canvas.winfo_width()
        self.width = width
        if height is None:
            height = canvas.winfo_height()
        self.height = height
        self.drawn_lines = []  # lines drawn

        min_xlen = 10
        min_ylen = min_xlen
        self.check_mod = check_mod
        self.tbmove = tbmove
        self.highlight_limit = highlight_limit

        rects = []
        rects_rows = []  # So we can pass row, col
        rects_cols = []
        self.down_click_call = down_click_call

        def rn(val):
            return int(round(val))

        xmin = .1 * float(width)
        xmax = .9 * float(width)
        xlen = (xmax - xmin) / float(ncols)
        min_xlen = float(min_xlen)
        if xlen < min_xlen:
            SlTrace.lg("xlen(%.0f) set to %.0f" % (xlen, min_xlen))
            xlen = min_xlen
        ymin = .1 * float(height)
        ymax = .9 * float(height)
        ylen = (ymax - ymin) / float(nrows)
        min_ylen = float(min_ylen)
        if ylen < min_ylen:
            SlTrace.lg("ylen(%.0f) set to %.0f" % (ylen, min_ylen))
            ylen = min_ylen
        for i in range(int(ncols)):
            col = i + 1
            x1 = xmin + i * xlen
            x2 = x1 + xlen
            for j in range(int(nrows)):
                row = j + 1
                y1 = ymin + j * ylen
                y2 = y1 + ylen
                rect = ((rn(x1), rn(y1)), (rn(x2), rn(y2)))
                rects.append(rect)
                rects_rows.append(row)
                rects_cols.append(col)

        self.area = SelectArea(canvas,
                               mw=mw,
                               tbmove=self.tbmove,
                               check_mod=self.check_mod,
                               down_click_call=self.down_click_call,
                               highlight_limit=self.highlight_limit)
        ###SelectRegion.reset()
        for i, rect in enumerate(rects):
            row = rects_rows[i]
            col = rects_cols[i]
            self.area.add_rect(rect,
                               row=row,
                               col=col,
                               draggable_edge=False,
                               draggable_corner=False,
                               draggable_region=False,
                               invisible_region=True,
                               invisible_edge=True)
        for part in self.area.get_parts():
            if part.is_corner():
                part.set(display_shape="circle", display_size=10, color="blue")
            elif part.is_edge():
                part.set(edge_width_select=50,
                         edge_width_display=5,
                         on_highlighting=True,
                         off_highlighting=True,
                         color="lightgreen")
            elif part.is_region():
                part.set(color='light slate gray')
                top_edge = part.get_top_edge()
                top_edge.row = part.row
                top_edge.col = part.col
                right_edge = part.get_right_edge()
                right_edge.row = part.row
                right_edge.col = part.col + 1
                botom_edge = part.get_botom_edge()
                botom_edge.row = part.row + 1
                botom_edge.col = part.col
                left_edge = part.get_left_edge()
                left_edge.row = part.row
                left_edge.col = part.col
        self.complete_square_call = None  # Setup for complete square call
        self.new_edge_call = None  # Setup for new edge call
        ###self.area.add_turned_on_part_call(self.new_edge)
        self.player_control = PlayerControl(self, display=False)

    def get_part(self, id=None, type=None, sub_type=None, row=None, col=None):
        """ Get basic part
        :id: unique part id
        :type: part type e.g., edge, region, corner
        :row:  part row
        :col: part column
        :returns: part, None if not found
        """
        return self.area.get_part(id=id,
                                  type=type,
                                  sub_type=sub_type,
                                  row=row,
                                  col=col)

    def get_parts(self, pt_type=None):
        """ Get parts in figure
        :pt_type: part type, default: all parts
                "corner", "edge", "region"
        """
        return self.area.get_parts(pt_type=pt_type)

    def get_legal_moves(self):
        """  Get edges that would be legal moves
        """
        edges = self.get_parts(pt_type="edge")
        moves = []
        for edge in edges:
            if not edge.is_turned_on():
                moves.append(edge)
        return moves

    def get_selects(self):
        """ GEt list of selected parts
        :returns: list, empty if none
        """
        return self.area.get_selects()

    def get_selected_part(self):
        """ Get selected part
        :returns: selected part, None if none selected
        """
        return self.area.get_selected_part()

    def get_parts_at(self, x, y, sz_type=SelectPart.SZ_SELECT):
        """ Check if any part is at canvas location provided
        If found list of parts
        :Returns: SelectPart[]
        """
        return self.area.get_parts_at(x, y, sz_type=sz_type)

    def get_xy(self):
        """ get current mouse position (or last one recongnized
        :returns: x,y on area canvas, None if never been anywhere
        """
        return self.area.get_xy()

    def add_complete_square_call(self, call_back):
        """ Add function to be called upon completed square
        :call_back: call back (edge, region) - None - remove call back
        """
        self.complete_square_call = call_back

    def add_down_click_call(self, call):
        """ Add down click processing function
        :call: down click processing function
        """
        self.area.add_down_click_call(call)

    def add_new_edge_call(self, call_back):
        """ Add function to be called upon newly added edge
        :call_back: call back (edge) - None - remove call back
        """
        self.new_edge_call = call_back

    def complete_square(self, edge, regions):
        """ Report completed square
        :edge: - edge that completed the region
        :regions: - completed region(s)
        """
        SlTrace.lg("Completed region edge=%s" % (edge), "complete_square")
        if self.complete_square_call is not None:
            self.complete_square_call(edge, regions)

    def set_down_click_call(self, down_click_call):
        """ Direct down_click processing
        """
        self.down_click_call = down_click_call

    def is_square_complete(self, edge, squares=None, ifadd=False):
        """ Determine if this edge completes a square(s)
        :edge: - potential completing edge
        :squares: list, to which any completed squares(regions) are added
                Default: no regions are added
        :returns: True iff one or more squares are completed
        """
        return self.area.is_square_complete(edge, squares=squares, ifadd=ifadd)

    def square_complete_distance(self, edge, squares_distances=None):
        """ Determine minimum number of moves, including this
        move to complete a square
        :edge: - potential completing edge
        :squares_distances: list, of (distance, square) pairs
                Default: no entries returned
                if no connected squares - empty list returned
        :returns: closest distance, NOT_CLOSE if no squares
        """
        return self.area.square_complete_distance(
            edge, squares_distances=squares_distances)

    def new_edge(self, edge):
        """ Report new edge added
        :edge: - edge that was added
        """
        SlTrace.lg("SelectSquares.new_edge: edge=%s" % (edge), "new_edge")
        if self.new_edge_call is not None:
            self.new_edge_call(edge)
        self.area.stroke_info.setup()  # Reset stroke search

    def disable_moves(self):
        """ Disable(ignore) moves by user
        """
        self.area.disable_moves()

    def enable_moves(self):
        """ Enable moves by user
        """
        self.area.enable_moves()

    def down_click(self, part, event=None):
        """ Process down click over highlighted part
        All highlighted parts elicit a call
        :part: highlighted part
        :event: event if available
        :Returns: True if processing is complete
        """
        if self.down_click_call is not None:
            return self.down_click_call(part, event=event)
        """ Self processing
        """
        if part.is_edge() and not part.is_turned_on():
            SlTrace.lg("turning on %s" % part, "turning_on")
            self.drawn_lines.append(part)
            part.turn_on(player=self.get_player())
            regions = part.get_adjacents()  # Look if we completed any squares
            for square in regions:
                if square.is_complete():
                    self.complete_square(part, square)

            return True  # Indicate processing is done

        return False

    def get_player(self):
        """ Get current player
        """
        if self.player_control is None:
            return None

        return self.player_control.get_player()

    def player_control(self):
        """ Setup player control
        """
        if self.player_control is None:
            self.player_control = PlayerControl(self, display=False)
        self.player_control.control_display()

    def stroke_call(self, part=None, x=None, y=None):
        """ Call back from sel_area.add_stroke_call
        """

        self.down_click(part)

    def display(self):
        self.area.display()

    def destroy(self):
        if self.area is not None:
            self.area.destroy()
            self.area = None
        if self.canvas is not None:
            self.canvas.destroy()
            self.canvas = None

    def remove_parts(self, parts):
        """ Remove deleted or changed parts
        Only clears display, leaving part in place
        :parts: list of parts to be removed
        """
        for part in parts:
            d_part = self.area.get_part(id=part.part_id)
            if d_part is None:
                raise SelectError("No part(id=%d) found %s" %
                                  (part.part_id, part))
                continue
            if d_part.row == 2:
                pass
            d_part.display_clear()
            d_part.set(invisible=True)

    def insert_parts(self, parts):
        """ Add new or changed parts
        Replaces part of same id, redisplaying
        :parts: list of parts to be env_added
        """
        for part in parts:
            d_part = self.area.get_part(id=part.part_id)
            if d_part is None:
                raise SelectError("insert_parts: No part(id=%d) found %s" %
                                  (part.part_id, part))
                continue
            self.set_part(part)

    def select_clear(self, parts=None):
        """ Select part(s)
        :parts: part or list of parts
                default: all selected
        """
        self.area.select_clear(parts=parts)

    def select_set(self, parts, keep=False):
        """ Select part(s)
        :parts: part(s) to select/deselect
        """
        self.area.select_set(parts, keep=keep)

    def set_part(self, part):
        """ Set base part.contents to values of Part
        
        :part: part structure with new values
        """
        pt = self.area.parts_by_id[part.part_id]
        if pt is None:
            SlTrace.lg("part %s(%d) is not in area - skipped" %
                       (part, part.part_id))
            return

        pt.__dict__ = part.__dict__.copy()

    def set_stroke_move(self, use_stroke=True):
        """ Enable/Disable use of stroke moves
        Generally for use in touch screens
        """
        self.area.set_stroke_move(use_stroke)