diff --git a/common/dialog_about/AboutDialog_main.cpp b/common/dialog_about/AboutDialog_main.cpp
index 2fd9e39a79..32d67904ff 100644
--- a/common/dialog_about/AboutDialog_main.cpp
+++ b/common/dialog_about/AboutDialog_main.cpp
@@ -151,11 +151,17 @@ static void InitKiCadAboutNew( AboutAppInfo& info )
<< HtmlHyperlink( wxT( "https://launchpad.net/kicad" ),
_( "Developer's website on Launchpad" ) )
<< wxT("" );
+
description << wxT( "
" )
<< HtmlHyperlink( wxT( "https://github.com/KiCad/" ),
_( "Our official Repository for component and footprint libraries" ) )
<< wxT( "" );
+ description << wxT( "" )
+ << HtmlHyperlink( wxT( "https://github.com/KiCad/Footprint_Wizards" ),
+ _( "Footprint wizards info on our official repository " ) )
+ << wxT( "" );
+
description << wxT( "" )
<< _( "Non official repositories" )
<< wxT( "" );
diff --git a/pcbnew/class_footprint_wizard.h b/pcbnew/class_footprint_wizard.h
index 20fc1d443f..2ffc65811d 100644
--- a/pcbnew/class_footprint_wizard.h
+++ b/pcbnew/class_footprint_wizard.h
@@ -33,6 +33,17 @@
#include
#include
+// Allowable parameter types for PCB wizards
+const wxString WIZARD_PARAM_UNITS_MM = "mm"; // Millimetres
+const wxString WIZARD_PARAM_UNITS_MILS = "mils"; // Mils / thou
+const wxString WIZARD_PARAM_UNITS_FLOAT = "float"; // Floating point (dimensionless)
+const wxString WIZARD_PARAM_UNITS_INTEGER = "integer"; // Integer (dimensionless)
+const wxString WIZARD_PARAM_UNITS_BOOL = "bool"; // Boolean option
+const wxString WIZARD_PARAM_UNITS_RADIANS = "radians"; // Angle (radians)
+const wxString WIZARD_PARAM_UNITS_DEGREES = "degrees"; // Angle (degrees)
+const wxString WIZARD_PARAM_UNITS_PERCENT = "%"; // Percent (0% -> 100%)
+const wxString WIZARD_PARAM_UNITS_STRING = "string"; // String
+
/**
* Class FOOTPRINT_WIZARD
* This is the parent class from where any footprint wizard class must
@@ -104,6 +115,20 @@ public:
*/
virtual wxArrayString GetParameterErrors( int aPage ) = 0;
+ /**
+ * Function GetParameterHints
+ * @param aPage is the page we want to know the hints of
+ * @return an array of hints (if any) for the parameters, empty string for no hints
+ */
+ virtual wxArrayString GetParameterHints( int aPage ) = 0;
+
+ /**
+ * Function GetParamaterDesignators
+ * @param aPage is the page we want to know the designators of
+ * @return an array of designators (blank strings for no designators
+ */
+ virtual wxArrayString GetParameterDesignators( int aPage ) = 0;
+
/**
* Function SetParameterValues
* @param aPage is the page we want to set the parameters in
@@ -112,6 +137,12 @@ public:
*/
virtual wxString SetParameterValues( int aPage, wxArrayString& aValues ) = 0;
+ /**
+ * Function ResetParameters
+ * Reset all wizard parameters to default values
+ */
+ virtual void ResetParameters() = 0;
+
/**
* Function GetModule
* This method builds the module itself and returns it to the caller function
diff --git a/pcbnew/footprint_wizard.cpp b/pcbnew/footprint_wizard.cpp
index 6e9b17ec79..02b379c667 100644
--- a/pcbnew/footprint_wizard.cpp
+++ b/pcbnew/footprint_wizard.cpp
@@ -203,6 +203,22 @@ void FOOTPRINT_WIZARD_FRAME::SelectCurrentWizard( wxCommandEvent& event )
SelectFootprintWizard();
}
+void FOOTPRINT_WIZARD_FRAME::DefaultParameters( wxCommandEvent& event )
+{
+ FOOTPRINT_WIZARD* footprintWizard = GetMyWizard();
+
+ if ( footprintWizard == NULL )
+ return;
+
+ footprintWizard->ResetParameters();
+
+ // Reload
+ ReCreateParameterList();
+ ReloadFootprint();
+ DisplayWizardInfos();
+
+}
+
void FOOTPRINT_WIZARD_FRAME::ParametersUpdated( wxGridEvent& event )
{
@@ -223,39 +239,12 @@ void FOOTPRINT_WIZARD_FRAME::ParametersUpdated( wxGridEvent& event )
int count = m_parameterGrid->GetNumberRows();
// Skip extra event, useless
- if( event.GetString() == m_parameterGrid->GetCellValue( event.GetRow(), m_columnPrmValue ) )
+ if( event.GetString() == m_parameterGrid->GetCellValue( event.GetRow(), WIZ_COL_VALUE ) )
return;
for( int prm_id = 0; prm_id < count; ++prm_id )
{
- wxString value = m_parameterGrid->GetCellValue( prm_id, m_columnPrmValue );
-
- // if this parameter is expected to be an internal
- // unit convert it back from the user format
- if( ptList[prm_id]==wxT( "IU" ) )
- {
- // If our locale is set to use, for decimal point, just change it
- // to be scripting compatible
- LOCALE_IO toggle;
- double dValue;
-
- value.ToDouble( &dValue );
-
- // convert from mils to inches where it's needed
- if( g_UserUnit==INCHES )
- dValue = dValue / 1000.0;
-
- dValue = From_User_Unit( g_UserUnit, dValue );
-
- // Internal units are int. Print them as int.
- value.Printf( "%d", KiROUND( dValue ) );
-
- if( prmValues[prm_id].EndsWith(".0") )
- {
- prmValues[prm_id].RemoveLast();
- prmValues[prm_id].RemoveLast();
- }
- }
+ wxString value = m_parameterGrid->GetCellValue( prm_id, WIZ_COL_VALUE);
if( prmValues[prm_id] != value )
{
diff --git a/pcbnew/footprint_wizard_frame.cpp b/pcbnew/footprint_wizard_frame.cpp
index 6112fa6018..bb2e99bb48 100644
--- a/pcbnew/footprint_wizard_frame.cpp
+++ b/pcbnew/footprint_wizard_frame.cpp
@@ -44,6 +44,8 @@
#include "footprint_wizard_frame.h"
#include
#include
+#include
+#include
#include
#include
@@ -61,6 +63,9 @@ BEGIN_EVENT_TABLE( FOOTPRINT_WIZARD_FRAME, EDA_DRAW_FRAME )
EVT_TOOL( ID_FOOTPRINT_WIZARD_SELECT_WIZARD,
FOOTPRINT_WIZARD_FRAME::SelectCurrentWizard )
+ EVT_TOOL( ID_FOOTPRINT_WIZARD_RESET_TO_DEFAULT,
+ FOOTPRINT_WIZARD_FRAME::DefaultParameters )
+
EVT_TOOL( ID_FOOTPRINT_WIZARD_NEXT,
FOOTPRINT_WIZARD_FRAME::Process_Special_Functions )
@@ -74,6 +79,7 @@ BEGIN_EVENT_TABLE( FOOTPRINT_WIZARD_FRAME, EDA_DRAW_FRAME )
FOOTPRINT_WIZARD_FRAME::Show3D_Frame )
// listbox events
+
EVT_LISTBOX( ID_FOOTPRINT_WIZARD_PAGE_LIST, FOOTPRINT_WIZARD_FRAME::ClickOnPageList )
EVT_GRID_CMD_CELL_CHANGED( ID_FOOTPRINT_WIZARD_PARAMETER_LIST,
FOOTPRINT_WIZARD_FRAME::ParametersUpdated )
@@ -81,10 +87,6 @@ BEGIN_EVENT_TABLE( FOOTPRINT_WIZARD_FRAME, EDA_DRAW_FRAME )
EVT_MENU( ID_SET_RELATIVE_OFFSET, FOOTPRINT_WIZARD_FRAME::OnSetRelativeOffset )
END_EVENT_TABLE()
-// Column index to display parameters in m_parameterGrid
-int FOOTPRINT_WIZARD_FRAME::m_columnPrmName = 0;
-int FOOTPRINT_WIZARD_FRAME::m_columnPrmValue = 1;
-int FOOTPRINT_WIZARD_FRAME::m_columnPrmUnit = 2;
#define FOOTPRINT_WIZARD_FRAME_NAME wxT( "FootprintWizard" )
@@ -255,6 +257,13 @@ void FOOTPRINT_WIZARD_FRAME::ExportSelectedFootprint( wxCommandEvent& aEvent )
Close();
}
+void FOOTPRINT_WIZARD_FRAME::OnGridSize( wxSizeEvent& aSizeEvent )
+{
+ // Resize the parameter columns
+ ResizeParamColumns();
+
+ aSizeEvent.Skip();
+}
void FOOTPRINT_WIZARD_FRAME::OnSize( wxSizeEvent& SizeEv )
{
@@ -278,9 +287,10 @@ void FOOTPRINT_WIZARD_FRAME::initParameterGrid()
m_parameterGrid->CreateGrid( 0, 3 );
// Columns
- m_parameterGrid->SetColLabelValue( m_columnPrmName, _( "Parameter" ) );
- m_parameterGrid->SetColLabelValue( m_columnPrmValue, _( "Value" ) );
- m_parameterGrid->SetColLabelValue( m_columnPrmUnit, _( "Units" ) );
+ m_parameterGrid->SetColLabelValue( WIZ_COL_NAME, _( "Parameter" ) );
+ m_parameterGrid->SetColLabelValue( WIZ_COL_VALUE, _( "Value" ) );
+ m_parameterGrid->SetColLabelValue( WIZ_COL_UNITS, _( "Units" ) );
+
m_parameterGrid->SetColLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTRE );
m_parameterGrid->AutoSizeColumns();
@@ -288,6 +298,11 @@ void FOOTPRINT_WIZARD_FRAME::initParameterGrid()
m_parameterGrid->AutoSizeRows();
m_parameterGrid->SetRowLabelSize( 25 );
m_parameterGrid->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
+
+ m_parameterGrid->DisableDragGridSize();
+ m_parameterGrid->DisableDragColSize();
+
+ m_parameterGrid->Connect( wxEVT_SIZE, wxSizeEventHandler(FOOTPRINT_WIZARD_FRAME::OnGridSize), NULL, this );
}
@@ -336,63 +351,117 @@ void FOOTPRINT_WIZARD_FRAME::ReCreateParameterList()
m_parameterGrid->ClearGrid();
- // Get the list of names, values, and types
- wxArrayString fpList = footprintWizard->GetParameterNames( page );
- wxArrayString fvList = footprintWizard->GetParameterValues( page );
- wxArrayString ptList = footprintWizard->GetParameterTypes( page );
+ // Get the list of names, values, types, hints and designators
+ wxArrayString designatorsList = footprintWizard->GetParameterDesignators( page );
+ wxArrayString namesList = footprintWizard->GetParameterNames( page );
+ wxArrayString valuesList = footprintWizard->GetParameterValues( page );
+ wxArrayString typesList = footprintWizard->GetParameterTypes( page );
+ wxArrayString hintsList = footprintWizard->GetParameterHints( page );
// Dimension the wxGrid
if( m_parameterGrid->GetNumberRows() > 0 )
m_parameterGrid->DeleteRows( 0, m_parameterGrid->GetNumberRows() );
- m_parameterGrid->AppendRows( fpList.size() );
+ m_parameterGrid->AppendRows( namesList.size() );
- wxString value, units;
- for( unsigned int i = 0; i< fpList.size(); i++ )
+ wxString designator, name, value, units, hint;
+
+ for( unsigned int i = 0; i< namesList.size(); i++ )
{
- value = fvList[i];
+ designator = designatorsList[i];
+ name = namesList[i];
+ value = valuesList[i];
+ units = typesList[i];
+ hint = hintsList[i];
- m_parameterGrid->SetCellValue( i, m_columnPrmName, fpList[i] );
- m_parameterGrid->SetReadOnly( i, m_columnPrmName );
+ m_parameterGrid->SetRowLabelValue( i, designator );
- if( ptList[i]==wxT( "IU" ) )
+ // Set the 'Name'
+ m_parameterGrid->SetCellValue( i, WIZ_COL_NAME, name );
+ m_parameterGrid->SetReadOnly( i, WIZ_COL_NAME );
+ m_parameterGrid->SetCellAlignment( i, WIZ_COL_NAME, wxALIGN_LEFT, wxALIGN_CENTRE );
+
+ // Set the editor type of the
+
+ // Boolean parameters can be displayed using a checkbox
+ if ( units == WIZARD_PARAM_UNITS_BOOL )
{
- LOCALE_IO toggle;
+ wxGridCellBoolEditor *boolEditor = new wxGridCellBoolEditor;
+ boolEditor->UseStringValues("True","False");
+ m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, boolEditor );
- // We are handling internal units, so convert them to the current
- // system selected units and store into value.
- double dValue;
-
- value.ToDouble( &dValue );
-
- dValue = To_User_Unit( g_UserUnit, dValue );
-
- if( g_UserUnit==INCHES ) // we convert inches into mils for more detail
- {
- dValue = dValue * 1000.0;
- units = wxT( "mils" );
- }
- else if( g_UserUnit==MILLIMETRES )
- {
- units = wxT( "mm" );
- }
-
- // Use Double2Str to build the string, because useless trailing 0
- // are removed. The %f format does not remove them
- std::string s = Double2Str( dValue );
- value = FROM_UTF8( s.c_str() );
+ m_parameterGrid->SetCellRenderer( i, WIZ_COL_VALUE, new wxGridCellBoolRenderer );
}
- else if( ptList[i]==wxT( "UNITS" ) ) // 1,2,3,4,5 ... N
+ // Parameters that can be selected from a list of multiple options
+ else if ( units.Contains( "," ) ) // Indicates list of available options
{
+ wxStringTokenizer tokenizer( units, "," );
+ wxArrayString options;
+
+ while ( tokenizer.HasMoreTokens() )
+ {
+ options.Add( tokenizer.GetNextToken() );
+ }
+
+ m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellChoiceEditor( options ) );
+
units = wxT( "" );
}
+ // Integer parameters
+ else if ( units == WIZARD_PARAM_UNITS_INTEGER )
+ {
+ m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellNumberEditor );
+ }
+ // Non-integer numerical parameters
+ else if ( ( units == WIZARD_PARAM_UNITS_MM ) ||
+ ( units == WIZARD_PARAM_UNITS_MILS ) ||
+ ( units == WIZARD_PARAM_UNITS_FLOAT ) ||
+ ( units == WIZARD_PARAM_UNITS_RADIANS ) ||
+ ( units == WIZARD_PARAM_UNITS_DEGREES ) ||
+ ( units == WIZARD_PARAM_UNITS_PERCENT ) )
+ {
+ m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellFloatEditor );
- m_parameterGrid->SetCellValue( i, m_columnPrmValue, value );
- m_parameterGrid->SetCellValue( i, m_columnPrmUnit, units );
- m_parameterGrid->SetReadOnly( i, m_columnPrmUnit );
+ // Convert separators to the locale-specific character
+ value.Replace( ",", wxNumberFormatter::GetDecimalSeparator() );
+ value.Replace( ".", wxNumberFormatter::GetDecimalSeparator() );
+ }
+
+
+ // Set the 'Units'
+ m_parameterGrid->SetCellValue( i, WIZ_COL_UNITS, units );
+ m_parameterGrid->SetReadOnly( i, WIZ_COL_UNITS );
+ m_parameterGrid->SetCellAlignment( i, WIZ_COL_UNITS, wxALIGN_LEFT, wxALIGN_CENTRE );
+
+ // Set the 'Value'
+ m_parameterGrid->SetCellValue( i, WIZ_COL_VALUE, value );
+ m_parameterGrid->SetCellAlignment( i, WIZ_COL_VALUE, wxALIGN_CENTRE, wxALIGN_CENTRE );
}
+ ResizeParamColumns();
+
+}
+
+void FOOTPRINT_WIZARD_FRAME::ResizeParamColumns()
+{
+
+ // Parameter grid is not yet configured
+ if ( ( m_parameterGrid == NULL ) || ( m_parameterGrid->GetNumberCols() == 0 ) )
+ return;
+
+ // first auto-size the columns to ensure enough space around text
m_parameterGrid->AutoSizeColumns();
+
+ // Auto-size the value column
+ int width = m_parameterGrid->GetClientSize().GetWidth() -
+ m_parameterGrid->GetRowLabelSize() -
+ m_parameterGrid->GetColSize( WIZ_COL_NAME ) -
+ m_parameterGrid->GetColSize( WIZ_COL_UNITS );
+
+ if ( width > m_parameterGrid->GetColMinimalAcceptableWidth() )
+ {
+ m_parameterGrid->SetColSize( WIZ_COL_VALUE, width );
+ }
}
@@ -593,6 +662,13 @@ void FOOTPRINT_WIZARD_FRAME::ReCreateHToolbar()
_( "Select the wizard script to load and run" ) );
m_mainToolBar->AddSeparator();
+
+ m_mainToolBar->AddTool( ID_FOOTPRINT_WIZARD_RESET_TO_DEFAULT, wxEmptyString,
+ KiBitmap( reload_xpm ),
+ _( "Reset the wizard parameters to default values ") );
+
+ m_mainToolBar->AddSeparator();
+
m_mainToolBar->AddTool( ID_FOOTPRINT_WIZARD_PREVIOUS, wxEmptyString,
KiBitmap( lib_previous_xpm ),
_( "Select previous parameters page" ) );
@@ -655,13 +731,13 @@ FOOTPRINT_WIZARD_MESSAGES::FOOTPRINT_WIZARD_MESSAGES( FOOTPRINT_WIZARD_FRAME* aP
wxCAPTION | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT )
{
m_canClose = false;
- wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
- SetSizer( bSizer );
+ wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
+ SetSizer( bSizer );
- m_messageWindow = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
+ m_messageWindow = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE|wxTE_READONLY );
- bSizer->Add( m_messageWindow, 1, wxEXPAND, 0 );
+ bSizer->Add( m_messageWindow, 1, wxEXPAND, 0 );
m_config = aCfg;
@@ -670,7 +746,7 @@ FOOTPRINT_WIZARD_MESSAGES::FOOTPRINT_WIZARD_MESSAGES( FOOTPRINT_WIZARD_FRAME* aP
SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
m_messageWindow->SetMinSize( wxSize( 350, 250 ) );
- Layout();
+ Layout();
bSizer->SetSizeHints( this );
}
diff --git a/pcbnew/footprint_wizard_frame.h b/pcbnew/footprint_wizard_frame.h
index 97fabe166d..302d74601d 100644
--- a/pcbnew/footprint_wizard_frame.h
+++ b/pcbnew/footprint_wizard_frame.h
@@ -39,9 +39,16 @@ class wxGrid;
class wxGridEvent;
class FOOTPRINT_EDIT_FRAME;
-// A helper class to display messages when building a footprin
+// A helper class to display messages when building a footprint
class FOOTPRINT_WIZARD_MESSAGES;
+enum WizardParameterColumnNames
+{
+ WIZ_COL_NAME = 0,
+ WIZ_COL_VALUE,
+ WIZ_COL_UNITS
+};
+
/**
* Class FOOTPRINT_WIZARD_FRAME
*/
@@ -54,11 +61,6 @@ private:
int m_parameterGridWidth; ///< size of the grid
FOOTPRINT_WIZARD_MESSAGES* m_messagesFrame;
- // Column index to display parameters in m_parameterGrid
- static int m_columnPrmName;
- static int m_columnPrmValue;
- static int m_columnPrmUnit;
-
protected:
wxString m_wizardName; ///< name of the current wizard
wxString m_wizardDescription; ///< description of the wizard
@@ -76,6 +78,8 @@ private:
void OnSize( wxSizeEvent& event ) override;
+ void OnGridSize( wxSizeEvent& aSizeEvent );
+
/**
* Function ExportSelectedFootprint();
* will let the caller exit from the wait loop, and get the built footprint
@@ -103,6 +107,11 @@ private:
*/
void ReCreateParameterList();
+ /**
+ * Expand the 'Value' column to fill available
+ */
+ void ResizeParamColumns();
+
/**
* Function initParameterGrid
* Prepare the grid where parameters are displayed
@@ -168,6 +177,8 @@ private:
void SelectCurrentWizard( wxCommandEvent& event );
+ void DefaultParameters( wxCommandEvent& event );
+
/**
* Function ParametersUpdated
* Update the footprint python parameters values from the values in grid
diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp
index b760c5b202..2767f1d336 100644
--- a/pcbnew/pcbnew.cpp
+++ b/pcbnew/pcbnew.cpp
@@ -200,7 +200,6 @@ PGM_BASE& Pgm()
#if defined( KICAD_SCRIPTING )
static bool scriptingSetup()
{
- wxString path_frag;
#if defined( __WINDOWS__ )
// If our python.exe (in kicad/bin) exists, force our kicad python environment
@@ -227,14 +226,6 @@ static bool scriptingSetup()
wxSetEnv( wxT( "PATH" ), kipython );
}
- // wizard plugins are stored in ../share/kicad/scripting/plugins.
- // so add the base scripting path to python scripting default search paths
- // which are ( [KICAD_PATH] is an environment variable to define)
- // [KICAD_PATH]/scripting
- // [KICAD_PATH]/scripting/plugins
- // Add this default search path:
- path_frag = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
-
#elif defined( __WXMAC__ )
// This path is given to LoadPlugins() from kicadplugins.i, which
@@ -278,13 +269,9 @@ static bool scriptingSetup()
wxSetEnv( wxT( "PYTHONPATH" ), pypath );
- // Add this default search path:
- path_frag = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
#endif
- // path_frag is the path to the bundled scripts and plugins, all other paths are
- // determined by the python pcbnew.py initialisation code.
- if( !pcbnewInitPythonScripting( TO_UTF8( path_frag ) ) )
+ if ( !pcbnewInitPythonScripting( TO_UTF8( PyScriptingPath() ) ) )
{
wxLogError( "pcbnewInitPythonScripting() failed." );
return false;
diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h
index 801a4e021c..19614c05d1 100644
--- a/pcbnew/pcbnew_id.h
+++ b/pcbnew/pcbnew_id.h
@@ -392,6 +392,7 @@ enum pcbnew_ids
ID_FOOTPRINT_WIZARD_PAGES_WINDOW,
ID_FOOTPRINT_WIZARD_PARAMETERS_WINDOW,
ID_FOOTPRINT_WIZARD_SELECT_WIZARD,
+ ID_FOOTPRINT_WIZARD_RESET_TO_DEFAULT,
ID_FOOTPRINT_WIZARD_EXPORT_TO_BOARD,
ID_UPDATE_PCB_FROM_SCH,
diff --git a/pcbnew/python/plugins/FPC_(SMD_type)_footprintwizard.py b/pcbnew/python/plugins/FPC_wizard.py
similarity index 93%
rename from pcbnew/python/plugins/FPC_(SMD_type)_footprintwizard.py
rename to pcbnew/python/plugins/FPC_wizard.py
index bf77e93241..7f10b0b042 100644
--- a/pcbnew/python/plugins/FPC_(SMD_type)_footprintwizard.py
+++ b/pcbnew/python/plugins/FPC_wizard.py
@@ -17,10 +17,9 @@
from __future__ import division
import pcbnew
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
-
-class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
+class FPC_FootprintWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "FPC (SMT connector)"
@@ -29,11 +28,11 @@ class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
return "FPC (SMT connector) Footprint Wizard"
def GetValue(self):
- pins = self.parameters["Pads"]["*n"]
+ pins = self.parameters["Pads"]["n"]
return "FPC_%d" % pins
def GenerateParameterList(self):
- self.AddParam( "Pads", "n", self.uNatural, 40 )
+ self.AddParam( "Pads", "n", self.uInteger, 40 )
self.AddParam( "Pads", "pitch", self.uMM, 0.5 )
self.AddParam( "Pads", "width", self.uMM, 0.25 )
self.AddParam( "Pads", "height", self.uMM, 1.6)
@@ -56,13 +55,12 @@ class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
return pad
def CheckParameters(self):
- p = self.parameters
- self.CheckParamInt( "Pads", "*n" ) # not internal units preceded by "*"
-
+ #TODO implement custom parameter checking
+ pass
def BuildThisFootprint(self):
p = self.parameters
- pad_count = int(p["Pads"]["*n"])
+ pad_count = int(p["Pads"]["n"])
pad_width = p["Pads"]["width"]
pad_height = p["Pads"]["height"]
pad_pitch = p["Pads"]["pitch"]
diff --git a/pcbnew/python/plugins/FootprintWizardDrawingAids.py b/pcbnew/python/plugins/FootprintWizardBase.py
similarity index 79%
rename from pcbnew/python/plugins/FootprintWizardDrawingAids.py
rename to pcbnew/python/plugins/FootprintWizardBase.py
index 036f09a616..6a57ac1fe4 100644
--- a/pcbnew/python/plugins/FootprintWizardDrawingAids.py
+++ b/pcbnew/python/plugins/FootprintWizardBase.py
@@ -15,10 +15,150 @@
#
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
+
+ """
+ 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:
+ GetValue()
+ GenerateParameterList()
+ CheckParameters()
+ BuildThisFootprint()
+ GetName()
+ GetDescription()
+ """
+
+ def __init__(self):
+ pcbnew.FootprintWizardPlugin.__init__(self)
+ self.GenerateParameterList()
+
+ def GetName(self):
+ """
+ Retun 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 implment
+ this to draw what you want
+ """
+ raise NotImplementedError
+
+ # Do not override this method!
+ 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
+
+ # Perform default checks on all params
+ for p in self.params:
+ p.ClearErrors()
+ p.Check() # use defaults
+
+ self.CheckParameters() # User error checks
+
+
+ if self.AnyErrors(): # Errors were detected!
+
+ 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)
+
+ 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 += self.Show()
+
+ self.draw = 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
+
+ def SetModule3DModel(self):
+ pass
+
+ 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)
class FootprintWizardDrawingAids:
"""
@@ -294,6 +434,7 @@ class FootprintWizardDrawingAids:
If filled is true, the thickness and radius of the line will be set
such that the circle appears filled
"""
+
circle = pcbnew.EDGE_MODULE(self.module)
start = self.TransformPoint(x, y)
@@ -362,21 +503,22 @@ class FootprintWizardDrawingAids:
_PolyLineInternal(pts) # original
- if mirrorX is not None:
- self.TransformFlip(mirrorX, 0, self.flipX)
- _PolyLineInternal(pts)
- self.PopTransform()
-
- if mirrorY is not None:
- self.TransformFlipOrigin(0, mirrorY, self.flipY)
- _PolyLineInternal(pts)
- self.PopTransform()
-
if mirrorX is not None and mirrorY is not None:
self.TransformFlip(mirrorX, mirrorY, self.flipBoth) # both
_PolyLineInternal(pts)
self.PopTransform()
+ elif mirrorX is not None:
+ self.TransformFlip(mirrorX, 0, self.flipX)
+ _PolyLineInternal(pts)
+ self.PopTransform()
+
+ elif mirrorY is not None:
+ self.TransformFlip(0, mirrorY, self.flipY)
+ _PolyLineInternal(pts)
+ self.PopTransform()
+
+
def Reference(self, x, y, size, orientation_degree = 0):
"""
Draw the module's reference as the given point.
@@ -529,4 +671,4 @@ class FootprintWizardDrawingAids:
[0, 0]]
self.Polyline(pts)
- self.PopTransform(2)
+ self.PopTransform(2)
\ No newline at end of file
diff --git a/pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py b/pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py
deleted file mode 100644
index d891b7a462..0000000000
--- a/pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py
+++ /dev/null
@@ -1,348 +0,0 @@
-# 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
diff --git a/pcbnew/python/plugins/bga_wizard.py b/pcbnew/python/plugins/bga_wizard.py
index 8ce17e6a6b..6705450800 100644
--- a/pcbnew/python/plugins/bga_wizard.py
+++ b/pcbnew/python/plugins/bga_wizard.py
@@ -17,7 +17,7 @@
from __future__ import division
import pcbnew
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
import PadArray as PA
@@ -29,7 +29,7 @@ class BGAPadGridArray(PA.PadGridArray):
n_x + 1)
-class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
+class BGAWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "BGA"
@@ -38,35 +38,48 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
return "Ball Grid Array Footprint Wizard"
def GenerateParameterList(self):
- self.AddParam("Pads", "pad pitch", self.uMM, 1)
- self.AddParam("Pads", "pad size", self.uMM, 0.5)
- self.AddParam("Pads", "row count", self.uNatural, 5)
- self.AddParam("Pads", "column count", self.uNatural, 5)
- self.AddParam("Pads", "outline x margin", self.uMM, 1)
- self.AddParam("Pads", "outline y margin", self.uMM, 1)
+ self.AddParam("Pads", "pitch", self.uMM, 1, designator='p')
+ self.AddParam("Pads", "size", self.uMM, 0.5)
+ self.AddParam("Pads", "columns", self.uInteger, 5, designator="nx")
+ self.AddParam("Pads", "rows", self.uInteger, 5, designator="ny")
+
+ self.AddParam("Package", "width", self.uMM, 6, designator='X')
+ self.AddParam("Package", "length", self.uMM, 6, designator='Y')
+ self.AddParam("Package", "margin", self.uMM, 0.25, min_value=0.2, hint="Courtyard margin")
def CheckParameters(self):
- self.CheckParamInt("Pads", "*row count")
- self.CheckParamInt("Pads", "*column count")
+
+ # check that the package is large enough
+ width = pcbnew.ToMM(self.parameters['Pads']['pitch'] * self.parameters['Pads']['columns'])
+
+ length = pcbnew.ToMM(self.parameters['Pads']['pitch'] * self.parameters['Pads']['rows'])
+
+ self.CheckParam('Package','width',min_value=width,info="Package width is too small (< {w}mm)".format(w=width))
+ self.CheckParam('Package','length',min_value=length,info="Package length is too small (< {l}mm".format(l=length))
def GetValue(self):
- pins = (self.parameters["Pads"]["*row count"]
- * self.parameters["Pads"]["*column count"])
+ pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"])
- return "BGA_%d" % pins
+ return "BGA-{n}_{a}x{b}_{x}x{y}mm".format(
+ n = pins,
+ a = self.parameters['Pads']['columns'],
+ b = self.parameters['Pads']['rows'],
+ x = pcbnew.ToMM(self.parameters['Package']['width']),
+ y = pcbnew.ToMM(self.parameters['Package']['length'])
+ )
def BuildThisFootprint(self):
pads = self.parameters["Pads"]
- rows = pads["*row count"]
- cols = pads["*column count"]
- pad_size = pads["pad size"]
+ rows = pads["rows"]
+ cols = pads["columns"]
+ pad_size = pads["size"]
pad_size = pcbnew.wxSize(pad_size, pad_size)
- pad_pitch = pads["pad pitch"]
+ pad_pitch = pads["pitch"]
# add in the pads
- pad = PA.PadMaker(self.module).SMTRoundPad(pads["pad size"])
+ pad = PA.PadMaker(self.module).SMTRoundPad(pads["size"])
pin1_pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2,
-((rows - 1) * pad_pitch) / 2)
@@ -74,21 +87,68 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
array = BGAPadGridArray(pad, cols, rows, pad_pitch, pad_pitch)
array.AddPadsToModule(self.draw)
- #box
- ssx = -pin1_pos.x + pads["outline x margin"]
- ssy = -pin1_pos.y + pads["outline y margin"]
+ # Draw box outline on F.Fab layer
+ self.draw.SetLayer(pcbnew.F_Fab)
+ ssx = self.parameters['Package']['width'] / 2
+ ssy = self.parameters['Package']['length'] / 2
- self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2,
- pads["outline x margin"])
+ # Bevel should be 1mm nominal but we'll allow smaller values
+ if pcbnew.ToMM(ssx) < 1:
+ bevel = ssx
+ else:
+ bevel = pcbnew.FromMM(1)
+
+ # Box with 1mm bevel as per IPC7351C
+ self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, bevel)
+
+ # Add IPC markings to F_Silk layer
+ self.draw.SetLayer(pcbnew.F_SilkS)
+ offset = pcbnew.FromMM(0.15)
+ len_x = 0.5 * ssx
+ len_y = 0.5 * ssy
+
+ edge = [
+ [ ssx + offset - len_x, -ssy - offset],
+ [ ssx + offset, -ssy - offset],
+ [ ssx + offset, -ssy - offset + len_y],
+ ]
+
+ # Draw three square edges
+ self.draw.Polyline(edge)
+ self.draw.Polyline(edge, mirrorY=0)
+ self.draw.Polyline(edge, mirrorX=0, mirrorY=0)
+
+ # Draw pin-1 marker
+ bevel += offset
+ pin1 = [
+ [ -ssx - offset + len_x, -ssy - offset],
+ [ -ssx - offset + bevel, -ssy - offset],
+ [ -ssx - offset, -ssy - offset + bevel],
+ [ -ssx - offset, -ssy - offset + len_y],
+ ]
+
+ # Remove lines if the package is too small
+ if bevel > len_x:
+ pin1 = pin1[1:]
+
+ if bevel > len_y:
+ pin1 = pin1[:-1]
+
+ self.draw.Polyline(pin1)
+
+ # Draw a circle in the bevel void
+ self.draw.Circle( -ssx, -ssy, pcbnew.FromMM(0.2), filled=True)
# Courtyard
- cmargin = self.draw.GetLineThickness()
+ cmargin = self.parameters['Package']['margin']
self.draw.SetLayer(pcbnew.F_CrtYd)
sizex = (ssx + cmargin) * 2
sizey = (ssy + cmargin) * 2
+
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
- sizex = self.PutOnGridMM(sizex, 0.1)
- sizey = self.PutOnGridMM(sizey, 0.1)
+ sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+ sizey = pcbnew.PutOnGridMM(sizey, 0.1)
+
# set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey)
diff --git a/pcbnew/python/plugins/circular_pad_array_wizard.py b/pcbnew/python/plugins/circular_pad_array_wizard.py
index 7a42c8fe84..42c08c9c7a 100644
--- a/pcbnew/python/plugins/circular_pad_array_wizard.py
+++ b/pcbnew/python/plugins/circular_pad_array_wizard.py
@@ -18,11 +18,11 @@ from __future__ import division
import math
import pcbnew
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
import PadArray as PA
-class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin):
+class circular_pad_array_wizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "Circular Pad Array"
@@ -32,52 +32,84 @@ class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin):
def GenerateParameterList(self):
- self.AddParam("Pads", "n", self.uNatural, 6)
- self.AddParam("Pads", "pad width", self.uMM, 1.5)
- self.AddParam("Pads", "drill", self.uMM, 1)
- self.AddParam("Pads", "circle diameter", self.uMM, 5)
- self.AddParam("Pads", "first pad angle", self.uNatural, 0)
- self.AddParam("Pads", "number clockwise", self.uBool, True)
- self.AddParam("Pads", "first pad number", self.uNatural, 1)
+ self.AddParam("Pads", "count", self.uInteger, 6, min_value=1, designator='n')
+ self.AddParam("Pads", "center diameter", self.uMM, 5, min_value=0, designator='r', hint="Centre distance between pads")
+ self.AddParam("Pads", "diameter", self.uMM, 1.5)
+ self.AddParam("Pads", "drill", self.uMM, 0.8)
+ self.AddParam("Pads", "angle", self.uDegrees, 0, designator='a')
+
+ self.AddParam("Numbering", "initial", self.uInteger, 1, min_value=1)
+ #self.AddParam("Numbering", "increment", self.uInteger, 1, min_value=1)
+ self.AddParam("Numbering", "clockwise", self.uBool, True)
+
+ self.AddParam("Outline", "diameter", self.uMM, 7, designator='D')
+ self.AddParam("Outline", "margin", self.uMM, 0.25, min_value=0.2)
def CheckParameters(self):
- self.CheckParamInt("Pads", "*n")
- self.CheckParamInt("Pads", "*first pad number")
- self.CheckParamBool("Pads", "*number clockwise")
+ pads = self.parameters['Pads']
+ numbering = self.parameters['Numbering']
+ outline = self.parameters['Outline']
+
+ # Check that pads do not overlap
+ pad_dia = pcbnew.ToMM(pads['diameter'])
+ centres = pcbnew.ToMM(pads['center diameter'])
+ n_pads = pads['count']
+
+ self.CheckParam('Pads','diameter',max_value=centres*math.pi/n_pads,info="Pads overlap")
+
+ # Check that the pads fit inside the outline
+ d_min = pad_dia + centres
+
+ self.CheckParam("Outline","diameter",min_value=d_min, info="Outline diameter is too small")
+
def GetValue(self):
- pins = self.parameters["Pads"]["*n"]
+ pins = self.parameters["Pads"]["count"]
return "CPA_%d" % pins
def BuildThisFootprint(self):
- prm = self.parameters['Pads']
+ pads = self.parameters['Pads']
+ numbering = self.parameters['Numbering']
+ outline = self.parameters['Outline']
- pad_size = prm['pad width']
+ pad_size = pads['diameter']
- pad = PA.PadMaker(self.module).THPad(
- prm['pad width'], prm['pad width'], prm['drill'])
+ pad = PA.PadMaker(self.module).THPad(pads['diameter'], pads['diameter'], pads['drill'])
array = PA.PadCircleArray(
- pad, prm['*n'], prm['circle diameter'] / 2,
- angle_offset=prm["*first pad angle"],
+ pad, pads['count'], pads['center diameter'] / 2,
+ angle_offset=pads["angle"],
centre=pcbnew.wxPoint(0, 0),
- clockwise=prm["*number clockwise"])
+ clockwise=numbering["clockwise"])
- array.SetFirstPadInArray(prm["*first pad number"])
+ array.SetFirstPadInArray(numbering["initial"])
array.AddPadsToModule(self.draw)
- body_radius = (prm['circle diameter'] + prm['pad width'])/2 + self.draw.GetLineThickness()
+ # Draw the outline
+ body_radius = outline['diameter'] / 2
+ self.draw.SetLayer(pcbnew.F_Fab)
+ self.draw.GetLineThickness()
self.draw.Circle(0, 0, body_radius)
+ #silkscreen
+ body_radius += pcbnew.FromMM(0.15)
+ self.draw.SetLayer(pcbnew.F_SilkS)
+ self.draw.Circle(0, 0, body_radius)
+
+ # courtyard
+ self.draw.SetLayer(pcbnew.F_CrtYd)
+ self.draw.SetLineThickness(pcbnew.FromMM(0.05))
+ self.draw.Circle(0, 0, body_radius + outline['margin'])
+
+ # Text size
+
text_size = self.GetTextSize() # IPC nominal
thickness = self.GetTextThickness()
- textposy = body_radius + self.draw.GetLineThickness()/2 + self.GetTextSize()/2 + thickness
+ textposy = body_radius + self.draw.GetLineThickness()/2 + self.GetTextSize()/2 + thickness + + outline['margin']
self.draw.Value( 0, textposy, text_size )
self.draw.Reference( 0, -textposy, text_size )
-
-
circular_pad_array_wizard().register()
diff --git a/pcbnew/python/plugins/qfn_wizard.py b/pcbnew/python/plugins/qfn_wizard.py
index d6c3a614b1..f36165bc53 100644
--- a/pcbnew/python/plugins/qfn_wizard.py
+++ b/pcbnew/python/plugins/qfn_wizard.py
@@ -17,55 +17,80 @@
from __future__ import division
import pcbnew
-import HelpfulFootprintWizardPlugin as HFPW
+import pcbnew
+import FootprintWizardBase
import PadArray as PA
-class QFNWizard(HFPW.HelpfulFootprintWizardPlugin):
+class QFNWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "QFN"
def GetDescription(self):
- return "Quad Flat No-lead with Exposed Pad footprint wizard"
+ return "Quad Flat No-lead (QFN) footprint wizard"
def GenerateParameterList(self):
- self.AddParam("Pads", "n", self.uNatural, 100)
- self.AddParam("Pads", "pad pitch", self.uMM, 0.5)
- self.AddParam("Pads", "pad width", self.uMM, 0.25)
- self.AddParam("Pads", "pad length", self.uMM, 1.5)
- self.AddParam("Pads", "oval", self.uBool, True)
- self.AddParam("Pads", "thermal vias", self.uBool, True)
- self.AddParam("Pads", "thermal vias drill", self.uMM, 0.3)
- self.AddParam("Pads", "epad subdiv x", self.uNatural, 2)
- self.AddParam("Pads", "epad subdiv y", self.uNatural, 2)
- self.AddParam("Package", "package width", self.uMM, 14)
- self.AddParam("Package", "package height", self.uMM, 14)
- self.AddParam("Package", "courtyard margin", self.uMM, 1)
+ #TODO - Allow different number of pads in x and y directions
+
+ self.AddParam("Pads", "n", self.uInteger, 100, multiple=4, min_value=4)
+ self.AddParam("Pads", "pitch", self.uMM, 0.5, designator='e')
+ self.AddParam("Pads", "width", self.uMM, 0.25, designator='X1')
+ self.AddParam("Pads", "length", self.uMM, 1.5, designator='Y1')
+ self.AddParam("Pads", "oval", self.uBool, True)
+
+ self.AddParam("EPad", "epad", self.uBool, True)
+ self.AddParam("EPad", "width", self.uMM, 10, designator="E2")
+ self.AddParam("EPad", "length", self.uMM, 10, designator="D2")
+ self.AddParam("EPad", "thermal vias", self.uBool, False)
+ self.AddParam("EPad", "thermal vias drill", self.uMM, 1, min_value=0.1)
+ self.AddParam("EPad", "x divisions", self.uInteger, 2, min_value=1)
+ self.AddParam("EPad", "y divisions", self.uInteger, 2, min_value=1)
+ self.AddParam("EPad", "paste margin", self.uMM, 0.1)
+
+ self.AddParam("Package", "width", self.uMM, 14, designator='E')
+ self.AddParam("Package", "height", self.uMM, 14, designator='D')
+ self.AddParam("Package", "margin", self.uMM, 0.25, minValue=0.2)
+
+ @property
+ def pads(self):
+ return self.parameters['Pads']
+
+ @property
+ def epad(self):
+ return self.parameters['EPad']
+
+ @property
+ def package(self):
+ return self.parameters['Package']
def CheckParameters(self):
- self.CheckParamInt("Pads", "*n", is_multiple_of=4)
- self.CheckParamBool("Pads", "*oval")
- self.CheckParamBool("Pads", "*thermal vias")
+ pass
def GetValue(self):
- return "QFN_%d" % self.parameters["Pads"]["*n"]
+
+ return "QFN-{n}_{ep}{x:g}x{y:g}_Pitch{p:g}mm".format(
+ n = self.pads['n'],
+ ep = "EP_" if self.epad['epad'] else '',
+ x = pcbnew.ToMM(self.package['width']),
+ y = pcbnew.ToMM(self.package['height']),
+ p = pcbnew.ToMM(self.pads['pitch'])
+ )
def BuildThisFootprint(self):
- pads = self.parameters["Pads"]
- pad_pitch = pads["pad pitch"]
- pad_length = pads["pad length"]
- pad_width = pads["pad width"]
+ pad_pitch = self.pads["pitch"]
+ pad_length = self.pads["length"]
+ pad_width = self.pads["width"]
- v_pitch = self.parameters["Package"]["package height"]
- h_pitch = self.parameters["Package"]["package width"]
+ v_pitch = self.package["height"]
+ h_pitch = self.package["width"]
- pads_per_row = pads["*n"] // 4
+ pads_per_row = int(self.pads["n"] // 4)
row_len = (pads_per_row - 1) * pad_pitch
- pad_shape = pcbnew.PAD_SHAPE_OVAL if pads["*oval"] else pcbnew.PAD_SHAPE_RECT
+ pad_shape = pcbnew.PAD_SHAPE_OVAL if self.pads["oval"] else pcbnew.PAD_SHAPE_RECT
h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width,
shape=pad_shape, rot_degree=90.0)
@@ -97,79 +122,88 @@ class QFNWizard(HFPW.HelpfulFootprintWizardPlugin):
array.SetFirstPadInArray(3*pads_per_row + 1)
array.AddPadsToModule(self.draw)
- lim_x = self.parameters["Package"]["package width"] / 2
- lim_y = self.parameters["Package"]["package height"] / 2
+ lim_x = self.package["width"] / 2
+ lim_y = self.package["height"] / 2
inner = (row_len / 2) + pad_pitch
# epad
- epad_width = self.parameters["Package"]["package height"] - (2*pad_length)
- epad_length = self.parameters["Package"]["package width"] - (2*pad_length)
- epad_subdv_x = pads["*epad subdiv x"]
- epad_subdv_y = pads["*epad subdiv y"]
- epad_via_drill = pads["thermal vias drill"]
+ epad_width = self.epad["width"]
+ epad_length = self.epad["length"]
- if (epad_subdv_y != 0 and epad_subdv_x != 0) and (epad_subdv_y != 1 or epad_subdv_x != 1):
- # Create the master pad (one area) on front solder mask, and perhaps of front copper layer
- # at location 0,0
- emasterpad = PA.PadMaker(self.module).SMDPad( epad_length, epad_width,
- shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0.0)
- emasterpad.SetLayerSet(pcbnew.LSET(pcbnew.F_Mask)) # currently, only on solder mask
- emasterpad.SetPadName(pads["*n"]+1)
- self.module.Add(emasterpad)
+ epad_ny = self.epad["x divisions"]
+ epad_nx = self.epad["y divisions"]
- px = pcbnew.FromMM(0.1); py = pcbnew.FromMM(0.1)
- esubpad_size_x = epad_length / epad_subdv_x - px
- esubpad_size_y = epad_width / epad_subdv_y - py
- epad1_pos = pcbnew.wxPoint(-(esubpad_size_x*(epad_subdv_x-1)/2), -esubpad_size_y*(epad_subdv_y-1)/2)
- epad = PA.PadMaker(self.module).SMDPad( esubpad_size_y, esubpad_size_x,
- shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0.0)
- array = PA.EPADGridArray(epad, epad_subdv_x, epad_subdv_y, esubpad_size_x + px, esubpad_size_y + py, pcbnew.wxPoint(0,0))
- array.SetFirstPadInArray(pads["*n"]+1)
- array.AddPadsToModule(self.draw)
- if pads["*thermal vias"]:
- via_diam = min(esubpad_size_y, esubpad_size_x)/3.
- thpad = PA.PadMaker(self.module).THRoundPad(via_diam, min(via_diam/2, epad_via_drill))
- layerset = pcbnew.LSET.AllCuMask()
- layerset.AddLayer(pcbnew.B_Mask)
- layerset.AddLayer(pcbnew.F_Mask)
- thpad.SetLayerSet(layerset)
- array2 = PA.EPADGridArray(thpad, epad_subdv_x, epad_subdv_y, esubpad_size_x + px, esubpad_size_y + py, pcbnew.wxPoint(0,0))
- array2.SetFirstPadInArray(pads["*n"]+1)
- array2.AddPadsToModule(self.draw)
- else:
- epad = PA.PadMaker(self.module).SMDPad(epad_length, epad_width)
- epad_pos = pcbnew.wxPoint(0,0)
- array = PA.PadLineArray(epad, 1, 1, False, epad_pos)
- array.SetFirstPadInArray(pads["*n"]+1)
- array.AddPadsToModule(self.draw)
- if pads["*thermal vias"]:
- via_diam = min(epad_length, epad_width)/3.
- thpad = PA.PadMaker(self.module).THRoundPad( via_diam, min(via_diam/2, epad_via_drill))
- layerset = pcbnew.LSET.AllCuMask()
- layerset.AddLayer(pcbnew.B_Mask)
- layerset.AddLayer(pcbnew.F_Mask)
- thpad.SetLayerSet(layerset)
- array2 = PA.PadLineArray(thpad, 1, 1, False, epad_pos)
- array2.SetFirstPadInArray(pads["*n"]+1)
- array2.AddPadsToModule(self.draw)
+ epad_via_drill = self.epad["thermal vias drill"]
- #top left - diagonal
- self.draw.Line(-lim_x, -inner, -inner, -lim_y)
- # top right
- self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)])
- # bottom left
- self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)])
- # bottom right
- self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)])
+ # Create a central exposed pad?
+ if self.epad['epad'] == True:
+
+ epad_num = self.pads['n'] + 1
+
+ epad_w = epad_length / epad_nx
+ epad_l = epad_width / epad_ny
+
+ # Create the epad
+ epad = PA.PadMaker(self.module).SMDPad( epad_w, epad_l, shape=pcbnew.PAD_SHAPE_RECT )
+ epad.SetLocalSolderPasteMargin( -1 * self.epad['paste margin'] )
+ # set pad layers
+ layers = pcbnew.LSET(pcbnew.F_Mask)
+ layers.AddLayer(pcbnew.F_Cu)
+ layers.AddLayer(pcbnew.F_Paste)
+ epad.SetPadName(epad_num)
+
+ array = PA.EPADGridArray( epad, epad_ny, epad_nx, epad_l, epad_w, pcbnew.wxPoint(0,0) )
+ array.SetFirstPadInArray(epad_num)
+ array.AddPadsToModule(self.draw)
+
+ if self.epad['thermal vias']:
+
+ # create the thermal via
+ via_diam = min(epad_w, epad_l) / 2
+ via_drill = min(via_diam / 2, epad_via_drill)
+ via = PA.PadMaker(self.module).THRoundPad(via_diam, via_drill)
+ layers = pcbnew.LSET.AllCuMask()
+ layers.AddLayer(pcbnew.B_Mask)
+ layers.AddLayer(pcbnew.F_Mask)
+ via.SetLayerSet(layers)
+
+ via_array = PA.EPADGridArray(via, epad_ny, epad_nx, epad_l, epad_w, pcbnew.wxPoint(0,0) )
+ via_array.SetFirstPadInArray(epad_num)
+ via_array.AddPadsToModule(self.draw)
+
+ # Draw the package outline on the F.Fab layer
+ bevel = min( pcbnew.FromMM(1.0), self.package['width']/2, self.package['height']/2 )
+
+ self.draw.SetLayer(pcbnew.F_Fab)
+
+ w = self.package['width']
+ h = self.package['height']
+
+ self.draw.BoxWithDiagonalAtCorner(0, 0, w, h, bevel)
+
+ # Silkscreen
+ self.draw.SetLayer(pcbnew.F_SilkS)
+
+ offset = self.draw.GetLineThickness()
+ clip = row_len / 2 + self.pads['pitch']
+
+ self.draw.Polyline( [ [ clip, -h/2-offset], [ w/2+offset,-h/2-offset], [ w/2+offset, -clip] ] ) # top right
+ self.draw.Polyline( [ [ clip, h/2+offset], [ w/2+offset, h/2+offset], [ w/2+offset, clip] ] ) # bottom right
+ self.draw.Polyline( [ [-clip, h/2+offset], [-w/2-offset, h/2+offset], [-w/2-offset, clip] ] ) # bottom left
+
+ # Add pin-1 indication as per IPC-7351C
+ self.draw.Line(-clip, -h/2-offset, -w/2-pad_length/2, -h/2-offset)
# Courtyard
- cmargin = self.parameters["Package"]["courtyard margin"]
+ cmargin = self.package["margin"]
self.draw.SetLayer(pcbnew.F_CrtYd)
- sizex = (lim_x + cmargin) * 2 + pad_length/2.
- sizey = (lim_y + cmargin) * 2 + pad_length/2.
+
+ sizex = (lim_x + cmargin) * 2 + pad_length
+ sizey = (lim_y + cmargin) * 2 + pad_length
+
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
- sizex = self.PutOnGridMM(sizex, 0.1)
- sizey = self.PutOnGridMM(sizey, 0.1)
+ sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+ sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
thick = self.draw.GetLineThickness()
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
diff --git a/pcbnew/python/plugins/qfp_wizard.py b/pcbnew/python/plugins/qfp_wizard.py
index 04664aa5f4..4c18e89609 100644
--- a/pcbnew/python/plugins/qfp_wizard.py
+++ b/pcbnew/python/plugins/qfp_wizard.py
@@ -17,53 +17,64 @@
from __future__ import division
import pcbnew
-import HelpfulFootprintWizardPlugin
+import FootprintWizardBase
import PadArray as PA
-
-class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
+class QFPWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "QFP"
def GetDescription(self):
- return "Quad Flat Package footprint wizard"
+ return "Quad Flat Package (QFP) footprint wizard"
def GenerateParameterList(self):
- self.AddParam("Pads", "n", self.uNatural, 100)
- self.AddParam("Pads", "pad pitch", self.uMM, 0.5)
- self.AddParam("Pads", "pad width", self.uMM, 0.25)
- self.AddParam("Pads", "pad length", self.uMM, 1.5)
- self.AddParam("Pads", "vertical pitch", self.uMM, 15)
- self.AddParam("Pads", "horizontal pitch", self.uMM, 15)
+ self.AddParam("Pads", "n", self.uInteger, 100, multiple=4, min_value=4)
+ self.AddParam("Pads", "pitch", self.uMM, 0.5, designator='e')
+ self.AddParam("Pads", "width", self.uMM, 0.25, designator='X1')
+ self.AddParam("Pads", "length", self.uMM, 1.5, designator='Y1')
+ self.AddParam("Pads", "horizontal spacing", self.uMM, 15, designator='C1')
+ self.AddParam("Pads", "vertical spacing", self.uMM, 15, designator='C2')
self.AddParam("Pads", "oval", self.uBool, True)
- self.AddParam("Package", "package width", self.uMM, 14)
- self.AddParam("Package", "package height", self.uMM, 14)
- self.AddParam("Package", "courtyard margin", self.uMM, 1)
+ self.AddParam("Package", "width", self.uMM, 14, designator='D1')
+ self.AddParam("Package", "height", self.uMM, 14, designator='E1')
+ self.AddParam("Package", "courtyard margin", self.uMM, 0.25, min_value=0.2)
+
+ @property
+ def pads(self):
+ return self.parameters['Pads']
+
+ @property
+ def package(self):
+ return self.parameters['Package']
def CheckParameters(self):
- self.CheckParamInt("Pads", "*n", is_multiple_of=4)
- self.CheckParamBool("Pads", "*oval")
+ # todo - custom checking
+ pass
def GetValue(self):
- return "QFP_%d" % self.parameters["Pads"]["*n"]
+ return "QFP-{n}_{x:g}x{y:g}_Pitch{p:g}mm".format(
+ n = self.pads['n'],
+ x = pcbnew.ToMM(self.package['width']),
+ y = pcbnew.ToMM(self.package['height']),
+ p = pcbnew.ToMM(self.pads['pitch'])
+ )
def BuildThisFootprint(self):
- pads = self.parameters["Pads"]
- pad_pitch = pads["pad pitch"]
- pad_length = self.parameters["Pads"]["pad length"]
- pad_width = self.parameters["Pads"]["pad width"]
+ pad_pitch = self.pads["pitch"]
+ pad_length = self.pads["length"]
+ pad_width = self.pads["width"]
- v_pitch = pads["vertical pitch"]
- h_pitch = pads["horizontal pitch"]
+ v_pitch = self.pads["vertical spacing"]
+ h_pitch = self.pads["horizontal spacing"]
- pads_per_row = pads["*n"] // 4
+ pads_per_row = int(self.pads["n"] // 4)
row_len = (pads_per_row - 1) * pad_pitch
- pad_shape = pcbnew.PAD_SHAPE_OVAL if pads["*oval"] else pcbnew.PAD_SHAPE_RECT
+ pad_shape = pcbnew.PAD_SHAPE_OVAL if self.pads["oval"] else pcbnew.PAD_SHAPE_RECT
h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width,
shape=pad_shape, rot_degree=90.0)
@@ -95,27 +106,49 @@ class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
array.SetFirstPadInArray(3*pads_per_row + 1)
array.AddPadsToModule(self.draw)
- lim_x = self.parameters["Package"]["package width"] / 2
- lim_y = self.parameters["Package"]["package height"] / 2
+ offset = pcbnew.FromMM(0.15)
+
+ x = self.parameters["Package"]["width"] / 2 + offset
+ y = self.parameters["Package"]["height"] / 2 + offset
inner = (row_len / 2) + pad_pitch
- #top left - diagonal
- self.draw.Line(-lim_x, -inner, -inner, -lim_y)
+ # Add outline to F_Fab layer
+ self.draw.SetLayer(pcbnew.F_Fab)
+
+ bevel = min( pcbnew.FromMM(1.0), self.package['width']/2, self.package['height']/2 )
+
+ w = self.package['width']
+ h = self.package['height']
+
+ # outermost limits of pins
+ right_edge = (h_pitch + pad_length) / 2
+ left_edge = -right_edge
+
+ bottom_edge = (v_pitch + pad_length) / 2
+ top_edge = -bottom_edge
+
+ self.draw.BoxWithDiagonalAtCorner(0, 0, w, h, bevel)
+
+ # Draw silkscreen
+ self.draw.SetLayer(pcbnew.F_SilkS)
+
+ #top left - as per IPC-7351C
+ self.draw.Polyline([(-inner, -y), (-x, -y), (-x, -inner), (left_edge, -inner)])
# top right
- self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)])
+ self.draw.Polyline([(inner, -y), (x, -y), (x, -inner)])
# bottom left
- self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)])
+ self.draw.Polyline([(-inner, y), (-x, y), (-x, inner)])
# bottom right
- self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)])
+ self.draw.Polyline([(inner, y), (x, y), (x, inner)])
# Courtyard
cmargin = self.parameters["Package"]["courtyard margin"]
self.draw.SetLayer(pcbnew.F_CrtYd)
- sizex = (lim_x + cmargin) * 2 + pad_length
- sizey = (lim_y + cmargin) * 2 + pad_length
+ sizex = (right_edge + cmargin) * 2
+ sizey = (bottom_edge + cmargin) * 2
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
- sizex = self.PutOnGridMM(sizex, 0.1)
- sizey = self.PutOnGridMM(sizey, 0.1)
+ sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+ sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
thick = self.draw.GetLineThickness()
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
diff --git a/pcbnew/python/plugins/qrcode.py b/pcbnew/python/plugins/qrcode.py
new file mode 100644
index 0000000000..1eecc61f4a
--- /dev/null
+++ b/pcbnew/python/plugins/qrcode.py
@@ -0,0 +1,827 @@
+#
+# QR Code Generator for Python
+#
+# Copyright (c) 2012 Kazuhiko Arase
+#
+# URL: http://www.d-project.com/
+#
+# Licensed under the MIT license:
+# http://www.opensource.org/licenses/mit-license.php
+#
+# The word 'QR Code' is registered trademark of
+# DENSO WAVE INCORPORATED
+# http://www.denso-wave.com/qrcode/faqpatent-e.html
+#
+
+"""QR Code Generator for Python
+
+ from qrcode import QRCode, ErrorCorrectLevel
+
+ # generate with explicit type number
+ qr = QRCode()
+ qr.setTypeNumber(4)
+ qr.setErrorCorrectLevel(ErrorCorrectLevel.M)
+ qr.addData('here comes qr!')
+ qr.make()
+
+ # generate with auto type number
+ # qr = QRCode.getMinimumQRCode('here comes qr!', ErrorCorrectLevel.M)
+
+ # create an image
+ for r in range(qr.getModuleCount() ):
+ for c in range(qr.getModuleCount() ):
+ color = black if qr.isDark(r, c) else white
+ # set pixel ...
+
+"""
+
+class QRCode:
+
+ PAD0 = 0xEC
+ PAD1 = 0x11
+
+ def __init__(self):
+ self.typeNumber = 1
+ self.errorCorrectLevel = ErrorCorrectLevel.H
+ self.qrDataList = []
+ self.modules = []
+ self.moduleCount = 0
+
+ def getTypeNumber(self):
+ return self.typeNumber
+
+ def setTypeNumber(self, typeNumber):
+ self.typeNumber = typeNumber
+
+ def getErrorCorrectLevel(self):
+ return self.errorCorrectLevel
+
+ def setErrorCorrectLevel(self, errorCorrectLevel):
+ self.errorCorrectLevel = errorCorrectLevel
+
+ def clearData(self):
+ self.qrDataList = []
+
+ def addData(self, data):
+ self.qrDataList.append(QR8BitByte(data) )
+
+ def getDataCount(self):
+ return len(self.qrDataList)
+
+ def getData(self, index):
+ return self.qrDataList[index]
+
+ def isDark(self, row, col):
+ return (self.modules[row][col] if self.modules[row][col] != None
+ else False)
+
+ def getModuleCount(self):
+ return self.moduleCount
+
+ def make(self):
+ self._make(False, self._getBestMaskPattern() )
+
+ def _getBestMaskPattern(self):
+ minLostPoint = 0
+ pattern = 0
+ for i in range(8):
+ self._make(True, i)
+ lostPoint = QRUtil.getLostPoint(self)
+ if i == 0 or minLostPoint > lostPoint:
+ minLostPoint = lostPoint
+ pattern = i
+ return pattern
+
+ def _make(self, test, maskPattern):
+
+ self.moduleCount = self.typeNumber * 4 + 17
+ self.modules = [[None] * self.moduleCount
+ for i in range(self.moduleCount)]
+
+ self._setupPositionProbePattern(0, 0)
+ self._setupPositionProbePattern(self.moduleCount - 7, 0)
+ self._setupPositionProbePattern(0, self.moduleCount - 7)
+
+ self._setupPositionAdjustPattern()
+ self._setupTimingPattern()
+
+ self._setupTypeInfo(test, maskPattern)
+
+ if self.typeNumber >= 7:
+ self._setupTypeNumber(test)
+
+ data = QRCode._createData(
+ self.typeNumber,
+ self.errorCorrectLevel,
+ self.qrDataList)
+
+ self._mapData(data, maskPattern)
+
+ def _mapData(self, data, maskPattern):
+
+ rows = list(range(self.moduleCount) )
+ cols = [col - 1 if col <= 6 else col
+ for col in range(self.moduleCount - 1, 0, -2)]
+ maskFunc = QRUtil.getMaskFunction(maskPattern)
+
+ byteIndex = 0
+ bitIndex = 7
+
+ for col in cols:
+ rows.reverse()
+ for row in rows:
+ for c in range(2):
+ if self.modules[row][col - c] == None:
+
+ dark = False
+ if byteIndex < len(data):
+ dark = ( (data[byteIndex] >> bitIndex) & 1) == 1
+ if maskFunc(row, col - c):
+ dark = not dark
+ self.modules[row][col - c] = dark
+
+ bitIndex -= 1
+ if bitIndex == -1:
+ byteIndex += 1
+ bitIndex = 7
+
+ def _setupPositionAdjustPattern(self):
+ pos = QRUtil.getPatternPosition(self.typeNumber)
+ for row in pos:
+ for col in pos:
+ if self.modules[row][col] != None:
+ continue
+ for r in range(-2, 3):
+ for c in range(-2, 3):
+ self.modules[row + r][col + c] = (
+ r == -2 or r == 2 or c == -2 or c == 2
+ or (r == 0 and c == 0) )
+
+ def _setupPositionProbePattern(self, row, col):
+ for r in range(-1, 8):
+ for c in range(-1, 8):
+ if (row + r <= -1 or self.moduleCount <= row + r
+ or col + c <= -1 or self.moduleCount <= col + c):
+ continue
+ self.modules[row + r][col + c] = (
+ (0 <= r and r <= 6 and (c == 0 or c == 6) )
+ or (0 <= c and c <= 6 and (r == 0 or r == 6) )
+ or (2 <= r and r <= 4 and 2 <= c and c <= 4) )
+
+ def _setupTimingPattern(self):
+ for r in range(8, self.moduleCount - 8):
+ if self.modules[r][6] != None:
+ continue
+ self.modules[r][6] = r % 2 == 0
+ for c in range(8, self.moduleCount - 8):
+ if self.modules[6][c] != None:
+ continue
+ self.modules[6][c] = c % 2 == 0
+
+ def _setupTypeNumber(self, test):
+ bits = QRUtil.getBCHTypeNumber(self.typeNumber)
+ for i in range(18):
+ self.modules[i // 3][i % 3 + self.moduleCount - 8 - 3] = (
+ not test and ( (bits >> i) & 1) == 1)
+ for i in range(18):
+ self.modules[i % 3 + self.moduleCount - 8 - 3][i // 3] = (
+ not test and ( (bits >> i) & 1) == 1)
+
+ def _setupTypeInfo(self, test, maskPattern):
+
+ data = (self.errorCorrectLevel << 3) | maskPattern
+ bits = QRUtil.getBCHTypeInfo(data)
+
+ # vertical
+ for i in range(15):
+ mod = not test and ( (bits >> i) & 1) == 1
+ if i < 6:
+ self.modules[i][8] = mod
+ elif i < 8:
+ self.modules[i + 1][8] = mod
+ else:
+ self.modules[self.moduleCount - 15 + i][8] = mod
+
+ # horizontal
+ for i in range(15):
+ mod = not test and ( (bits >> i) & 1) == 1
+ if i < 8:
+ self.modules[8][self.moduleCount - i - 1] = mod
+ elif i < 9:
+ self.modules[8][15 - i - 1 + 1] = mod
+ else:
+ self.modules[8][15 - i - 1] = mod
+
+ # fixed
+ self.modules[self.moduleCount - 8][8] = not test
+
+ @staticmethod
+ def _createData(typeNumber, errorCorrectLevel, dataArray):
+
+ rsBlocks = RSBlock.getRSBlocks(typeNumber, errorCorrectLevel)
+
+ buffer = BitBuffer()
+
+ for data in dataArray:
+ buffer.put(data.getMode(), 4)
+ buffer.put(data.getLength(), data.getLengthInBits(typeNumber) )
+ data.write(buffer)
+
+ totalDataCount = sum(rsBlock.getDataCount()
+ for rsBlock in rsBlocks)
+
+ if buffer.getLengthInBits() > totalDataCount * 8:
+ raise Exception('code length overflow. (%s > %s)' %
+ (buffer.getLengthInBits(), totalDataCount * 8) )
+
+ # end code
+ if buffer.getLengthInBits() + 4 <= totalDataCount * 8:
+ buffer.put(0, 4)
+
+ # padding
+ while buffer.getLengthInBits() % 8 != 0:
+ buffer.put(False)
+
+ # padding
+ while True:
+ if buffer.getLengthInBits() >= totalDataCount * 8:
+ break
+ buffer.put(QRCode.PAD0, 8)
+ if buffer.getLengthInBits() >= totalDataCount * 8:
+ break
+ buffer.put(QRCode.PAD1, 8)
+
+ return QRCode._createBytes(buffer, rsBlocks)
+
+ @staticmethod
+ def _createBytes(buffer, rsBlocks):
+
+ offset = 0
+
+ maxDcCount = 0
+ maxEcCount = 0
+
+ dcdata = [None] * len(rsBlocks)
+ ecdata = [None] * len(rsBlocks)
+
+ for r in range(len(rsBlocks) ):
+
+ dcCount = rsBlocks[r].getDataCount()
+ ecCount = rsBlocks[r].getTotalCount() - dcCount
+
+ maxDcCount = max(maxDcCount, dcCount)
+ maxEcCount = max(maxEcCount, ecCount)
+
+ dcdata[r] = [0] * dcCount
+ for i in range(len(dcdata[r] ) ):
+ dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]
+ offset += dcCount
+
+ rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount)
+ rawPoly = Polynomial(dcdata[r], rsPoly.getLength() - 1)
+
+ modPoly = rawPoly.mod(rsPoly)
+ ecdata[r] = [0] * (rsPoly.getLength() - 1)
+ for i in range(len(ecdata[r]) ):
+ modIndex = i + modPoly.getLength() - len(ecdata[r])
+ ecdata[r][i] = modPoly.get(modIndex) if modIndex >= 0 else 0
+
+ totalCodeCount = sum(rsBlock.getTotalCount()
+ for rsBlock in rsBlocks)
+
+ data = [0] * totalCodeCount
+
+ index = 0
+
+ for i in range(maxDcCount):
+ for r in range(len(rsBlocks) ):
+ if i < len(dcdata[r] ):
+ data[index] = dcdata[r][i]
+ index += 1
+
+ for i in range(maxEcCount):
+ for r in range(len(rsBlocks) ):
+ if i < len(ecdata[r] ):
+ data[index] = ecdata[r][i]
+ index += 1
+
+ return data
+
+ @staticmethod
+ def getMinimumQRCode(data, errorCorrectLevel):
+ mode = Mode.MODE_8BIT_BYTE # fixed to 8bit byte
+ qr = QRCode()
+ qr.setErrorCorrectLevel(errorCorrectLevel)
+ qr.addData(data)
+ length = qr.getData(0).getLength()
+ for typeNumber in range(1, 11):
+ if length <= QRUtil.getMaxLength(
+ typeNumber, mode, errorCorrectLevel):
+ qr.setTypeNumber(typeNumber)
+ break
+ qr.make()
+ return qr
+
+class Mode:
+ MODE_NUMBER = 1 << 0
+ MODE_ALPHA_NUM = 1 << 1
+ MODE_8BIT_BYTE = 1 << 2
+ MODE_KANJI = 1 << 3
+
+class ErrorCorrectLevel:
+ L = 1 # 7%
+ M = 0 # 15%
+ Q = 3 # 25%
+ H = 2 # 30%
+
+class MaskPattern:
+ PATTERN000 = 0
+ PATTERN001 = 1
+ PATTERN010 = 2
+ PATTERN011 = 3
+ PATTERN100 = 4
+ PATTERN101 = 5
+ PATTERN110 = 6
+ PATTERN111 = 7
+
+class QRUtil:
+
+ @staticmethod
+ def getPatternPosition(typeNumber):
+ return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]
+
+ PATTERN_POSITION_TABLE = [
+ [],
+ [6, 18],
+ [6, 22],
+ [6, 26],
+ [6, 30],
+ [6, 34],
+ [6, 22, 38],
+ [6, 24, 42],
+ [6, 26, 46],
+ [6, 28, 50],
+ [6, 30, 54],
+ [6, 32, 58],
+ [6, 34, 62],
+ [6, 26, 46, 66],
+ [6, 26, 48, 70],
+ [6, 26, 50, 74],
+ [6, 30, 54, 78],
+ [6, 30, 56, 82],
+ [6, 30, 58, 86],
+ [6, 34, 62, 90],
+ [6, 28, 50, 72, 94],
+ [6, 26, 50, 74, 98],
+ [6, 30, 54, 78, 102],
+ [6, 28, 54, 80, 106],
+ [6, 32, 58, 84, 110],
+ [6, 30, 58, 86, 114],
+ [6, 34, 62, 90, 118],
+ [6, 26, 50, 74, 98, 122],
+ [6, 30, 54, 78, 102, 126],
+ [6, 26, 52, 78, 104, 130],
+ [6, 30, 56, 82, 108, 134],
+ [6, 34, 60, 86, 112, 138],
+ [6, 30, 58, 86, 114, 142],
+ [6, 34, 62, 90, 118, 146],
+ [6, 30, 54, 78, 102, 126, 150],
+ [6, 24, 50, 76, 102, 128, 154],
+ [6, 28, 54, 80, 106, 132, 158],
+ [6, 32, 58, 84, 110, 136, 162],
+ [6, 26, 54, 82, 110, 138, 166],
+ [6, 30, 58, 86, 114, 142, 170]
+ ]
+
+ MAX_LENGTH = [
+ [ [41, 25, 17, 10], [34, 20, 14, 8], [27, 16, 11, 7], [17, 10, 7, 4] ],
+ [ [77, 47, 32, 20], [63, 38, 26, 16], [48, 29, 20, 12], [34, 20, 14, 8] ],
+ [ [127, 77, 53, 32], [101, 61, 42, 26], [77, 47, 32, 20], [58, 35, 24, 15] ],
+ [ [187, 114, 78, 48], [149, 90, 62, 38], [111, 67, 46, 28], [82, 50, 34, 21] ],
+ [ [255, 154, 106, 65], [202, 122, 84, 52], [144, 87, 60, 37], [106, 64, 44, 27] ],
+ [ [322, 195, 134, 82], [255, 154, 106, 65], [178, 108, 74, 45], [139, 84, 58, 36] ],
+ [ [370, 224, 154, 95], [293, 178, 122, 75], [207, 125, 86, 53], [154, 93, 64, 39] ],
+ [ [461, 279, 192, 118], [365, 221, 152, 93], [259, 157, 108, 66], [202, 122, 84, 52] ],
+ [ [552, 335, 230, 141], [432, 262, 180, 111], [312, 189, 130, 80], [235, 143, 98, 60] ],
+ [ [652, 395, 271, 167], [513, 311, 213, 131], [364, 221, 151, 93], [288, 174, 119, 74] ]
+ ]
+
+ @staticmethod
+ def getMaxLength(typeNumber, mode, errorCorrectLevel):
+ t = typeNumber - 1
+ e = {
+ ErrorCorrectLevel.L: 0,
+ ErrorCorrectLevel.M: 1,
+ ErrorCorrectLevel.Q: 2,
+ ErrorCorrectLevel.H: 3
+ }[errorCorrectLevel]
+ m = {
+ Mode.MODE_NUMBER: 0,
+ Mode.MODE_ALPHA_NUM: 1,
+ Mode.MODE_8BIT_BYTE: 2,
+ Mode.MODE_KANJI: 3
+ }[mode]
+ return QRUtil.MAX_LENGTH[t][e][m]
+
+ @staticmethod
+ def getErrorCorrectPolynomial(errorCorrectLength):
+ a = Polynomial([1])
+ for i in range(errorCorrectLength):
+ a = a.multiply(Polynomial([1, QRMath.gexp(i)]) )
+ return a
+
+ @staticmethod
+ def getMaskFunction(maskPattern):
+ return {
+ MaskPattern.PATTERN000:
+ lambda i, j: (i + j) % 2 == 0,
+ MaskPattern.PATTERN001:
+ lambda i, j: i % 2 == 0,
+ MaskPattern.PATTERN010:
+ lambda i, j: j % 3 == 0,
+ MaskPattern.PATTERN011:
+ lambda i, j: (i + j) % 3 == 0,
+ MaskPattern.PATTERN100:
+ lambda i, j: (i // 2 + j // 3) % 2 == 0,
+ MaskPattern.PATTERN101:
+ lambda i, j: (i * j) % 2 + (i * j) % 3 == 0,
+ MaskPattern.PATTERN110:
+ lambda i, j: ( (i * j) % 2 + (i * j) % 3) % 2 == 0,
+ MaskPattern.PATTERN111:
+ lambda i, j: ( (i * j) % 3 + (i + j) % 2) % 2 == 0
+ }[maskPattern]
+
+ @staticmethod
+ def getLostPoint(qrcode):
+
+ moduleCount = qrcode.getModuleCount()
+ lostPoint = 0
+
+ # LEVEL1
+ for row in range(moduleCount):
+ for col in range(moduleCount):
+ sameCount = 0
+ dark = qrcode.isDark(row, col)
+ for r in range(-1, 2):
+ if row + r < 0 or moduleCount <= row + r:
+ continue
+ for c in range(-1, 2):
+ if col + c < 0 or moduleCount <= col + c:
+ continue
+ if r == 0 and c == 0:
+ continue
+ if dark == qrcode.isDark(row + r, col + c):
+ sameCount += 1
+ if sameCount > 5:
+ lostPoint += (3 + sameCount - 5)
+
+ # LEVEL2
+ for row in range(moduleCount - 1):
+ for col in range(moduleCount - 1):
+ count = 0
+ if qrcode.isDark(row, col):
+ count += 1
+ if qrcode.isDark(row + 1, col):
+ count += 1
+ if qrcode.isDark(row, col + 1):
+ count += 1
+ if qrcode.isDark(row + 1, col + 1):
+ count += 1
+ if count == 0 or count == 4:
+ lostPoint += 3
+
+ # LEVEL3
+ for row in range(moduleCount):
+ for col in range(moduleCount - 6):
+ if (qrcode.isDark(row, col)
+ and not qrcode.isDark(row, col + 1)
+ and qrcode.isDark(row, col + 2)
+ and qrcode.isDark(row, col + 3)
+ and qrcode.isDark(row, col + 4)
+ and not qrcode.isDark(row, col + 5)
+ and qrcode.isDark(row, col + 6) ):
+ lostPoint += 40
+
+ for col in range(moduleCount):
+ for row in range(moduleCount - 6):
+ if (qrcode.isDark(row, col)
+ and not qrcode.isDark(row + 1, col)
+ and qrcode.isDark(row + 2, col)
+ and qrcode.isDark(row + 3, col)
+ and qrcode.isDark(row + 4, col)
+ and not qrcode.isDark(row + 5, col)
+ and qrcode.isDark(row + 6, col) ):
+ lostPoint += 40
+
+ # LEVEL4
+ darkCount = 0
+ for col in range(moduleCount):
+ for row in range(moduleCount):
+ if qrcode.isDark(row, col):
+ darkCount += 1
+
+ ratio = abs(100 * darkCount // moduleCount // moduleCount - 50) // 5
+ lostPoint += ratio * 10
+
+ return lostPoint
+
+ G15 = ( (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) |
+ (1 << 2) | (1 << 1) | (1 << 0) )
+ G18 = ( (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) |
+ (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0) )
+ G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1)
+
+ @staticmethod
+ def getBCHTypeInfo(data):
+ d = data << 10
+ while QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0:
+ d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) -
+ QRUtil.getBCHDigit(QRUtil.G15) ) )
+ return ( (data << 10) | d) ^ QRUtil.G15_MASK
+
+ @staticmethod
+ def getBCHTypeNumber(data):
+ d = data << 12
+ while QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0:
+ d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) -
+ QRUtil.getBCHDigit(QRUtil.G18) ) )
+ return (data << 12) | d
+
+ @staticmethod
+ def getBCHDigit(data):
+ digit = 0
+ while data != 0:
+ digit += 1
+ data >>= 1
+ return digit
+
+ @staticmethod
+ def stringToBytes(s):
+ return [ord(c) & 0xff for c in s]
+
+class QR8BitByte:
+
+ def __init__(self, data):
+ self.mode = Mode.MODE_8BIT_BYTE
+ self.data = data
+
+ def getMode(self):
+ return self.mode
+
+ def getData(self):
+ return self.data
+
+ '''
+ def write(self, buffer): raise Exception('not implemented.')
+ def getLength(self): raise Exception('not implemented.')
+ '''
+
+ def write(self, buffer):
+ data = QRUtil.stringToBytes(self.getData() )
+ for d in data:
+ buffer.put(d, 8)
+
+ def getLength(self):
+ return len(QRUtil.stringToBytes(self.getData() ) )
+
+ def getLengthInBits(self, type):
+ if 1 <= type and type < 10: # 1 - 9
+ return {
+ Mode.MODE_NUMBER: 10,
+ Mode.MODE_ALPHA_NUM: 9,
+ Mode.MODE_8BIT_BYTE: 8,
+ Mode.MODE_KANJI: 8
+ }[self.mode]
+
+ elif type < 27: # 10 - 26
+ return {
+ Mode.MODE_NUMBER: 12,
+ Mode.MODE_ALPHA_NUM: 11,
+ Mode.MODE_8BIT_BYTE: 16,
+ Mode.MODE_KANJI: 10
+ }[self.mode]
+
+ elif type < 41: # 27 - 40
+ return {
+ Mode.MODE_NUMBER: 14,
+ Mode.MODE_ALPHA_NUM: 13,
+ Mode.MODE_8BIT_BYTE: 16,
+ Mode.MODE_KANJI: 12
+ }[self.mode]
+
+ else:
+ raise Exception('type:%s' % type)
+
+class QRMath:
+
+ EXP_TABLE = None
+ LOG_TABLE = None
+
+ @staticmethod
+ def _init():
+
+ QRMath.EXP_TABLE = [0] * 256
+ for i in range(256):
+ QRMath.EXP_TABLE[i] = (1 << i if i < 8 else
+ QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^
+ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8])
+
+ QRMath.LOG_TABLE = [0] * 256
+ for i in range(255):
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[i] ] = i
+
+ @staticmethod
+ def glog(n):
+ if n < 1:
+ raise Exception('log(%s)' % n)
+ return QRMath.LOG_TABLE[n]
+
+ @staticmethod
+ def gexp(n):
+ while n < 0:
+ n += 255
+ while n >= 256:
+ n -= 255
+ return QRMath.EXP_TABLE[n]
+
+# initialize statics
+QRMath._init()
+
+class Polynomial:
+
+ def __init__(self, num, shift=0):
+ offset = 0
+ length = len(num)
+ while offset < length and num[offset] == 0:
+ offset += 1
+ self.num = num[offset:] + [0] * shift
+
+ def get(self, index):
+ return self.num[index]
+
+ def getLength(self):
+ return len(self.num)
+
+ def __repr__(self):
+ return ','.join( [str(self.get(i) )
+ for i in range(self.getLength() ) ] )
+
+ def toLogString(self):
+ return ','.join( [str(QRMath.glog(self.get(i) ) )
+ for i in range(self.getLength() ) ] )
+
+ def multiply(self, e):
+ num = [0] * (self.getLength() + e.getLength() - 1)
+ for i in range(self.getLength() ):
+ for j in range(e.getLength() ):
+ num[i + j] ^= QRMath.gexp(QRMath.glog(self.get(i) ) +
+ QRMath.glog(e.get(j) ) )
+ return Polynomial(num)
+
+ def mod(self, e):
+ if self.getLength() - e.getLength() < 0:
+ return self
+ ratio = QRMath.glog(self.get(0) ) - QRMath.glog(e.get(0) )
+ num = self.num[:]
+ for i in range(e.getLength() ):
+ num[i] ^= QRMath.gexp(QRMath.glog(e.get(i) ) + ratio)
+ return Polynomial(num).mod(e)
+
+class RSBlock:
+
+ RS_BLOCK_TABLE = [
+
+ # L
+ # M
+ # Q
+ # H
+
+ # 1
+ [1, 26, 19],
+ [1, 26, 16],
+ [1, 26, 13],
+ [1, 26, 9],
+
+ # 2
+ [1, 44, 34],
+ [1, 44, 28],
+ [1, 44, 22],
+ [1, 44, 16],
+
+ # 3
+ [1, 70, 55],
+ [1, 70, 44],
+ [2, 35, 17],
+ [2, 35, 13],
+
+ # 4
+ [1, 100, 80],
+ [2, 50, 32],
+ [2, 50, 24],
+ [4, 25, 9],
+
+ # 5
+ [1, 134, 108],
+ [2, 67, 43],
+ [2, 33, 15, 2, 34, 16],
+ [2, 33, 11, 2, 34, 12],
+
+ # 6
+ [2, 86, 68],
+ [4, 43, 27],
+ [4, 43, 19],
+ [4, 43, 15],
+
+ # 7
+ [2, 98, 78],
+ [4, 49, 31],
+ [2, 32, 14, 4, 33, 15],
+ [4, 39, 13, 1, 40, 14],
+
+ # 8
+ [2, 121, 97],
+ [2, 60, 38, 2, 61, 39],
+ [4, 40, 18, 2, 41, 19],
+ [4, 40, 14, 2, 41, 15],
+
+ # 9
+ [2, 146, 116],
+ [3, 58, 36, 2, 59, 37],
+ [4, 36, 16, 4, 37, 17],
+ [4, 36, 12, 4, 37, 13],
+
+ # 10
+ [2, 86, 68, 2, 87, 69],
+ [4, 69, 43, 1, 70, 44],
+ [6, 43, 19, 2, 44, 20],
+ [6, 43, 15, 2, 44, 16]
+ ]
+
+ def __init__(self, totalCount, dataCount):
+ self.totalCount = totalCount
+ self.dataCount = dataCount
+
+ def getDataCount(self):
+ return self.dataCount
+
+ def getTotalCount(self):
+ return self.totalCount
+
+ def __repr__(self):
+ return ('(total=%s,data=%s)' % (self.totalCount, self.dataCount) )
+
+ @staticmethod
+ def getRSBlocks(typeNumber, errorCorrectLevel):
+ rsBlock = RSBlock.getRsBlockTable(typeNumber, errorCorrectLevel)
+ length = len(rsBlock) // 3
+ list = []
+ for i in range(length):
+ count = rsBlock[i * 3 + 0]
+ totalCount = rsBlock[i * 3 + 1]
+ dataCount = rsBlock[i * 3 + 2]
+ list += [RSBlock(totalCount, dataCount)] * count
+ return list
+
+ @staticmethod
+ def getRsBlockTable(typeNumber, errorCorrectLevel):
+ return {
+ ErrorCorrectLevel.L:
+ RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 0],
+ ErrorCorrectLevel.M:
+ RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 1],
+ ErrorCorrectLevel.Q:
+ RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 2],
+ ErrorCorrectLevel.H:
+ RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 3]
+ }[errorCorrectLevel]
+
+class BitBuffer:
+
+ def __init__(self, inclements=32):
+ self.inclements = inclements
+ self.buffer = [0] * self.inclements
+ self.length = 0
+
+ def getBuffer(self):
+ return self.buffer
+
+ def getLengthInBits(self):
+ return self.length
+
+ def get(self, index):
+ return ( (self.buffer[index // 8] >> (7 - index % 8) ) & 1) == 1
+
+ def putBit(self, bit):
+ if self.length == len(self.buffer) * 8:
+ self.buffer += [0] * self.inclements
+ if bit:
+ self.buffer[self.length // 8] |= (0x80 >> (self.length % 8) )
+ self.length += 1
+
+ def put(self, num, length):
+ for i in range(length):
+ self.putBit( ( (num >> (length - i - 1) ) & 1) == 1)
+
+ def __repr__(self):
+ return ''.join('1' if self.get(i) else '0'
+ for i in range(self.getLengthInBits() ) )
diff --git a/pcbnew/python/plugins/qrcode_footprint_wizard.py b/pcbnew/python/plugins/qrcode_footprint_wizard.py
new file mode 100644
index 0000000000..f4858ea150
--- /dev/null
+++ b/pcbnew/python/plugins/qrcode_footprint_wizard.py
@@ -0,0 +1,120 @@
+# 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 FootprintWizardBase
+
+# Additional import for QRCode
+# see https://github.com/kazuhikoarase/qrcode-generator/blob/master/python/qrcode.py
+import qrcode
+
+class QRCodeWizard(FootprintWizardBase.FootprintWizard):
+ GetName = lambda self: '2D Barcode QRCode'
+ GetDescription = lambda self: 'QR Code'
+ GetReferencePrefix = lambda self: 'QR***'
+ GetValue = lambda self: self.module.Value().GetText()
+
+ def GenerateParameterList(self):
+ self.AddParam("Barcode", "Pixel Width", self.uMM, 0.5, min_value=0.4)
+ self.AddParam("Barcode", "Border", self.uInteger, 0)
+ self.AddParam("Barcode", "Contents", self.uString, 'Example')
+ self.AddParam("Barcode", "Negative", self.uBool, False)
+ self.AddParam("Barcode", "Use SilkS layer", self.uBool, False)
+ self.AddParam("Barcode", "Use Cu layer", self.uBool, True)
+ self.AddParam("Caption", "Enabled", self.uBool, True)
+ self.AddParam("Caption", "Height", self.uMM, 1.2)
+ self.AddParam("Caption", "Thickness", self.uMM, 0.12)
+
+
+ def CheckParameters(self):
+ self.Barcode = str(self.parameters['Barcode']['Contents'])
+ self.X = self.parameters['Barcode']['Pixel Width']
+ self.negative = self.parameters['Barcode']['Negative']
+ self.UseSilkS = self.parameters['Barcode']['Use SilkS layer']
+ self.UseCu = self.parameters['Barcode']['Use Cu layer']
+ self.border = int(self.parameters['Barcode']['Border'])
+ self.textHeight = int(self.parameters['Caption']['Height'])
+ self.module.Value().SetText(str(self.Barcode) )
+
+ # Build Qrcode
+ self.qr = qrcode.QRCode()
+ self.qr.setTypeNumber(4)
+ # ErrorCorrectLevel: L = 7%, M = 15% Q = 25% H = 30%
+ self.qr.setErrorCorrectLevel(qrcode.ErrorCorrectLevel.M)
+ self.qr.addData(str(self.Barcode))
+ self.qr.make()
+
+ def _drawPixel(self, xposition, yposition):
+ # build a rectangular pad: as a dot
+ pad = pcbnew.D_PAD(self.module)
+ pad.SetSize(pcbnew.wxSize(self.X, self.X))
+ pad.SetShape(pcbnew.PAD_SHAPE_RECT)
+ pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
+ layerset = pcbnew.LSET()
+ if self.UseCu:
+ layerset.AddLayer(pcbnew.F_Cu)
+ layerset.AddLayer(pcbnew.F_Mask)
+ if self.UseSilkS:
+ layerset.AddLayer(pcbnew.F_SilkS)
+ pad.SetLayerSet( layerset )
+ pad.SetPosition(pcbnew.wxPoint(xposition,yposition))
+ pad.SetPadName("1")
+ self.module.Add(pad)
+
+
+ def BuildThisFootprint(self):
+ if self.border >= 0:
+ # Adding border: Create a new array larger than the self.qr.modules
+ sz = self.qr.modules.__len__() + (self.border * 2)
+ arrayToDraw = [ [ 0 for a in range(sz) ] for b in range(sz) ]
+ lineposition = self.border
+ for i in self.qr.modules:
+ columnposition = self.border
+ for j in i:
+ arrayToDraw[lineposition][columnposition] = j
+ columnposition += 1
+ lineposition += 1
+ else:
+ # No border: using array as is
+ arrayToDraw = self.qr.modules
+
+ # used many times...
+ half_number_of_elements = arrayToDraw.__len__() / 2
+
+ # Center position of QrCode
+ yposition = - int(half_number_of_elements * self.X)
+ for line in arrayToDraw:
+ xposition = - int(half_number_of_elements * self.X)
+ for pixel in line:
+ # Trust table for drawing a pixel
+ # Negative is a boolean;
+ # each pixel is a boolean (need to draw of not)
+ # Negative | Pixel | Result
+ # 0 | 0 | 0
+ # 0 | 1 | 1
+ # 1 | 0 | 1
+ # 1 | 1 | 0
+ # => Draw as Xor
+ if self.negative != pixel: # Xor...
+ self._drawPixel(xposition, yposition)
+ xposition += self.X
+ yposition += self.X
+ #int((5 + half_number_of_elements) * self.X))
+ textPosition = int((self.textHeight) + ((1 + half_number_of_elements) * self.X))
+ self.module.Value().SetPosition(pcbnew.wxPoint(0, - textPosition))
+ self.module.Reference().SetPosition(pcbnew.wxPoint(0, textPosition))
+ self.module.Value().SetLayer(pcbnew.F_SilkS)
+
+QRCodeWizard().register()
diff --git a/pcbnew/python/plugins/sdip_wizard.py b/pcbnew/python/plugins/sdip_wizard.py
index fad99f1dbe..ce7afb3937 100644
--- a/pcbnew/python/plugins/sdip_wizard.py
+++ b/pcbnew/python/plugins/sdip_wizard.py
@@ -17,7 +17,7 @@
from __future__ import division
import pcbnew
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
import PadArray as PA
@@ -35,7 +35,7 @@ class RowedGridArray(PA.PadGridArray):
return x+1
-class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
+class RowedFootprint(FootprintWizardBase.FootprintWizard):
pad_count_key = 'pad count'
row_count_key = 'row count'
@@ -50,31 +50,25 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
def GenerateParameterList(self):
# defaults for a DIP package
- self.AddParam("Pads", self.pad_count_key, self.uNatural, 24)
- self.AddParam("Pads", self.row_count_key, self.uNatural, 2)
+ self.AddParam("Pads", self.pad_count_key, self.uInteger, 24)
+ self.AddParam("Pads", self.row_count_key, self.uInteger, 2, min_value=1, max_value=2)
self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False)
self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5)
self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5)
def CheckParameters(self):
- self.CheckParamInt("Pads", '*' + self.row_count_key, min_value=1, max_value=2)
- self.CheckParamInt(
- "Pads", '*' + self.pad_count_key,
- is_multiple_of=self.parameters["Pads"]['*' + self.row_count_key])
-
- # can do this internally to parameter manager?
- self.CheckParamBool("Body", '*' + self.silkscreen_inside_key)
+ self.CheckParam("Pads", self.pad_count_key, multiple=self.parameters['Pads'][self.row_count_key], info='Pads must be multiple of row count')
def BuildThisFootprint(self):
pads = self.parameters["Pads"]
body = self.parameters["Body"]
- num_pads = pads['*' + self.pad_count_key]
+ num_pads = pads[self.pad_count_key]
pad_length = pads[self.pad_length_key]
pad_width = pads[self.pad_width_key]
row_pitch = pads[self.row_spacing_key]
pad_pitch = pads[self.pad_pitch_key]
- num_rows = pads['*' + self.row_count_key]
+ num_rows = pads[self.row_count_key]
pads_per_row = num_pads // num_rows
@@ -96,7 +90,7 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
ssx_offset = -pad_width / 2 - body[self.outline_x_margin_key]
ssy_offset = -pad_length / 2 - body[self.outline_y_margin_key]
- if body['*' + self.silkscreen_inside_key]:
+ if body[self.silkscreen_inside_key]:
ssy_offset *= -1
ssx = -pin1_posX - ssx_offset
@@ -110,8 +104,8 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
sizex = (ssx + cmargin) * 2
sizey = (ssy + cmargin) * 2
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
- sizex = self.PutOnGridMM(sizex, 0.1)
- sizey = self.PutOnGridMM(sizey, 0.1)
+ sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+ sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey)
@@ -156,8 +150,8 @@ class SDIPWizard(RowedFootprint):
def GetValue(self):
pads = self.parameters["Pads"]
- rows = pads['*' + self.row_count_key]
- pad_count = pads['*' + self.pad_count_key]
+ rows = pads[self.row_count_key]
+ pad_count = pads[self.pad_count_key]
row_dist_mil = pcbnew.Iu2Mils(int(self.parameters["Pads"][self.row_spacing_key])) #int(self.parameters["Pads"][self.row_spacing_key] / 2.54 * 100)
pad_shape = ""
@@ -185,7 +179,7 @@ class SDIPWizard(RowedFootprint):
def DrawBox(self, ssx, ssy):
- if self.parameters["Pads"]['*' + self.row_count_key] == 2:
+ if self.parameters["Pads"][self.row_count_key] == 2:
# ----------
# |8 7 6 5 |
@@ -208,7 +202,7 @@ class SDIPWizard(RowedFootprint):
#line between pin1 and pin2
pad_pitch = self.parameters["Pads"][self.pad_pitch_key]
- pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key]
+ pad_cnt = self.parameters["Pads"][self.pad_count_key]
line_x = ( pad_cnt/2 - 1) * pad_pitch
self.draw.VLine(-line_x, -ssy, ssy * 2)
@@ -226,7 +220,7 @@ class SOICWizard(RowedFootprint):
return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard"
def GetValue(self):
- pad_count = self.parameters["Pads"]['*' + self.pad_count_key]
+ pad_count = self.parameters["Pads"][self.pad_count_key]
return "%s-%d" % ("SOIC", pad_count)
def GenerateParameterList(self):
diff --git a/pcbnew/python/plugins/touch_slider_wizard.py b/pcbnew/python/plugins/touch_slider_wizard.py
index e27fffb687..7fc300aed6 100644
--- a/pcbnew/python/plugins/touch_slider_wizard.py
+++ b/pcbnew/python/plugins/touch_slider_wizard.py
@@ -24,10 +24,10 @@
#
from pcbnew import *
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
+import pcbnew
-
-class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
+class TouchSliderWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
"""
@@ -44,16 +44,23 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
return 'Capacitive Touch Slider wizard'
def GetValue(self):
- steps = int(self.parameters["Pads"]["*steps"])
- return "TS"+str(steps)
+ return "TouchSlider-{s}_{x:g}x{y:g}mm".format(
+ s = self.pads['steps'],
+ x = pcbnew.ToMM(self.pads['length']),
+ y = pcbnew.ToMM(self.pads['width'])
+ )
def GenerateParameterList(self):
- self.AddParam("Pads", "steps", self.uNatural, 4)
- self.AddParam("Pads", "bands", self.uNatural, 2)
+ self.AddParam("Pads", "steps", self.uInteger, 4, min_value=2)
+ self.AddParam("Pads", "bands", self.uInteger, 2, min_value=1)
self.AddParam("Pads", "width", self.uMM, 10)
self.AddParam("Pads", "length", self.uMM, 50)
self.AddParam("Pads", "clearance", self.uMM, 1)
+ @property
+ def pads(self):
+ return self.parameters['Pads']
+
# build a rectangular pad
def smdRectPad(self,module,size,pos,name):
pad = D_PAD(module)
@@ -82,18 +89,8 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
# This method checks the parameters provided to wizard and set errors
def CheckParameters(self):
- prms = self.parameters["Pads"]
- steps = prms["*steps"]
- bands = prms["*bands"]
-
- if steps < 1:
- self.parameter_errors["Pads"]["*steps"]="steps must be positive"
- if bands < 1:
- self.parameter_errors["Pads"]["*bands"]="bands must be positive"
-
- touch_width = prms["width"]
- touch_length = prms["length"]
- touch_clearance = prms["clearance"]
+ #TODO - implement custom checks
+ pass
# The start pad is made of a rectangular pad plus a couple of
# triangular pads facing tips on the middle/right of the first
@@ -177,18 +174,18 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
# build the footprint from parameters
# FIX ME: the X and Y position of the footprint can be better.
def BuildThisFootprint(self):
- prm = self.parameters["Pads"]
- steps = int(prm["*steps"])
- bands = int(prm["*bands"])
- touch_width = prm["width"]
- touch_length = prm["length"]
- touch_clearance = prm["clearance"]
+
+ steps = self.pads["steps"]
+ bands = self.pads["bands"]
+ touch_width = self.pads["width"]
+ touch_length = self.pads["length"]
+ touch_clearance = self.pads["clearance"]
step_length = float(touch_length) / float(steps)
t_size = self.GetTextSize()
w_text = self.draw.GetLineThickness()
- ypos = touch_width/(bands*2) + t_size/2 + w_text
+ ypos = touch_width/2 + t_size/2 + w_text
self.draw.Value(0, -ypos, t_size)
ypos += t_size + w_text*2
self.draw.Reference(0, -ypos, t_size)
@@ -197,9 +194,13 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
self.module.SetAttributes(MOD_CMS)
# starting pad
- pos = wxPointMM(0,0)
band_width = touch_width/bands
+ xpos = -0.5 * (steps - 1) * step_length
+ ypos = -0.5 * (bands - 1) * band_width
+
+ pos = wxPointMM(pcbnew.ToMM(xpos), pcbnew.ToMM(ypos))
+
for b in range(bands):
self.AddStrip(pos,steps,band_width,step_length,touch_clearance)
pos += wxPoint(0,band_width)
diff --git a/pcbnew/python/plugins/uss39_barcode.py b/pcbnew/python/plugins/uss39_barcode.py
index ee867758f1..1c3b234637 100644
--- a/pcbnew/python/plugins/uss39_barcode.py
+++ b/pcbnew/python/plugins/uss39_barcode.py
@@ -15,8 +15,7 @@
from __future__ import division
import pcbnew as B
-
-import HelpfulFootprintWizardPlugin
+import FootprintWizardBase
'''
Created on Jan 16, 2015
@@ -49,7 +48,7 @@ class Uss39:
# Reformated text with start and end characters
return reduce(lambda a1, a2: a1 + [0] + a2, [map(int, ptd[c]) for c in ("*%s*" % self.makePrintable(text))])
-class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
+class Uss39Wizard(FootprintWizardBase.FootprintWizard):
GetName = lambda self: 'BARCODE USS-39'
GetDescription = lambda self: 'USS-39 Barcode'
GetReferencePrefix = lambda self: 'BARCODE'
@@ -61,18 +60,20 @@ class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
self.AddParam("Barcode", "Height", self.uMM, 3.0)
self.AddParam("Barcode", "Margin", self.uMM, 2.0)
self.AddParam("Barcode", "Contents", self.uString, 'BARCODE')
+
self.AddParam("Caption", "Enabled", self.uBool, True)
self.AddParam("Caption", "Height", self.uMM, 1.2)
self.AddParam("Caption", "Thickness", self.uMM, 0.12)
def CheckParameters(self):
+
# Reset constants
self.CourtyardLineWidth = B.FromMM(0.05)
# Set bar height to the greater of 6.35mm or 0.15*L
# Set quiet width to 10*X
# User-defined parameters
# Create barcode object
- self.Barcode = Uss39('=' + str(self.parameters['Barcode']['*Contents']))
+ self.Barcode = Uss39('=' + str(self.parameters['Barcode']['Contents']))
self.X = int(self.parameters['Barcode']['Pixel Width'])
self.module.Value().SetText( str(self.Barcode) )
self.C = len(str(self.Barcode))
@@ -146,4 +147,4 @@ class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
self.draw.Circle(0, 0, B.FromMM(0.25))
self.module.Value().SetLayer(B.F_Fab)
-Uss39Wizard().register()
+Uss39Wizard().register()
\ No newline at end of file
diff --git a/pcbnew/python/plugins/zip_wizard.py b/pcbnew/python/plugins/zip_wizard.py
index 4ffa7f1523..e348617797 100644
--- a/pcbnew/python/plugins/zip_wizard.py
+++ b/pcbnew/python/plugins/zip_wizard.py
@@ -17,52 +17,41 @@
from __future__ import division
import pcbnew
-import HelpfulFootprintWizardPlugin as HFPW
+import FootprintWizardBase
import PadArray as PA
-class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
-
- pad_count_key = '#pad count'
- line_count_key = '#line count'
- pad_vertical_size_key = 'pad vertical size'
- pad_horizontal_size_key = 'pad horizontal size'
- line_spacing_key = 'line spacing'
- pad_pitch_key = 'pad pitch'
- drill_size_key = 'drill size'
-
- courtyard_x_margin_key = 'courtyard x margin'
- courtyard_y_margin_key = 'courtyard y margin'
- outline_x_margin_key = 'outline x margin'
- outline_y_margin_key = 'outline y margin'
- silkscreen_inside_key = 'silk screen inside'
+class RowedFootprint(FootprintWizardBase.FootprintWizard):
def GenerateParameterList(self):
# defaults for a ZIP package
- self.AddParam("Pads", self.pad_count_key, self.uNatural, 24)
- self.AddParam("Pads", self.line_count_key, self.uNatural, 2)
- self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False)
- self.AddParam("Body", self.courtyard_x_margin_key, self.uMM, 1)
- self.AddParam("Body", self.courtyard_y_margin_key, self.uMM, 1)
+ self.AddParam("Pads", "pad count", self.uInteger, 24)
+ self.AddParam("Pads", "line count", self.uInteger, 2)
+
+ self.AddParam("Body", "silkscreen inside", self.uBool, False)
+ self.AddParam("Body", "courtyard margin", self.uMM, 0.5, min_value=0.2)
+
+ @property
+ def pads(self):
+ return self.parameters['Pads']
+
+ @property
+ def body(self):
+ return self.parameters['Body']
def CheckParameters(self):
- self.CheckParamInt("Pads", '*' + self.pad_count_key)
- self.CheckParamInt("Pads", '*' + self.line_count_key)
-
- # can do this internally to parameter manager?
- self.CheckParamBool("Body", '*' + self.silkscreen_inside_key)
+ # TODO - implement custom checks
+ pass
def BuildThisFootprint(self):
- pads = self.parameters["Pads"]
- body = self.parameters["Body"]
- pad_count = pads['*' + self.pad_count_key]
- pad_Vsize = pads[self.pad_vertical_size_key]
- pad_Hsize = pads[self.pad_horizontal_size_key]
- line_pitch = pads[self.line_spacing_key]
- pad_pitch = pads[self.pad_pitch_key]
- line_count = pads['*' + self.line_count_key]
+ pad_count = self.pads['pad count']
+ pad_Vsize = self.pads['pad height']
+ pad_Hsize = self.pads['pad width']
+ line_pitch = self.pads['line spacing']
+ pad_pitch = self.pads['pitch']
+ line_count = self.pads['line count']
if line_count == 1:
singleline = True
@@ -74,12 +63,12 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
array = PA.PadZGridArray(pad, pad_count, line_count, line_pitch, pad_pitch)
array.AddPadsToModule(self.draw)
-
+
# draw the Silk Screen
pads_per_line = pad_count // line_count
row_length = pad_pitch * (pads_per_line - 1) # fenceposts
- ssx_offset = pad_Hsize / 2 + body[self.outline_x_margin_key]
- ssy_offset = pad_Vsize / 2 + body[self.outline_y_margin_key]
+ ssx_offset = pad_Hsize / 2 + self.body['outline x margin']
+ ssy_offset = pad_Vsize / 2 + self.body['outline y margin']
pin1posX = pad_pitch * (pad_count - 1) / 2
pin1posY = line_pitch * (line_count - 1) / 2
@@ -91,7 +80,7 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
# body inside pads is possible only for 2 rows.
# for other values, there is no room
linew = self.draw.GetLineThickness()
- if body['*'+self.silkscreen_inside_key] and line_count == 2:
+ if self.body['silkscreen inside'] and line_count == 2:
cornery = pin1posY - ssy_offset
if cornery < linew:
cornery = linew
@@ -99,15 +88,15 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
self.DrawBox(leftx*2, cornery*2)
# Courtyard
- cmarginx = body[self.courtyard_x_margin_key]
- cmarginy = body[self.courtyard_y_margin_key]
+ cmarginx = self.body['courtyard margin']
+ cmarginy = cmarginx
self.draw.SetLayer(pcbnew.F_CrtYd)
thick = self.draw.GetLineThickness()
sizex = (pin1posX + cmarginx) * 2 + pad_Hsize + thick
sizey = (pin1posY + cmarginy) * 2 + pad_Vsize + thick
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
- sizex = self.PutOnGridMM(sizex, 0.1)
- sizey = self.PutOnGridMM(sizey, 0.1)
+ sizex = pcbnew.PutOnGridMM(sizex, 0.1)
+ sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey)
@@ -152,17 +141,17 @@ class ZIPWizard(RowedFootprint):
def GenerateParameterList(self):
RowedFootprint.GenerateParameterList(self)
- self.AddParam("Pads", self.pad_pitch_key, self.uMM, 1.27)
- self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 1.2)
- self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 2)
- self.AddParam("Pads", self.line_spacing_key, self.uMM, 2.54)
- self.AddParam("Pads", self.drill_size_key, self.uMM, 0.8)
- self.AddParam("Body", self.outline_x_margin_key, self.uMM, 1)
- self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5)
+ self.AddParam("Pads", "pitch", self.uMM, 1.27)
+ self.AddParam("Pads", "pad width", self.uMM, 1.2)
+ self.AddParam("Pads", "pad height", self.uMM, 2)
+ self.AddParam("Pads", "line spacing", self.uMM, 2.54)
+ self.AddParam("Pads", "drill size", self.uMM, 0.8)
+ self.AddParam("Body", 'outline x margin', self.uMM, 1)
+ self.AddParam("Body", 'outline y margin', self.uMM, 0.5)
def GetValue(self):
- rows = self.parameters["Pads"]['*' + self.line_count_key]
- pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key]
+ rows = self.pads['line count']
+ pad_cnt = self.pads['pad count']
if rows == 1:
name = "SIP"
@@ -174,9 +163,9 @@ class ZIPWizard(RowedFootprint):
return "%s-%d" % (name, pad_cnt)
def GetPad(self):
- pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key]
- pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key]
- drill = self.parameters["Pads"][self.drill_size_key]
+ pad_Vsize = self.pads['pad height']
+ pad_Hsize = self.pads['pad width']
+ drill = self.pads['drill size']
return PA.PadMaker(self.module).THPad(
pad_Vsize, pad_Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL)
@@ -192,23 +181,23 @@ class ZOICWizard(RowedFootprint):
return "ZOIC, etc, Footprint Wizard"
def GetValue(self):
- return "%s-%d" % ("ZOIC", self.parameters["Pads"]['*' + self.pad_count_key])
+ return "%s-%d" % ("ZOIC-", self.pads['pad count'])
def GenerateParameterList(self):
RowedFootprint.GenerateParameterList(self)
#and override some of them
- self.AddParam("Pads", self.pad_pitch_key, self.uMM, 0.6)
- self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 0.6)
- self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 1.8)
- self.AddParam("Pads", self.line_spacing_key, self.uMM, 5.2)
+ self.AddParam("Pads", "pitch", self.uMM, 0.6)
+ self.AddParam("Pads", "pad width", self.uMM, 0.6)
+ self.AddParam("Pads", "pad height", self.uMM, 1.8)
+ self.AddParam("Pads", "line spacing", self.uMM, 5.2)
- self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5)
- self.AddParam("Body", self.outline_y_margin_key, self.uMM, 1)
+ self.AddParam("Body", "outline x margin", self.uMM, 0.5)
+ self.AddParam("Body", "outline y margin", self.uMM, 1)
def GetPad(self):
- pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key]
- pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key]
+ pad_Vsize = self.pads['pad height']
+ pad_Hsize = self.pads['pad width']
return PA.PadMaker(self.module).SMDPad(
pad_Vsize, pad_Hsize, shape=pcbnew.PAD_SHAPE_RECT)
diff --git a/pcbnew/swig/pcbnew_footprint_wizards.cpp b/pcbnew/swig/pcbnew_footprint_wizards.cpp
index e0f3854788..3070b7a8df 100644
--- a/pcbnew/swig/pcbnew_footprint_wizards.cpp
+++ b/pcbnew/swig/pcbnew_footprint_wizards.cpp
@@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo
- * Copyright (C) 2013 KiCad Developers, see CHANGELOG.TXT for contributors.
+ * Copyright (C) 2016 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
@@ -237,24 +237,9 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterTypes( int aPage )
PyObject* arglist = Py_BuildValue( "(i)", aPage );
- ret = CallRetArrayStrMethod( "GetParameterNames", arglist );
+ ret = CallRetArrayStrMethod( "GetParameterTypes", arglist );
Py_DECREF( arglist );
- for( unsigned i = 0; i 100%)
+uString = "string" # Raw string
+
+uNumeric = [uMM, uMils, uFloat, uInteger, uDegrees, uRadians, uPercent] # List of numeric types
+uUnits = [uMM, uMils, uFloat, uInteger, uBool, uDegrees, uRadians, uPercent, uString] # List of allowable types
+
+class FootprintWizardParameter(object):
+ _true = ['true','t','y','yes','on','1',1,]
+ _false = ['false','f','n','no','off','0',0,'',None]
+
+ _bools = _true + _false
+
+ def __init__(self, page, name, units, default, **kwarg):
+ self.page = page
+ self.name = name
+ self.hint = kwarg.get('hint','') # Parameter hint (shown as mouse-over text)
+ self.designator = kwarg.get('designator',' ') # Parameter designator such as "e, D, p" (etc)
+
+ if units.lower() in uUnits:
+ self.units = units.lower()
+ elif units.lower() == 'percent':
+ self.units = uPercent
+ elif type(units) in [list, tuple]: # Convert a list of options into a single string
+ self.units = ",".join([str(el).strip() for el in units])
+ else:
+ self.units = units
+
+ self.multiple = int(kwarg.get('multiple',1)) # Check integer values are multiples of this number
+ self.min_value = kwarg.get('min_value',None) # Check numeric values are above or equal to this number
+ self.max_value = kwarg.get('max_value',None) # Check numeric values are below or equal to this number
+
+ self.SetValue(default)
+ self.default = self.raw_value # Save value as default
+
+ def ClearErrors(self):
+ self.error_list = []
+
+ def AddError(self, err, info=None):
+
+ if err in self.error_list: # prevent duplicate error messages
+ return
+ if info is not None:
+ err = err + " (" + str(info) + ")"
+
+ self.error_list.append(err)
+
+ def Check(self, min_value=None, max_value=None, multiple=None, info=None):
+
+ if min_value is None:
+ min_value = self.min_value
+ if max_value is None:
+ max_value = self.max_value
+ if multiple is None:
+ multiple = self.multiple
+
+ if self.units not in uUnits and ',' not in self.units: # Allow either valid units or a list of strings
+ self.AddError("type '{t}' unknown".format(t=self.units),info)
+ self.AddError("Allowable types: " + str(self.units),info)
+
+ if self.units in uNumeric:
+ try:
+ to_num = float(self.raw_value)
+
+ if min_value is not None: # Check minimum value if it is present
+ if to_num < min_value:
+ self.AddError("value '{v}' is below minimum ({m})".format(v=self.raw_value,m=min_value),info)
+
+ if max_value is not None: # Check maximum value if it is present
+ if to_num > max_value:
+ self.AddError("value '{v}' is above maximum ({m})".format(v=self.raw_value,m=max_value),info)
+
+ except:
+ self.AddError("value '{v}' is not of type '{t}'".format(v = self.raw_value, t=self.units),info)
+
+ if self.units == uInteger: # Perform integer specific checks
+ try:
+ to_int = int(self.raw_value)
+
+ if multiple is not None and multiple > 1:
+ if (to_int % multiple) > 0:
+ self.AddError("value '{v}' is not a multiple of {m}".format(v=self.raw_value,m=multiple),info)
+ except:
+ self.AddError("value {'v}' is not an integer".format(v=self.raw_value),info)
+
+ if self.units == uBool: # Check that the value is of a correct boolean format
+ if self.raw_value in [True,False] or str(self.raw_value).lower() in self._bools:
+ pass
+ else:
+ self.AddError("value '{v}' is not a boolean value".format(v = self.raw_value),info)
+
+ @property
+ def value(self): # Return the current value, converted to appropriate units (from string representation) if required
+ v = str(self.raw_value) # Enforce string type for known starting point
+
+ if self.units == uInteger: # Integer values
+ return int(v)
+ elif self.units in uNumeric: # Any values that use floating points
+ v = v.replace(",",".") # Replace "," separators with "."
+ v = float(v)
+
+ if self.units == uMM: # Convert from millimetres to nanometres
+ return FromMM(v)
+
+ elif self.units == uMils: # Convert from mils to nanometres
+ return FromMils(v)
+
+ else: # Any other floating-point values
+ return v
+
+ elif self.units == uBool:
+ if v.lower() in self._true:
+ return True
+ else:
+ return False
+ else:
+ return v
+
+ def DefaultValue(self): # Reset the value of the parameter to its default
+ self.raw_value = str(self.default)
+
+ def SetValue(self, new_value): # Update the value
+ new_value = str(new_value)
+
+ if len(new_value.strip()) == 0:
+ if not self.units in [uString, uBool]:
+ return # Ignore empty values unless for strings or bools
+
+ if self.units == uBool: # Enforce the same boolean representation as is used in KiCad
+ new_value = "1" if new_value.lower() in self._true else "0"
+ elif self.units in uNumeric:
+ new_value = new_value.replace(",", ".") # Enforce decimal point separators
+ elif ',' in self.units: # Select from a list of values
+ if new_value not in self.units.split(','):
+ new_value = self.units.split(',')[0]
+
+ self.raw_value = new_value
+
+ def __str__(self): # pretty-print the parameter
+
+ s = self.name + ": " + str(self.raw_value)
+
+ if self.units in [uMM, uMils, uPercent, uRadians, uDegrees]:
+ s += self.units
+ elif self.units == uBool: # Special case for Boolean values
+ s = self.name + ": {b}".format(b = "True" if self.value else "False")
+ elif self.units == uString:
+ s = self.name + ": '" + self.raw_value + "'"
+
+ return s
+
+class FootprintWizardPlugin(KiCadPlugin, object):
def __init__(self):
KiCadPlugin.__init__(self)
self.defaults()
def defaults(self):
self.module = None
- self.parameters = {}
- self.parameter_errors={}
- self.name = "Undefined Footprint Wizard plugin"
- self.description = ""
+ self.params = [] # List of added parameters that observes addition order
+
+ self.name = "KiCad FP Wizard"
+ self.description = "Undefined Footprint Wizard plugin"
self.image = ""
self.buildmessages = ""
- def GetName(self):
+ def AddParam(self, page, name, unit, default, **kwarg):
+
+ if self.GetParam(page,name) is not None: # Param already exists!
+ return
+
+ param = FootprintWizardParameter(page, name, unit, default, **kwarg) # Create a new parameter
+ self.params.append(param)
+
+ @property
+ def parameters(self): # This is a helper function that returns a nested (unordered) dict of the VALUES of parameters
+ pages = {} # Page dict
+ for p in self.params:
+ if p.page not in pages:
+ pages[p.page] = {}
+
+ pages[p.page][p.name] = p.value # Return the 'converted' value (convert from string to actual useful units)
+
+ return pages
+
+ @property
+ def values(self): # Same as above
+ return self.parameters
+
+ def ResetWizard(self): # Reset all parameters to default values
+ for p in self.params:
+ p.DefaultValue()
+
+ def GetName(self): # Return the name of this wizard
return self.name
- def GetImage(self):
+ def GetImage(self): # Return the filename of the preview image associated with this wizard
return self.image
- def GetDescription(self):
+ def GetDescription(self): # Return the description text
return self.description
+ def GetValue(self):
+ raise NotImplementedError
- def GetNumParameterPages(self):
- return len(self.parameters)
+ def GetReferencePrefix(self):
+ return "REF" # Default reference prefix for any footprint
- def GetParameterPageName(self,page_n):
- return self.page_order[page_n]
+ def GetParam(self, page, name): # Grab a parameter
+ for p in self.params:
+ if p.page == page and p.name == name:
+ return p
- def GetParameterNames(self,page_n):
- name = self.GetParameterPageName(page_n)
- return self.parameter_order[name]
+ return None
- def GetParameterValues(self,page_n):
- name = self.GetParameterPageName(page_n)
- names = self.GetParameterNames(page_n)
- values = [self.parameters[name][n] for n in names]
- return map(lambda x: str(x), values) # list elements as strings
+ def CheckParam(self, page, name, **kwarg):
+ self.GetParam(page,name).Check(**kwarg)
- def GetParameterErrors(self,page_n):
- self.CheckParameters()
- name = self.GetParameterPageName(page_n)
- names = self.GetParameterNames(page_n)
- values = [self.parameter_errors[name][n] for n in names]
- return map(lambda x: str(x), values) # list elements as strings
+ def AnyErrors(self):
+ return any([len(p.error_list) > 0 for p in self.params])
- def CheckParameters(self):
- return ""
+ @property
+ def pages(self): # Return an (ordered) list of the available page names
+ page_list = []
+ for p in self.params:
+ if p.page not in page_list:
+ page_list.append(p.page)
- def ConvertValue(self,v):
- try:
- v = float(v)
- except:
- pass
- if type(v) is float:
- if ceil(v) == floor(v):
- v = int(v)
- return v
+ return page_list
+ def GetNumParameterPages(self): # Return the number of parameter pages
+ return len(self.pages)
- def SetParameterValues(self,page_n,values):
- name = self.GetParameterPageName(page_n)
- keys = self.GetParameterNames(page_n)
- for n, key in enumerate(keys):
- val = self.ConvertValue(values[n])
- self.parameters[name][key] = val
+ def GetParameterPageName(self,page_n): # Return the name of a page at a given index
+ return self.pages[page_n]
+ def GetParametersByPageName(self, page_name): # Return a list of parameters on a given page
+ params = []
- def ClearErrors(self):
- errs={}
+ for p in self.params:
+ if p.page == page_name:
+ params.append(p)
- for page in self.parameters.keys():
- page_dict = self.parameters[page]
- page_params = {}
- for param in page_dict.keys():
- page_params[param]=""
+ return params
- errs[page]=page_params
+ def GetParametersByPageIndex(self, page_index): # Return an ordered list of parameters on a given page
+ return self.GetParametersByPageName(self.GetParameterPageName(page_index))
- self.parameter_errors = errs
+ def GetParameterDesignators(self, page_index): # Return a list of designators associated with a given page
+ params = self.GetParametersByPageIndex(page_index)
+ return [p.designator for p in params]
+ def GetParameterNames(self,page_index): # Return the list of names associated with a given page
+ params = self.GetParametersByPageIndex(page_index)
+ return [p.name for p in params]
+
+ def GetParameterValues(self,page_index): # Return the list of values associated with a given page
+ params = self.GetParametersByPageIndex(page_index)
+ return [str(p.raw_value) for p in params]
+
+ def GetParameterErrors(self,page_index): # Return list of errors associated with a given page
+ params = self.GetParametersByPageIndex(page_index)
+ return [str("\n".join(p.error_list)) for p in params]
+
+ def GetParameterTypes(self, page_index): # Return list of units associated with a given page
+ params = self.GetParametersByPageIndex(page_index)
+ return [str(p.units) for p in params]
+
+ def GetParameterHints(self, page_index): # Return a list of units associated with a given page
+ params = self.GetParametersByPageIndex(page_index)
+ return [str(p.hint) for p in params]
+
+ def GetParameterDesignators(self, page_index): # Return a list of designators associated with a given page
+ params = self.GetParametersByPageIndex(page_index)
+ return [str(p.designator) for p in params]
+
+ def SetParameterValues(self, page_index, list_of_values): # Update values on a given page
+
+ params = self.GetParametersByPageIndex(page_index)
+
+ for i, param in enumerate(params):
+ if i >= len(list_of_values):
+ break
+ param.SetValue(list_of_values[i])
def GetFootprint( self ):
self.BuildFootprint()
@@ -297,17 +499,30 @@ class FootprintWizardPlugin(KiCadPlugin):
return self.buildmessages
def Show(self):
- print "Footprint Wizard Name: ",self.GetName()
- print "Footprint Wizard Description: ",self.GetDescription()
+ text = "Footprint Wizard Name: {name}\n".format(name=self.GetName())
+ text += "Footprint Wizard Description: {desc}\n".format(desc=self.GetDescription())
+
n_pages = self.GetNumParameterPages()
- print " setup pages: ",n_pages
- for page in range(0,n_pages):
- name = self.GetParameterPageName(page)
- values = self.GetParameterValues(page)
- names = self.GetParameterNames(page)
- print "page %d) %s"%(page,name)
- for n in range (0,len(values)):
- print "\t%s\t:\t%s"%(names[n],values[n])
+
+ text += "Pages: {n}\n".format(n=n_pages)
+
+ for i in range(n_pages):
+ name = self.GetParameterPageName(i)
+
+ params = self.GetParametersByPageName(name)
+
+ text += "{name}\n".format(name=name)
+
+ for j in range(len(params)):
+ text += ("\t{param}{err}\n".format(
+ param = str(params[j]),
+ err = ' *' if len(params[j].error_list) > 0 else ''
+ ))
+
+ if self.AnyErrors():
+ text += " * Errors exist for these parameters"
+
+ return text
class ActionPlugin(KiCadPlugin):
def __init__(self):