← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Python FP wizard helper: docstrings and rounded/chamfered rects

 

Hi,

Here's an update patch set that adds to the existing Pcbnew Python docs. It
seems to work quite well (see screenshot).

I have also added docs for the PadArray module and fixed a doxygen warning
about obsolete parameters.

Cheers,

John

On Tue, Jun 12, 2018 at 3:02 PM, Wayne Stambaugh <stambaughw@xxxxxxxxx>
wrote:

> On 6/12/2018 9:34 AM, John Beard wrote:
> > Hi Nick and Wayne,
> >
> > The patches as they are don't hook into the existing Python API doxygen
> > stuff as it's not exactly the same as the Python API, it's a helper
> > layer on top of that, and I was't sure if that would be OK.
> >
> > I will take a look at adding it to the existing Python doc generation if
> > that's an acceptable way to present it.
>
> This makes more sense to me than adding it to the c++ source documentation.
>
> >
> > Cheers,
> >
> > John
> >
> > On Tue, Jun 12, 2018 at 2:11 PM, Nick Østergaard <oe.nick@xxxxxxxxx
> > <mailto:oe.nick@xxxxxxxxx>> wrote:
> >
> >     We already have doxygen generation for the python API, although
> >     people say that it is easier to read the C++ one. It is generated
> >     with the doxygen-python make target.
> >     See http://docs.kicad-pcb.org/doxygen-python/
> >     <http://docs.kicad-pcb.org/doxygen-python/>
> >
> >     Does the additions in 0002 add to the normal python docs?
> >
> >     2018-06-12 15:07 GMT+02:00 Wayne Stambaugh <stambaughw@xxxxxxxxx
> >     <mailto:stambaughw@xxxxxxxxx>>:
> >
> >         Hey John,
> >
> >         I like the idea of using doxygen to document the python
> >         plugins.  The
> >         current Doxyfile does not include .py files so that would need to
> >         change.  Before we do that, I would like to see a new section
> (maybe
> >         "Python Plugins") added to the documentation to separate the
> python
> >         plugin code from the c++ source documentation.  I can commit
> >         your patch
> >         as is and you can make the doxygen changes in a later patch or I
> can
> >         wait for you to create a new patch with all of the changes.  I'm
> >         fine
> >         either way.
> >
> >         Cheers,
> >
> >         Wayne
> >
> >         On 6/4/2018 7:33 AM, John Beard wrote:
> >         > Hi,
> >         >
> >         > Here is a simple patch sequence for the Python Footprint
> >         Wizard helpers:
> >         >
> >         > 1) Minor spelling and formatting tidy-up
> >         > 2) Add docstrings for the wizard base. As this is intended to
> >         be used
> >         > by writers of new plugins, having the functions documented is
> >         probably
> >         > a Good Idea (TM)
> >         > 3) Add rounded rectangle and chamfered rectangle helpers.
> >         Useful for
> >         > some footprints or even board outlines.
> >         >
> >         > I used Doxygen-style docstrings, but I haven't actually done
> >         anything
> >         > about building actual output docs with it. Any thoughts of if
> that
> >         > should be done, and if so, where to put it?
> >         >
> >         > There shouldn't be anything here that will break existing
> >         plugins, the
> >         > only API changes are additions.
> >         >
> >         > Cheers,
> >         >
> >         > John
> >         >
> >         >
> >         >
> >         > _______________________________________________
> >         > Mailing list: https://launchpad.net/~kicad-developers
> >         <https://launchpad.net/~kicad-developers>
> >         > Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >         <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >         > Unsubscribe : https://launchpad.net/~kicad-developers
> >         <https://launchpad.net/~kicad-developers>
> >         > More help   : https://help.launchpad.net/ListHelp
> >         <https://help.launchpad.net/ListHelp>
> >         >
> >
> >         _______________________________________________
> >         Mailing list: https://launchpad.net/~kicad-developers
> >         <https://launchpad.net/~kicad-developers>
> >         Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >         <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >         Unsubscribe : https://launchpad.net/~kicad-developers
> >         <https://launchpad.net/~kicad-developers>
> >         More help   : https://help.launchpad.net/ListHelp
> >         <https://help.launchpad.net/ListHelp>
> >
> >
> >
> >     _______________________________________________
> >     Mailing list: https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
> >     Unsubscribe : https://launchpad.net/~kicad-developers
> >     <https://launchpad.net/~kicad-developers>
> >     More help   : https://help.launchpad.net/ListHelp
> >     <https://help.launchpad.net/ListHelp>
> >
> >
>
From 318452ebc9a227545b118088ea2f1fb7e0cad186 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Fri, 1 Jun 2018 12:19:31 +0100
Subject: [PATCH 1/9] Pcbnew FP wizard: minor style fixes

This contains only a few minor style fixes according to PEP8
for FootprintWizardBase.py, and some spelling fixes
---
 pcbnew/python/plugins/FootprintWizardBase.py | 89 +++++++++++---------
 1 file changed, 48 insertions(+), 41 deletions(-)

diff --git a/pcbnew/python/plugins/FootprintWizardBase.py b/pcbnew/python/plugins/FootprintWizardBase.py
index 158128aea..023ee33a8 100644
--- a/pcbnew/python/plugins/FootprintWizardBase.py
+++ b/pcbnew/python/plugins/FootprintWizardBase.py
@@ -18,20 +18,21 @@ from __future__ import division
 import pcbnew
 import math
 
+
 # Base class for creating footprint wizards
 # Inherit this class to make a new wizard
 class FootprintWizard(pcbnew.FootprintWizardPlugin):
 
     # Copy units from pcbnew
-    uMM         = pcbnew.uMM
-    uMils       = pcbnew.uMils
-    uFloat      = pcbnew.uFloat
-    uInteger    = pcbnew.uInteger
-    uBool       = pcbnew.uBool
-    uRadians    = pcbnew.uRadians
-    uDegrees    = pcbnew.uDegrees
-    uPercent    = pcbnew.uPercent
-    uString     = pcbnew.uString
+    uMM = pcbnew.uMM
+    uMils = pcbnew.uMils
+    uFloat = pcbnew.uFloat
+    uInteger = pcbnew.uInteger
+    uBool = pcbnew.uBool
+    uRadians = pcbnew.uRadians
+    uDegrees = pcbnew.uDegrees
+    uPercent = pcbnew.uPercent
+    uString = pcbnew.uString
 
     """
     A class to simplify many aspects of footprint creation, leaving only
@@ -52,7 +53,7 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
 
     def GetName(self):
         """
-        Retun the name of the footprint wizard
+        Return the name of the footprint wizard
         """
         raise NotImplementedError
 
@@ -72,7 +73,7 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         """
         Footprint parameter specification is done here
         """
-    	raise NotImplementedError
+        raise NotImplementedError
 
     def CheckParameters(self):
         """
@@ -84,43 +85,46 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         """
         Draw the footprint.
 
-        This is specific to each footprint class, you need to implment
+        This is specific to each footprint class, you need to implement
         this to draw what you want
         """
         raise NotImplementedError
 
     # Do not override this method!
