mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 18:23:15 +02:00
Rename FPID to LIB_ID as is now used as a generic library identifier and is no longer specific to footprints. Remove all mention of footprint from the new LIB_ID doxygen comments and code. Rename files fpid.h and fpid.cpp to lib_id.h and lib_id.cpp. Rename fp_lib_table.keywords file to lib_table.keywords and adjust CMake build dependencies accordingly. Update all source files effected by the code and file name changes. Update .gitignore for file name changes.
349 lines
10 KiB
Python
349 lines
10 KiB
Python
# 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
|
|
"""
|
|
error = ""
|
|
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:
|
|
error = "Warning: Unknown unit type: %s" % unit
|
|
return error
|
|
|
|
if unit in [self.uNatural, self.uBool, self.uString]:
|
|
param = "*%s" % param # star prefix for natural
|
|
|
|
if section not in self.parameters:
|
|
if not hasattr(self, 'page_order'):
|
|
self.page_order = []
|
|
self.page_order.append(section)
|
|
self.parameters[section] = {}
|
|
if not hasattr(self, 'parameter_order'):
|
|
self.parameter_order = {}
|
|
self.parameter_order[section] = []
|
|
|
|
self.parameters[section][param] = val
|
|
self.parameter_order[section].append(param)
|
|
|
|
return error
|
|
|
|
|
|
def _PrintParameterTable(self):
|
|
"""
|
|
Pretty-print the parameters we have
|
|
"""
|
|
message = ""
|
|
|
|
for name, section in self.parameters.iteritems():
|
|
message += " %s:\n" % 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)
|
|
|
|
message += " %s: %s%s\n" % (key, value, unit)
|
|
|
|
return message
|
|
|
|
|
|
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
|
|
"""
|
|
errors = ""
|
|
|
|
for name, section in self.parameter_errors.iteritems():
|
|
printed_section = False
|
|
|
|
for key, value in section.iteritems():
|
|
if value:
|
|
if not printed_section:
|
|
errors += " %s:" % name
|
|
|
|
errors += " %s: %s (have %s)\n" % (
|
|
key, value, self.parameters[name][key])
|
|
|
|
return errors
|
|
|
|
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():
|
|
return False
|
|
|
|
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] > max_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
|
|
|
|
# this value come from our KiCad Library Convention 1.0
|
|
def GetReferencePrefix(self):
|
|
return "REF"
|
|
|
|
def GetImage(self):
|
|
return ""
|
|
|
|
def GetTextSize(self):
|
|
"""
|
|
IPC nominal
|
|
"""
|
|
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
|
|
"""
|
|
return pcbnew.FromMM(0.15)
|
|
|
|
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 PutOnGridMM(self, value, gridSizeMM=0.05):
|
|
"""
|
|
Round the value (in KiCAD internal units 1nm) according to the
|
|
provided gridSize in mm.
|
|
"""
|
|
thresh = pcbnew.FromMM(gridSizeMM)
|
|
res = round(value/thresh)*thresh
|
|
return res
|
|
|
|
def PutOnGridMils(self, value, gridSizeMil=2):
|
|
"""
|
|
Round the value (in KiCAD internal units 1nm) according to the
|
|
provided gridSize in mil.
|
|
"""
|
|
thresh = pcbnew.FromMils(gridSizeMil)
|
|
res = round(value/thresh)*thresh
|
|
return res
|
|
|
|
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 implementing class
|
|
"""
|
|
|
|
self.buildmessages = ""
|
|
|
|
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():
|
|
self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
|
|
self.buildmessages += self._PrintParameterErrors()
|
|
return
|
|
|
|
self.buildmessages = ("Building new %s footprint with the following parameters:\n"
|
|
% self.name)
|
|
|
|
self.buildmessages += self._PrintParameterTable()
|
|
|
|
self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids(
|
|
self.module)
|
|
|
|
self.module.SetValue(self.GetValue())
|
|
self.module.SetReference("%s**" % self.GetReferencePrefix())
|
|
|
|
fpid = pcbnew.LIB_ID(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
|
|
|
|
return
|