kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #26493
[PATCH] Footprint Wizards Update
Hi All,
I have improved upon the footprint wizards patch, taking suggestions into
account.
patch file attached.
There is an in-depth discussion of the new format:
https://github.com/KiCad/Footprint_Wizards/wiki
In particular, instructions on how to update old wizards to the new format:
https://github.com/KiCad/Footprint_Wizards/wiki/Updating-Older-Wizards
Cheers,
Oliver
From 24eb825a1ab85536b309f6b21cac4851ee7142b8 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@xxxxxxxxx>
Date: Thu, 15 Sep 2016 22:41:20 +1000
Subject: [PATCH] Improvements for footprint wizards
Added new parameter types (various)
Added ability to reset wizard parameters back to default values
Added checkbox rendering for boolean values
Added listbox selection for "multiple choice" parameters
Added parameter designators (optional) which replace the row numbers
Numerical validators for integer and float numbers
Allows locale-specific comma separators for decimals
Parameter grid resizes to fit available space
Refactored python footprint wizard code
Updated all default wizard scripts for compatibility with new wizard functionality
---
pcbnew/class_footprint_wizard.h | 31 +
pcbnew/dialogs/dialog_footprint_wizard_list.cpp | 23 +-
pcbnew/footprint_wizard.cpp | 47 +-
pcbnew/footprint_wizard_frame.cpp | 168 +++--
pcbnew/footprint_wizard_frame.h | 23 +-
pcbnew/pcbnew.cpp | 16 +-
pcbnew/pcbnew_id.h | 2 +
.../plugins/FPC_(SMD_type)_footprintwizard.py | 159 -----
pcbnew/python/plugins/FPC_wizard.py | 157 +++++
pcbnew/python/plugins/FootprintWizardBase.py | 683 +++++++++++++++++++++
.../python/plugins/FootprintWizardDrawingAids.py | 532 ----------------
.../python/plugins/HelpfulFootprintWizardPlugin.py | 348 -----------
pcbnew/python/plugins/bga_wizard.py | 120 +++-
pcbnew/python/plugins/circular_pad_array_wizard.py | 86 ++-
pcbnew/python/plugins/qfn_wizard.py | 222 ++++---
pcbnew/python/plugins/qfp_wizard.py | 105 ++--
pcbnew/python/plugins/sdip_wizard.py | 38 +-
pcbnew/python/plugins/touch_slider_wizard.py | 57 +-
pcbnew/python/plugins/uss39_barcode.py | 11 +-
pcbnew/python/plugins/zip_wizard.py | 115 ++--
pcbnew/swig/pcbnew_footprint_wizards.cpp | 47 +-
pcbnew/swig/pcbnew_footprint_wizards.h | 3 +
pcbnew/swig/python_scripting.cpp | 28 +
pcbnew/swig/python_scripting.h | 3 +
pcbnew/swig/units.i | 12 +-
scripting/kicadplugins.i | 364 ++++++++---
26 files changed, 1868 insertions(+), 1532 deletions(-)
delete mode 100644 pcbnew/python/plugins/FPC_(SMD_type)_footprintwizard.py
create mode 100644 pcbnew/python/plugins/FPC_wizard.py
create mode 100644 pcbnew/python/plugins/FootprintWizardBase.py
delete mode 100644 pcbnew/python/plugins/FootprintWizardDrawingAids.py
delete mode 100644 pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py
diff --git a/pcbnew/class_footprint_wizard.h b/pcbnew/class_footprint_wizard.h
index 20fc1d4..2ffc658 100644
--- a/pcbnew/class_footprint_wizard.h
+++ b/pcbnew/class_footprint_wizard.h
@@ -33,6 +33,17 @@
#include <vector>
#include <wxPcbStruct.h>
+// 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
@@ -105,6 +116,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
* @param aValues are the values we want to set into the parameters
@@ -113,6 +138,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
* @return PCB module built from the parameters given to the class
diff --git a/pcbnew/dialogs/dialog_footprint_wizard_list.cpp b/pcbnew/dialogs/dialog_footprint_wizard_list.cpp
index 8c49d6b..5bc650d 100644
--- a/pcbnew/dialogs/dialog_footprint_wizard_list.cpp
+++ b/pcbnew/dialogs/dialog_footprint_wizard_list.cpp
@@ -33,9 +33,14 @@
#include <kiface_i.h>
#include <dialog_footprint_wizard_list.h>
#include <class_footprint_wizard.h>
+#include <python_scripting.h>
+
+enum FPGeneratorRowNames
+{
+ FP_GEN_ROW_NAME = 0,
+ FP_GEN_ROW_DESCR,
+};
-#define ROW_NAME 0
-#define ROW_DESCR 1
#define FPWIZARTDLIST_HEIGHT_KEY wxT( "FpWizardListHeight" )
#define FPWIZARTDLIST_WIDTH_KEY wxT( "FpWizardListWidth" )
@@ -64,11 +69,21 @@ DIALOG_FOOTPRINT_WIZARD_LIST::DIALOG_FOOTPRINT_WIZARD_LIST( wxWindow* aParent )
wxString description = wizard->GetDescription();
wxString image = wizard->GetImage();
- m_footprintGeneratorsGrid->SetCellValue( i, ROW_NAME, name );
- m_footprintGeneratorsGrid->SetCellValue( i, ROW_DESCR, description );
+ m_footprintGeneratorsGrid->SetCellValue( i, FP_GEN_ROW_NAME, name );
+ m_footprintGeneratorsGrid->SetCellValue( i, FP_GEN_ROW_DESCR, description );
}
+ m_footprintGeneratorsGrid->AutoSizeColumns();
+
+ // Auto-expand the description column
+ int width = m_footprintGeneratorsGrid->GetClientSize().GetWidth() -
+ m_footprintGeneratorsGrid->GetRowLabelSize() -
+ m_footprintGeneratorsGrid->GetColSize( FP_GEN_ROW_NAME );
+
+ if ( width > m_footprintGeneratorsGrid->GetColMinimalAcceptableWidth() )
+ m_footprintGeneratorsGrid->SetColSize( FP_GEN_ROW_DESCR, width );
+
// Select the first row
m_footprintGeneratorsGrid->ClearSelection();
m_footprintGeneratorsGrid->SelectRow( 0, false );
diff --git a/pcbnew/footprint_wizard.cpp b/pcbnew/footprint_wizard.cpp
index 6e9b17e..02b379c 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 6112fa6..327de48 100644
--- a/pcbnew/footprint_wizard_frame.cpp
+++ b/pcbnew/footprint_wizard_frame.cpp
@@ -44,6 +44,8 @@
#include "footprint_wizard_frame.h"
#include <footprint_info.h>
#include <wx/grid.h>
+#include <wx/tokenzr.h>
+#include <wx/numformatter.h>
#include <hotkeys.h>
#include <wildcards_and_files_ext.h>
@@ -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" ) )
- {
- LOCALE_IO toggle;
+ // 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 );
- // We are handling internal units, so convert them to the current
- // system selected units and store into value.
- double dValue;
+ // Set the editor type of the
- value.ToDouble( &dValue );
+ // Boolean parameters can be displayed using a checkbox
+ if ( units == WIZARD_PARAM_UNITS_BOOL )
+ {
+ wxGridCellBoolEditor *boolEditor = new wxGridCellBoolEditor;
+ boolEditor->UseStringValues( "1", "0" );
+ m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, boolEditor );
- dValue = To_User_Unit( g_UserUnit, dValue );
+ m_parameterGrid->SetCellRenderer( i, WIZ_COL_VALUE, new wxGridCellBoolRenderer );
+ }
+ // 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;
- if( g_UserUnit==INCHES ) // we convert inches into mils for more detail
- {
- dValue = dValue * 1000.0;
- units = wxT( "mils" );
- }
- else if( g_UserUnit==MILLIMETRES )
+ while ( tokenizer.HasMoreTokens() )
{
- units = wxT( "mm" );
+ options.Add( tokenizer.GetNextToken() );
}
- // 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->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellChoiceEditor( options ) );
+
+ units = wxT( "" );
}
- else if( ptList[i]==wxT( "UNITS" ) ) // 1,2,3,4,5 ... N
+ // Integer parameters
+ else if ( units == WIZARD_PARAM_UNITS_INTEGER )
{
- units = wxT( "" );
+ 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 );
+
+ // Convert separators to the locale-specific character
+ value.Replace( ",", wxNumberFormatter::GetDecimalSeparator() );
+ value.Replace( ".", wxNumberFormatter::GetDecimalSeparator() );
}
- m_parameterGrid->SetCellValue( i, m_columnPrmValue, value );
- m_parameterGrid->SetCellValue( i, m_columnPrmUnit, units );
- m_parameterGrid->SetReadOnly( i, m_columnPrmUnit );
+
+ // 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 97fabe1..acd3900 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
@@ -74,6 +76,8 @@ public:
private:
+ void OnGridSize( wxSizeEvent& aSizeEvent );
+
void OnSize( wxSizeEvent& event ) override;
/**
@@ -104,6 +108,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 8979cb2..a45ede3 100644
--- a/pcbnew/pcbnew.cpp
+++ b/pcbnew/pcbnew.cpp
@@ -201,7 +201,6 @@ PGM_BASE& Pgm()
#if defined( KICAD_SCRIPTING )
static bool scriptingSetup()
{
- wxString path_frag;
#if defined( __MINGW32__ )
// force python environment under Windows:
@@ -224,6 +223,7 @@ static bool scriptingSetup()
ppath << kipython << wxT( "/pylib;" );
ppath << kipython << wxT( "/lib;" );
ppath << kipython << wxT( "/dll" );
+
wxSetEnv( wxT( "PYTHONPATH" ), ppath );
// DBG( std::cout << "set PYTHONPATH to " << TO_UTF8( ppath ) << "\n"; )
@@ -239,14 +239,6 @@ static bool scriptingSetup()
}
}
- // 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
@@ -290,13 +282,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( wxT( "pcbnewInitPythonScripting() failed." ) );
return false;
diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h
index 92201c4..6690edf 100644
--- a/pcbnew/pcbnew_id.h
+++ b/pcbnew/pcbnew_id.h
@@ -390,6 +390,8 @@ enum pcbnew_ids
ID_FOOTPRINT_WIZARD_PAGES_WINDOW,
ID_FOOTPRINT_WIZARD_PARAMETERS_WINDOW,
ID_FOOTPRINT_WIZARD_SELECT_WIZARD,
+ ID_FOOTPRINT_WIZARD_DOWNLOAD_WIZARDS,
+ 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_(SMD_type)_footprintwizard.py
deleted file mode 100644
index bf77e93..0000000
--- a/pcbnew/python/plugins/FPC_(SMD_type)_footprintwizard.py
+++ /dev/null
@@ -1,159 +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.
-#
-
-from __future__ import division
-import pcbnew
-
-import HelpfulFootprintWizardPlugin as HFPW
-
-
-class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
-
- def GetName(self):
- return "FPC (SMT connector)"
-
- def GetDescription(self):
- return "FPC (SMT connector) Footprint Wizard"
-
- def GetValue(self):
- pins = self.parameters["Pads"]["*n"]
- return "FPC_%d" % pins
-
- def GenerateParameterList(self):
- self.AddParam( "Pads", "n", self.uNatural, 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)
- self.AddParam( "Shield", "shield_to_pad", self.uMM, 1.6 )
- self.AddParam( "Shield", "from_top", self.uMM, 1.3 )
- self.AddParam( "Shield", "width", self.uMM, 1.5 )
- self.AddParam( "Shield", "height", self.uMM, 2 )
-
-
- # build a rectangular pad
- def smdRectPad(self,module,size,pos,name):
- pad = pcbnew.D_PAD(module)
- pad.SetSize(size)
- pad.SetShape(pcbnew.PAD_SHAPE_RECT)
- pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
- pad.SetLayerSet( pad.SMDMask() )
- pad.SetPos0(pos)
- pad.SetPosition(pos)
- pad.SetPadName(name)
- return pad
-
- def CheckParameters(self):
- p = self.parameters
- self.CheckParamInt( "Pads", "*n" ) # not internal units preceded by "*"
-
-
- def BuildThisFootprint(self):
- p = self.parameters
- pad_count = int(p["Pads"]["*n"])
- pad_width = p["Pads"]["width"]
- pad_height = p["Pads"]["height"]
- pad_pitch = p["Pads"]["pitch"]
- shl_width = p["Shield"]["width"]
- shl_height = p["Shield"]["height"]
- shl_to_pad = p["Shield"]["shield_to_pad"]
- shl_from_top = p["Shield"]["from_top"]
-
- offsetX = pad_pitch * ( pad_count-1 ) / 2
- size_pad = pcbnew.wxSize( pad_width, pad_height )
- size_shld = pcbnew.wxSize(shl_width, shl_height)
- size_text = self.GetTextSize() # IPC nominal
-
- # Gives a position and size to ref and value texts:
- textposy = pad_height/2 + pcbnew.FromMM(1) + self.GetTextThickness()
- self.draw.Reference( 0, textposy, size_text )
-
- textposy = textposy + size_text + self.GetTextThickness()
- self.draw.Value( 0, textposy, size_text )
-
- # create a pad array and add it to the module
- for n in range ( 0, pad_count ):
- xpos = pad_pitch*n - offsetX
- pad = self.smdRectPad(self.module,size_pad, pcbnew.wxPoint(xpos,0),str(n+1))
- self.module.Add(pad)
-
-
- # Mechanical shield pads: left pad and right pad
- xpos = -shl_to_pad-offsetX
- pad_s0_pos = pcbnew.wxPoint(xpos,shl_from_top)
- pad_s0 = self.smdRectPad(self.module, size_shld, pad_s0_pos, "0")
- xpos = (pad_count-1) * pad_pitch+shl_to_pad - offsetX
- pad_s1_pos = pcbnew.wxPoint(xpos,shl_from_top)
- pad_s1 = self.smdRectPad(self.module, size_shld, pad_s1_pos, "0")
-
- self.module.Add(pad_s0)
- self.module.Add(pad_s1)
-
- # add footprint outline
- linewidth = self.draw.GetLineThickness()
- margin = linewidth
-
- # upper line
- posy = -pad_height/2 - linewidth/2 - margin
- xstart = - pad_pitch*0.5-offsetX
- xend = pad_pitch * pad_count + xstart;
- self.draw.Line( xstart, posy, xend, posy )
-
- # lower line
- posy = pad_height/2 + linewidth/2 + margin
- self.draw.Line(xstart, posy, xend, posy)
-
- # around left mechanical pad (the outline around right pad is mirrored/y axix)
- yend = pad_s0_pos.y + shl_height/2 + margin
- self.draw.Line(xstart, posy, xstart, yend)
- self.draw.Line(-xstart, posy, -xstart, yend)
-
- posy = yend
- xend = pad_s0_pos.x - (shl_width/2 + linewidth + margin*2)
- self.draw.Line(xstart, posy, xend, posy)
-
- # right pad side
- self.draw.Line(-xstart, posy, -xend, yend)
-
- # set SMD attribute
- self.module.SetAttributes(pcbnew.MOD_CMS)
-
- # vertical segment at left of the pad
- xstart = xend
- yend = posy - (shl_height + linewidth + margin*2)
- self.draw.Line(xstart, posy, xend, yend)
-
- # right pad side
- self.draw.Line(-xstart, posy, -xend, yend)
-
- # horizontal segment above the pad
- xstart = xend
- xend = - pad_pitch*0.5-offsetX
- posy = yend
- self.draw.Line(xstart, posy, xend, yend)
-
- # right pad side
- self.draw.Line(-xstart, posy,-xend, yend)
-
- # vertical segment above the pad
- xstart = xend
- yend = -pad_height/2 - linewidth/2 - margin
- self.draw.Line(xstart, posy, xend, yend)
-
- # right pad side
- self.draw.Line(-xstart, posy, -xend, yend)
-
-
-FPC_FootprintWizard().register()
diff --git a/pcbnew/python/plugins/FPC_wizard.py b/pcbnew/python/plugins/FPC_wizard.py
new file mode 100644
index 0000000..7f10b0b
--- /dev/null
+++ b/pcbnew/python/plugins/FPC_wizard.py
@@ -0,0 +1,157 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+from __future__ import division
+import pcbnew
+
+import FootprintWizardBase
+
+class FPC_FootprintWizard(FootprintWizardBase.FootprintWizard):
+
+ def GetName(self):
+ return "FPC (SMT connector)"
+
+ def GetDescription(self):
+ return "FPC (SMT connector) Footprint Wizard"
+
+ def GetValue(self):
+ pins = self.parameters["Pads"]["n"]
+ return "FPC_%d" % pins
+
+ def GenerateParameterList(self):
+ 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)
+ self.AddParam( "Shield", "shield_to_pad", self.uMM, 1.6 )
+ self.AddParam( "Shield", "from_top", self.uMM, 1.3 )
+ self.AddParam( "Shield", "width", self.uMM, 1.5 )
+ self.AddParam( "Shield", "height", self.uMM, 2 )
+
+
+ # build a rectangular pad
+ def smdRectPad(self,module,size,pos,name):
+ pad = pcbnew.D_PAD(module)
+ pad.SetSize(size)
+ pad.SetShape(pcbnew.PAD_SHAPE_RECT)
+ pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
+ pad.SetLayerSet( pad.SMDMask() )
+ pad.SetPos0(pos)
+ pad.SetPosition(pos)
+ pad.SetPadName(name)
+ return pad
+
+ def CheckParameters(self):
+ #TODO implement custom parameter checking
+ pass
+
+ def BuildThisFootprint(self):
+ p = self.parameters
+ pad_count = int(p["Pads"]["n"])
+ pad_width = p["Pads"]["width"]
+ pad_height = p["Pads"]["height"]
+ pad_pitch = p["Pads"]["pitch"]
+ shl_width = p["Shield"]["width"]
+ shl_height = p["Shield"]["height"]
+ shl_to_pad = p["Shield"]["shield_to_pad"]
+ shl_from_top = p["Shield"]["from_top"]
+
+ offsetX = pad_pitch * ( pad_count-1 ) / 2
+ size_pad = pcbnew.wxSize( pad_width, pad_height )
+ size_shld = pcbnew.wxSize(shl_width, shl_height)
+ size_text = self.GetTextSize() # IPC nominal
+
+ # Gives a position and size to ref and value texts:
+ textposy = pad_height/2 + pcbnew.FromMM(1) + self.GetTextThickness()
+ self.draw.Reference( 0, textposy, size_text )
+
+ textposy = textposy + size_text + self.GetTextThickness()
+ self.draw.Value( 0, textposy, size_text )
+
+ # create a pad array and add it to the module
+ for n in range ( 0, pad_count ):
+ xpos = pad_pitch*n - offsetX
+ pad = self.smdRectPad(self.module,size_pad, pcbnew.wxPoint(xpos,0),str(n+1))
+ self.module.Add(pad)
+
+
+ # Mechanical shield pads: left pad and right pad
+ xpos = -shl_to_pad-offsetX
+ pad_s0_pos = pcbnew.wxPoint(xpos,shl_from_top)
+ pad_s0 = self.smdRectPad(self.module, size_shld, pad_s0_pos, "0")
+ xpos = (pad_count-1) * pad_pitch+shl_to_pad - offsetX
+ pad_s1_pos = pcbnew.wxPoint(xpos,shl_from_top)
+ pad_s1 = self.smdRectPad(self.module, size_shld, pad_s1_pos, "0")
+
+ self.module.Add(pad_s0)
+ self.module.Add(pad_s1)
+
+ # add footprint outline
+ linewidth = self.draw.GetLineThickness()
+ margin = linewidth
+
+ # upper line
+ posy = -pad_height/2 - linewidth/2 - margin
+ xstart = - pad_pitch*0.5-offsetX
+ xend = pad_pitch * pad_count + xstart;
+ self.draw.Line( xstart, posy, xend, posy )
+
+ # lower line
+ posy = pad_height/2 + linewidth/2 + margin
+ self.draw.Line(xstart, posy, xend, posy)
+
+ # around left mechanical pad (the outline around right pad is mirrored/y axix)
+ yend = pad_s0_pos.y + shl_height/2 + margin
+ self.draw.Line(xstart, posy, xstart, yend)
+ self.draw.Line(-xstart, posy, -xstart, yend)
+
+ posy = yend
+ xend = pad_s0_pos.x - (shl_width/2 + linewidth + margin*2)
+ self.draw.Line(xstart, posy, xend, posy)
+
+ # right pad side
+ self.draw.Line(-xstart, posy, -xend, yend)
+
+ # set SMD attribute
+ self.module.SetAttributes(pcbnew.MOD_CMS)
+
+ # vertical segment at left of the pad
+ xstart = xend
+ yend = posy - (shl_height + linewidth + margin*2)
+ self.draw.Line(xstart, posy, xend, yend)
+
+ # right pad side
+ self.draw.Line(-xstart, posy, -xend, yend)
+
+ # horizontal segment above the pad
+ xstart = xend
+ xend = - pad_pitch*0.5-offsetX
+ posy = yend
+ self.draw.Line(xstart, posy, xend, yend)
+
+ # right pad side
+ self.draw.Line(-xstart, posy,-xend, yend)
+
+ # vertical segment above the pad
+ xstart = xend
+ yend = -pad_height/2 - linewidth/2 - margin
+ self.draw.Line(xstart, posy, xend, yend)
+
+ # right pad side
+ self.draw.Line(-xstart, posy, -xend, yend)
+
+
+FPC_FootprintWizard().register()
diff --git a/pcbnew/python/plugins/FootprintWizardBase.py b/pcbnew/python/plugins/FootprintWizardBase.py
new file mode 100644
index 0000000..f9cfffb
--- /dev/null
+++ b/pcbnew/python/plugins/FootprintWizardBase.py
@@ -0,0 +1,683 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+from __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
+
+ # Copy conversion functions from pcbnew
+ ToMM = pcbnew.ToMM
+ FromMM = pcbnew.FromMM
+ ToMils = pcbnew.ToMils
+ FromMils = pcbnew.FromMils
+
+ PutOnGridMM = pcbnew.PutOnGridMM
+ PutOnGridMils = pcbnew.PutOnGridMils
+
+ """
+ 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.FPID(self.module.GetValue()) # the name in library
+ self.module.SetFPID(fpid)
+
+ self.SetModule3DModel() # add a 3d module if specified
+
+ thick = self.GetTextThickness()
+
+ self.module.Reference().SetThickness(thick)
+ self.module.Value().SetThickness(thick)
+
+ self.BuildThisFootprint() # implementer's build function
+
+ 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:
+ """
+ Collection of handy functions to simplify drawing shapes from within
+ footprint wizards
+
+ A "drawing context" is provided which can be used to set and retain
+ settings such as line thickness and layer
+ """
+
+ # directions (in degrees, compass-like)
+ dirN = 0
+ dirNE = 45
+ dirE = 90
+ dirSE = 135
+ dirS = 180
+ dirSW = 225
+ dirW = 270
+ dirNW = 315
+
+ # flip constants
+ flipNone = 0
+ flipX = 1 # flip X values, i.e. about Y
+ flipY = 2 # flip Y valuersabout X
+ flipBoth = 3
+
+ xfrmIDENTITY = [1, 0, 0, 0, 1, 0] # no transform
+
+ # these values come from our KiCad Library Convention 0.11
+ defaultLineThickness = pcbnew.FromMM(0.15)
+
+ def DefaultGraphicLayer(self):
+ return pcbnew.F_SilkS
+
+ def DefaultTextValueLayer(self):
+ return pcbnew.F_Fab
+
+ def __init__(self, module):
+ self.module = module
+ # drawing context defaults
+ self.dc = {
+ 'layer': self.DefaultGraphicLayer(),
+ 'lineThickness': self.defaultLineThickness,
+ 'transforms': [],
+ 'transform': self.xfrmIDENTITY
+ }
+
+ def PushTransform(self, mat):
+ """
+ Add a transform to the top of the stack and recompute the
+ overall transform
+ """
+ self.dc['transforms'].append(mat)
+ self.RecomputeTransforms()
+
+ def PopTransform(self, num=1):
+ """
+ Remove a transform from the top of the stack and recompute the
+ overall transform
+ """
+
+ for i in range(num):
+ mat = self.dc['transforms'].pop()
+ self.RecomputeTransforms()
+ return mat
+
+ def ResetTransform(self):
+ """
+ Reset the transform stack to the identity matrix
+ """
+ self.dc['transforms'] = []
+ self.RecomputeTransforms()
+
+ def _ComposeMatricesWithIdentity(self, mats):
+ """
+ Compose a sequence of matrices together by sequential
+ pre-mutiplciation with the identity matrix
+ """
+
+ x = self.xfrmIDENTITY
+
+ for mat in mats:
+ #precompose with each transform in turn
+ x = [
+ x[0] * mat[0] + x[1] * mat[3],
+ x[0] * mat[1] + x[1] * mat[4],
+ x[0] * mat[2] + x[1] * mat[5] + x[2],
+ x[3] * mat[0] + x[4] * mat[3],
+ x[3] * mat[1] + x[4] * mat[4],
+ x[3] * mat[2] + x[4] * mat[5] + x[5]]
+
+ return x
+
+ def RecomputeTransforms(self):
+ """
+ Re-compute the transform stack into a single transform and
+ store in the DC
+ """
+ self.dc['transform'] = self._ComposeMatricesWithIdentity(
+ self.dc['transforms'])
+
+ def TransformTranslate(self, x, y, push=True):
+ """
+ Set up and return a transform matrix representing a translartion
+ optionally pushing onto the stack
+
+ ( 1 0 x )
+ ( 0 1 y )
+ """
+ mat = [1, 0, x, 0, 1, y]
+
+ if push:
+ self.PushTransform(mat)
+ return mat
+
+ def TransformFlipOrigin(self, flip, push=True):
+ """
+ Set up and return a transform matrix representing a horizontal,
+ vertical or both flip about the origin
+ """
+ mat = None
+ if flip == self.flipX:
+ mat = [-1, 0, 0, 0, 1, 0]
+ elif flip == self.flipY:
+ mat = [1, 0, 0, 0, -1, 0]
+ elif flip == self.flipBoth:
+ mat = [-1, 0, 0, 0, -1, 0]
+ elif flip == self.flipNone:
+ mat = self.xfrmIDENTITY
+ else:
+ raise ValueError
+
+ if push:
+ self.PushTransform(mat)
+ return mat
+
+ def TransformFlip(self, x, y, flip=flipNone, push=True):
+ """
+ Set up and return a transform matrix representing a horizontal,
+ vertical or both flip about a point (x,y)
+
+ This is performed by a translate-to-origin, flip, translate-
+ back sequence
+ """
+ mats = [self.TransformTranslate(x, y, push=False),
+ self.TransformFlipOrigin(flip, push=False),
+ self.TransformTranslate(-x, -y, push=False)]
+
+ #distill into a single matrix
+ mat = self._ComposeMatricesWithIdentity(mats)
+
+ if push:
+ self.PushTransform(mat)
+ return mat
+
+ def TransformRotationOrigin(self, rot, push=True):
+ """
+ Set up and return a transform matrix representing a rotation
+ about the origin, and optionally push onto the stack
+
+ ( cos(t) -sin(t) 0 )
+ ( sin(t) cos(t) 0 )
+ """
+ rads = rot * math.pi / 180
+ mat = [math.cos(rads), -math.sin(rads), 0,
+ math.sin(rads), math.cos(rads), 0]
+
+ if push:
+ self.PushTransform(mat)
+ return mat
+
+ def TransformRotation(self, x, y, rot, push=True):
+ """
+ Set up and return a transform matrix representing a rotation
+ about the point (x,y), and optionally push onto the stack
+
+ This is performed by a translate-to-origin, rotate, translate-
+ back sequence
+ """
+
+ mats = [self.TransformTranslate(x, y, push=False),
+ self.TransformRotationOrigin(rot, push=False),
+ self.TransformTranslate(-x, -y, push=False)]
+
+ #distill into a single matrix
+ mat = self._ComposeMatricesWithIdentity(mats)
+
+ if push:
+ self.PushTransform(mat)
+ return mat
+
+ def TransformScaleOrigin(self, sx, sy=None, push=True):
+ """
+ Set up and return a transform matrix representing a scale about
+ the origin, and optionally push onto the stack
+
+ ( sx 0 0 )
+ ( 0 sy 0 )
+ """
+
+ if sy is None:
+ sy = sx
+
+ mat = [sx, 0, 0, 0, sy, 0]
+
+ if push:
+ self.PushTransform(mat)
+ return mat
+
+ def TransformPoint(self, x, y, mat=None):
+ """
+ Return a point (x, y) transformed by the given matrix, or if
+ that is not given, the drawing context transform
+ """
+
+ if not mat:
+ mat = self.dc['transform']
+
+ return pcbnew.wxPoint(x * mat[0] + y * mat[1] + mat[2],
+ x * mat[3] + y * mat[4] + mat[5])
+
+ def SetLineThickness(self, lineThickness):
+ """
+ Set the current pen lineThickness used for subsequent drawing
+ operations
+ """
+ self.dc['lineThickness'] = lineThickness
+
+ def SetLineTickness(self, lineThickness):
+ """
+ Old version of SetLineThickness.
+ Does the same thing, but is is only here for compatibility with old scripts
+ Set the current pen lineThickness used for subsequent drawing
+ operations
+ """
+ self.dc['lineThickness'] = lineThickness
+
+ def GetLineThickness(self):
+ """
+ Get the current drawing context line thickness
+ """
+ return self.dc['lineThickness']
+
+ def SetLayer(self, layer):
+ """
+ Set the current drawing layer, used for subsequent drawing
+ operations
+ """
+ self.dc['layer'] = layer
+
+ def GetLayer(self):
+ """
+ return the current drawing layer, used drawing operations
+ """
+ return self.dc['layer']
+
+ def Line(self, x1, y1, x2, y2):
+ """
+ Draw a line from (x1, y1) to (x2, y2)
+ """
+ outline = pcbnew.EDGE_MODULE(self.module)
+ outline.SetWidth(self.GetLineThickness())
+ outline.SetLayer(self.GetLayer())
+ outline.SetShape(pcbnew.S_SEGMENT)
+ start = self.TransformPoint(x1, y1)
+ end = self.TransformPoint(x2, y2)
+ outline.SetStartEnd(start, end)
+ self.module.Add(outline)
+
+ def Circle(self, x, y, r, filled=False):
+ """
+ Draw a circle at (x,y) of radius r
+ If filled is true, the thickness and radius of the line will be set
+ such that the circle appears filled
+ """
+
+ circle = pcbnew.EDGE_MODULE(self.module)
+ start = self.TransformPoint(x, y)
+
+ if filled:
+ circle.SetWidth(r)
+ end = self.TransformPoint(x, y + r/2)
+ else:
+ circle.SetWidth(self.dc['lineThickness'])
+ end = self.TransformPoint(x, y + r)
+
+ circle.SetLayer(self.dc['layer'])
+ circle.SetShape(pcbnew.S_CIRCLE)
+ circle.SetStartEnd(start, end)
+ self.module.Add(circle)
+
+ def Arc(self, cx, cy, sx, sy, a):
+ """
+ Draw an arc based on centre, start and angle
+
+ The transform matrix is applied
+
+ Note that this won't work properly if the result is not a
+ circular arc (eg a horzontal scale)
+ """
+ circle = pcbnew.EDGE_MODULE(self.module)
+ circle.SetWidth(self.dc['lineThickness'])
+
+ center = self.TransformPoint(cx, cy)
+ start = self.TransformPoint(sx, sy)
+
+ circle.SetLayer(self.dc['layer'])
+ circle.SetShape(pcbnew.S_ARC)
+
+ # check if the angle needs to be reverse (a flip scaling)
+ if cmp(self.dc['transform'][0], 0) != cmp(self.dc['transform'][4], 0):
+ a = -a
+
+ circle.SetAngle(a)
+ circle.SetStartEnd(center, start)
+ self.module.Add(circle)
+
+ # extends from (x1,y1) right
+ def HLine(self, x, y, l):
+ """
+ Draw a horizontal line from (x,y), rightwards
+ """
+ self.Line(x, y, x + l, y)
+
+ def VLine(self, x, y, l):
+ """
+ Draw a vertical line from (x1,y1), downwards
+ """
+ self.Line(x, y, x, y + l)
+
+ def Polyline(self, pts, mirrorX=None, mirrorY=None):
+ """
+ Draw a polyline, optinally mirroring around the given points
+ """
+
+ def _PolyLineInternal(pts):
+ if len(pts) < 2:
+ return
+
+ for i in range(0, len(pts) - 1):
+ self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1])
+
+ _PolyLineInternal(pts) # original
+
+ 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.
+
+ The actual setting of the reference is not done in this drawing
+ aid - that is up to the wizard
+ """
+
+ text_size = pcbnew.wxSize(size, size)
+
+ self.module.Reference().SetPos0(self.TransformPoint(x, y))
+ self.module.Reference().SetTextPosition(
+ self.module.Reference().GetPos0())
+ self.module.Reference().SetSize(text_size)
+ self.module.Reference().SetOrientation(orientation_degree*10) # internal angles are in 0.1 deg
+
+ def Value(self, x, y, size, orientation_degree = 0):
+ """
+ As for references, draw the module's value
+ """
+ text_size = pcbnew.wxSize(size, size)
+
+ self.module.Value().SetPos0(self.TransformPoint(x, y))
+ self.module.Value().SetTextPosition(self.module.Value().GetPos0())
+ self.module.Value().SetSize(text_size)
+ self.module.Value().SetLayer(self.DefaultTextValueLayer())
+ self.module.Value().SetOrientation(orientation_degree*10) # internal angles are in 0.1 deg
+
+ def Box(self, x, y, w, h):
+ """
+ Draw a rectangular box, centred at (x,y), with given width and
+ height
+ """
+
+ pts = [[x - w/2, y - h/2], # left
+ [x + w/2, y - h/2], # right
+ [x + w/2, y + h/2], # bottom
+ [x - w/2, y + h/2], # top
+ [x - w/2, y - h/2]] # close
+
+ self.Polyline(pts)
+
+ def NotchedCircle(self, x, y, r, notch_w, notch_h, rotate=0):
+ """
+ Circle radus r centred at (x, y) with a raised or depressed notch
+ at the top
+ Notch height is measured from the top of the circle radius
+ """
+
+ self.TransformRotation(x, y, rotate)
+
+ # find the angle where the notch vertical meets the circle
+ angle_intercept = math.asin(notch_w/(2 * r))
+
+ # and find the co-ords of this point
+ sx = math.sin(angle_intercept) * r
+ sy = -math.cos(angle_intercept) * r
+
+ # NOTE: this may be out by a factor of ten one day
+ arc_angle = (math.pi * 2 - angle_intercept * 2) * (1800/math.pi)
+
+ self.Arc(x,y, sx, sy, arc_angle)
+
+ pts = [[sx, sy],
+ [sx, -r - notch_h],
+ [-sx, -r - notch_h],
+ [-sx, sy]]
+
+ self.Polyline(pts)
+ self.PopTransform()
+
+ def NotchedBox(self, x, y, w, h, notchW, notchH, rotate=0):
+ """
+ Draw a box with a notch in the top edge
+ """
+
+ self.TransformRotation(x, y, rotate)
+
+ # limit to half the overall width
+ notchW = min(x + w/2, notchW)
+
+ # draw notch
+ self.Polyline([ # three sides of box
+ (x - w/2, y - h/2),
+ (x - w/2, y + h/2),
+ (x + w/2, y + h/2),
+ (x + w/2, y - h/2),
+ # the notch
+ (notchW/2, y - h/2),
+ (notchW/2, y - h/2 + notchH),
+ (-notchW/2, y - h/2 + notchH),
+ (-notchW/2, y - h/2),
+ (x - w/2, y - h/2)
+ ])
+
+ self.PopTransform()
+
+ def BoxWithDiagonalAtCorner(self, x, y, w, h,
+ setback=pcbnew.FromMM(1.27), flip=flipNone):
+ """
+ Draw a box with a diagonal at the top left corner
+ """
+
+ self.TransformFlip(x, y, flip, push=True)
+
+ pts = [[x - w/2 + setback, y - h/2],
+ [x - w/2, y - h/2 + setback],
+ [x - w/2, y + h/2],
+ [x + w/2, y + h/2],
+ [x + w/2, y - h/2],
+ [x - w/2 + setback, y - h/2]]
+
+ self.Polyline(pts)
+
+ self.PopTransform()
+
+ def BoxWithOpenCorner(self, x, y, w, h,
+ setback=pcbnew.FromMM(1.27), flip=flipNone):
+ """
+ Draw a box with an opening at the top left corner
+ """
+
+ self.TransformTranslate(x, y)
+ self.TransformFlipOrigin(flip)
+
+ pts = [[- w/2, - h/2 + setback],
+ [- w/2, + h/2],
+ [+ w/2, + h/2],
+ [+ w/2, - h/2],
+ [- w/2 + setback, - h/2]]
+
+ self.Polyline(pts)
+
+ self.PopTransform(num=2)
+
+ def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)):
+ """
+ Draw a marker arrow facing in the given direction, with the
+ point at (x,y)
+
+ Direction of 0 is north
+ """
+
+ self.TransformTranslate(x, y)
+ self.TransformRotationOrigin(direction)
+
+ pts = [[0, 0],
+ [width / 2, width / 2],
+ [-width / 2, width / 2],
+ [0, 0]]
+
+ self.Polyline(pts)
+ self.PopTransform(2)
\ No newline at end of file
diff --git a/pcbnew/python/plugins/FootprintWizardDrawingAids.py b/pcbnew/python/plugins/FootprintWizardDrawingAids.py
deleted file mode 100644
index 036f09a..0000000
--- a/pcbnew/python/plugins/FootprintWizardDrawingAids.py
+++ /dev/null
@@ -1,532 +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.
-#
-
-from __future__ import division
-
-import pcbnew
-import math
-
-
-class FootprintWizardDrawingAids:
- """
- Collection of handy functions to simplify drawing shapes from within
- footprint wizards
-
- A "drawing context" is provided which can be used to set and retain
- settings such as line thickness and layer
- """
-
- # directions (in degrees, compass-like)
- dirN = 0
- dirNE = 45
- dirE = 90
- dirSE = 135
- dirS = 180
- dirSW = 225
- dirW = 270
- dirNW = 315
-
- # flip constants
- flipNone = 0
- flipX = 1 # flip X values, i.e. about Y
- flipY = 2 # flip Y valuersabout X
- flipBoth = 3
-
- xfrmIDENTITY = [1, 0, 0, 0, 1, 0] # no transform
-
- # these values come from our KiCad Library Convention 0.11
- defaultLineThickness = pcbnew.FromMM(0.15)
-
- def DefaultGraphicLayer(self):
- return pcbnew.F_SilkS
-
- def DefaultTextValueLayer(self):
- return pcbnew.F_Fab
-
- def __init__(self, module):
- self.module = module
- # drawing context defaults
- self.dc = {
- 'layer': self.DefaultGraphicLayer(),
- 'lineThickness': self.defaultLineThickness,
- 'transforms': [],
- 'transform': self.xfrmIDENTITY
- }
-
- def PushTransform(self, mat):
- """
- Add a transform to the top of the stack and recompute the
- overall transform
- """
- self.dc['transforms'].append(mat)
- self.RecomputeTransforms()
-
- def PopTransform(self, num=1):
- """
- Remove a transform from the top of the stack and recompute the
- overall transform
- """
-
- for i in range(num):
- mat = self.dc['transforms'].pop()
- self.RecomputeTransforms()
- return mat
-
- def ResetTransform(self):
- """
- Reset the transform stack to the identity matrix
- """
- self.dc['transforms'] = []
- self.RecomputeTransforms()
-
- def _ComposeMatricesWithIdentity(self, mats):
- """
- Compose a sequence of matrices together by sequential
- pre-mutiplciation with the identity matrix
- """
-
- x = self.xfrmIDENTITY
-
- for mat in mats:
- #precompose with each transform in turn
- x = [
- x[0] * mat[0] + x[1] * mat[3],
- x[0] * mat[1] + x[1] * mat[4],
- x[0] * mat[2] + x[1] * mat[5] + x[2],
- x[3] * mat[0] + x[4] * mat[3],
- x[3] * mat[1] + x[4] * mat[4],
- x[3] * mat[2] + x[4] * mat[5] + x[5]]
-
- return x
-
- def RecomputeTransforms(self):
- """
- Re-compute the transform stack into a single transform and
- store in the DC
- """
- self.dc['transform'] = self._ComposeMatricesWithIdentity(
- self.dc['transforms'])
-
- def TransformTranslate(self, x, y, push=True):
- """
- Set up and return a transform matrix representing a translartion
- optionally pushing onto the stack
-
- ( 1 0 x )
- ( 0 1 y )
- """
- mat = [1, 0, x, 0, 1, y]
-
- if push:
- self.PushTransform(mat)
- return mat
-
- def TransformFlipOrigin(self, flip, push=True):
- """
- Set up and return a transform matrix representing a horizontal,
- vertical or both flip about the origin
- """
- mat = None
- if flip == self.flipX:
- mat = [-1, 0, 0, 0, 1, 0]
- elif flip == self.flipY:
- mat = [1, 0, 0, 0, -1, 0]
- elif flip == self.flipBoth:
- mat = [-1, 0, 0, 0, -1, 0]
- elif flip == self.flipNone:
- mat = self.xfrmIDENTITY
- else:
- raise ValueError
-
- if push:
- self.PushTransform(mat)
- return mat
-
- def TransformFlip(self, x, y, flip=flipNone, push=True):
- """
- Set up and return a transform matrix representing a horizontal,
- vertical or both flip about a point (x,y)
-
- This is performed by a translate-to-origin, flip, translate-
- back sequence
- """
- mats = [self.TransformTranslate(x, y, push=False),
- self.TransformFlipOrigin(flip, push=False),
- self.TransformTranslate(-x, -y, push=False)]
-
- #distill into a single matrix
- mat = self._ComposeMatricesWithIdentity(mats)
-
- if push:
- self.PushTransform(mat)
- return mat
-
- def TransformRotationOrigin(self, rot, push=True):
- """
- Set up and return a transform matrix representing a rotation
- about the origin, and optionally push onto the stack
-
- ( cos(t) -sin(t) 0 )
- ( sin(t) cos(t) 0 )
- """
- rads = rot * math.pi / 180
- mat = [math.cos(rads), -math.sin(rads), 0,
- math.sin(rads), math.cos(rads), 0]
-
- if push:
- self.PushTransform(mat)
- return mat
-
- def TransformRotation(self, x, y, rot, push=True):
- """
- Set up and return a transform matrix representing a rotation
- about the point (x,y), and optionally push onto the stack
-
- This is performed by a translate-to-origin, rotate, translate-
- back sequence
- """
-
- mats = [self.TransformTranslate(x, y, push=False),
- self.TransformRotationOrigin(rot, push=False),
- self.TransformTranslate(-x, -y, push=False)]
-
- #distill into a single matrix
- mat = self._ComposeMatricesWithIdentity(mats)
-
- if push:
- self.PushTransform(mat)
- return mat
-
- def TransformScaleOrigin(self, sx, sy=None, push=True):
- """
- Set up and return a transform matrix representing a scale about
- the origin, and optionally push onto the stack
-
- ( sx 0 0 )
- ( 0 sy 0 )
- """
-
- if sy is None:
- sy = sx
-
- mat = [sx, 0, 0, 0, sy, 0]
-
- if push:
- self.PushTransform(mat)
- return mat
-
- def TransformPoint(self, x, y, mat=None):
- """
- Return a point (x, y) transformed by the given matrix, or if
- that is not given, the drawing context transform
- """
-
- if not mat:
- mat = self.dc['transform']
-
- return pcbnew.wxPoint(x * mat[0] + y * mat[1] + mat[2],
- x * mat[3] + y * mat[4] + mat[5])
-
- def SetLineThickness(self, lineThickness):
- """
- Set the current pen lineThickness used for subsequent drawing
- operations
- """
- self.dc['lineThickness'] = lineThickness
-
- def SetLineTickness(self, lineThickness):
- """
- Old version of SetLineThickness.
- Does the same thing, but is is only here for compatibility with old scripts
- Set the current pen lineThickness used for subsequent drawing
- operations
- """
- self.dc['lineThickness'] = lineThickness
-
- def GetLineThickness(self):
- """
- Get the current drawing context line thickness
- """
- return self.dc['lineThickness']
-
- def SetLayer(self, layer):
- """
- Set the current drawing layer, used for subsequent drawing
- operations
- """
- self.dc['layer'] = layer
-
- def GetLayer(self):
- """
- return the current drawing layer, used drawing operations
- """
- return self.dc['layer']
-
- def Line(self, x1, y1, x2, y2):
- """
- Draw a line from (x1, y1) to (x2, y2)
- """
- outline = pcbnew.EDGE_MODULE(self.module)
- outline.SetWidth(self.GetLineThickness())
- outline.SetLayer(self.GetLayer())
- outline.SetShape(pcbnew.S_SEGMENT)
- start = self.TransformPoint(x1, y1)
- end = self.TransformPoint(x2, y2)
- outline.SetStartEnd(start, end)
- self.module.Add(outline)
-
- def Circle(self, x, y, r, filled=False):
- """
- Draw a circle at (x,y) of radius r
- If filled is true, the thickness and radius of the line will be set
- such that the circle appears filled
- """
- circle = pcbnew.EDGE_MODULE(self.module)
- start = self.TransformPoint(x, y)
-
- if filled:
- circle.SetWidth(r)
- end = self.TransformPoint(x, y + r/2)
- else:
- circle.SetWidth(self.dc['lineThickness'])
- end = self.TransformPoint(x, y + r)
-
- circle.SetLayer(self.dc['layer'])
- circle.SetShape(pcbnew.S_CIRCLE)
- circle.SetStartEnd(start, end)
- self.module.Add(circle)
-
- def Arc(self, cx, cy, sx, sy, a):
- """
- Draw an arc based on centre, start and angle
-
- The transform matrix is applied
-
- Note that this won't work properly if the result is not a
- circular arc (eg a horzontal scale)
- """
- circle = pcbnew.EDGE_MODULE(self.module)
- circle.SetWidth(self.dc['lineThickness'])
-
- center = self.TransformPoint(cx, cy)
- start = self.TransformPoint(sx, sy)
-
- circle.SetLayer(self.dc['layer'])
- circle.SetShape(pcbnew.S_ARC)
-
- # check if the angle needs to be reverse (a flip scaling)
- if cmp(self.dc['transform'][0], 0) != cmp(self.dc['transform'][4], 0):
- a = -a
-
- circle.SetAngle(a)
- circle.SetStartEnd(center, start)
- self.module.Add(circle)
-
- # extends from (x1,y1) right
- def HLine(self, x, y, l):
- """
- Draw a horizontal line from (x,y), rightwards
- """
- self.Line(x, y, x + l, y)
-
- def VLine(self, x, y, l):
- """
- Draw a vertical line from (x1,y1), downwards
- """
- self.Line(x, y, x, y + l)
-
- def Polyline(self, pts, mirrorX=None, mirrorY=None):
- """
- Draw a polyline, optinally mirroring around the given points
- """
-
- def _PolyLineInternal(pts):
- if len(pts) < 2:
- return
-
- for i in range(0, len(pts) - 1):
- self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1])
-
- _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()
-
- def Reference(self, x, y, size, orientation_degree = 0):
- """
- Draw the module's reference as the given point.
-
- The actual setting of the reference is not done in this drawing
- aid - that is up to the wizard
- """
-
- text_size = pcbnew.wxSize(size, size)
-
- self.module.Reference().SetPos0(self.TransformPoint(x, y))
- self.module.Reference().SetTextPosition(
- self.module.Reference().GetPos0())
- self.module.Reference().SetSize(text_size)
- self.module.Reference().SetOrientation(orientation_degree*10) # internal angles are in 0.1 deg
-
- def Value(self, x, y, size, orientation_degree = 0):
- """
- As for references, draw the module's value
- """
- text_size = pcbnew.wxSize(size, size)
-
- self.module.Value().SetPos0(self.TransformPoint(x, y))
- self.module.Value().SetTextPosition(self.module.Value().GetPos0())
- self.module.Value().SetSize(text_size)
- self.module.Value().SetLayer(self.DefaultTextValueLayer())
- self.module.Value().SetOrientation(orientation_degree*10) # internal angles are in 0.1 deg
-
- def Box(self, x, y, w, h):
- """
- Draw a rectangular box, centred at (x,y), with given width and
- height
- """
-
- pts = [[x - w/2, y - h/2], # left
- [x + w/2, y - h/2], # right
- [x + w/2, y + h/2], # bottom
- [x - w/2, y + h/2], # top
- [x - w/2, y - h/2]] # close
-
- self.Polyline(pts)
-
- def NotchedCircle(self, x, y, r, notch_w, notch_h, rotate=0):
- """
- Circle radus r centred at (x, y) with a raised or depressed notch
- at the top
- Notch height is measured from the top of the circle radius
- """
-
- self.TransformRotation(x, y, rotate)
-
- # find the angle where the notch vertical meets the circle
- angle_intercept = math.asin(notch_w/(2 * r))
-
- # and find the co-ords of this point
- sx = math.sin(angle_intercept) * r
- sy = -math.cos(angle_intercept) * r
-
- # NOTE: this may be out by a factor of ten one day
- arc_angle = (math.pi * 2 - angle_intercept * 2) * (1800/math.pi)
-
- self.Arc(x,y, sx, sy, arc_angle)
-
- pts = [[sx, sy],
- [sx, -r - notch_h],
- [-sx, -r - notch_h],
- [-sx, sy]]
-
- self.Polyline(pts)
- self.PopTransform()
-
- def NotchedBox(self, x, y, w, h, notchW, notchH, rotate=0):
- """
- Draw a box with a notch in the top edge
- """
-
- self.TransformRotation(x, y, rotate)
-
- # limit to half the overall width
- notchW = min(x + w/2, notchW)
-
- # draw notch
- self.Polyline([ # three sides of box
- (x - w/2, y - h/2),
- (x - w/2, y + h/2),
- (x + w/2, y + h/2),
- (x + w/2, y - h/2),
- # the notch
- (notchW/2, y - h/2),
- (notchW/2, y - h/2 + notchH),
- (-notchW/2, y - h/2 + notchH),
- (-notchW/2, y - h/2),
- (x - w/2, y - h/2)
- ])
-
- self.PopTransform()
-
- def BoxWithDiagonalAtCorner(self, x, y, w, h,
- setback=pcbnew.FromMM(1.27), flip=flipNone):
- """
- Draw a box with a diagonal at the top left corner
- """
-
- self.TransformFlip(x, y, flip, push=True)
-
- pts = [[x - w/2 + setback, y - h/2],
- [x - w/2, y - h/2 + setback],
- [x - w/2, y + h/2],
- [x + w/2, y + h/2],
- [x + w/2, y - h/2],
- [x - w/2 + setback, y - h/2]]
-
- self.Polyline(pts)
-
- self.PopTransform()
-
- def BoxWithOpenCorner(self, x, y, w, h,
- setback=pcbnew.FromMM(1.27), flip=flipNone):
- """
- Draw a box with an opening at the top left corner
- """
-
- self.TransformTranslate(x, y)
- self.TransformFlipOrigin(flip)
-
- pts = [[- w/2, - h/2 + setback],
- [- w/2, + h/2],
- [+ w/2, + h/2],
- [+ w/2, - h/2],
- [- w/2 + setback, - h/2]]
-
- self.Polyline(pts)
-
- self.PopTransform(num=2)
-
- def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)):
- """
- Draw a marker arrow facing in the given direction, with the
- point at (x,y)
-
- Direction of 0 is north
- """
-
- self.TransformTranslate(x, y)
- self.TransformRotationOrigin(direction)
-
- pts = [[0, 0],
- [width / 2, width / 2],
- [-width / 2, width / 2],
- [0, 0]]
-
- self.Polyline(pts)
- self.PopTransform(2)
diff --git a/pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py b/pcbnew/python/plugins/HelpfulFootprintWizardPlugin.py
deleted file mode 100644
index a742067..0000000
--- 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.FPID(self.module.GetValue()) # the name in library
- self.module.SetFPID(fpid)
-
- self.SetModule3DModel() # add a 3d module if specified
-
- thick = self.GetTextThickness()
-
- self.module.Reference().SetThickness(thick)
- self.module.Value().SetThickness(thick)
-
- self.BuildThisFootprint() # implementer's build function
-
- return
diff --git a/pcbnew/python/plugins/bga_wizard.py b/pcbnew/python/plugins/bga_wizard.py
index 8ce17e6..6529bca 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,44 +29,57 @@ class BGAPadGridArray(PA.PadGridArray):
n_x + 1)
-class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
+class BGAWizard(FootprintWizardBase.FootprintWizard):
def GetName(self):
return "BGA"
def GetDescription(self):
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"])
-
- return "BGA_%d" % pins
+ pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"])
+
+ 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"]
-
- self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2,
- pads["outline x 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
+
+ # 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 7a42c8f..6c3e995 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):
+
+ 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")
- self.CheckParamInt("Pads", "*n")
- self.CheckParamInt("Pads", "*first pad number")
- self.CheckParamBool("Pads", "*number clockwise")
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 d6c3a61..0e1bbc6 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)
+
+ #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("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)
-
+
+ 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"]
-
- 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)
-
- 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)
-
- #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)])
+ epad_width = self.epad["width"]
+ epad_length = self.epad["length"]
+
+ epad_ny = self.epad["x divisions"]
+ epad_nx = self.epad["y divisions"]
+
+ epad_via_drill = self.epad["thermal vias drill"]
+
+ # 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 04664aa..c50d86e 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/sdip_wizard.py b/pcbnew/python/plugins/sdip_wizard.py
index fad99f1..e69d52d 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 e27fffb..0970cfd 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,8 +194,12 @@ 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)
diff --git a/pcbnew/python/plugins/uss39_barcode.py b/pcbnew/python/plugins/uss39_barcode.py
index ee86775..7821153 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 4ffa7f1..aeb45d3 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
@@ -78,8 +67,8 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
# 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 e0f3854..6de4b06 100644
--- a/pcbnew/swig/pcbnew_footprint_wizards.cpp
+++ b/pcbnew/swig/pcbnew_footprint_wizards.cpp
@@ -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<ret.GetCount(); i++ )
- {
- wxString rest;
- wxString item = ret[i];
-
- if( item.StartsWith( wxT( "*" ), &rest ) )
- {
- ret[i] = wxT( "UNITS" ); // units
- }
- else
- {
- ret[i] = wxT( "IU" ); // internal units
- }
- }
-
return ret;
}
@@ -284,6 +269,29 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterErrors( int aPage )
return ret;
}
+wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterHints( int aPage )
+{
+ PyLOCK lock;
+
+ PyObject* arglist = Py_BuildValue( "(i)", aPage );
+ wxArrayString ret = CallRetArrayStrMethod( "GetParameterHints", arglist );
+
+ Py_DECREF( arglist );
+
+ return ret;
+}
+
+wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterDesignators( int aPage )
+{
+ PyLOCK lock;
+
+ PyObject* arglist = Py_BuildValue( "(i)", aPage );
+ wxArrayString ret = CallRetArrayStrMethod( "GetParameterDesignators", arglist );
+
+ Py_DECREF( arglist );
+
+ return ret;
+}
wxString PYTHON_FOOTPRINT_WIZARD::SetParameterValues( int aPage, wxArrayString& aValues )
{
@@ -309,6 +317,13 @@ wxString PYTHON_FOOTPRINT_WIZARD::SetParameterValues( int aPage, wxArrayString&
return res;
}
+void PYTHON_FOOTPRINT_WIZARD::ResetParameters()
+{
+ PyLOCK lock;
+
+ CallMethod( "ResetWizard", NULL );
+}
+
// this is a SWIG function declaration -from module.i
MODULE* PyModule_to_MODULE( PyObject* obj0 );
diff --git a/pcbnew/swig/pcbnew_footprint_wizards.h b/pcbnew/swig/pcbnew_footprint_wizards.h
index 0027639..4a8f9df 100644
--- a/pcbnew/swig/pcbnew_footprint_wizards.h
+++ b/pcbnew/swig/pcbnew_footprint_wizards.h
@@ -54,8 +54,11 @@ public:
wxArrayString GetParameterTypes( int aPage ) override;
wxArrayString GetParameterValues( int aPage ) override;
wxArrayString GetParameterErrors( int aPage ) override;
+ wxArrayString GetParameterHints( int aPage ) override;
+ wxArrayString GetParameterDesignators( int aPage) override;
// must return an empty string or an error description
wxString SetParameterValues( int aPage, wxArrayString& aValues ) override;
+ void ResetParameters() override;
MODULE* GetFootprint( wxString * aMessages ) override;
void* GetObject() override;
};
diff --git a/pcbnew/swig/python_scripting.cpp b/pcbnew/swig/python_scripting.cpp
index 059ceef..d54998b 100644
--- a/pcbnew/swig/python_scripting.cpp
+++ b/pcbnew/swig/python_scripting.cpp
@@ -37,6 +37,8 @@
#include <colors.h>
#include <macros.h>
+#include <pgm_base.h>
+
/* init functions defined by swig */
extern "C" void init_kicad( void );
@@ -359,3 +361,29 @@ wxString PyErrStringWithTraceback()
return err;
}
+
+/**
+ * Find the Python scripting path
+ */
+wxString PyScriptingPath()
+{
+ wxString path;
+
+ //TODO should this be a user configurable variable eg KISCRIPT ?
+#if defined( __WXMAC__ )
+ path = GetOSXKicadDataDir() + wxT( "/scripting" );
+#else
+ path = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
+#endif
+
+ wxFileName scriptPath( path );
+
+ scriptPath.MakeAbsolute();
+
+ return scriptPath.GetFullPath();
+}
+
+wxString PyPluginsPath()
+{
+ return PyScriptingPath() + wxFileName::GetPathSeparator() + "plugins";
+}
diff --git a/pcbnew/swig/python_scripting.h b/pcbnew/swig/python_scripting.h
index ca25de4..7d835e2 100644
--- a/pcbnew/swig/python_scripting.h
+++ b/pcbnew/swig/python_scripting.h
@@ -59,4 +59,7 @@ public:
wxArrayString PyArrayStringToWx( PyObject* arr );
wxString PyErrStringWithTraceback();
+wxString PyScriptingPath();
+wxString PyPluginsPath();
+
#endif // __PYTHON_SCRIPTING_H
diff --git a/pcbnew/swig/units.i b/pcbnew/swig/units.i
index bae5edc..0a0701a 100644
--- a/pcbnew/swig/units.i
+++ b/pcbnew/swig/units.i
@@ -51,9 +51,17 @@
def FromMils(mils):
if type(mils) in [int,float]:
- return int(float(mils)*float(IU_PER_MILS))
+ return int(float(mils)*float(IU_PER_MILS))
elif type(mils) in [wxPoint,wxSize]:
- return tuple(map(FromMils,mils))
+ return tuple(map(FromMils,mils))
+
+ def PutOnGridMM(value, gridSizeMM):
+ thresh = FromMM(gridSizeMM)
+ return round(value/thresh)*thresh
+
+ def PutOnGridMils(value, gridSizeMils):
+ thresh = FromMils(gridSizeMils)
+ return round(value/thresh)*thresh
def wxSizeMM(mmx,mmy): return wxSize(FromMM(mmx),FromMM(mmy))
def wxSizeMils(mmx,mmy): return wxSize(FromMils(mmx),FromMils(mmy))
diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
index 918896d..6edde1c 100644
--- a/scripting/kicadplugins.i
+++ b/scripting/kicadplugins.i
@@ -146,16 +146,18 @@ def LoadPlugins(bundlepath=None):
if module == '__init__.py' or module[-3:] != '.py':
continue
- mod = __import__(module[:-3], locals(), globals())
-
- module_filename = plugins_dir+"/"+module
- mtime = os.path.getmtime(module_filename)
- if hasattr(mod,'register'):
- KICAD_PLUGINS[module]={"filename":module_filename,
- "modification_time":mtime,
- "object":mod.register(),
- "module":mod}
+ try: # If there is an error loading the script, skip it
+ mod = __import__(module[:-3], locals(), globals())
+ module_filename = plugins_dir+"/"+module
+ mtime = os.path.getmtime(module_filename)
+ if hasattr(mod,'register'):
+ KICAD_PLUGINS[module]={"filename":module_filename,
+ "modification_time":mtime,
+ "object":mod.register(),
+ "module":mod}
+ except:
+ pass
class KiCadPlugin:
@@ -196,88 +198,295 @@ class FilePlugin(KiCadPlugin):
from math import ceil, floor, sqrt
-class FootprintWizardPlugin(KiCadPlugin):
+uMM = "mm" # Millimetres
+uMils = "mils" # Mils
+uFloat = "float" # Natural number units (dimensionless)
+uInteger = "integer" # Integer (no decimals, numeric, dimensionless)
+uBool = "bool" # Boolean value
+uRadians = "radians" # Angular units (radians)
+uDegrees = "degrees" # Angular units (degrees)
+uPercent = "%" # Percent (0% -> 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 AnyErrors(self):
+ return any([len(p.error_list) > 0 for p in self.params])
- 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
+ @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 CheckParameters(self):
- return ""
+ return page_list
- 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
+ def GetNumParameterPages(self): # Return the number of parameter pages
+ return len(self.pages)
+ def GetParameterPageName(self,page_n): # Return the name of a page at a given index
+ return self.pages[page_n]
- 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 GetParametersByPageName(self, page_name): # Return a list of parameters on a given page
+ params = []
+ for p in self.params:
+ if p.page == page_name:
+ params.append(p)
- def ClearErrors(self):
- errs={}
+ return params
+
+ def GetParametersByPageIndex(self, page_index): # Return an ordered list of parameters on a given page
+ return self.GetParametersByPageName(self.GetParameterPageName(page_index))
+
+ 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]
- for page in self.parameters.keys():
- page_dict = self.parameters[page]
- page_params = {}
- for param in page_dict.keys():
- page_params[param]=""
+ 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]
- errs[page]=page_params
+ def SetParameterValues(self, page_index, list_of_values): # Update values on a given page
- self.parameter_errors = errs
+ 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()
@@ -290,18 +499,31 @@ 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):
KiCadPlugin.__init__(self)
--
1.9.5.msysgit.0
Follow ups