-    def BuildFootprint( self ):
+    def BuildFootprint(self):
         """
-        Actually make the footprint. We defer all but the setup to
+        Actually make the footprint. We defer all but the set-up to
         the implementing class
         """
 
         self.buildmessages = ""
         self.module = pcbnew.MODULE(None)  # create a new module
 
-        # Perform default checks on all params
+        # Perform default checks on all parameters
         for p in self.params:
             p.ClearErrors()
-            p.Check() # use defaults
-
-        self.CheckParameters() # User error checks
+            p.Check()  # use defaults
 
+        self.CheckParameters()  # User error checks
 
         if self.AnyErrors():  # Errors were detected!
 
-            self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
+            self.buildmessages = ("Cannot build footprint: "
+                                  "Parameters have errors:\n")
 
             for p in self.params:
                 if len(p.error_list) > 0:
-                    self.buildmessages +="['{page}']['{name}']:\n".format(page=p.page,name=p.name)
+                    self.buildmessages += "['{page}']['{name}']:\n".format(
+                        page=p.page, name=p.name)
 
                     for error in p.error_list:
                         self.buildmessages += "\t" + error + "\n"
 
             return
 
-        self.buildmessages = ("Building new {name} footprint with the following parameters:\n".format(name=self.name))
+        self.buildmessages = (
+            "Building new {name} footprint with the following parameters:\n"
+            .format(name=self.name))
 
         self.buildmessages += self.Show()
 
@@ -133,7 +137,7 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         fpid = pcbnew.LIB_ID(self.module.GetValue())  # the name in library
         self.module.SetFPID(fpid)
 
-        self.SetModule3DModel()  # add a 3d module if specified
+        self.SetModule3DModel()  # add a 3D module if specified
 
         thick = self.GetTextThickness()
 
@@ -160,6 +164,7 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         """
         return pcbnew.FromMM(0.15)
 
+
 class FootprintWizardDrawingAids:
     """
     Collection of handy functions to simplify drawing shapes from within
@@ -180,10 +185,10 @@ class FootprintWizardDrawingAids:
     dirNW = 315
 
     # flip constants
-    flipNone = 0
-    flipX = 1  # flip X values, i.e. about Y
-    flipY = 2  # flip Y valuersabout X
-    flipBoth = 3
+    flipNone = 0  # no flip transform
+    flipX = 1  # flip X values, i.e. about the Y-axis
+    flipY = 2  # flip Y values, i.e. about the X-axis
+    flipBoth = 3  # flip X and Y values, equivalent to a 180-degree rotation
 
     xfrmIDENTITY = [1, 0, 0, 0, 1, 0]  # no transform
 
@@ -235,13 +240,13 @@ class FootprintWizardDrawingAids:
     def _ComposeMatricesWithIdentity(self, mats):
         """
         Compose a sequence of matrices together by sequential
-        pre-mutiplciation with the identity matrix
+        pre-multiplication with the identity matrix
         """
 
         x = self.xfrmIDENTITY
 
         for mat in mats:
