# 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 math import FootprintWizardDrawingAids class FootprintWizardParameterManager: """ Functions for helpfully managing parameters to a KiCAD Footprint Wizard. Abstracts away from whatever structure is used by pcbnew's footprint wizard class """ def __init__(self): self.parameters = {} self.GenerateParameterList() def GenerateParameterList(self): """ Construct parameters here, or leave out to have no parameters """ pass def CheckParameters(self): """ Implement this to make checks on parameter values, filling parameter_errors (or using the checker routines) Subclasses can implment their own and override the parent defaults and add new ones """ pass uMM = 1 uMils = 2 uNatural = 3 uBool = 4 uString = 5 def AddParam(self, section, param, unit, default, hint=''): """ Add a parameter with some properties. TODO: Hints are not supported, as there is as yet nowhere to put them in the KiCAD interface """ val = None if unit == self.uMM: val = pcbnew.FromMM(default) elif unit == self.uMils: val = pcbnew.FromMils(default) elif unit == self.uNatural: val = default elif unit == self.uString: val = str(default) elif unit == self.uBool: val = "True" if default else "False" # ugly stringing else: print "Warning: Unknown unit type: %s" % unit return if unit in [self.uNatural, self.uBool, self.uString]: param = "*%s" % param # star prefix for natural if section not in self.parameters: self.parameters[section] = {} self.parameters[section][param] = val def _PrintParameterTable(self): """ Pretty-print the parameters we have """ for name, section in self.parameters.iteritems(): print " %s:" % name for key, value in section.iteritems(): unit = "" if ((type(value) is int or type(value) is float) and not "*" in key): unit = "mm" if "*" in key: key = key[1:] else: value = pcbnew.ToMM(value) print " %s: %s%s" % (key, value, unit) def _ParametersHaveErrors(self): """ Return true if we discovered errors during parameter processing """ for name, section in self.parameter_errors.iteritems(): for k, v in section.iteritems(): if v: return True return False def _PrintParameterErrors(self): """ Pretty-print parameters with errors """ for name, section in self.parameter_errors.iteritems(): printed_section = False for key, value in section.iteritems(): if value: if not printed_section: print " %s:" % name print " %s: %s (have %s)" % ( key, value, self.parameters[name][key]) def ProcessParameters(self): """ Make sure the parameters we have meet whatever expectations the footprint wizard has of them """ self.ClearErrors() self.CheckParameters() if self._ParametersHaveErrors(): print "Cannot build footprint: Parameters have errors:" self._PrintParameterErrors() return False print ("Building new %s footprint with the following parameters:" % self.name) self._PrintParameterTable() return True ################################################################# # PARAMETER CHECKERS ################################################################# def CheckParamInt(self, section, param, min_value=1, max_value=None, is_multiple_of=1): """ Make sure a parameter can be made into an int, and enforce limits if required """ try: self.parameters[section][param] = ( int(self.parameters[section][param])) except ValueError: self.parameter_errors[section][param] = ( "Must be a valid integer") return if min_value is not None and ( self.parameters[section][param] < min_value): self.parameter_errors[section][param] = ( "Must be greater than or equal to %d" % (min_value)) return if max_value is not None and ( self.parameters[section][param] > min_value): self.parameter_errors[section][param] = ( "Must be less than or equal to %d" % (max_value)) return if is_multiple_of > 1 and ( self.parameters[section][param] % is_multiple_of) > 0: self.parameter_errors[section][param] = ( "Must be a multiple of %d" % is_multiple_of) return return def CheckParamBool(self, section, param): """ Make sure a parameter looks like a boolean, convert to native boolean type if so """ if str(self.parameters[section][param]).lower() in [ "true", "t", "y", "yes", "on", "1", "1.0"]: self.parameters[section][param] = True return elif str(self.parameters[section][param]).lower() in [ "false", "f", "n", "no", "off", "0", "0.0"]: self.parameters[section][param] = False return self.parameter_errors[section][param] = "Must be boolean (true/false)" return class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin, FootprintWizardParameterManager): """ A class to simplify many aspects of footprint creation, leaving only the foot-print specific routines to the wizards themselves Generally, you need to implement: GetReference() GetValue() GenerateParameterList() CheckParameters() BuildThisFootprint() GetName() GetDescription() """ def __init__(self): pcbnew.FootprintWizardPlugin.__init__(self) FootprintWizardParameterManager.__init__(self) self.name = self.GetName() self.decription = self.GetDescription() self.image = self.GetImage() def GetValue(self): raise NotImplementedError def GetReferencePrefix(self): return "U" # footprints needing wizards of often ICs def GetImage(self): return "" def GetTextSize(self): """ IPC nominal """ return pcbnew.FromMM(1.2) def GetTextThickness(self): """ Thicker than IPC guidelines (10% of text height = 0.12mm) as 5 wires/mm is a common silk screen limitation """ return pcbnew.FromMM(0.2) def SetModule3DModel(self): """ Set a 3D model for the module Default is to do nothing, you need to implement this if you have a model to set FIXME: This doesn't seem to be enabled yet? """ pass def BuildThisFootprint(self): """ Draw the footprint. This is specific to each footprint class, you need to implment this to draw what you want """ raise NotImplementedError def BuildFootprint(self): """ Actually make the footprint. We defer all but the setup to the implmenting class """ self.module = pcbnew.MODULE(None) # create a new module # do it first, so if we return early, we don't segfault KiCad if not self.ProcessParameters(): return self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids( self.module) self.module.SetValue(self.GetValue()) self.module.SetReference("%s**" % self.GetReferencePrefix()) fpid = pcbnew.FPID(self.module.GetValue()) # the name in library self.module.SetFPID(fpid) self.SetModule3DModel() # add a 3d module if specified thick = self.GetTextThickness() self.module.Reference().SetThickness(thick) self.module.Value().SetThickness(thick) self.BuildThisFootprint() # implementer's build function