← Back to team overview

kicad-developers team mailing list archive

Re: Current state of ActionPlugin

 

Dear All,

As requested please find attached a patch to create 2 samples for the
action menu:
- add_automatic_border.py
Automaticaly found smaller area including all modules/coper area/text...
and build or update rectangular PCB edge

- text_by_date.py
Change any text with content "$date$" on the PCB to "$date$" + current date.
=> This example was the first I propose. It is also the one I put inside
the documentation.


Regards,
Le 23/01/2017 à 14:30, jp charras a écrit :
> Le 23/01/2017 à 14:19, Nick Østergaard a écrit :
>> Should it be enabled in the windows nightlies now or should we wait a bit?
>>
> 
> Before enabling it, please wait for a few tests from devs and J-S.
> Perhaps also a bit of doc and 2 or 3 samples which do not delete anything (could be in demos) is
> welcome.
> This is in the J-S Reynaud's hands.
> 
> 
>> 2017-01-23 14:13 GMT+01:00 jp charras <jp.charras@xxxxxxxxxx>:
>>> Le 18/01/2017 à 18:00, Jean-Samuel Reynaud a écrit :
>>>> Just to know if this patch is now acceptable ? Did you plan to commit it ?
>>>>
>>>> Thanks,
>>>> Le 17/01/2017 à 19:46, Jean-Samuel Reynaud a écrit :
>>>>>> yes i was trying to subtly imply that :)
>>>>> ok, find attached the patch with the about box updated ;)
>>>>>
>>>>> Regards,
>>>>>
>>>
>>> I committed the patch ( with fixes, see comments) in rev:
>>> 2b5769c0a8568e421c2152177a8f1c27d9bf9cb5
>>>
>>> Jean-Samuel, thanks, and please carefully test this rev, to be sure I did not break anything.
>>>
>>> The action plugin feature must be currently seen as an experimental feature, for developers, because
>>> it can easily break a board.
>>> It is enabled if the option -DKICAD_SCRIPTING_ACTION_MENU=ON is added to the cmake command line.
>>>
>>> I am thinking this feature need to be tested, and enhancements added (especially if a script deletes
>>> a board item), and this is the reason I see it as currently experimental.
>>>
>>> However, now it exists, it can be tested.
>>>
>>>
>>> --
>>> Jean-Pierre CHARRAS
>>>
>>> _______________________________________________
>>> Mailing list: https://launchpad.net/~kicad-developers
>>> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
>>> Unsubscribe : https://launchpad.net/~kicad-developers
>>> More help   : https://help.launchpad.net/ListHelp
>>
> 
> 

From bcdd8c17da55b3d3c07252f1a370e7dd3890e96d Mon Sep 17 00:00:00 2001
From: Jean-Samuel Reynaud <js.reynaud@xxxxxxxxx>
Date: Fri, 27 Jan 2017 11:06:28 +0100
Subject: [PATCH] Add ActionPlugin example

---
 .../add_automatic_border.py                        | 247 +++++++++++++++++++++
 demos/python_scripts_examples/text_by_date.py      |  59 +++++
 2 files changed, 306 insertions(+)
 create mode 100644 demos/python_scripts_examples/add_automatic_border.py
 create mode 100644 demos/python_scripts_examples/text_by_date.py