-            #precompose with each transform in turn
+            # Pre-compose with each transform in turn
             x = [
                 x[0] * mat[0] + x[1] * mat[3],
                 x[0] * mat[1] + x[1] * mat[4],
@@ -262,7 +267,7 @@ class FootprintWizardDrawingAids:
 
     def TransformTranslate(self, x, y, push=True):
         """
-        Set up and return a transform matrix representing a translartion
+        Set up and return a transform matrix representing a translation
         optionally pushing onto the stack
 
         (   1  0   x  )
@@ -307,7 +312,7 @@ class FootprintWizardDrawingAids:
                 self.TransformFlipOrigin(flip, push=False),
                 self.TransformTranslate(-x, -y, push=False)]
 
-        #distill into a single matrix
+        # Distil into a single matrix
         mat = self._ComposeMatricesWithIdentity(mats)
 
         if push:
@@ -343,7 +348,7 @@ class FootprintWizardDrawingAids:
                 self.TransformRotationOrigin(rot, push=False),
                 self.TransformTranslate(-x, -y, push=False)]
 
-        #distill into a single matrix
+        # Distil into a single matrix
         mat = self._ComposeMatricesWithIdentity(mats)
 
         if push:
@@ -390,7 +395,8 @@ class FootprintWizardDrawingAids:
     def SetLineTickness(self, lineThickness):
         """
         Old version of SetLineThickness.
-        Does the same thing, but is is only here for compatibility with old scripts
+        Does the same thing, but is is only here for compatibility with old
+        scripts.
         Set the current pen lineThickness used for subsequent drawing
         operations
         """
@@ -457,7 +463,7 @@ class FootprintWizardDrawingAids:
         The transform matrix is applied
 
         Note that this won't work properly if the result is not a
-        circular arc (eg a horizontal scale)
+        circular arc (e.g. a horizontal scale)
         """
         circle = pcbnew.EDGE_MODULE(self.module)
         circle.SetWidth(self.dc['lineThickness'])
@@ -491,7 +497,7 @@ class FootprintWizardDrawingAids:
 
     def Polyline(self, pts, mirrorX=None, mirrorY=None):
         """
-        Draw a polyline, optinally mirroring around the given points
+        Draw a polyline, optionally mirroring around the given points
         """
 
         def _PolyLineInternal(pts):
@@ -518,8 +524,7 @@ class FootprintWizardDrawingAids:
             _PolyLineInternal(pts)
             self.PopTransform()
 
-
-    def Reference(self, x, y, size, orientation_degree = 0):
+    def Reference(self, x, y, size, orientation_degree=0):
         """
         Draw the module's reference as the given point.
 
@@ -533,9 +538,10 @@ class FootprintWizardDrawingAids:
         self.module.Reference().SetPosition(
             self.module.Reference().GetPos0())
         self.module.Reference().SetTextSize(text_size)
-        self.module.Reference().SetTextAngle(orientation_degree*10)   # internal angles are in 0.1 deg
+        # internal angles are in 0.1 deg
+        self.module.Reference().SetTextAngle(orientation_degree * 10)
 
-    def Value(self, x, y, size, orientation_degree = 0):
+    def Value(self, x, y, size, orientation_degree=0):
         """
         As for references, draw the module's value
         """
@@ -545,7 +551,8 @@ class FootprintWizardDrawingAids:
         self.module.Value().SetPosition(self.module.Value().GetPos0())
         self.module.Value().SetTextSize(text_size)
         self.module.Value().SetLayer(self.DefaultTextValueLayer())
-        self.module.Value().SetTextAngle(orientation_degree*10)   # internal angles are in 0.1 deg
+        # internal angles are in 0.1 deg
+        self.module.Value().SetTextAngle(orientation_degree * 10)
 
     def Box(self, x, y, w, h):
         """
@@ -563,7 +570,7 @@ class FootprintWizardDrawingAids:
 
     def NotchedCircle(self, x, y, r, notch_w, notch_h, rotate=0):
         """
-        Circle radus r centred at (x, y) with a raised or depressed notch
+        Circle radius r centred at (x, y) with a raised or depressed notch
         at the top
         Notch height is measured from the top of the circle radius
         """
@@ -580,7 +587,7 @@ class FootprintWizardDrawingAids:
         # NOTE: this may be out by a factor of ten one day
         arc_angle = (math.pi * 2 - angle_intercept * 2) * (1800/math.pi)
 
-        self.Arc(x,y, sx, sy, arc_angle)
+        self.Arc(x, y, sx, sy, arc_angle)
 
         pts = [[sx,  sy],
                [sx,  -r - notch_h],
-- 
2.17.0

From fcab6b0f69f324eb7839340bdb8f3e68dc5838f5 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Fri, 1 Jun 2018 13:11:03 +0100
Subject: [PATCH 2/9] Pcbnew FP wizard: add docstring for parameters

Adds more verbose and complete explanations of the
FootprintWizardBase and FootprintWizardDrawingAids functions.
---
 pcbnew/python/plugins/FootprintWizardBase.py | 265 ++++++++++++++-----
 1 file changed, 197 insertions(+), 68 deletions(-)

diff --git a/pcbnew/python/plugins/FootprintWizardBase.py b/pcbnew/python/plugins/FootprintWizardBase.py
index 023ee33a8..a4f4996f4 100644
--- a/pcbnew/python/plugins/FootprintWizardBase.py
+++ b/pcbnew/python/plugins/FootprintWizardBase.py
@@ -19,24 +19,15 @@ import pcbnew
 import math
 
 
-# Base class for creating footprint wizards
-# Inherit this class to make a new wizard
 class FootprintWizard(pcbnew.FootprintWizardPlugin):
+    """!
+    A class to simplify many aspects of footprint creation, leaving only
+    the foot-print specific routines to the wizards themselves.
 
-    # Copy units from pcbnew
-    uMM = pcbnew.uMM
-    uMils = pcbnew.uMils
-    uFloat = pcbnew.uFloat
-    uInteger = pcbnew.uInteger
-    uBool = pcbnew.uBool
-    uRadians = pcbnew.uRadians
-    uDegrees = pcbnew.uDegrees
-    uPercent = pcbnew.uPercent
-    uString = pcbnew.uString
+    Inherit this class to make a new wizard.
 
-    """
-    A class to simplify many aspects of footprint creation, leaving only
-    the foot-print specific routines to the wizards themselves
+    Provides simplified access to helpers like drawing functions, a transform
+    matrix stack and simple parameter checking.
 
     Generally, you need to implement:
         GetValue()
@@ -47,42 +38,53 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         GetDescription()
     """
 
+    # Copy units from pcbnew
+    uMM = pcbnew.uMM
+    uMils = pcbnew.uMils
+    uFloat = pcbnew.uFloat
+    uInteger = pcbnew.uInteger
+    uBool = pcbnew.uBool
+    uRadians = pcbnew.uRadians
+    uDegrees = pcbnew.uDegrees
+    uPercent = pcbnew.uPercent
+    uString = pcbnew.uString
+
     def __init__(self):
         pcbnew.FootprintWizardPlugin.__init__(self)
         self.GenerateParameterList()
 
     def GetName(self):
-        """
+        """!
         Return the name of the footprint wizard
         """
         raise NotImplementedError
 
     def GetDescription(self):
-        """
+        """!
         Return the footprint wizard description
         """
         raise NotImplementedError
 
     def GetValue(self):
-        """
+        """!
         Return the value (name) of the generated footprint
         """
         raise NotImplementedError
 
     def GenerateParameterList(self):
-        """
+        """!
         Footprint parameter specification is done here
         """
         raise NotImplementedError
 
     def CheckParameters(self):
-        """
+        """!
         Any custom parameter checking should be performed here
         """
         raise NotImplementedError
 
     def BuildThisFootprint(self):
-        """
+        """!
         Draw the footprint.
 
         This is specific to each footprint class, you need to implement
@@ -92,7 +94,7 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
 
     # Do not override this method!
     def BuildFootprint(self):
-        """
+        """!
         Actually make the footprint. We defer all but the set-up to
         the implementing class
         """
@@ -149,16 +151,21 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
         return
 
     def SetModule3DModel(self):
+        """!
+        If your plug-in sets a 3D model, override this function
+        """
         pass
 
     def GetTextSize(self):
-        """
-        IPC nominal
+        """!
+        Get the default text size for the footprint. Override to change it.
+
+        Defaults to IPC nominal of 1.0mm
         """
         return pcbnew.FromMM(1.0)
 
     def GetTextThickness(self):
-        """
+        """!
         Thicker than IPC guidelines (10% of text height = 0.12mm)
         as 5 wires/mm is a common silk screen limitation
         """
@@ -166,12 +173,14 @@ class FootprintWizard(pcbnew.FootprintWizardPlugin):
 
 
 class FootprintWizardDrawingAids:
-    """
+    """!
     Collection of handy functions to simplify drawing shapes from within
     footprint wizards
 
     A "drawing context" is provided which can be used to set and retain
-    settings such as line thickness and layer
+    settings such as line thickness and layer. The DC also contains a
+    "transform stack", which allows easy positioning and transforming of
+    drawn elements without lots of geometric book-keeping.
     """
 
     # directions (in degrees, compass-like)
@@ -184,7 +193,7 @@ class FootprintWizardDrawingAids:
     dirW = 270
     dirNW = 315
 
-    # flip constants
+    # Flip constants
     flipNone = 0  # no flip transform
     flipX = 1  # flip X values, i.e. about the Y-axis
     flipY = 2  # flip Y values, i.e. about the X-axis
@@ -212,17 +221,22 @@ class FootprintWizardDrawingAids:
         }
 
     def PushTransform(self, mat):
-        """
+        """!
         Add a transform to the top of the stack and recompute the
         overall transform
+
+        @param mat: the transform matrix to add to the stack
         """
         self.dc['transforms'].append(mat)
         self.RecomputeTransforms()
 
     def PopTransform(self, num=1):
-        """
+        """!
         Remove a transform from the top of the stack and recompute the
         overall transform
+
+        @param num: the number of transforms to pop from the stack.
+        @return the last popped transform
         """
 
         for i in range(num):
@@ -231,16 +245,19 @@ class FootprintWizardDrawingAids:
         return mat
 
     def ResetTransform(self):
-        """
-        Reset the transform stack to the identity matrix
+        """!
+        Reset the transform stack to the identity matrix.
         """
         self.dc['transforms'] = []
         self.RecomputeTransforms()
 
     def _ComposeMatricesWithIdentity(self, mats):
-        """
+        """!
         Compose a sequence of matrices together by sequential
-        pre-multiplication with the identity matrix
+        pre-multiplication with the identity matrix.
+
+        @param mats: list of matrices to compose
+        @return: the composed transform matrix
         """
 
         x = self.xfrmIDENTITY
@@ -258,7 +275,7 @@ class FootprintWizardDrawingAids:
         return x
 
     def RecomputeTransforms(self):
-        """
+        """!
         Re-compute the transform stack into a single transform and
         store in the DC
         """
@@ -266,12 +283,17 @@ class FootprintWizardDrawingAids:
             self.dc['transforms'])
 
     def TransformTranslate(self, x, y, push=True):
-        """
+        """!
         Set up and return a transform matrix representing a translation
         optionally pushing onto the stack
 
         (   1  0   x  )
         (   0  1   y  )
+
+        @param x: translation in x-direction
+        @param y: translation in y-direction
+        @param push: add this transform to the current stack
+        @return the generated transform matrix
         """
         mat = [1, 0, x, 0, 1, y]
 
@@ -280,9 +302,13 @@ class FootprintWizardDrawingAids:
         return mat
 
     def TransformFlipOrigin(self, flip, push=True):
-        """
+        """!
         Set up and return a transform matrix representing a horizontal,
         vertical or both flip about the origin
+
+        @param flip: one of flipNone, flipX, flipY, flipBoth
+        @param push: add this transform to the current stack
+        @return the generated transform matrix
         """
         mat = None
         if flip == self.flipX:
@@ -301,12 +327,18 @@ class FootprintWizardDrawingAids:
         return mat
 
     def TransformFlip(self, x, y, flip=flipNone, push=True):
-        """
+        """!
         Set up and return a transform matrix representing a horizontal,
         vertical or both flip about a point (x,y)
 
         This is performed by a translate-to-origin, flip, translate-
-        back sequence
+        back sequence.
+
+        @param x: the x co-ordinate of the flip point
+        @param y: the y co-ordinate of the flip point
+        @param flip: one of flipNone, flipX, flipY, flipBoth
+        @param push: add this transform to the current stack
+        @return the generated transform matrix
         """
         mats = [self.TransformTranslate(x, y, push=False),
                 self.TransformFlipOrigin(flip, push=False),
@@ -320,12 +352,16 @@ class FootprintWizardDrawingAids:
         return mat
 
     def TransformRotationOrigin(self, rot, push=True):
-        """
+        """!
         Set up and return a transform matrix representing a rotation
         about the origin, and optionally push onto the stack
 
         (   cos(t)  -sin(t)   0  )
         (   sin(t)   cos(t)   0  )
+
+        @param rot: the rotation angle in degrees
+        @param push: add this transform to the current stack
+        @return the generated transform matrix
         """
         rads = rot * math.pi / 180
         mat = [math.cos(rads), -math.sin(rads), 0,
@@ -336,12 +372,18 @@ class FootprintWizardDrawingAids:
         return mat
 
     def TransformRotation(self, x, y, rot, push=True):
-        """
+        """!
         Set up and return a transform matrix representing a rotation
         about the point (x,y), and optionally push onto the stack
 
         This is performed by a translate-to-origin, rotate, translate-
         back sequence
+
+        @param x: the x co-ordinate of the rotation centre
+        @param y: the y co-ordinate of the rotation centre
+        @param rot: the rotation angle in degrees
+        @param push: add this transform to the current stack
+        @return the generated transform matrix
         """
 
         mats = [self.TransformTranslate(x, y, push=False),
@@ -356,12 +398,17 @@ class FootprintWizardDrawingAids:
         return mat
 
     def TransformScaleOrigin(self, sx, sy=None, push=True):
-        """
+        """!
         Set up and return a transform matrix representing a scale about
         the origin, and optionally push onto the stack
 
         (   sx   0   0  )
         (    0  sy   0  )
+
+        @param sx: the scale factor in the x direction
+        @param sy: the scale factor in the y direction
+        @param push: add this transform to the current stack
+        @return the generated transform matrix
         """
 
         if sy is None:
@@ -374,9 +421,14 @@ class FootprintWizardDrawingAids:
         return mat
 
     def TransformPoint(self, x, y, mat=None):
-        """
+        """!
         Return a point (x, y) transformed by the given matrix, or if
         that is not given, the drawing context transform
+
+        @param x: the x co-ordinate of the point to transform
+        @param y: the y co-ordinate of the point to transform
+        @param mat: the transform matrix to use or None to use the current DC's
+        @return: the transformed point as a wxPoint
         """
 
         if not mat:
@@ -386,43 +438,47 @@ class FootprintWizardDrawingAids:
                               x * mat[3] + y * mat[4] + mat[5])
 
     def SetLineThickness(self, lineThickness):
-        """
+        """!
         Set the current pen lineThickness used for subsequent drawing
         operations
+
+        @param lineThickness: the new line thickness to set
         """
         self.dc['lineThickness'] = lineThickness
 
     def SetLineTickness(self, lineThickness):
-        """
+        """!
         Old version of SetLineThickness.
         Does the same thing, but is is only here for compatibility with old
         scripts.
         Set the current pen lineThickness used for subsequent drawing
         operations
+
+        @param lineThickness: the new line thickness to set
         """
-        self.dc['lineThickness'] = lineThickness
+        self.SetLineThickness(lineThickness)
 
     def GetLineThickness(self):
-        """
+        """!
         Get the current drawing context line thickness
         """
         return self.dc['lineThickness']
 
     def SetLayer(self, layer):
-        """
+        """!
         Set the current drawing layer, used for subsequent drawing
         operations
         """
         self.dc['layer'] = layer
 
     def GetLayer(self):
-        """
-        return the current drawing layer, used drawing operations
+        """!
+        Return the current drawing layer, used for drawing operations
         """
         return self.dc['layer']
 
     def Line(self, x1, y1, x2, y2):
-        """
+        """!
         Draw a line from (x1, y1) to (x2, y2)
         """
         outline = pcbnew.EDGE_MODULE(self.module)
@@ -435,10 +491,16 @@ class FootprintWizardDrawingAids:
         self.module.Add(outline)
 
     def Circle(self, x, y, r, filled=False):
-        """
+        """!
         Draw a circle at (x,y) of radius r
         If filled is true, the thickness and radius of the line will be set
         such that the circle appears filled
+
+        @param x: the x co-ordinate of the arc centre
+        @param y: the y co-ordinate of the arc centre
+        @param r: the circle's radius
+        @param filled: True to draw a filled circle, False to use the current
+                       DC line thickness
         """
 
         circle = pcbnew.EDGE_MODULE(self.module)
@@ -457,13 +519,19 @@ class FootprintWizardDrawingAids:
         self.module.Add(circle)
 
     def Arc(self, cx, cy, sx, sy, a):
-        """
+        """!
         Draw an arc based on centre, start and angle
 
         The transform matrix is applied
 
         Note that this won't work properly if the result is not a
         circular arc (e.g. a horizontal scale)
+
+        @param cx: the x co-ordinate of the arc centre
+        @param cy: the y co-ordinate of the arc centre
+        @param sx: the x co-ordinate of the arc start point
+        @param sy: the y co-ordinate of the arc start point
+        @param a: the arc's central angle (in deci-degrees)
         """
         circle = pcbnew.EDGE_MODULE(self.module)
         circle.SetWidth(self.dc['lineThickness'])
@@ -482,22 +550,33 @@ class FootprintWizardDrawingAids:
         circle.SetStartEnd(center, start)
         self.module.Add(circle)
 
-    # extends from (x1,y1) right
     def HLine(self, x, y, l):
-        """
+        """!
         Draw a horizontal line from (x,y), rightwards
+
+        @param x: line start x co-ordinate
+        @param y: line start y co-ordinate
+        @param l: line length
         """
         self.Line(x, y, x + l, y)
 
     def VLine(self, x, y, l):
-        """
+        """!
         Draw a vertical line from (x1,y1), downwards
+
+        @param x: line start x co-ordinate
+        @param y: line start y co-ordinate
+        @param l: line length
         """
         self.Line(x, y, x, y + l)
 
     def Polyline(self, pts, mirrorX=None, mirrorY=None):
-        """
+        """!
         Draw a polyline, optionally mirroring around the given points
+
+        @param pts: list of polyline vertices (list of (x, y))
+        @param mirrorX: x co-ordinate of mirror point (None for no x-flip)
+        @param mirrorY: y co-ordinate of mirror point (None for no y-flip)
         """
 
         def _PolyLineInternal(pts):
@@ -525,11 +604,16 @@ class FootprintWizardDrawingAids:
             self.PopTransform()
 
     def Reference(self, x, y, size, orientation_degree=0):
-        """
+        """!
         Draw the module's reference as the given point.
 
         The actual setting of the reference is not done in this drawing
         aid - that is up to the wizard
+
+        @param x: the x position of the reference
+        @param y: the y position of the reference
+        @param size: the text size (in both directions)
+        @param orientation_degree: text orientation in degrees
         """
 
         text_size = pcbnew.wxSize(size, size)
@@ -542,8 +626,13 @@ class FootprintWizardDrawingAids:
         self.module.Reference().SetTextAngle(orientation_degree * 10)
 
     def Value(self, x, y, size, orientation_degree=0):
-        """
+        """!
         As for references, draw the module's value
+
+        @param x: the x position of the value
+        @param y: the y position of the value
+        @param size: the text size (in both directions)
+        @param orientation_degree: text orientation in degrees
         """
         text_size = pcbnew.wxSize(size, size)
 
@@ -555,9 +644,14 @@ class FootprintWizardDrawingAids:
         self.module.Value().SetTextAngle(orientation_degree * 10)
 
     def Box(self, x, y, w, h):
-        """
+        """!
         Draw a rectangular box, centred at (x,y), with given width and
         height
+
+        @param x: the x co-ordinate of the box's centre
+        @param y: the y co-ordinate of the box's centre
+        @param w: the width of the box
+        @param h: the height of the box
         """
 
         pts = [[x - w/2, y - h/2],  # left
@@ -569,10 +663,17 @@ class FootprintWizardDrawingAids:
         self.Polyline(pts)
 
     def NotchedCircle(self, x, y, r, notch_w, notch_h, rotate=0):
-        """
+        """!
         Circle radius r centred at (x, y) with a raised or depressed notch
         at the top
         Notch height is measured from the top of the circle radius
+
+        @param x: the x co-ordinate of the circle's centre
+        @param y: the y co-ordinate of the circle's centre
+        @param r: the radius of the circle
+        @param notch_w: the width of the notch
+        @param notch_h: the height of the notch
+        @param rotate: the rotation of the whole figure, in degrees
         """
 
         self.TransformRotation(x, y, rotate)
@@ -598,8 +699,16 @@ class FootprintWizardDrawingAids:
         self.PopTransform()
 
     def NotchedBox(self, x, y, w, h, notchW, notchH, rotate=0):
-        """
-        Draw a box with a notch in the top edge
+        """!
+        Draw a box with a notch in the centre of the top edge
+
+        @param x: the x co-ordinate of the circle's centre
+        @param y: the y co-ordinate of the circle's centre
+        @param w: the width of the box
+        @param h: the height of the box
+        @param notchW: the width of the notch
+        @param notchH: the height of the notch
+        @param rotate: the rotation of the whole figure, in degrees
         """
 
         self.TransformRotation(x, y, rotate)
@@ -625,8 +734,16 @@ class FootprintWizardDrawingAids:
 
     def BoxWithDiagonalAtCorner(self, x, y, w, h,
                                 setback=pcbnew.FromMM(1.27), flip=flipNone):
-        """
-        Draw a box with a diagonal at the top left corner
+        """!
+        Draw a box with a diagonal at the top left corner.
+
+        @param x: the x co-ordinate of the circle's centre
+        @param y: the y co-ordinate of the circle's centre
+        @param w: the width of the box
+        @param h: the height of the box
+        @param setback: the set-back of the diagonal, in both x and y
+        @param flip: one of flipNone, flipX, flipY or flipBoth to change the
+         diagonal corner
         """
 
         self.TransformFlip(x, y, flip, push=True)
@@ -644,8 +761,16 @@ class FootprintWizardDrawingAids:
 
     def BoxWithOpenCorner(self, x, y, w, h,
                           setback=pcbnew.FromMM(1.27), flip=flipNone):
-        """
+        """!
         Draw a box with an opening at the top left corner
+
+        @param x: the x co-ordinate of the circle's centre
+        @param y: the y co-ordinate of the circle's centre
+        @param w: the width of the box
+        @param h: the height of the box
+        @param setback: the set-back of the opening, in both x and y
+        @param flip: one of flipNone, flipX, flipY or flipBoth to change the
+         open corner position
         """
 
         self.TransformTranslate(x, y)
@@ -662,11 +787,15 @@ class FootprintWizardDrawingAids:
         self.PopTransform(num=2)
 
     def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)):
-        """
+        """!
         Draw a marker arrow facing in the given direction, with the
         point at (x,y)
 
-        Direction of 0 is north
+        @param x: x position of the arrow tip
+        @param y: y position of the arrow tip
+        @param direction: arrow direction in degrees (0 is "north", can use
+         dir* shorthands)
+        @param width: arrow width
         """
 
         self.TransformTranslate(x, y)
-- 
2.17.0

From b216b5e761d441ddd461673293f8bce84c4aab7b Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Fri, 1 Jun 2018 12:08:26 +0100
Subject: [PATCH 3/9] Pcbnew FP wizard: add rounded and chamfered box aids

These functions are fairly common drawing operations that otherwise
require fairly complex calculations, which would be better not to
have to repeat.
---
 pcbnew/python/plugins/FootprintWizardBase.py | 70 +++++++++++++++++++-
 1 file changed, 69 insertions(+), 1 deletion(-)

diff --git a/pcbnew/python/plugins/FootprintWizardBase.py b/pcbnew/python/plugins/FootprintWizardBase.py
index a4f4996f4..8d1860dc1 100644
--- a/pcbnew/python/plugins/FootprintWizardBase.py
+++ b/pcbnew/python/plugins/FootprintWizardBase.py
@@ -786,6 +786,74 @@ class FootprintWizardDrawingAids:
 
         self.PopTransform(num=2)
 
+    def RoundedBox(self, x, y, w, h, rad):
+        """!
+        Draw a box with rounded corners (i.e. a 90-degree circular arc)
+
+        :param x: the x co-ordinate of the box's centre
+        :param y: the y co-ordinate of the box's centre
+        :param w: the width of the box
+        :param h: the height of the box
+        :param rad: the radius of the corner rounds
+        """
+
+        x_inner = w - rad * 2
+        y_inner = h - rad * 2
+
+        x_left = x - w / 2
+        y_top = y - h / 2
+
+        # Draw straight sections
+        self.HLine(x_left + rad, y_top, x_inner)
+        self.HLine(x_left + rad, -y_top, x_inner)
+
+        self.VLine(x_left, y_top + rad, y_inner)
+        self.VLine(-x_left, y_top + rad, y_inner)
+
+        # corner arcs
+        ninety_deg = 90 * 10  # deci-degs
+        cx = x - w / 2 + rad
+        cy = y - h / 2 + rad
+
+        # top left
+        self.Arc(+cx, +cy, +x_left, +cy, +ninety_deg)
+        self.Arc(-cx, +cy, -x_left, +cy, -ninety_deg)
+        self.Arc(+cx, -cy, +x_left, -cy, -ninety_deg)
+        self.Arc(-cx, -cy, -x_left, -cy, +ninety_deg)
+
+    def ChamferedBox(self, x, y, w, h, chamfer_x, chamfer_y):
+        """!
+        Draw a box with chamfered corners.
+
+        :param x: the x co-ordinate of the box's centre
+        :param y: the y co-ordinate of the box's centre
+        :param w: the width of the box
+        :param h: the height of the box
+        :param chamfer_x: the size of the chamfer set-back in the x direction
+        :param chamfer_y: the size of the chamfer set-back in the y direction
+        """
+        # outermost dimensions
+        x_left = x - w / 2
+        y_top = y - h / 2
+
+        # x and y co-ordinates of inner edges of chamfers
+        x_inner = x_left + chamfer_x
+        y_inner = y_top + chamfer_y
+
+        pts = [
+            [+x_inner, +y_top],
+            [-x_inner, +y_top],
+            [-x_left,  +y_inner],
+            [-x_left,  -y_inner],
+            [-x_inner, -y_top],
+            [+x_inner, -y_top],
+            [+x_left,  -y_inner],
+            [+x_left,  +y_inner],
+            [+x_inner, +y_top],
+        ]
+
+        self.draw.Polyline(pts)
+
     def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)):
         """!
         Draw a marker arrow facing in the given direction, with the
@@ -807,4 +875,4 @@ class FootprintWizardDrawingAids:
                [0,          0]]
 
         self.Polyline(pts)
-        self.PopTransform(2)
\ No newline at end of file
+        self.PopTransform(2)
-- 
2.17.0

From cb582cfe75038a7f31fa006594af791de45d8aca Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 13 Jun 2018 10:55:19 +0100
Subject: [PATCH 4/9] Pcbnew Python: Add plugin documentation

This adds the Python plugin helper classes to the generated
documentation.
---
 pcbnew/CMakeLists.txt | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index 079d43250..b690359ab 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -521,14 +521,23 @@ if( DOXYGEN_FOUND )
             DEPENDS doxygen-python-xml
             )
 
-        # create doxygen-python html
+        # The sources to give to the Python Doxygen target
+        set( DOXYGEN_PYTHON_SOURCES
+            ${CMAKE_CURRENT_BINARY_DIR}/pcbnew.py
+            ${CMAKE_CURRENT_SOURCE_DIR}/python/plugins/FootprintWizardBase.py
+            ${CMAKE_CURRENT_SOURCE_DIR}/python/plugins/PadArray.py )
+
+        # The Doxyfile expects a space-separated list in the env var
+        string(REPLACE ";" " " DOXYGEN_PYTHON_SOURCES_STR "${DOXYGEN_PYTHON_SOURCES}")
+
+        # Create doxygen-python html
         add_custom_target( doxygen-python
             ${CMAKE_COMMAND} -E remove_directory doxygen-python
-            COMMAND PYTHON_SOURCES_TO_DOC=${CMAKE_CURRENT_BINARY_DIR}/pcbnew.py ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile_python
+            COMMAND ${CMAKE_COMMAND} -E env PYTHON_SOURCES_TO_DOC=${DOXYGEN_PYTHON_SOURCES_STR} ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile_python
             WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
             DEPENDS Doxyfile_python
             DEPENDS xml-to-docstrings
-            DEPENDS pcbnew.py
+            DEPENDS ${DOXYGEN_PYTHON_SOURCES}
             COMMENT "building doxygen docs into directory doxygen-python/html"
             )
     endif()