diff --git a/demos/python_scripts_examples/add_automatic_border.py b/demos/python_scripts_examples/add_automatic_border.py
new file mode 100644
index 0000000..ad71c8f
--- /dev/null
+++ b/demos/python_scripts_examples/add_automatic_border.py
@@ -0,0 +1,247 @@
+#  add_automatic_border.py
+#
+# Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors.
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA 02110-1301, USA.
+#
+#
+
+from pcbnew import *
+import re
+import datetime
+
+
+class add_automatic_border( ActionPlugin ):
+    """
+    add_automatic_border: An other sample plugin as an example of ActionPlugin
+    Build PCB edges to include all PCB elements
+    How to use:
+    - add all your modules/track/area/text...
+    - Call the plugin
+    - An including PCB edge will be created automaticaly
+    """
+
+    def defaults( self ):
+        """
+        Method defaults must be redefined
+        self.name should be the menu label to use
+        self.category should be the category (not yet used)
+        self.description should be a comprehensive description
+          of the plugin
+        """
+        self.name = "Add or update automatic PCB edges"
+        self.category = "Modify PCB"
+        self.description = "Automaticaly add or update edges on an existing PCB"
+        # Offset between existing elements and edge we will add/update (fixed at 2.54mm)
+        self.offset = FromMM( 2.54 )
+        # Attach to a grid step (fixed at 2.54mm)
+        self.grid = FromMM( 2.54 )
+
+    def min( self, a, b ):
+        """
+        Method min: Found min between a and b even is one is None
+        """
+        if a is None:
+            return b
+        if b is None:
+            return a
+        if a < b:
+            return a
+        return b
+
+    def max( self, a, b ):
+        """
+        Method max: Found max between a and b even is one is None
+        """
+        if a is None:
+            return b
+        if b is None:
+            return a
+        if a > b:
+            return a
+        return b
+
+    def Run( self ):
+        """
+        Method Run is called by Action menu
+        """
+        pcb = GetBoard()
+        # Find including area on min/max x/y
+        min_x = None
+        max_x = None
+        min_y = None
+        max_y = None
+
+        # Enum all area
+        for i in range( pcb.GetAreaCount() ):
+            min_x = self.min( min_x, pcb.GetArea( i ).GetBoundingBox().GetX() )
+            min_y = self.min( min_y, pcb.GetArea( i ).GetBoundingBox().GetY() )
+            max_x = self.max( max_x, \
+                                  pcb.GetArea( i ).GetBoundingBox().GetX() \
+                                  + pcb.GetArea( i ).GetBoundingBox().GetWidth() )
+            max_y = self.max( max_y, \
+                                  pcb.GetArea( i ).GetBoundingBox().GetY() \
+                                  + pcb.GetArea( i ).GetBoundingBox().GetHeight() )
+
+        # Same with track
+        for track in pcb.GetTracks():
+            min_x = self.min( min_x, track.GetStart().x )
+            min_y = self.min( min_y, track.GetStart().y )
+            max_x = self.max( max_x, track.GetEnd().x )
+            max_y = self.max( max_y, track.GetEnd().y )
+
+            min_x = self.min( min_x, track.GetEnd().x )
+            min_y = self.min( min_y, track.GetEnd().y )
+            max_x = self.max( max_x, track.GetStart().x )
+            max_y = self.max( max_y, track.GetStart().y )
+
+        # Variable to store PCB edges we found
+        west = None
+        north = None
+        east = None
+        south = None
+
+        for draw in pcb.GetDrawings():
+            edge_candidate = False
+            #  Detect if current drawing is a PCB edge
+            # and a candicate for north/south/east or west
+            if draw.GetClass() == 'DRAWSEGMENT' \
+              and draw.GetLayer() == Edge_Cuts:
+                # Try candicate for east/west ?
+                if draw.GetStart().x == draw.GetEnd().x:
+                    if west is None and east is None:
+                        west = draw
+                        edge_candidate = True
+                    elif west is None: # east is not none
+                        if draw.GetStart().x < east.GetStart().x:
+                            west = draw
+                            edge_candidate = True
+                        else:
+                            west = east
+                            east = draw
+                            edge_candidate = True
+                    elif east is None: # west is not none
+                        if draw.GetStart().x > west.GetStart().x:
+                            east = draw
+                            edge_candidate = True
+                        else:
+                            east = west
+                            west = draw
+                            edge_candidate = True
+                    else:
+                        None # west and east are already found...
+
+                # Try candicate for north/south ?
+                if draw.GetStart().y == draw.GetEnd().y:
+                    if north is None and south is None:
+                        north = draw
+                        edge_candidate = True
+                    elif north is None: # south is not none
+                        if draw.GetStart().y < south.GetStart().y:
+                            north = draw
+                            edge_candidate = True
+                        else:
+                            north = south
+                            south = draw
+                            edge_candidate = True
+                    elif south is None: # north is not none
+                        if draw.GetStart().y > north.GetStart().y:
+                            south = draw
+                            edge_candidate = True
+                        else:
+                            south = north
+                            north = draw
+                            edge_candidate = True
+                    else:
+                        None # north and south are already found...
+            # Not a edge candidate: use it to find including edges
+            if not edge_candidate:
+                bbox = draw.GetBoundingBox()
+                min_x = self.min( min_x, bbox.GetX() )
+                min_y = self.min( min_y, bbox.GetY() )
+                max_x = self.max( max_x, bbox.GetX() + bbox.GetWidth() )
+                max_y = self.max( max_y, bbox.GetY() + bbox.GetHeight() )
+
+        # Same with modules: Find including area
+        for module in pcb.GetModules():
+            bbox = module.GetBoundingBox()
+            min_x = self.min( min_x, bbox.GetX() )
+            min_y = self.min( min_y, bbox.GetY() )
+            max_x = self.max( max_x, bbox.GetX() + bbox.GetWidth() )
+            max_y = self.max( max_y, bbox.GetY() + bbox.GetHeight() )
+
+        # Add a space between including area and edge (3mm)
+        min_x = min_x - self.offset
+        min_y = min_y - self.offset
+        max_x = max_x + self.offset
+        max_y = max_y + self.offset
+
+        # Fix on the defined grid
+        min_x = min_x - (min_x % self.grid)
+        min_y = min_y - (min_y % self.grid)
+        if ( max_x % self.grid ) != 0:
+            max_x = max_x - (max_x % self.grid) + self.grid
+        if ( max_y % self.grid ) != 0:
+            max_y = max_y - (max_y % self.grid) + self.grid
+
+        # Add or update all edges
+        need_add = False
+        if west is None:
+            need_add = True
+            west = DRAWSEGMENT()
+            west.SetLayer( Edge_Cuts )
+
+        west.SetStart( wxPoint( min_x, min_y ) )
+        west.SetEnd( wxPoint( min_x, max_y ) )
+        if need_add:
+            pcb.Add( west )
+
+        need_add = False
+        if north is None:
+            need_add = True
+            north = DRAWSEGMENT()
+            north.SetLayer( Edge_Cuts )
+
+        north.SetStart( wxPoint( min_x, min_y ) )
+        north.SetEnd( wxPoint( max_x, min_y ) )
+        if need_add:
+            pcb.Add( north )
+
+        need_add = False
+        if east is None:
+            need_add = True
+            east = DRAWSEGMENT()
+            east.SetLayer( Edge_Cuts )
+
+        east.SetStart( wxPoint( max_x, min_y ) )
+        east.SetEnd( wxPoint( max_x, max_y ) )
+        if need_add:
+            pcb.Add( east )
+
+        need_add = False
+        if south is None:
+            need_add = True
+            south = DRAWSEGMENT()
+            south.SetLayer( Edge_Cuts )
+
+        south.SetStart( wxPoint( min_x, max_y ) )
+        south.SetEnd( wxPoint( max_x, max_y ) )
+        if need_add:
+            pcb.Add( south )
+
+
+# Register the action
+add_automatic_border().register()
diff --git a/demos/python_scripts_examples/text_by_date.py b/demos/python_scripts_examples/text_by_date.py
new file mode 100644
index 0000000..6e96530
--- /dev/null
+++ b/demos/python_scripts_examples/text_by_date.py
@@ -0,0 +1,59 @@
+#  text_by_date.py
+#
+# Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors.
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+#  MA 02110-1301, USA.
+#
+#
+
+import pcbnew
+import re
+import datetime
+
+
+class text_by_date( pcbnew.ActionPlugin ):
+    """
+    test_by_date: A sample plugin as an example of ActionPlugin
+    Add the date to any text field of the board where the content is '$date$'
+    How to use:
+    - Add a text on your board with the content '$date$'
+    - Call the plugin
+    - Automaticaly the date will be added to the text (format YYYY-MM-DD)
+    """
+
+    def defaults( self ):
+        """
+        Method defaults must be redefined
+        self.name should be the menu label to use
+        self.category should be the category (not yet used)
+        self.description should be a comprehensive description
+          of the plugin
+        """
+        self.name = "Add date on PCB"
+        self.category = "Modify PCB"
+        self.description = "Automaticaly add date on an existing PCB"
+
+    def Run( self ):
+        pcb = pcbnew.GetBoard()
+        for draw in pcb.m_Drawings:
+            if draw.GetClass() == 'PTEXT':
+                txt = re.sub( "\$date\$ [0-9]{4}-[0-9]{2}-[0-9]{2}",
+                                 "$date$", draw.GetText() )
+                if txt == "$date$":
+                    draw.SetText( "$date$ %s"%datetime.date.today() )
+
+
+text_by_date().register()
-- 
2.7.4


References