-- 
2.17.0

From 983f7f1c5d96ace4f483b07f84a357a6213829fa Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 13 Jun 2018 12:05:12 +0100
Subject: [PATCH 5/9] Pcbnew Python: Add PadArray module documentation

---
 pcbnew/python/plugins/PadArray.py | 216 +++++++++++++++++++++++++++---
 1 file changed, 194 insertions(+), 22 deletions(-)

diff --git a/pcbnew/python/plugins/PadArray.py b/pcbnew/python/plugins/PadArray.py
index 28b8d4bbb..56f24f833 100644
--- a/pcbnew/python/plugins/PadArray.py
+++ b/pcbnew/python/plugins/PadArray.py
@@ -25,14 +25,27 @@ import math
 import pcbnew
 
 class PadMaker:
-    """
-    Useful construction functions for common types of pads
+    """!
+    Useful construction functions for common types of pads, providing
+    sensible defaults for common pads.
     """
 
     def __init__(self, module):
+        """!
+        @param module: the module the pads will be part of
+        """
         self.module = module
 
-    def THPad(self, Vsize, Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL, rot_degree = 0):
+    def THPad(self, Vsize, Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL,
+              rot_degree = 0):
+        """!
+        A basic through-hole pad of the given size and shape
+        @param Vsize: the vertical size of the pad
+        @param Hsize: the horizontal size of the pad
+        @param drill: the drill diameter
+        @param shape: the shape of the pad
+        @param rot_degree: the pad rotation, in degrees
+        """
         pad = pcbnew.D_PAD(self.module)
         pad.SetSize(pcbnew.wxSize(Hsize, Vsize))
         pad.SetShape(shape)
@@ -44,10 +57,20 @@ class PadMaker:
         return pad
 
     def THRoundPad(self, size, drill):
+        """!
+        A round though-hole pad. A shortcut for THPad()
+        @param size: pad diameter
+        @param drill: drill diameter
+        """
         pad = self.THPad(size, size, drill, shape=pcbnew.PAD_SHAPE_CIRCLE)
         return pad
 
     def NPTHRoundPad(self, drill):
+        """!
+        A round non-plated though hole (NPTH)
+
+        @param drill: the drill diameter (equals the NPTH diameter)
+        """
         pad = pcbnew.D_PAD(self.module)
         pad.SetSize(pcbnew.wxSize(drill, drill))
         pad.SetShape(pcbnew.PAD_SHAPE_CIRCLE)
@@ -57,6 +80,14 @@ class PadMaker:
         return pad
 
     def SMDPad(self, Vsize, Hsize, shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0):
+        """
+        Create a surface-mount pad of the given size and shape
+        @param Vsize: the vertical size of the pad
+        @param Hsize: the horizontal size of the pad
+        @param drill: the drill diameter
+        @param shape: the shape of the pad
+        @param rot_degree: the pad rotation, in degrees
+        """
         pad = pcbnew.D_PAD(self.module)
         pad.SetSize(pcbnew.wxSize(Hsize, Vsize))
         pad.SetShape(shape)
@@ -67,11 +98,25 @@ class PadMaker:
         return pad
 
     def SMTRoundPad(self, size):
+        """!
+        A round surface-mount pad. A shortcut for SMDPad()
+        @param size: pad diameter
+        """
         pad = self.SMDPad(size, size, shape=pcbnew.PAD_SHAPE_CIRCLE)
         return pad
 
 
 class PadArray:
+    """!
+    A class to assist in creating repetitive grids of pads
+
+    Generally, PadArrays have an internal prototypical pad, and copy this
+    for each pad in the array. They can also have a special pad for the
+    first pad, and a custom function to name the pad.
+
+    Generally, PadArray is used as a base class for more specific array
+    types.
+    """
 
     def __init__(self):
         self.firstPadNum = 1
@@ -79,21 +124,43 @@ class PadArray:
         self.firstPad = None
 
     def SetPinNames(self, pinNames):
-        """
-        Set a name for all the pins
+        """!
+        Set a name for all the pins. If given, this overrides the
+        naming function.
+
+        @param pinNames: the name to use for all pins
         """
         self.pinNames = pinNames
 
     def SetFirstPadType(self, firstPad):
+        """!
+        If the array has a different first pad, this is the pad that
+        is used
+        @param firstPad: the prototypical first pad
+        """
         self.firstPad = firstPad
 
     def SetFirstPadInArray(self, fpNum):
+        """!
+        Set the numbering for the first pad in the array
+        @param fpNum: the number for the first pad
+        """
         self.firstPadNum = fpNum
 
     def AddPad(self, pad):
+        """!
+        Add a pad to the array, under the same moodule as the main
+        prototype pad
+        @param pad: pad to add
+        """
         self.pad.GetParent().Add(pad)
 
     def GetPad(self, is_first_pad, pos):
+        """!
+        Get a pad in the array with the given position
+        @param is_first_pad: use the special first pad if there is one
+        @param pos: the pad position
+        """
         if (self.firstPad and is_first_pad):
             pad = self.firstPad
         else:
@@ -107,6 +174,10 @@ class PadArray:
         return pad
 
     def GetName(self, *args, **kwargs):
+        """!
+        Get the pad name from the naming function, or the pre-set
+        pinNames parameter (set with SetPinNames)
+        """
 
         if self.pinNames is None:
             return self.NamingFunction(*args, **kwargs)
@@ -114,16 +185,28 @@ class PadArray:
         return self.pinNames
 
     def NamingFunction(self, *args, **kwargs):
-        """
+        """!
         Implement this as needed for each array type
         """
         raise NotImplementedError;
 
 
 class PadGridArray(PadArray):
+    """!
+    A basic grid of pads
+    """
 
     def __init__(self, pad, nx, ny, px, py, centre=pcbnew.wxPoint(0, 0)):
+        """!
+        @param pad: the prototypical pad of the array
+        @param nx: number of pads in x-direction
+        @param ny: number of pads in y-direction
+        @param px: pitch in x-direction
+        @param py: pitch in y-direction
+        @param centre: array centre point
+        """
         PadArray.__init__(self)
+
         # this pad is more of a "context", we will use it as a source of
         # pad data, but not actually add it
         self.pad = pad
@@ -133,12 +216,18 @@ class PadGridArray(PadArray):
         self.py = py
         self.centre = centre
 
-    # handy utility function 1 - A, 2 - B, 26 - AA, etc
-    # aIndex = 0 for 0 - A
-    # alphabet = set of allowable chars if not A-Z,
-    #            eg ABCDEFGHJKLMNPRTUVWY for BGA
     def AlphaNameFromNumber(self, n, aIndex=1,
                             alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
+        """!
+        Utility function to generate an alphabetical name:
+
+        eg. 1 - A, 2 - B, 26 - AA, etc
+
+        @param aIndex: index of 'A': 0 for 0 - A
+        @param n: the pad index
+        @param alphabet: set of allowable chars if not A-Z,
+            e.g. ABCDEFGHJKLMNPRTUVWY for BGA
+        """
 
         div, mod = divmod(n - aIndex, len(alphabet))
         alpha = alphabet[mod]
@@ -148,12 +237,22 @@ class PadGridArray(PadArray):
 
         return alpha
 
-    # right to left, top to bottom
     def NamingFunction(self, x, y):
+        """!
+        Implementation of the naming function: right to left, top-to-bottom
+
+        @param x: the pad x index
+        @param y: the pad y index
+        """
         return self.firstPadNum + (self.nx * y + x)
 
     #relocate the pad and add it as many times as we need
     def AddPadsToModule(self, dc):
+        """!
+        Create the pads and add them to the module in the correct positions
+
+        @param dc: the drawing context
+        """
 
         pin1posX = self.centre.x - self.px * (self.nx - 1) / 2
         pin1posY = self.centre.y - self.py * (self.ny - 1) / 2
@@ -168,17 +267,37 @@ class PadGridArray(PadArray):
                 pad.SetName(self.GetName(x,y))
                 self.AddPad(pad)
 
-class EPADGridArray(PadGridArray):
 
-  def NamingFunction(self, nx, ny):
-    return self.firstPadNum
+class EPADGridArray(PadGridArray):
+    """!
+    A pad grid array with a fixed name, used for things like thermal
+    pads and via grids.
+    """
 
+    def NamingFunction(self, nx, ny):
+        """!
+        Simply return the firstPadNum
+        @param nx: not used
+        @param ny: not used
+        """
+        return self.firstPadNum
 
 
 class PadZGridArray(PadArray):
+    """!
+    A staggered pin array
+    """
 
     def __init__(self, pad, pad_count, line_count, line_pitch,
                  pad_pitch, centre=pcbnew.wxPoint(0, 0)):
+        """!
+        @param pad: the prototypical pad
+        @param pad_count: total pad count
+        @param line_count: number of staggered lines
+        @param line_pitch: distance between lines
+        @param pad_pitch: distance between pads in a line
+        @param centre: array centre point
+        """
         PadArray.__init__(self)
         # this pad is more of a "context", we will use it as a source of
         # pad data, but not actually add it
@@ -189,13 +308,18 @@ class PadZGridArray(PadArray):
         self.pad_pitch = pad_pitch
         self.centre = centre
 
-
-    # right to left, top to bottom
     def NamingFunction(self, pad_pos):
+        """!
+        Naming just increased with pad index in array
+        """
         return self.firstPadNum + pad_pos
 
-    #relocate the pad and add it as many times as we need
     def AddPadsToModule(self, dc):
+        """!
+        Create the pads and add them to the module in the correct positions
+
+        @param dc: the drawing context
+        """
 
         pin1posX = self.centre.x - self.pad_pitch * (self.pad_count - 1) / 2
         pin1posY = self.centre.y + self.line_pitch * (self.line_count - 1) / 2
@@ -215,20 +339,48 @@ class PadZGridArray(PadArray):
             if line >= self.line_count:
                 line = 0
 
+
 class PadLineArray(PadGridArray):
+    """!
+    Shortcut cases for a single-row grid array. Can be used for
+    constructing sections of larger footprints.
+    """
 
     def __init__(self, pad, n, pitch, isVertical,
                  centre=pcbnew.wxPoint(0, 0)):
+        """!
+        @param pad: the prototypical pad
+        @param n: number of pads in array
+        @param pitch: distance between pad centres
+        @param isVertical: horizontal or vertical array (can also use the
+        drawing contexts transforms for more control)
+        @param centre: array centre
+        """
 
         if isVertical:
             PadGridArray.__init__(self, pad, 1, n, 0, pitch, centre)
         else:
             PadGridArray.__init__(self, pad, n, 1, pitch, 0, centre)
 
+
 class PadCircleArray(PadArray):
+    """!
+    Circular pad array
+    """
 
     def __init__(self, pad, n, r, angle_offset=0, centre=pcbnew.wxPoint(0, 0),
-                 clockwise=True, padRotationEnable=False, padRotationOffset =0):
+                 clockwise=True, padRotationEnable=False, padRotationOffset=0):
+        """!
+        @param pad: the prototypical pad
+        @param n: number of pads in array
+        @param r: the circle radius
+        @param angle_offset: angle of the first pad
+        @param centre: array centre point
+        @param clockwise: array increases in a clockwise direction
+        @param padRotationEnable: also rotate pads when placing
+        @param padRotationOffset: rotation of first pad
+        """
+
         PadArray.__init__(self)
         # this pad is more of a "context", we will use it as a source of
         # pad data, but not actually add it
@@ -241,12 +393,19 @@ class PadCircleArray(PadArray):
         self.padRotationEnable = padRotationEnable
         self.padRotationOffset = padRotationOffset
 
-    # around the circle, CW or CCW according to the flag
     def NamingFunction(self, n):
+        """!
+        Naming around the circle, CW or CCW according to the clockwise flag
+        """
         return str(self.firstPadNum + n)
 
-    #relocate the pad and add it as many times as we need
     def AddPadsToModule(self, dc):
+        """!
+        Create the pads and add them to the module in the correct positions
+
+        @param dc: the drawing context
+        """
+
         for pin in range(0, self.n):
             angle = self.angle_offset + (360 / self.n) * pin
 
@@ -264,21 +423,34 @@ class PadCircleArray(PadArray):
             pad.SetName(self.GetName(pin))
             self.AddPad(pad)
 
+
 class PadCustomArray(PadArray):
-    """
+    """!
     Layout pads according to a custom array of [x,y] data
     """
 
     def __init__(self, pad, array):
+        """!
+        @param pad: the prototypical pad
+        @param array: the position data array
+        """
         PadArray.__init__(self)
         self.pad = pad
         self.array = array
 
     def NamingFunction(self, n):
+        """!
+        Simple increment along the given array
+        @param n: the pad index in the array
+        """
         return str(self.firstPadNum + n)
 
-    #relocate the pad and add it as many times as we need
     def AddPadsToModule(self, dc):
+        """!
+        Create the pads and add them to the module in the correct positions
+
+        @param dc: the drawing context
+        """
 
         for i in range(len(self.array)):
             pos = dc.TransformPoint(self.array[i][0], self.array[i][1])
-- 
2.17.0

From 25428d80ff63589ebec12f277c2490098286ca05 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 13 Jun 2018 12:13:37 +0100
Subject: [PATCH 6/9] Pcbnew Python: use python super for inherited classes

---
 pcbnew/python/plugins/PadArray.py | 38 +++++++++++++++----------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/pcbnew/python/plugins/PadArray.py b/pcbnew/python/plugins/PadArray.py
index 56f24f833..9ecd98389 100644
--- a/pcbnew/python/plugins/PadArray.py
+++ b/pcbnew/python/plugins/PadArray.py
@@ -24,7 +24,7 @@ from __future__ import division
 import math
 import pcbnew
 
-class PadMaker:
+class PadMaker(object):
     """!
     Useful construction functions for common types of pads, providing
     sensible defaults for common pads.
@@ -106,7 +106,7 @@ class PadMaker:
         return pad
 
 
-class PadArray:
+class PadArray(object):
     """!
     A class to assist in creating repetitive grids of pads
 
@@ -118,9 +118,16 @@ class PadArray:
     types.
     """
 
-    def __init__(self):
+    def __init__(self, pad):
+        """!
+        @param pad: the prototypical pad
+        """
         self.firstPadNum = 1
         self.pinNames = None
+
+        # this pad is more of a "context", we will use it as a source of
+        # pad data, but not actually add it
+        self.pad = pad
         self.firstPad = None
 
     def SetPinNames(self, pinNames):
@@ -205,11 +212,8 @@ class PadGridArray(PadArray):
         @param py: pitch in y-direction
         @param centre: array centre point
         """
-        PadArray.__init__(self)
+        super(PadGridArray, self).__init__(pad)
 
-        # this pad is more of a "context", we will use it as a source of
-        # pad data, but not actually add it
-        self.pad = pad
         self.nx = int(nx)
         self.ny = int(ny)
         self.px = px
@@ -298,10 +302,8 @@ class PadZGridArray(PadArray):
         @param pad_pitch: distance between pads in a line
         @param centre: array centre point
         """
-        PadArray.__init__(self)
-        # this pad is more of a "context", we will use it as a source of
-        # pad data, but not actually add it
-        self.pad = pad
+        super(PadZGridArray, self).__init__(pad)
+
         self.pad_count = int(pad_count)
         self.line_count = int(line_count)
         self.line_pitch = line_pitch
@@ -358,9 +360,9 @@ class PadLineArray(PadGridArray):
         """
 
         if isVertical:
-            PadGridArray.__init__(self, pad, 1, n, 0, pitch, centre)
+            super(PadLineArray, self).__init__(pad, 1, n, 0, pitch, centre)
         else:
-            PadGridArray.__init__(self, pad, n, 1, pitch, 0, centre)
+            super(PadLineArray, self).__init__(pad, n, 1, pitch, 0, centre)
 
 
 class PadCircleArray(PadArray):
@@ -381,10 +383,8 @@ class PadCircleArray(PadArray):
         @param padRotationOffset: rotation of first pad
         """
 
-        PadArray.__init__(self)
-        # this pad is more of a "context", we will use it as a source of
-        # pad data, but not actually add it
-        self.pad = pad
+        super(PadCircleArray, self).__init__(pad)
+
         self.n = int(n)
         self.r = r
         self.angle_offset = angle_offset
@@ -434,8 +434,8 @@ class PadCustomArray(PadArray):
         @param pad: the prototypical pad
         @param array: the position data array
         """
-        PadArray.__init__(self)
-        self.pad = pad
+        super(PadCustomArray, self).__init__(pad)
+
         self.array = array
 
     def NamingFunction(self, n):
-- 
2.17.0

From 7cf5c221206a3e31152e1ed620fc841c2ef2581e Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Wed, 13 Jun 2018 12:27:06 +0100
Subject: [PATCH 7/9] Pcbnew Python docs: remove obsolete Doxygen parameters

These two parameters were unused and were producing warings:

* XML_SCHEMA
* XML_DTD
---
 pcbnew/Doxyfile_python | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/pcbnew/Doxyfile_python b/pcbnew/Doxyfile_python
index 8854efbe4..c06292721 100644
--- a/pcbnew/Doxyfile_python
+++ b/pcbnew/Doxyfile_python
@@ -1780,18 +1780,6 @@ GENERATE_XML           = NO
 
 XML_OUTPUT             = xml
 
-# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
-# validating XML parser to check the syntax of the XML files.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_SCHEMA             =
-
-# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
-# validating XML parser to check the syntax of the XML files.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_DTD                =
-
 # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
 # listings (including syntax highlighting and cross-referencing information) to
 # the XML output. Note that enabling this will significantly increase the size
-- 
2.17.0

Attachment: kicad_python_docs.png
Description: PNG image


Follow ups

References