Footprint Wizards Update

This commit is contained in:
Oliver Walters 2017-01-03 16:01:47 +01:00 committed by jean-pierre charras
parent d1278a48b6
commit 66ee44cb1f
23 changed files with 2063 additions and 844 deletions

View File

@ -151,11 +151,17 @@ static void InitKiCadAboutNew( AboutAppInfo& info )
<< HtmlHyperlink( wxT( "https://launchpad.net/kicad" ), << HtmlHyperlink( wxT( "https://launchpad.net/kicad" ),
_( "Developer's website on Launchpad" ) ) _( "Developer's website on Launchpad" ) )
<< wxT("</li>" ); << wxT("</li>" );
description << wxT( "<li>" ) description << wxT( "<li>" )
<< HtmlHyperlink( wxT( "https://github.com/KiCad/" ), << HtmlHyperlink( wxT( "https://github.com/KiCad/" ),
_( "Our official Repository for component and footprint libraries" ) ) _( "Our official Repository for component and footprint libraries" ) )
<< wxT( "</li>" ); << wxT( "</li>" );
description << wxT( "<li>" )
<< HtmlHyperlink( wxT( "https://github.com/KiCad/Footprint_Wizards" ),
_( "Footprint wizards info on our official repository " ) )
<< wxT( "</li>" );
description << wxT( "<p><u>" ) description << wxT( "<p><u>" )
<< _( "Non official repositories" ) << _( "Non official repositories" )
<< wxT( "</u>" ); << wxT( "</u>" );

View File

@ -33,6 +33,17 @@
#include <vector> #include <vector>
#include <wxPcbStruct.h> #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 * Class FOOTPRINT_WIZARD
* This is the parent class from where any footprint wizard class must * This is the parent class from where any footprint wizard class must
@ -104,6 +115,20 @@ public:
*/ */
virtual wxArrayString GetParameterErrors( int aPage ) = 0; 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 * Function SetParameterValues
* @param aPage is the page we want to set the parameters in * @param aPage is the page we want to set the parameters in
@ -112,6 +137,12 @@ public:
*/ */
virtual wxString SetParameterValues( int aPage, wxArrayString& aValues ) = 0; virtual wxString SetParameterValues( int aPage, wxArrayString& aValues ) = 0;
/**
* Function ResetParameters
* Reset all wizard parameters to default values
*/
virtual void ResetParameters() = 0;
/** /**
* Function GetModule * Function GetModule
* This method builds the module itself and returns it to the caller function * This method builds the module itself and returns it to the caller function

View File

@ -203,6 +203,22 @@ void FOOTPRINT_WIZARD_FRAME::SelectCurrentWizard( wxCommandEvent& event )
SelectFootprintWizard(); 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 ) void FOOTPRINT_WIZARD_FRAME::ParametersUpdated( wxGridEvent& event )
{ {
@ -223,39 +239,12 @@ void FOOTPRINT_WIZARD_FRAME::ParametersUpdated( wxGridEvent& event )
int count = m_parameterGrid->GetNumberRows(); int count = m_parameterGrid->GetNumberRows();
// Skip extra event, useless // 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; return;
for( int prm_id = 0; prm_id < count; ++prm_id ) for( int prm_id = 0; prm_id < count; ++prm_id )
{ {
wxString value = m_parameterGrid->GetCellValue( prm_id, m_columnPrmValue ); wxString value = m_parameterGrid->GetCellValue( prm_id, WIZ_COL_VALUE);
// 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();
}
}
if( prmValues[prm_id] != value ) if( prmValues[prm_id] != value )
{ {

View File

@ -44,6 +44,8 @@
#include "footprint_wizard_frame.h" #include "footprint_wizard_frame.h"
#include <footprint_info.h> #include <footprint_info.h>
#include <wx/grid.h> #include <wx/grid.h>
#include <wx/tokenzr.h>
#include <wx/numformatter.h>
#include <hotkeys.h> #include <hotkeys.h>
#include <wildcards_and_files_ext.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, EVT_TOOL( ID_FOOTPRINT_WIZARD_SELECT_WIZARD,
FOOTPRINT_WIZARD_FRAME::SelectCurrentWizard ) FOOTPRINT_WIZARD_FRAME::SelectCurrentWizard )
EVT_TOOL( ID_FOOTPRINT_WIZARD_RESET_TO_DEFAULT,
FOOTPRINT_WIZARD_FRAME::DefaultParameters )
EVT_TOOL( ID_FOOTPRINT_WIZARD_NEXT, EVT_TOOL( ID_FOOTPRINT_WIZARD_NEXT,
FOOTPRINT_WIZARD_FRAME::Process_Special_Functions ) FOOTPRINT_WIZARD_FRAME::Process_Special_Functions )
@ -74,6 +79,7 @@ BEGIN_EVENT_TABLE( FOOTPRINT_WIZARD_FRAME, EDA_DRAW_FRAME )
FOOTPRINT_WIZARD_FRAME::Show3D_Frame ) FOOTPRINT_WIZARD_FRAME::Show3D_Frame )
// listbox events // listbox events
EVT_LISTBOX( ID_FOOTPRINT_WIZARD_PAGE_LIST, FOOTPRINT_WIZARD_FRAME::ClickOnPageList ) EVT_LISTBOX( ID_FOOTPRINT_WIZARD_PAGE_LIST, FOOTPRINT_WIZARD_FRAME::ClickOnPageList )
EVT_GRID_CMD_CELL_CHANGED( ID_FOOTPRINT_WIZARD_PARAMETER_LIST, EVT_GRID_CMD_CELL_CHANGED( ID_FOOTPRINT_WIZARD_PARAMETER_LIST,
FOOTPRINT_WIZARD_FRAME::ParametersUpdated ) 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 ) EVT_MENU( ID_SET_RELATIVE_OFFSET, FOOTPRINT_WIZARD_FRAME::OnSetRelativeOffset )
END_EVENT_TABLE() 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" ) #define FOOTPRINT_WIZARD_FRAME_NAME wxT( "FootprintWizard" )
@ -255,6 +257,13 @@ void FOOTPRINT_WIZARD_FRAME::ExportSelectedFootprint( wxCommandEvent& aEvent )
Close(); Close();
} }
void FOOTPRINT_WIZARD_FRAME::OnGridSize( wxSizeEvent& aSizeEvent )
{
// Resize the parameter columns
ResizeParamColumns();
aSizeEvent.Skip();
}
void FOOTPRINT_WIZARD_FRAME::OnSize( wxSizeEvent& SizeEv ) void FOOTPRINT_WIZARD_FRAME::OnSize( wxSizeEvent& SizeEv )
{ {
@ -278,9 +287,10 @@ void FOOTPRINT_WIZARD_FRAME::initParameterGrid()
m_parameterGrid->CreateGrid( 0, 3 ); m_parameterGrid->CreateGrid( 0, 3 );
// Columns // Columns
m_parameterGrid->SetColLabelValue( m_columnPrmName, _( "Parameter" ) ); m_parameterGrid->SetColLabelValue( WIZ_COL_NAME, _( "Parameter" ) );
m_parameterGrid->SetColLabelValue( m_columnPrmValue, _( "Value" ) ); m_parameterGrid->SetColLabelValue( WIZ_COL_VALUE, _( "Value" ) );
m_parameterGrid->SetColLabelValue( m_columnPrmUnit, _( "Units" ) ); m_parameterGrid->SetColLabelValue( WIZ_COL_UNITS, _( "Units" ) );
m_parameterGrid->SetColLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTRE ); m_parameterGrid->SetColLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTRE );
m_parameterGrid->AutoSizeColumns(); m_parameterGrid->AutoSizeColumns();
@ -288,6 +298,11 @@ void FOOTPRINT_WIZARD_FRAME::initParameterGrid()
m_parameterGrid->AutoSizeRows(); m_parameterGrid->AutoSizeRows();
m_parameterGrid->SetRowLabelSize( 25 ); m_parameterGrid->SetRowLabelSize( 25 );
m_parameterGrid->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); 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(); m_parameterGrid->ClearGrid();
// Get the list of names, values, and types // Get the list of names, values, types, hints and designators
wxArrayString fpList = footprintWizard->GetParameterNames( page ); wxArrayString designatorsList = footprintWizard->GetParameterDesignators( page );
wxArrayString fvList = footprintWizard->GetParameterValues( page ); wxArrayString namesList = footprintWizard->GetParameterNames( page );
wxArrayString ptList = footprintWizard->GetParameterTypes( page ); wxArrayString valuesList = footprintWizard->GetParameterValues( page );
wxArrayString typesList = footprintWizard->GetParameterTypes( page );
wxArrayString hintsList = footprintWizard->GetParameterHints( page );
// Dimension the wxGrid // Dimension the wxGrid
if( m_parameterGrid->GetNumberRows() > 0 ) if( m_parameterGrid->GetNumberRows() > 0 )
m_parameterGrid->DeleteRows( 0, m_parameterGrid->GetNumberRows() ); m_parameterGrid->DeleteRows( 0, m_parameterGrid->GetNumberRows() );
m_parameterGrid->AppendRows( fpList.size() ); m_parameterGrid->AppendRows( namesList.size() );
wxString value, units; wxString designator, name, value, units, hint;
for( unsigned int i = 0; i< fpList.size(); i++ )
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->SetRowLabelValue( i, designator );
m_parameterGrid->SetReadOnly( i, m_columnPrmName );
if( ptList[i]==wxT( "IU" ) ) // Set the 'Name'
m_parameterGrid->SetCellValue( i, WIZ_COL_NAME, name );
m_parameterGrid->SetReadOnly( i, WIZ_COL_NAME );
m_parameterGrid->SetCellAlignment( i, WIZ_COL_NAME, wxALIGN_LEFT, wxALIGN_CENTRE );
// Set the editor type of the
// Boolean parameters can be displayed using a checkbox
if ( units == WIZARD_PARAM_UNITS_BOOL )
{ {
LOCALE_IO toggle; wxGridCellBoolEditor *boolEditor = new wxGridCellBoolEditor;
boolEditor->UseStringValues("True","False");
m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, boolEditor );
// We are handling internal units, so convert them to the current m_parameterGrid->SetCellRenderer( i, WIZ_COL_VALUE, new wxGridCellBoolRenderer );
// system selected units and store into value.
double dValue;
value.ToDouble( &dValue );
dValue = To_User_Unit( g_UserUnit, dValue );
if( g_UserUnit==INCHES ) // we convert inches into mils for more detail
{
dValue = dValue * 1000.0;
units = wxT( "mils" );
}
else if( g_UserUnit==MILLIMETRES )
{
units = wxT( "mm" );
}
// Use Double2Str to build the string, because useless trailing 0
// are removed. The %f format does not remove them
std::string s = Double2Str( dValue );
value = FROM_UTF8( s.c_str() );
} }
else if( ptList[i]==wxT( "UNITS" ) ) // 1,2,3,4,5 ... N // Parameters that can be selected from a list of multiple options
else if ( units.Contains( "," ) ) // Indicates list of available options
{ {
wxStringTokenizer tokenizer( units, "," );
wxArrayString options;
while ( tokenizer.HasMoreTokens() )
{
options.Add( tokenizer.GetNextToken() );
}
m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellChoiceEditor( options ) );
units = wxT( "" ); units = wxT( "" );
} }
// Integer parameters
else if ( units == WIZARD_PARAM_UNITS_INTEGER )
{
m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellNumberEditor );
}
// Non-integer numerical parameters
else if ( ( units == WIZARD_PARAM_UNITS_MM ) ||
( units == WIZARD_PARAM_UNITS_MILS ) ||
( units == WIZARD_PARAM_UNITS_FLOAT ) ||
( units == WIZARD_PARAM_UNITS_RADIANS ) ||
( units == WIZARD_PARAM_UNITS_DEGREES ) ||
( units == WIZARD_PARAM_UNITS_PERCENT ) )
{
m_parameterGrid->SetCellEditor( i, WIZ_COL_VALUE, new wxGridCellFloatEditor );
m_parameterGrid->SetCellValue( i, m_columnPrmValue, value ); // Convert separators to the locale-specific character
m_parameterGrid->SetCellValue( i, m_columnPrmUnit, units ); value.Replace( ",", wxNumberFormatter::GetDecimalSeparator() );
m_parameterGrid->SetReadOnly( i, m_columnPrmUnit ); value.Replace( ".", wxNumberFormatter::GetDecimalSeparator() );
}
// Set the 'Units'
m_parameterGrid->SetCellValue( i, WIZ_COL_UNITS, units );
m_parameterGrid->SetReadOnly( i, WIZ_COL_UNITS );
m_parameterGrid->SetCellAlignment( i, WIZ_COL_UNITS, wxALIGN_LEFT, wxALIGN_CENTRE );
// Set the 'Value'
m_parameterGrid->SetCellValue( i, WIZ_COL_VALUE, value );
m_parameterGrid->SetCellAlignment( i, WIZ_COL_VALUE, wxALIGN_CENTRE, wxALIGN_CENTRE );
} }
ResizeParamColumns();
}
void FOOTPRINT_WIZARD_FRAME::ResizeParamColumns()
{
// Parameter grid is not yet configured
if ( ( m_parameterGrid == NULL ) || ( m_parameterGrid->GetNumberCols() == 0 ) )
return;
// first auto-size the columns to ensure enough space around text
m_parameterGrid->AutoSizeColumns(); 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" ) ); _( "Select the wizard script to load and run" ) );
m_mainToolBar->AddSeparator(); 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, m_mainToolBar->AddTool( ID_FOOTPRINT_WIZARD_PREVIOUS, wxEmptyString,
KiBitmap( lib_previous_xpm ), KiBitmap( lib_previous_xpm ),
_( "Select previous parameters page" ) ); _( "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 ) wxCAPTION | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT )
{ {
m_canClose = false; m_canClose = false;
wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
SetSizer( bSizer ); SetSizer( bSizer );
m_messageWindow = new wxTextCtrl( this, wxID_ANY, wxEmptyString, m_messageWindow = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE|wxTE_READONLY ); wxTE_MULTILINE|wxTE_READONLY );
bSizer->Add( m_messageWindow, 1, wxEXPAND, 0 ); bSizer->Add( m_messageWindow, 1, wxEXPAND, 0 );
m_config = aCfg; 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 ); SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
m_messageWindow->SetMinSize( wxSize( 350, 250 ) ); m_messageWindow->SetMinSize( wxSize( 350, 250 ) );
Layout(); Layout();
bSizer->SetSizeHints( this ); bSizer->SetSizeHints( this );
} }

View File

@ -39,9 +39,16 @@ class wxGrid;
class wxGridEvent; class wxGridEvent;
class FOOTPRINT_EDIT_FRAME; 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; class FOOTPRINT_WIZARD_MESSAGES;
enum WizardParameterColumnNames
{
WIZ_COL_NAME = 0,
WIZ_COL_VALUE,
WIZ_COL_UNITS
};
/** /**
* Class FOOTPRINT_WIZARD_FRAME * Class FOOTPRINT_WIZARD_FRAME
*/ */
@ -54,11 +61,6 @@ private:
int m_parameterGridWidth; ///< size of the grid int m_parameterGridWidth; ///< size of the grid
FOOTPRINT_WIZARD_MESSAGES* m_messagesFrame; 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: protected:
wxString m_wizardName; ///< name of the current wizard wxString m_wizardName; ///< name of the current wizard
wxString m_wizardDescription; ///< description of the wizard wxString m_wizardDescription; ///< description of the wizard
@ -76,6 +78,8 @@ private:
void OnSize( wxSizeEvent& event ) override; void OnSize( wxSizeEvent& event ) override;
void OnGridSize( wxSizeEvent& aSizeEvent );
/** /**
* Function ExportSelectedFootprint(); * Function ExportSelectedFootprint();
* will let the caller exit from the wait loop, and get the built footprint * will let the caller exit from the wait loop, and get the built footprint
@ -103,6 +107,11 @@ private:
*/ */
void ReCreateParameterList(); void ReCreateParameterList();
/**
* Expand the 'Value' column to fill available
*/
void ResizeParamColumns();
/** /**
* Function initParameterGrid * Function initParameterGrid
* Prepare the grid where parameters are displayed * Prepare the grid where parameters are displayed
@ -168,6 +177,8 @@ private:
void SelectCurrentWizard( wxCommandEvent& event ); void SelectCurrentWizard( wxCommandEvent& event );
void DefaultParameters( wxCommandEvent& event );
/** /**
* Function ParametersUpdated * Function ParametersUpdated
* Update the footprint python parameters values from the values in grid * Update the footprint python parameters values from the values in grid

View File

@ -200,7 +200,6 @@ PGM_BASE& Pgm()
#if defined( KICAD_SCRIPTING ) #if defined( KICAD_SCRIPTING )
static bool scriptingSetup() static bool scriptingSetup()
{ {
wxString path_frag;
#if defined( __WINDOWS__ ) #if defined( __WINDOWS__ )
// If our python.exe (in kicad/bin) exists, force our kicad python environment // If our python.exe (in kicad/bin) exists, force our kicad python environment
@ -227,14 +226,6 @@ static bool scriptingSetup()
wxSetEnv( wxT( "PATH" ), kipython ); wxSetEnv( wxT( "PATH" ), kipython );
} }
// wizard plugins are stored in ../share/kicad/scripting/plugins.
// so add the base scripting path to python scripting default search paths
// which are ( [KICAD_PATH] is an environment variable to define)
// [KICAD_PATH]/scripting
// [KICAD_PATH]/scripting/plugins
// Add this default search path:
path_frag = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
#elif defined( __WXMAC__ ) #elif defined( __WXMAC__ )
// This path is given to LoadPlugins() from kicadplugins.i, which // This path is given to LoadPlugins() from kicadplugins.i, which
@ -278,13 +269,9 @@ static bool scriptingSetup()
wxSetEnv( wxT( "PYTHONPATH" ), pypath ); wxSetEnv( wxT( "PYTHONPATH" ), pypath );
// Add this default search path:
path_frag = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
#endif #endif
// path_frag is the path to the bundled scripts and plugins, all other paths are if ( !pcbnewInitPythonScripting( TO_UTF8( PyScriptingPath() ) ) )
// determined by the python pcbnew.py initialisation code.
if( !pcbnewInitPythonScripting( TO_UTF8( path_frag ) ) )
{ {
wxLogError( "pcbnewInitPythonScripting() failed." ); wxLogError( "pcbnewInitPythonScripting() failed." );
return false; return false;

View File

@ -392,6 +392,7 @@ enum pcbnew_ids
ID_FOOTPRINT_WIZARD_PAGES_WINDOW, ID_FOOTPRINT_WIZARD_PAGES_WINDOW,
ID_FOOTPRINT_WIZARD_PARAMETERS_WINDOW, ID_FOOTPRINT_WIZARD_PARAMETERS_WINDOW,
ID_FOOTPRINT_WIZARD_SELECT_WIZARD, ID_FOOTPRINT_WIZARD_SELECT_WIZARD,
ID_FOOTPRINT_WIZARD_RESET_TO_DEFAULT,
ID_FOOTPRINT_WIZARD_EXPORT_TO_BOARD, ID_FOOTPRINT_WIZARD_EXPORT_TO_BOARD,
ID_UPDATE_PCB_FROM_SCH, ID_UPDATE_PCB_FROM_SCH,

View File

@ -17,10 +17,9 @@
from __future__ import division from __future__ import division
import pcbnew import pcbnew
import HelpfulFootprintWizardPlugin as HFPW import FootprintWizardBase
class FPC_FootprintWizard(FootprintWizardBase.FootprintWizard):
class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
def GetName(self): def GetName(self):
return "FPC (SMT connector)" return "FPC (SMT connector)"
@ -29,11 +28,11 @@ class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
return "FPC (SMT connector) Footprint Wizard" return "FPC (SMT connector) Footprint Wizard"
def GetValue(self): def GetValue(self):
pins = self.parameters["Pads"]["*n"] pins = self.parameters["Pads"]["n"]
return "FPC_%d" % pins return "FPC_%d" % pins
def GenerateParameterList(self): def GenerateParameterList(self):
self.AddParam( "Pads", "n", self.uNatural, 40 ) self.AddParam( "Pads", "n", self.uInteger, 40 )
self.AddParam( "Pads", "pitch", self.uMM, 0.5 ) self.AddParam( "Pads", "pitch", self.uMM, 0.5 )
self.AddParam( "Pads", "width", self.uMM, 0.25 ) self.AddParam( "Pads", "width", self.uMM, 0.25 )
self.AddParam( "Pads", "height", self.uMM, 1.6) self.AddParam( "Pads", "height", self.uMM, 1.6)
@ -56,13 +55,12 @@ class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin):
return pad return pad
def CheckParameters(self): def CheckParameters(self):
p = self.parameters #TODO implement custom parameter checking
self.CheckParamInt( "Pads", "*n" ) # not internal units preceded by "*" pass
def BuildThisFootprint(self): def BuildThisFootprint(self):
p = self.parameters p = self.parameters
pad_count = int(p["Pads"]["*n"]) pad_count = int(p["Pads"]["n"])
pad_width = p["Pads"]["width"] pad_width = p["Pads"]["width"]
pad_height = p["Pads"]["height"] pad_height = p["Pads"]["height"]
pad_pitch = p["Pads"]["pitch"] pad_pitch = p["Pads"]["pitch"]

View File

@ -15,10 +15,150 @@
# #
from __future__ import division from __future__ import division
import pcbnew import pcbnew
import math import math
# Base class for creating footprint wizards
# Inherit this class to make a new wizard
class FootprintWizard(pcbnew.FootprintWizardPlugin):
# Copy units from pcbnew
uMM = pcbnew.uMM
uMils = pcbnew.uMils
uFloat = pcbnew.uFloat
uInteger = pcbnew.uInteger
uBool = pcbnew.uBool
uRadians = pcbnew.uRadians
uDegrees = pcbnew.uDegrees
uPercent = pcbnew.uPercent
uString = pcbnew.uString
"""
A class to simplify many aspects of footprint creation, leaving only
the foot-print specific routines to the wizards themselves
Generally, you need to implement:
GetValue()
GenerateParameterList()
CheckParameters()
BuildThisFootprint()
GetName()
GetDescription()
"""
def __init__(self):
pcbnew.FootprintWizardPlugin.__init__(self)
self.GenerateParameterList()
def GetName(self):
"""
Retun the name of the footprint wizard
"""
raise NotImplementedError
def GetDescription(self):
"""
Return the footprint wizard description
"""
raise NotImplementedError
def GetValue(self):
"""
Return the value (name) of the generated footprint
"""
raise NotImplementedError
def GenerateParameterList(self):
"""
Footprint parameter specification is done here
"""
raise NotImplementedError
def CheckParameters(self):
"""
Any custom parameter checking should be performed here
"""
raise NotImplementedError
def BuildThisFootprint(self):
"""
Draw the footprint.
This is specific to each footprint class, you need to implment
this to draw what you want
"""
raise NotImplementedError
# Do not override this method!
def BuildFootprint( self ):
"""
Actually make the footprint. We defer all but the setup to
the implementing class
"""
self.buildmessages = ""
self.module = pcbnew.MODULE(None) # create a new module
# Perform default checks on all params
for p in self.params:
p.ClearErrors()
p.Check() # use defaults
self.CheckParameters() # User error checks
if self.AnyErrors(): # Errors were detected!
self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
for p in self.params:
if len(p.error_list) > 0:
self.buildmessages +="['{page}']['{name}']:\n".format(page=p.page,name=p.name)
for error in p.error_list:
self.buildmessages += "\t" + error + "\n"
return
self.buildmessages = ("Building new {name} footprint with the following parameters:\n".format(name=self.name))
self.buildmessages += self.Show()
self.draw = FootprintWizardDrawingAids(
self.module)
self.module.SetValue(self.GetValue())
self.module.SetReference("%s**" % self.GetReferencePrefix())
fpid = pcbnew.LIB_ID(self.module.GetValue()) # the name in library
self.module.SetFPID(fpid)
self.SetModule3DModel() # add a 3d module if specified
thick = self.GetTextThickness()
self.module.Reference().SetThickness(thick)
self.module.Value().SetThickness(thick)
self.BuildThisFootprint() # implementer's build function
return
def SetModule3DModel(self):
pass
def GetTextSize(self):
"""
IPC nominal
"""
return pcbnew.FromMM(1.0)
def GetTextThickness(self):
"""
Thicker than IPC guidelines (10% of text height = 0.12mm)
as 5 wires/mm is a common silk screen limitation
"""
return pcbnew.FromMM(0.15)
class FootprintWizardDrawingAids: class FootprintWizardDrawingAids:
""" """
@ -294,6 +434,7 @@ class FootprintWizardDrawingAids:
If filled is true, the thickness and radius of the line will be set If filled is true, the thickness and radius of the line will be set
such that the circle appears filled such that the circle appears filled
""" """
circle = pcbnew.EDGE_MODULE(self.module) circle = pcbnew.EDGE_MODULE(self.module)
start = self.TransformPoint(x, y) start = self.TransformPoint(x, y)
@ -362,21 +503,22 @@ class FootprintWizardDrawingAids:
_PolyLineInternal(pts) # original _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: if mirrorX is not None and mirrorY is not None:
self.TransformFlip(mirrorX, mirrorY, self.flipBoth) # both self.TransformFlip(mirrorX, mirrorY, self.flipBoth) # both
_PolyLineInternal(pts) _PolyLineInternal(pts)
self.PopTransform() 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): def Reference(self, x, y, size, orientation_degree = 0):
""" """
Draw the module's reference as the given point. Draw the module's reference as the given point.
@ -529,4 +671,4 @@ class FootprintWizardDrawingAids:
[0, 0]] [0, 0]]
self.Polyline(pts) self.Polyline(pts)
self.PopTransform(2) self.PopTransform(2)

View File

@ -1,348 +0,0 @@
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
import pcbnew
import math
import FootprintWizardDrawingAids
class FootprintWizardParameterManager:
"""
Functions for helpfully managing parameters to a KiCAD Footprint
Wizard.
Abstracts away from whatever structure is used by pcbnew's footprint
wizard class
"""
def __init__(self):
self.parameters = {}
self.GenerateParameterList()
def GenerateParameterList(self):
"""
Construct parameters here, or leave out to have no parameters
"""
pass
def CheckParameters(self):
"""
Implement this to make checks on parameter values, filling
parameter_errors (or using the checker routines)
Subclasses can implment their own and override the parent
defaults and add new ones
"""
pass
uMM = 1
uMils = 2
uNatural = 3
uBool = 4
uString = 5
def AddParam(self, section, param, unit, default, hint=''):
"""
Add a parameter with some properties.
TODO: Hints are not supported, as there is as yet nowhere to
put them in the KiCAD interface
"""
error = ""
val = None
if unit == self.uMM:
val = pcbnew.FromMM(default)
elif unit == self.uMils:
val = pcbnew.FromMils(default)
elif unit == self.uNatural:
val = default
elif unit == self.uString:
val = str(default)
elif unit == self.uBool:
val = "True" if default else "False" # ugly stringing
else:
error = "Warning: Unknown unit type: %s" % unit
return error
if unit in [self.uNatural, self.uBool, self.uString]:
param = "*%s" % param # star prefix for natural
if section not in self.parameters:
if not hasattr(self, 'page_order'):
self.page_order = []
self.page_order.append(section)
self.parameters[section] = {}
if not hasattr(self, 'parameter_order'):
self.parameter_order = {}
self.parameter_order[section] = []
self.parameters[section][param] = val
self.parameter_order[section].append(param)
return error
def _PrintParameterTable(self):
"""
Pretty-print the parameters we have
"""
message = ""
for name, section in self.parameters.iteritems():
message += " %s:\n" % name
for key, value in section.iteritems():
unit = ""
if ((type(value) is int or type(value) is float)
and not "*" in key):
unit = "mm"
if "*" in key:
key = key[1:]
else:
value = pcbnew.ToMM(value)
message += " %s: %s%s\n" % (key, value, unit)
return message
def _ParametersHaveErrors(self):
"""
Return true if we discovered errors during parameter processing
"""
for name, section in self.parameter_errors.iteritems():
for k, v in section.iteritems():
if v:
return True
return False
def _PrintParameterErrors(self):
"""
Pretty-print parameters with errors
"""
errors = ""
for name, section in self.parameter_errors.iteritems():
printed_section = False
for key, value in section.iteritems():
if value:
if not printed_section:
errors += " %s:" % name
errors += " %s: %s (have %s)\n" % (
key, value, self.parameters[name][key])
return errors
def ProcessParameters(self):
"""
Make sure the parameters we have meet whatever expectations the
footprint wizard has of them
"""
self.ClearErrors()
self.CheckParameters()
if self._ParametersHaveErrors():
return False
return True
#################################################################
# PARAMETER CHECKERS
#################################################################
def CheckParamInt(self, section, param, min_value=1,
max_value=None, is_multiple_of=1):
"""
Make sure a parameter can be made into an int, and enforce
limits if required
"""
try:
self.parameters[section][param] = (
int(self.parameters[section][param]))
except ValueError:
self.parameter_errors[section][param] = (
"Must be a valid integer")
return
if min_value is not None and (
self.parameters[section][param] < min_value):
self.parameter_errors[section][param] = (
"Must be greater than or equal to %d" % (min_value))
return
if max_value is not None and (
self.parameters[section][param] > max_value):
self.parameter_errors[section][param] = (
"Must be less than or equal to %d" % (max_value))
return
if is_multiple_of > 1 and (
self.parameters[section][param] % is_multiple_of) > 0:
self.parameter_errors[section][param] = (
"Must be a multiple of %d" % is_multiple_of)
return
return
def CheckParamBool(self, section, param):
"""
Make sure a parameter looks like a boolean, convert to native
boolean type if so
"""
if str(self.parameters[section][param]).lower() in [
"true", "t", "y", "yes", "on", "1", "1.0"]:
self.parameters[section][param] = True
return
elif str(self.parameters[section][param]).lower() in [
"false", "f", "n", "no", "off", "0", "0.0"]:
self.parameters[section][param] = False
return
self.parameter_errors[section][param] = "Must be boolean (true/false)"
return
class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin,
FootprintWizardParameterManager):
"""
A class to simplify many aspects of footprint creation, leaving only
the foot-print specific routines to the wizards themselves
Generally, you need to implement:
GetReference()
GetValue()
GenerateParameterList()
CheckParameters()
BuildThisFootprint()
GetName()
GetDescription()
"""
def __init__(self):
pcbnew.FootprintWizardPlugin.__init__(self)
FootprintWizardParameterManager.__init__(self)
self.name = self.GetName()
self.decription = self.GetDescription()
self.image = self.GetImage()
def GetValue(self):
raise NotImplementedError
# this value come from our KiCad Library Convention 1.0
def GetReferencePrefix(self):
return "REF"
def GetImage(self):
return ""
def GetTextSize(self):
"""
IPC nominal
"""
return pcbnew.FromMM(1.0)
def GetTextThickness(self):
"""
Thicker than IPC guidelines (10% of text height = 0.12mm)
as 5 wires/mm is a common silk screen limitation
"""
return pcbnew.FromMM(0.15)
def SetModule3DModel(self):
"""
Set a 3D model for the module
Default is to do nothing, you need to implement this if you have
a model to set
FIXME: This doesn't seem to be enabled yet?
"""
pass
def PutOnGridMM(self, value, gridSizeMM=0.05):
"""
Round the value (in KiCAD internal units 1nm) according to the
provided gridSize in mm.
"""
thresh = pcbnew.FromMM(gridSizeMM)
res = round(value/thresh)*thresh
return res
def PutOnGridMils(self, value, gridSizeMil=2):
"""
Round the value (in KiCAD internal units 1nm) according to the
provided gridSize in mil.
"""
thresh = pcbnew.FromMils(gridSizeMil)
res = round(value/thresh)*thresh
return res
def BuildThisFootprint(self):
"""
Draw the footprint.
This is specific to each footprint class, you need to implment
this to draw what you want
"""
raise NotImplementedError
def BuildFootprint( self ):
"""
Actually make the footprint. We defer all but the setup to
the implementing class
"""
self.buildmessages = ""
self.module = pcbnew.MODULE(None) # create a new module
# do it first, so if we return early, we don't segfault KiCad
if not self.ProcessParameters():
self.buildmessages = "Cannot build footprint: Parameters have errors:\n"
self.buildmessages += self._PrintParameterErrors()
return
self.buildmessages = ("Building new %s footprint with the following parameters:\n"
% self.name)
self.buildmessages += self._PrintParameterTable()
self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids(
self.module)
self.module.SetValue(self.GetValue())
self.module.SetReference("%s**" % self.GetReferencePrefix())
fpid = pcbnew.LIB_ID(self.module.GetValue()) # the name in library
self.module.SetFPID(fpid)
self.SetModule3DModel() # add a 3d module if specified
thick = self.GetTextThickness()
self.module.Reference().SetThickness(thick)
self.module.Value().SetThickness(thick)
self.BuildThisFootprint() # implementer's build function
return

View File

@ -17,7 +17,7 @@
from __future__ import division from __future__ import division
import pcbnew import pcbnew
import HelpfulFootprintWizardPlugin as HFPW import FootprintWizardBase
import PadArray as PA import PadArray as PA
@ -29,7 +29,7 @@ class BGAPadGridArray(PA.PadGridArray):
n_x + 1) n_x + 1)
class BGAWizard(HFPW.HelpfulFootprintWizardPlugin): class BGAWizard(FootprintWizardBase.FootprintWizard):
def GetName(self): def GetName(self):
return "BGA" return "BGA"
@ -38,35 +38,48 @@ class BGAWizard(HFPW.HelpfulFootprintWizardPlugin):
return "Ball Grid Array Footprint Wizard" return "Ball Grid Array Footprint Wizard"
def GenerateParameterList(self): def GenerateParameterList(self):
self.AddParam("Pads", "pad pitch", self.uMM, 1) self.AddParam("Pads", "pitch", self.uMM, 1, designator='p')
self.AddParam("Pads", "pad size", self.uMM, 0.5) self.AddParam("Pads", "size", self.uMM, 0.5)
self.AddParam("Pads", "row count", self.uNatural, 5) self.AddParam("Pads", "columns", self.uInteger, 5, designator="nx")
self.AddParam("Pads", "column count", self.uNatural, 5) self.AddParam("Pads", "rows", self.uInteger, 5, designator="ny")
self.AddParam("Pads", "outline x margin", self.uMM, 1)
self.AddParam("Pads", "outline y margin", self.uMM, 1) 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): 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): def GetValue(self):
pins = (self.parameters["Pads"]["*row count"] pins = (self.parameters["Pads"]["rows"] * self.parameters["Pads"]["columns"])
* self.parameters["Pads"]["*column count"])
return "BGA_%d" % pins return "BGA-{n}_{a}x{b}_{x}x{y}mm".format(
n = pins,
a = self.parameters['Pads']['columns'],
b = self.parameters['Pads']['rows'],
x = pcbnew.ToMM(self.parameters['Package']['width']),
y = pcbnew.ToMM(self.parameters['Package']['length'])
)
def BuildThisFootprint(self): def BuildThisFootprint(self):
pads = self.parameters["Pads"] pads = self.parameters["Pads"]
rows = pads["*row count"] rows = pads["rows"]
cols = pads["*column count"] cols = pads["columns"]
pad_size = pads["pad size"] pad_size = pads["size"]
pad_size = pcbnew.wxSize(pad_size, pad_size) pad_size = pcbnew.wxSize(pad_size, pad_size)
pad_pitch = pads["pad pitch"] pad_pitch = pads["pitch"]
# add in the pads # 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, pin1_pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2,
-((rows - 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 = BGAPadGridArray(pad, cols, rows, pad_pitch, pad_pitch)
array.AddPadsToModule(self.draw) array.AddPadsToModule(self.draw)
#box # Draw box outline on F.Fab layer
ssx = -pin1_pos.x + pads["outline x margin"] self.draw.SetLayer(pcbnew.F_Fab)
ssy = -pin1_pos.y + pads["outline y margin"] ssx = self.parameters['Package']['width'] / 2
ssy = self.parameters['Package']['length'] / 2
self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, # Bevel should be 1mm nominal but we'll allow smaller values
pads["outline x margin"]) 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 # Courtyard
cmargin = self.draw.GetLineThickness() cmargin = self.parameters['Package']['margin']
self.draw.SetLayer(pcbnew.F_CrtYd) self.draw.SetLayer(pcbnew.F_CrtYd)
sizex = (ssx + cmargin) * 2 sizex = (ssx + cmargin) * 2
sizey = (ssy + cmargin) * 2 sizey = (ssy + cmargin) * 2
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1) sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1) sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC # set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05)) self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey) self.draw.Box(0, 0, sizex, sizey)

View File

@ -18,11 +18,11 @@ from __future__ import division
import math import math
import pcbnew import pcbnew
import HelpfulFootprintWizardPlugin as HFPW import FootprintWizardBase
import PadArray as PA import PadArray as PA
class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin): class circular_pad_array_wizard(FootprintWizardBase.FootprintWizard):
def GetName(self): def GetName(self):
return "Circular Pad Array" return "Circular Pad Array"
@ -32,52 +32,84 @@ class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin):
def GenerateParameterList(self): def GenerateParameterList(self):
self.AddParam("Pads", "n", self.uNatural, 6) self.AddParam("Pads", "count", self.uInteger, 6, min_value=1, designator='n')
self.AddParam("Pads", "pad width", self.uMM, 1.5) self.AddParam("Pads", "center diameter", self.uMM, 5, min_value=0, designator='r', hint="Centre distance between pads")
self.AddParam("Pads", "drill", self.uMM, 1) self.AddParam("Pads", "diameter", self.uMM, 1.5)
self.AddParam("Pads", "circle diameter", self.uMM, 5) self.AddParam("Pads", "drill", self.uMM, 0.8)
self.AddParam("Pads", "first pad angle", self.uNatural, 0) self.AddParam("Pads", "angle", self.uDegrees, 0, designator='a')
self.AddParam("Pads", "number clockwise", self.uBool, True)
self.AddParam("Pads", "first pad number", self.uNatural, 1) 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): def CheckParameters(self):
self.CheckParamInt("Pads", "*n") pads = self.parameters['Pads']
self.CheckParamInt("Pads", "*first pad number") numbering = self.parameters['Numbering']
self.CheckParamBool("Pads", "*number clockwise") outline = self.parameters['Outline']
# Check that pads do not overlap
pad_dia = pcbnew.ToMM(pads['diameter'])
centres = pcbnew.ToMM(pads['center diameter'])
n_pads = pads['count']
self.CheckParam('Pads','diameter',max_value=centres*math.pi/n_pads,info="Pads overlap")
# Check that the pads fit inside the outline
d_min = pad_dia + centres
self.CheckParam("Outline","diameter",min_value=d_min, info="Outline diameter is too small")
def GetValue(self): def GetValue(self):
pins = self.parameters["Pads"]["*n"] pins = self.parameters["Pads"]["count"]
return "CPA_%d" % pins return "CPA_%d" % pins
def BuildThisFootprint(self): 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( pad = PA.PadMaker(self.module).THPad(pads['diameter'], pads['diameter'], pads['drill'])
prm['pad width'], prm['pad width'], prm['drill'])
array = PA.PadCircleArray( array = PA.PadCircleArray(
pad, prm['*n'], prm['circle diameter'] / 2, pad, pads['count'], pads['center diameter'] / 2,
angle_offset=prm["*first pad angle"], angle_offset=pads["angle"],
centre=pcbnew.wxPoint(0, 0), 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) 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) 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 text_size = self.GetTextSize() # IPC nominal
thickness = self.GetTextThickness() 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.Value( 0, textposy, text_size )
self.draw.Reference( 0, -textposy, text_size ) self.draw.Reference( 0, -textposy, text_size )
circular_pad_array_wizard().register() circular_pad_array_wizard().register()

View File

@ -17,55 +17,80 @@
from __future__ import division from __future__ import division
import pcbnew import pcbnew
import HelpfulFootprintWizardPlugin as HFPW import pcbnew
import FootprintWizardBase
import PadArray as PA import PadArray as PA
class QFNWizard(HFPW.HelpfulFootprintWizardPlugin): class QFNWizard(FootprintWizardBase.FootprintWizard):
def GetName(self): def GetName(self):
return "QFN" return "QFN"
def GetDescription(self): def GetDescription(self):
return "Quad Flat No-lead with Exposed Pad footprint wizard" return "Quad Flat No-lead (QFN) footprint wizard"
def GenerateParameterList(self): def GenerateParameterList(self):
self.AddParam("Pads", "n", self.uNatural, 100)
self.AddParam("Pads", "pad pitch", self.uMM, 0.5)
self.AddParam("Pads", "pad width", self.uMM, 0.25)
self.AddParam("Pads", "pad length", self.uMM, 1.5)
self.AddParam("Pads", "oval", self.uBool, True)
self.AddParam("Pads", "thermal vias", self.uBool, True)
self.AddParam("Pads", "thermal vias drill", self.uMM, 0.3)
self.AddParam("Pads", "epad subdiv x", self.uNatural, 2)
self.AddParam("Pads", "epad subdiv y", self.uNatural, 2)
self.AddParam("Package", "package width", self.uMM, 14) #TODO - Allow different number of pads in x and y directions
self.AddParam("Package", "package height", self.uMM, 14)
self.AddParam("Package", "courtyard margin", self.uMM, 1) self.AddParam("Pads", "n", self.uInteger, 100, multiple=4, min_value=4)
self.AddParam("Pads", "pitch", self.uMM, 0.5, designator='e')
self.AddParam("Pads", "width", self.uMM, 0.25, designator='X1')
self.AddParam("Pads", "length", self.uMM, 1.5, designator='Y1')
self.AddParam("Pads", "oval", self.uBool, True)
self.AddParam("EPad", "epad", self.uBool, True)
self.AddParam("EPad", "width", self.uMM, 10, designator="E2")
self.AddParam("EPad", "length", self.uMM, 10, designator="D2")
self.AddParam("EPad", "thermal vias", self.uBool, False)
self.AddParam("EPad", "thermal vias drill", self.uMM, 1, min_value=0.1)
self.AddParam("EPad", "x divisions", self.uInteger, 2, min_value=1)
self.AddParam("EPad", "y divisions", self.uInteger, 2, min_value=1)
self.AddParam("EPad", "paste margin", self.uMM, 0.1)
self.AddParam("Package", "width", self.uMM, 14, designator='E')
self.AddParam("Package", "height", self.uMM, 14, designator='D')
self.AddParam("Package", "margin", self.uMM, 0.25, minValue=0.2)
@property
def pads(self):
return self.parameters['Pads']
@property
def epad(self):
return self.parameters['EPad']
@property
def package(self):
return self.parameters['Package']
def CheckParameters(self): def CheckParameters(self):
self.CheckParamInt("Pads", "*n", is_multiple_of=4) pass
self.CheckParamBool("Pads", "*oval")
self.CheckParamBool("Pads", "*thermal vias")
def GetValue(self): 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): def BuildThisFootprint(self):
pads = self.parameters["Pads"]
pad_pitch = pads["pad pitch"] pad_pitch = self.pads["pitch"]
pad_length = pads["pad length"] pad_length = self.pads["length"]
pad_width = pads["pad width"] pad_width = self.pads["width"]
v_pitch = self.parameters["Package"]["package height"] v_pitch = self.package["height"]
h_pitch = self.parameters["Package"]["package width"] 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 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, h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width,
shape=pad_shape, rot_degree=90.0) shape=pad_shape, rot_degree=90.0)
@ -97,79 +122,88 @@ class QFNWizard(HFPW.HelpfulFootprintWizardPlugin):
array.SetFirstPadInArray(3*pads_per_row + 1) array.SetFirstPadInArray(3*pads_per_row + 1)
array.AddPadsToModule(self.draw) array.AddPadsToModule(self.draw)
lim_x = self.parameters["Package"]["package width"] / 2 lim_x = self.package["width"] / 2
lim_y = self.parameters["Package"]["package height"] / 2 lim_y = self.package["height"] / 2
inner = (row_len / 2) + pad_pitch inner = (row_len / 2) + pad_pitch
# epad # epad
epad_width = self.parameters["Package"]["package height"] - (2*pad_length) epad_width = self.epad["width"]
epad_length = self.parameters["Package"]["package width"] - (2*pad_length) epad_length = self.epad["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): epad_ny = self.epad["x divisions"]
# Create the master pad (one area) on front solder mask, and perhaps of front copper layer epad_nx = self.epad["y divisions"]
# 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) epad_via_drill = self.epad["thermal vias drill"]
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 # Create a central exposed pad?
self.draw.Line(-lim_x, -inner, -inner, -lim_y) if self.epad['epad'] == True:
# top right
self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)]) epad_num = self.pads['n'] + 1
# bottom left
self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)]) epad_w = epad_length / epad_nx
# bottom right epad_l = epad_width / epad_ny
self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)])
# 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 # Courtyard
cmargin = self.parameters["Package"]["courtyard margin"] cmargin = self.package["margin"]
self.draw.SetLayer(pcbnew.F_CrtYd) 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 # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1) sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1) sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC # set courtyard line thickness to the one defined in KLC
thick = self.draw.GetLineThickness() thick = self.draw.GetLineThickness()
self.draw.SetLineThickness(pcbnew.FromMM(0.05)) self.draw.SetLineThickness(pcbnew.FromMM(0.05))

View File

@ -17,53 +17,64 @@
from __future__ import division from __future__ import division
import pcbnew import pcbnew
import HelpfulFootprintWizardPlugin import FootprintWizardBase
import PadArray as PA import PadArray as PA
class QFPWizard(FootprintWizardBase.FootprintWizard):
class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
def GetName(self): def GetName(self):
return "QFP" return "QFP"
def GetDescription(self): def GetDescription(self):
return "Quad Flat Package footprint wizard" return "Quad Flat Package (QFP) footprint wizard"
def GenerateParameterList(self): def GenerateParameterList(self):
self.AddParam("Pads", "n", self.uNatural, 100) self.AddParam("Pads", "n", self.uInteger, 100, multiple=4, min_value=4)
self.AddParam("Pads", "pad pitch", self.uMM, 0.5) self.AddParam("Pads", "pitch", self.uMM, 0.5, designator='e')
self.AddParam("Pads", "pad width", self.uMM, 0.25) self.AddParam("Pads", "width", self.uMM, 0.25, designator='X1')
self.AddParam("Pads", "pad length", self.uMM, 1.5) self.AddParam("Pads", "length", self.uMM, 1.5, designator='Y1')
self.AddParam("Pads", "vertical pitch", self.uMM, 15) self.AddParam("Pads", "horizontal spacing", self.uMM, 15, designator='C1')
self.AddParam("Pads", "horizontal pitch", self.uMM, 15) self.AddParam("Pads", "vertical spacing", self.uMM, 15, designator='C2')
self.AddParam("Pads", "oval", self.uBool, True) self.AddParam("Pads", "oval", self.uBool, True)
self.AddParam("Package", "package width", self.uMM, 14) self.AddParam("Package", "width", self.uMM, 14, designator='D1')
self.AddParam("Package", "package height", self.uMM, 14) self.AddParam("Package", "height", self.uMM, 14, designator='E1')
self.AddParam("Package", "courtyard margin", self.uMM, 1) 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): def CheckParameters(self):
self.CheckParamInt("Pads", "*n", is_multiple_of=4) # todo - custom checking
self.CheckParamBool("Pads", "*oval") pass
def GetValue(self): 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): def BuildThisFootprint(self):
pads = self.parameters["Pads"]
pad_pitch = pads["pad pitch"] pad_pitch = self.pads["pitch"]
pad_length = self.parameters["Pads"]["pad length"] pad_length = self.pads["length"]
pad_width = self.parameters["Pads"]["pad width"] pad_width = self.pads["width"]
v_pitch = pads["vertical pitch"] v_pitch = self.pads["vertical spacing"]
h_pitch = pads["horizontal pitch"] 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 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, h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width,
shape=pad_shape, rot_degree=90.0) shape=pad_shape, rot_degree=90.0)
@ -95,27 +106,49 @@ class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
array.SetFirstPadInArray(3*pads_per_row + 1) array.SetFirstPadInArray(3*pads_per_row + 1)
array.AddPadsToModule(self.draw) array.AddPadsToModule(self.draw)
lim_x = self.parameters["Package"]["package width"] / 2 offset = pcbnew.FromMM(0.15)
lim_y = self.parameters["Package"]["package height"] / 2
x = self.parameters["Package"]["width"] / 2 + offset
y = self.parameters["Package"]["height"] / 2 + offset
inner = (row_len / 2) + pad_pitch inner = (row_len / 2) + pad_pitch
#top left - diagonal # Add outline to F_Fab layer
self.draw.Line(-lim_x, -inner, -inner, -lim_y) 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 # 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 # 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 # 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 # Courtyard
cmargin = self.parameters["Package"]["courtyard margin"] cmargin = self.parameters["Package"]["courtyard margin"]
self.draw.SetLayer(pcbnew.F_CrtYd) self.draw.SetLayer(pcbnew.F_CrtYd)
sizex = (lim_x + cmargin) * 2 + pad_length sizex = (right_edge + cmargin) * 2
sizey = (lim_y + cmargin) * 2 + pad_length sizey = (bottom_edge + cmargin) * 2
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1) sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1) sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC # set courtyard line thickness to the one defined in KLC
thick = self.draw.GetLineThickness() thick = self.draw.GetLineThickness()
self.draw.SetLineThickness(pcbnew.FromMM(0.05)) self.draw.SetLineThickness(pcbnew.FromMM(0.05))

View File

@ -0,0 +1,827 @@
#
# QR Code Generator for Python
#
# Copyright (c) 2012 Kazuhiko Arase
#
# URL: http://www.d-project.com/
#
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php
#
# The word 'QR Code' is registered trademark of
# DENSO WAVE INCORPORATED
# http://www.denso-wave.com/qrcode/faqpatent-e.html
#
"""QR Code Generator for Python
from qrcode import QRCode, ErrorCorrectLevel
# generate with explicit type number
qr = QRCode()
qr.setTypeNumber(4)
qr.setErrorCorrectLevel(ErrorCorrectLevel.M)
qr.addData('here comes qr!')
qr.make()
# generate with auto type number
# qr = QRCode.getMinimumQRCode('here comes qr!', ErrorCorrectLevel.M)
# create an image
for r in range(qr.getModuleCount() ):
for c in range(qr.getModuleCount() ):
color = black if qr.isDark(r, c) else white
# set pixel ...
"""
class QRCode:
PAD0 = 0xEC
PAD1 = 0x11
def __init__(self):
self.typeNumber = 1
self.errorCorrectLevel = ErrorCorrectLevel.H
self.qrDataList = []
self.modules = []
self.moduleCount = 0
def getTypeNumber(self):
return self.typeNumber
def setTypeNumber(self, typeNumber):
self.typeNumber = typeNumber
def getErrorCorrectLevel(self):
return self.errorCorrectLevel
def setErrorCorrectLevel(self, errorCorrectLevel):
self.errorCorrectLevel = errorCorrectLevel
def clearData(self):
self.qrDataList = []
def addData(self, data):
self.qrDataList.append(QR8BitByte(data) )
def getDataCount(self):
return len(self.qrDataList)
def getData(self, index):
return self.qrDataList[index]
def isDark(self, row, col):
return (self.modules[row][col] if self.modules[row][col] != None
else False)
def getModuleCount(self):
return self.moduleCount
def make(self):
self._make(False, self._getBestMaskPattern() )
def _getBestMaskPattern(self):
minLostPoint = 0
pattern = 0
for i in range(8):
self._make(True, i)
lostPoint = QRUtil.getLostPoint(self)
if i == 0 or minLostPoint > lostPoint:
minLostPoint = lostPoint
pattern = i
return pattern
def _make(self, test, maskPattern):
self.moduleCount = self.typeNumber * 4 + 17
self.modules = [[None] * self.moduleCount
for i in range(self.moduleCount)]
self._setupPositionProbePattern(0, 0)
self._setupPositionProbePattern(self.moduleCount - 7, 0)
self._setupPositionProbePattern(0, self.moduleCount - 7)
self._setupPositionAdjustPattern()
self._setupTimingPattern()
self._setupTypeInfo(test, maskPattern)
if self.typeNumber >= 7:
self._setupTypeNumber(test)
data = QRCode._createData(
self.typeNumber,
self.errorCorrectLevel,
self.qrDataList)
self._mapData(data, maskPattern)
def _mapData(self, data, maskPattern):
rows = list(range(self.moduleCount) )
cols = [col - 1 if col <= 6 else col
for col in range(self.moduleCount - 1, 0, -2)]
maskFunc = QRUtil.getMaskFunction(maskPattern)
byteIndex = 0
bitIndex = 7
for col in cols:
rows.reverse()
for row in rows:
for c in range(2):
if self.modules[row][col - c] == None:
dark = False
if byteIndex < len(data):
dark = ( (data[byteIndex] >> bitIndex) & 1) == 1
if maskFunc(row, col - c):
dark = not dark
self.modules[row][col - c] = dark
bitIndex -= 1
if bitIndex == -1:
byteIndex += 1
bitIndex = 7
def _setupPositionAdjustPattern(self):
pos = QRUtil.getPatternPosition(self.typeNumber)
for row in pos:
for col in pos:
if self.modules[row][col] != None:
continue
for r in range(-2, 3):
for c in range(-2, 3):
self.modules[row + r][col + c] = (
r == -2 or r == 2 or c == -2 or c == 2
or (r == 0 and c == 0) )
def _setupPositionProbePattern(self, row, col):
for r in range(-1, 8):
for c in range(-1, 8):
if (row + r <= -1 or self.moduleCount <= row + r
or col + c <= -1 or self.moduleCount <= col + c):
continue
self.modules[row + r][col + c] = (
(0 <= r and r <= 6 and (c == 0 or c == 6) )
or (0 <= c and c <= 6 and (r == 0 or r == 6) )
or (2 <= r and r <= 4 and 2 <= c and c <= 4) )
def _setupTimingPattern(self):
for r in range(8, self.moduleCount - 8):
if self.modules[r][6] != None:
continue
self.modules[r][6] = r % 2 == 0
for c in range(8, self.moduleCount - 8):
if self.modules[6][c] != None:
continue
self.modules[6][c] = c % 2 == 0
def _setupTypeNumber(self, test):
bits = QRUtil.getBCHTypeNumber(self.typeNumber)
for i in range(18):
self.modules[i // 3][i % 3 + self.moduleCount - 8 - 3] = (
not test and ( (bits >> i) & 1) == 1)
for i in range(18):
self.modules[i % 3 + self.moduleCount - 8 - 3][i // 3] = (
not test and ( (bits >> i) & 1) == 1)
def _setupTypeInfo(self, test, maskPattern):
data = (self.errorCorrectLevel << 3) | maskPattern
bits = QRUtil.getBCHTypeInfo(data)
# vertical
for i in range(15):
mod = not test and ( (bits >> i) & 1) == 1
if i < 6:
self.modules[i][8] = mod
elif i < 8:
self.modules[i + 1][8] = mod
else:
self.modules[self.moduleCount - 15 + i][8] = mod
# horizontal
for i in range(15):
mod = not test and ( (bits >> i) & 1) == 1
if i < 8:
self.modules[8][self.moduleCount - i - 1] = mod
elif i < 9:
self.modules[8][15 - i - 1 + 1] = mod
else:
self.modules[8][15 - i - 1] = mod
# fixed
self.modules[self.moduleCount - 8][8] = not test
@staticmethod
def _createData(typeNumber, errorCorrectLevel, dataArray):
rsBlocks = RSBlock.getRSBlocks(typeNumber, errorCorrectLevel)
buffer = BitBuffer()
for data in dataArray:
buffer.put(data.getMode(), 4)
buffer.put(data.getLength(), data.getLengthInBits(typeNumber) )
data.write(buffer)
totalDataCount = sum(rsBlock.getDataCount()
for rsBlock in rsBlocks)
if buffer.getLengthInBits() > totalDataCount * 8:
raise Exception('code length overflow. (%s > %s)' %
(buffer.getLengthInBits(), totalDataCount * 8) )
# end code
if buffer.getLengthInBits() + 4 <= totalDataCount * 8:
buffer.put(0, 4)
# padding
while buffer.getLengthInBits() % 8 != 0:
buffer.put(False)
# padding
while True:
if buffer.getLengthInBits() >= totalDataCount * 8:
break
buffer.put(QRCode.PAD0, 8)
if buffer.getLengthInBits() >= totalDataCount * 8:
break
buffer.put(QRCode.PAD1, 8)
return QRCode._createBytes(buffer, rsBlocks)
@staticmethod
def _createBytes(buffer, rsBlocks):
offset = 0
maxDcCount = 0
maxEcCount = 0
dcdata = [None] * len(rsBlocks)
ecdata = [None] * len(rsBlocks)
for r in range(len(rsBlocks) ):
dcCount = rsBlocks[r].getDataCount()
ecCount = rsBlocks[r].getTotalCount() - dcCount
maxDcCount = max(maxDcCount, dcCount)
maxEcCount = max(maxEcCount, ecCount)
dcdata[r] = [0] * dcCount
for i in range(len(dcdata[r] ) ):
dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]
offset += dcCount
rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount)
rawPoly = Polynomial(dcdata[r], rsPoly.getLength() - 1)
modPoly = rawPoly.mod(rsPoly)
ecdata[r] = [0] * (rsPoly.getLength() - 1)
for i in range(len(ecdata[r]) ):
modIndex = i + modPoly.getLength() - len(ecdata[r])
ecdata[r][i] = modPoly.get(modIndex) if modIndex >= 0 else 0
totalCodeCount = sum(rsBlock.getTotalCount()
for rsBlock in rsBlocks)
data = [0] * totalCodeCount
index = 0
for i in range(maxDcCount):
for r in range(len(rsBlocks) ):
if i < len(dcdata[r] ):
data[index] = dcdata[r][i]
index += 1
for i in range(maxEcCount):
for r in range(len(rsBlocks) ):
if i < len(ecdata[r] ):
data[index] = ecdata[r][i]
index += 1
return data
@staticmethod
def getMinimumQRCode(data, errorCorrectLevel):
mode = Mode.MODE_8BIT_BYTE # fixed to 8bit byte
qr = QRCode()
qr.setErrorCorrectLevel(errorCorrectLevel)
qr.addData(data)
length = qr.getData(0).getLength()
for typeNumber in range(1, 11):
if length <= QRUtil.getMaxLength(
typeNumber, mode, errorCorrectLevel):
qr.setTypeNumber(typeNumber)
break
qr.make()
return qr
class Mode:
MODE_NUMBER = 1 << 0
MODE_ALPHA_NUM = 1 << 1
MODE_8BIT_BYTE = 1 << 2
MODE_KANJI = 1 << 3
class ErrorCorrectLevel:
L = 1 # 7%
M = 0 # 15%
Q = 3 # 25%
H = 2 # 30%
class MaskPattern:
PATTERN000 = 0
PATTERN001 = 1
PATTERN010 = 2
PATTERN011 = 3
PATTERN100 = 4
PATTERN101 = 5
PATTERN110 = 6
PATTERN111 = 7
class QRUtil:
@staticmethod
def getPatternPosition(typeNumber):
return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]
PATTERN_POSITION_TABLE = [
[],
[6, 18],
[6, 22],
[6, 26],
[6, 30],
[6, 34],
[6, 22, 38],
[6, 24, 42],
[6, 26, 46],
[6, 28, 50],
[6, 30, 54],
[6, 32, 58],
[6, 34, 62],
[6, 26, 46, 66],
[6, 26, 48, 70],
[6, 26, 50, 74],
[6, 30, 54, 78],
[6, 30, 56, 82],
[6, 30, 58, 86],
[6, 34, 62, 90],
[6, 28, 50, 72, 94],
[6, 26, 50, 74, 98],
[6, 30, 54, 78, 102],
[6, 28, 54, 80, 106],
[6, 32, 58, 84, 110],
[6, 30, 58, 86, 114],
[6, 34, 62, 90, 118],
[6, 26, 50, 74, 98, 122],
[6, 30, 54, 78, 102, 126],
[6, 26, 52, 78, 104, 130],
[6, 30, 56, 82, 108, 134],
[6, 34, 60, 86, 112, 138],
[6, 30, 58, 86, 114, 142],
[6, 34, 62, 90, 118, 146],
[6, 30, 54, 78, 102, 126, 150],
[6, 24, 50, 76, 102, 128, 154],
[6, 28, 54, 80, 106, 132, 158],
[6, 32, 58, 84, 110, 136, 162],
[6, 26, 54, 82, 110, 138, 166],
[6, 30, 58, 86, 114, 142, 170]
]
MAX_LENGTH = [
[ [41, 25, 17, 10], [34, 20, 14, 8], [27, 16, 11, 7], [17, 10, 7, 4] ],
[ [77, 47, 32, 20], [63, 38, 26, 16], [48, 29, 20, 12], [34, 20, 14, 8] ],
[ [127, 77, 53, 32], [101, 61, 42, 26], [77, 47, 32, 20], [58, 35, 24, 15] ],
[ [187, 114, 78, 48], [149, 90, 62, 38], [111, 67, 46, 28], [82, 50, 34, 21] ],
[ [255, 154, 106, 65], [202, 122, 84, 52], [144, 87, 60, 37], [106, 64, 44, 27] ],
[ [322, 195, 134, 82], [255, 154, 106, 65], [178, 108, 74, 45], [139, 84, 58, 36] ],
[ [370, 224, 154, 95], [293, 178, 122, 75], [207, 125, 86, 53], [154, 93, 64, 39] ],
[ [461, 279, 192, 118], [365, 221, 152, 93], [259, 157, 108, 66], [202, 122, 84, 52] ],
[ [552, 335, 230, 141], [432, 262, 180, 111], [312, 189, 130, 80], [235, 143, 98, 60] ],
[ [652, 395, 271, 167], [513, 311, 213, 131], [364, 221, 151, 93], [288, 174, 119, 74] ]
]
@staticmethod
def getMaxLength(typeNumber, mode, errorCorrectLevel):
t = typeNumber - 1
e = {
ErrorCorrectLevel.L: 0,
ErrorCorrectLevel.M: 1,
ErrorCorrectLevel.Q: 2,
ErrorCorrectLevel.H: 3
}[errorCorrectLevel]
m = {
Mode.MODE_NUMBER: 0,
Mode.MODE_ALPHA_NUM: 1,
Mode.MODE_8BIT_BYTE: 2,
Mode.MODE_KANJI: 3
}[mode]
return QRUtil.MAX_LENGTH[t][e][m]
@staticmethod
def getErrorCorrectPolynomial(errorCorrectLength):
a = Polynomial([1])
for i in range(errorCorrectLength):
a = a.multiply(Polynomial([1, QRMath.gexp(i)]) )
return a
@staticmethod
def getMaskFunction(maskPattern):
return {
MaskPattern.PATTERN000:
lambda i, j: (i + j) % 2 == 0,
MaskPattern.PATTERN001:
lambda i, j: i % 2 == 0,
MaskPattern.PATTERN010:
lambda i, j: j % 3 == 0,
MaskPattern.PATTERN011:
lambda i, j: (i + j) % 3 == 0,
MaskPattern.PATTERN100:
lambda i, j: (i // 2 + j // 3) % 2 == 0,
MaskPattern.PATTERN101:
lambda i, j: (i * j) % 2 + (i * j) % 3 == 0,
MaskPattern.PATTERN110:
lambda i, j: ( (i * j) % 2 + (i * j) % 3) % 2 == 0,
MaskPattern.PATTERN111:
lambda i, j: ( (i * j) % 3 + (i + j) % 2) % 2 == 0
}[maskPattern]
@staticmethod
def getLostPoint(qrcode):
moduleCount = qrcode.getModuleCount()
lostPoint = 0
# LEVEL1
for row in range(moduleCount):
for col in range(moduleCount):
sameCount = 0
dark = qrcode.isDark(row, col)
for r in range(-1, 2):
if row + r < 0 or moduleCount <= row + r:
continue
for c in range(-1, 2):
if col + c < 0 or moduleCount <= col + c:
continue
if r == 0 and c == 0:
continue
if dark == qrcode.isDark(row + r, col + c):
sameCount += 1
if sameCount > 5:
lostPoint += (3 + sameCount - 5)
# LEVEL2
for row in range(moduleCount - 1):
for col in range(moduleCount - 1):
count = 0
if qrcode.isDark(row, col):
count += 1
if qrcode.isDark(row + 1, col):
count += 1
if qrcode.isDark(row, col + 1):
count += 1
if qrcode.isDark(row + 1, col + 1):
count += 1
if count == 0 or count == 4:
lostPoint += 3
# LEVEL3
for row in range(moduleCount):
for col in range(moduleCount - 6):
if (qrcode.isDark(row, col)
and not qrcode.isDark(row, col + 1)
and qrcode.isDark(row, col + 2)
and qrcode.isDark(row, col + 3)
and qrcode.isDark(row, col + 4)
and not qrcode.isDark(row, col + 5)
and qrcode.isDark(row, col + 6) ):
lostPoint += 40
for col in range(moduleCount):
for row in range(moduleCount - 6):
if (qrcode.isDark(row, col)
and not qrcode.isDark(row + 1, col)
and qrcode.isDark(row + 2, col)
and qrcode.isDark(row + 3, col)
and qrcode.isDark(row + 4, col)
and not qrcode.isDark(row + 5, col)
and qrcode.isDark(row + 6, col) ):
lostPoint += 40
# LEVEL4
darkCount = 0
for col in range(moduleCount):
for row in range(moduleCount):
if qrcode.isDark(row, col):
darkCount += 1
ratio = abs(100 * darkCount // moduleCount // moduleCount - 50) // 5
lostPoint += ratio * 10
return lostPoint
G15 = ( (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) |
(1 << 2) | (1 << 1) | (1 << 0) )
G18 = ( (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) |
(1 << 8) | (1 << 5) | (1 << 2) | (1 << 0) )
G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1)
@staticmethod
def getBCHTypeInfo(data):
d = data << 10
while QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0:
d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) -
QRUtil.getBCHDigit(QRUtil.G15) ) )
return ( (data << 10) | d) ^ QRUtil.G15_MASK
@staticmethod
def getBCHTypeNumber(data):
d = data << 12
while QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0:
d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) -
QRUtil.getBCHDigit(QRUtil.G18) ) )
return (data << 12) | d
@staticmethod
def getBCHDigit(data):
digit = 0
while data != 0:
digit += 1
data >>= 1
return digit
@staticmethod
def stringToBytes(s):
return [ord(c) & 0xff for c in s]
class QR8BitByte:
def __init__(self, data):
self.mode = Mode.MODE_8BIT_BYTE
self.data = data
def getMode(self):
return self.mode
def getData(self):
return self.data
'''
def write(self, buffer): raise Exception('not implemented.')
def getLength(self): raise Exception('not implemented.')
'''
def write(self, buffer):
data = QRUtil.stringToBytes(self.getData() )
for d in data:
buffer.put(d, 8)
def getLength(self):
return len(QRUtil.stringToBytes(self.getData() ) )
def getLengthInBits(self, type):
if 1 <= type and type < 10: # 1 - 9
return {
Mode.MODE_NUMBER: 10,
Mode.MODE_ALPHA_NUM: 9,
Mode.MODE_8BIT_BYTE: 8,
Mode.MODE_KANJI: 8
}[self.mode]
elif type < 27: # 10 - 26
return {
Mode.MODE_NUMBER: 12,
Mode.MODE_ALPHA_NUM: 11,
Mode.MODE_8BIT_BYTE: 16,
Mode.MODE_KANJI: 10
}[self.mode]
elif type < 41: # 27 - 40
return {
Mode.MODE_NUMBER: 14,
Mode.MODE_ALPHA_NUM: 13,
Mode.MODE_8BIT_BYTE: 16,
Mode.MODE_KANJI: 12
}[self.mode]
else:
raise Exception('type:%s' % type)
class QRMath:
EXP_TABLE = None
LOG_TABLE = None
@staticmethod
def _init():
QRMath.EXP_TABLE = [0] * 256
for i in range(256):
QRMath.EXP_TABLE[i] = (1 << i if i < 8 else
QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^
QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8])
QRMath.LOG_TABLE = [0] * 256
for i in range(255):
QRMath.LOG_TABLE[QRMath.EXP_TABLE[i] ] = i
@staticmethod
def glog(n):
if n < 1:
raise Exception('log(%s)' % n)
return QRMath.LOG_TABLE[n]
@staticmethod
def gexp(n):
while n < 0:
n += 255
while n >= 256:
n -= 255
return QRMath.EXP_TABLE[n]
# initialize statics
QRMath._init()
class Polynomial:
def __init__(self, num, shift=0):
offset = 0
length = len(num)
while offset < length and num[offset] == 0:
offset += 1
self.num = num[offset:] + [0] * shift
def get(self, index):
return self.num[index]
def getLength(self):
return len(self.num)
def __repr__(self):
return ','.join( [str(self.get(i) )
for i in range(self.getLength() ) ] )
def toLogString(self):
return ','.join( [str(QRMath.glog(self.get(i) ) )
for i in range(self.getLength() ) ] )
def multiply(self, e):
num = [0] * (self.getLength() + e.getLength() - 1)
for i in range(self.getLength() ):
for j in range(e.getLength() ):
num[i + j] ^= QRMath.gexp(QRMath.glog(self.get(i) ) +
QRMath.glog(e.get(j) ) )
return Polynomial(num)
def mod(self, e):
if self.getLength() - e.getLength() < 0:
return self
ratio = QRMath.glog(self.get(0) ) - QRMath.glog(e.get(0) )
num = self.num[:]
for i in range(e.getLength() ):
num[i] ^= QRMath.gexp(QRMath.glog(e.get(i) ) + ratio)
return Polynomial(num).mod(e)
class RSBlock:
RS_BLOCK_TABLE = [
# L
# M
# Q
# H
# 1
[1, 26, 19],
[1, 26, 16],
[1, 26, 13],
[1, 26, 9],
# 2
[1, 44, 34],
[1, 44, 28],
[1, 44, 22],
[1, 44, 16],
# 3
[1, 70, 55],
[1, 70, 44],
[2, 35, 17],
[2, 35, 13],
# 4
[1, 100, 80],
[2, 50, 32],
[2, 50, 24],
[4, 25, 9],
# 5
[1, 134, 108],
[2, 67, 43],
[2, 33, 15, 2, 34, 16],
[2, 33, 11, 2, 34, 12],
# 6
[2, 86, 68],
[4, 43, 27],
[4, 43, 19],
[4, 43, 15],
# 7
[2, 98, 78],
[4, 49, 31],
[2, 32, 14, 4, 33, 15],
[4, 39, 13, 1, 40, 14],
# 8
[2, 121, 97],
[2, 60, 38, 2, 61, 39],
[4, 40, 18, 2, 41, 19],
[4, 40, 14, 2, 41, 15],
# 9
[2, 146, 116],
[3, 58, 36, 2, 59, 37],
[4, 36, 16, 4, 37, 17],
[4, 36, 12, 4, 37, 13],
# 10
[2, 86, 68, 2, 87, 69],
[4, 69, 43, 1, 70, 44],
[6, 43, 19, 2, 44, 20],
[6, 43, 15, 2, 44, 16]
]
def __init__(self, totalCount, dataCount):
self.totalCount = totalCount
self.dataCount = dataCount
def getDataCount(self):
return self.dataCount
def getTotalCount(self):
return self.totalCount
def __repr__(self):
return ('(total=%s,data=%s)' % (self.totalCount, self.dataCount) )
@staticmethod
def getRSBlocks(typeNumber, errorCorrectLevel):
rsBlock = RSBlock.getRsBlockTable(typeNumber, errorCorrectLevel)
length = len(rsBlock) // 3
list = []
for i in range(length):
count = rsBlock[i * 3 + 0]
totalCount = rsBlock[i * 3 + 1]
dataCount = rsBlock[i * 3 + 2]
list += [RSBlock(totalCount, dataCount)] * count
return list
@staticmethod
def getRsBlockTable(typeNumber, errorCorrectLevel):
return {
ErrorCorrectLevel.L:
RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 0],
ErrorCorrectLevel.M:
RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 1],
ErrorCorrectLevel.Q:
RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 2],
ErrorCorrectLevel.H:
RSBlock.RS_BLOCK_TABLE[ (typeNumber - 1) * 4 + 3]
}[errorCorrectLevel]
class BitBuffer:
def __init__(self, inclements=32):
self.inclements = inclements
self.buffer = [0] * self.inclements
self.length = 0
def getBuffer(self):
return self.buffer
def getLengthInBits(self):
return self.length
def get(self, index):
return ( (self.buffer[index // 8] >> (7 - index % 8) ) & 1) == 1
def putBit(self, bit):
if self.length == len(self.buffer) * 8:
self.buffer += [0] * self.inclements
if bit:
self.buffer[self.length // 8] |= (0x80 >> (self.length % 8) )
self.length += 1
def put(self, num, length):
for i in range(length):
self.putBit( ( (num >> (length - i - 1) ) & 1) == 1)
def __repr__(self):
return ''.join('1' if self.get(i) else '0'
for i in range(self.getLengthInBits() ) )

View File

@ -0,0 +1,120 @@
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import pcbnew
import FootprintWizardBase
# Additional import for QRCode
# see https://github.com/kazuhikoarase/qrcode-generator/blob/master/python/qrcode.py
import qrcode
class QRCodeWizard(FootprintWizardBase.FootprintWizard):
GetName = lambda self: '2D Barcode QRCode'
GetDescription = lambda self: 'QR Code'
GetReferencePrefix = lambda self: 'QR***'
GetValue = lambda self: self.module.Value().GetText()
def GenerateParameterList(self):
self.AddParam("Barcode", "Pixel Width", self.uMM, 0.5, min_value=0.4)
self.AddParam("Barcode", "Border", self.uInteger, 0)
self.AddParam("Barcode", "Contents", self.uString, 'Example')
self.AddParam("Barcode", "Negative", self.uBool, False)
self.AddParam("Barcode", "Use SilkS layer", self.uBool, False)
self.AddParam("Barcode", "Use Cu layer", self.uBool, True)
self.AddParam("Caption", "Enabled", self.uBool, True)
self.AddParam("Caption", "Height", self.uMM, 1.2)
self.AddParam("Caption", "Thickness", self.uMM, 0.12)
def CheckParameters(self):
self.Barcode = str(self.parameters['Barcode']['Contents'])
self.X = self.parameters['Barcode']['Pixel Width']
self.negative = self.parameters['Barcode']['Negative']
self.UseSilkS = self.parameters['Barcode']['Use SilkS layer']
self.UseCu = self.parameters['Barcode']['Use Cu layer']
self.border = int(self.parameters['Barcode']['Border'])
self.textHeight = int(self.parameters['Caption']['Height'])
self.module.Value().SetText(str(self.Barcode) )
# Build Qrcode
self.qr = qrcode.QRCode()
self.qr.setTypeNumber(4)
# ErrorCorrectLevel: L = 7%, M = 15% Q = 25% H = 30%
self.qr.setErrorCorrectLevel(qrcode.ErrorCorrectLevel.M)
self.qr.addData(str(self.Barcode))
self.qr.make()
def _drawPixel(self, xposition, yposition):
# build a rectangular pad: as a dot
pad = pcbnew.D_PAD(self.module)
pad.SetSize(pcbnew.wxSize(self.X, self.X))
pad.SetShape(pcbnew.PAD_SHAPE_RECT)
pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
layerset = pcbnew.LSET()
if self.UseCu:
layerset.AddLayer(pcbnew.F_Cu)
layerset.AddLayer(pcbnew.F_Mask)
if self.UseSilkS:
layerset.AddLayer(pcbnew.F_SilkS)
pad.SetLayerSet( layerset )
pad.SetPosition(pcbnew.wxPoint(xposition,yposition))
pad.SetPadName("1")
self.module.Add(pad)
def BuildThisFootprint(self):
if self.border >= 0:
# Adding border: Create a new array larger than the self.qr.modules
sz = self.qr.modules.__len__() + (self.border * 2)
arrayToDraw = [ [ 0 for a in range(sz) ] for b in range(sz) ]
lineposition = self.border
for i in self.qr.modules:
columnposition = self.border
for j in i:
arrayToDraw[lineposition][columnposition] = j
columnposition += 1
lineposition += 1
else:
# No border: using array as is
arrayToDraw = self.qr.modules
# used many times...
half_number_of_elements = arrayToDraw.__len__() / 2
# Center position of QrCode
yposition = - int(half_number_of_elements * self.X)
for line in arrayToDraw:
xposition = - int(half_number_of_elements * self.X)
for pixel in line:
# Trust table for drawing a pixel
# Negative is a boolean;
# each pixel is a boolean (need to draw of not)
# Negative | Pixel | Result
# 0 | 0 | 0
# 0 | 1 | 1
# 1 | 0 | 1
# 1 | 1 | 0
# => Draw as Xor
if self.negative != pixel: # Xor...
self._drawPixel(xposition, yposition)
xposition += self.X
yposition += self.X
#int((5 + half_number_of_elements) * self.X))
textPosition = int((self.textHeight) + ((1 + half_number_of_elements) * self.X))
self.module.Value().SetPosition(pcbnew.wxPoint(0, - textPosition))
self.module.Reference().SetPosition(pcbnew.wxPoint(0, textPosition))
self.module.Value().SetLayer(pcbnew.F_SilkS)
QRCodeWizard().register()

View File

@ -17,7 +17,7 @@
from __future__ import division from __future__ import division
import pcbnew import pcbnew
import HelpfulFootprintWizardPlugin as HFPW import FootprintWizardBase
import PadArray as PA import PadArray as PA
@ -35,7 +35,7 @@ class RowedGridArray(PA.PadGridArray):
return x+1 return x+1
class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): class RowedFootprint(FootprintWizardBase.FootprintWizard):
pad_count_key = 'pad count' pad_count_key = 'pad count'
row_count_key = 'row count' row_count_key = 'row count'
@ -50,31 +50,25 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
def GenerateParameterList(self): def GenerateParameterList(self):
# defaults for a DIP package # defaults for a DIP package
self.AddParam("Pads", self.pad_count_key, self.uNatural, 24) self.AddParam("Pads", self.pad_count_key, self.uInteger, 24)
self.AddParam("Pads", self.row_count_key, self.uNatural, 2) 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.silkscreen_inside_key, self.uBool, False)
self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5) self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5)
self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5) self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5)
def CheckParameters(self): def CheckParameters(self):
self.CheckParamInt("Pads", '*' + self.row_count_key, min_value=1, max_value=2) self.CheckParam("Pads", self.pad_count_key, multiple=self.parameters['Pads'][self.row_count_key], info='Pads must be multiple of row count')
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)
def BuildThisFootprint(self): def BuildThisFootprint(self):
pads = self.parameters["Pads"] pads = self.parameters["Pads"]
body = self.parameters["Body"] 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_length = pads[self.pad_length_key]
pad_width = pads[self.pad_width_key] pad_width = pads[self.pad_width_key]
row_pitch = pads[self.row_spacing_key] row_pitch = pads[self.row_spacing_key]
pad_pitch = pads[self.pad_pitch_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 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] ssx_offset = -pad_width / 2 - body[self.outline_x_margin_key]
ssy_offset = -pad_length / 2 - body[self.outline_y_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 ssy_offset *= -1
ssx = -pin1_posX - ssx_offset ssx = -pin1_posX - ssx_offset
@ -110,8 +104,8 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
sizex = (ssx + cmargin) * 2 sizex = (ssx + cmargin) * 2
sizey = (ssy + cmargin) * 2 sizey = (ssy + cmargin) * 2
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1) sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1) sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC # set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05)) self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey) self.draw.Box(0, 0, sizex, sizey)
@ -156,8 +150,8 @@ class SDIPWizard(RowedFootprint):
def GetValue(self): def GetValue(self):
pads = self.parameters["Pads"] pads = self.parameters["Pads"]
rows = pads['*' + self.row_count_key] rows = pads[self.row_count_key]
pad_count = pads['*' + self.pad_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) 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 = "" pad_shape = ""
@ -185,7 +179,7 @@ class SDIPWizard(RowedFootprint):
def DrawBox(self, ssx, ssy): 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 | # |8 7 6 5 |
@ -208,7 +202,7 @@ class SDIPWizard(RowedFootprint):
#line between pin1 and pin2 #line between pin1 and pin2
pad_pitch = self.parameters["Pads"][self.pad_pitch_key] 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 line_x = ( pad_cnt/2 - 1) * pad_pitch
self.draw.VLine(-line_x, -ssy, ssy * 2) self.draw.VLine(-line_x, -ssy, ssy * 2)
@ -226,7 +220,7 @@ class SOICWizard(RowedFootprint):
return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard" return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard"
def GetValue(self): 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) return "%s-%d" % ("SOIC", pad_count)
def GenerateParameterList(self): def GenerateParameterList(self):

View File

@ -24,10 +24,10 @@
# #
from pcbnew import * from pcbnew import *
import HelpfulFootprintWizardPlugin as HFPW import FootprintWizardBase
import pcbnew
class TouchSliderWizard(FootprintWizardBase.FootprintWizard):
class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
def GetName(self): def GetName(self):
""" """
@ -44,16 +44,23 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
return 'Capacitive Touch Slider wizard' return 'Capacitive Touch Slider wizard'
def GetValue(self): def GetValue(self):
steps = int(self.parameters["Pads"]["*steps"]) return "TouchSlider-{s}_{x:g}x{y:g}mm".format(
return "TS"+str(steps) s = self.pads['steps'],
x = pcbnew.ToMM(self.pads['length']),
y = pcbnew.ToMM(self.pads['width'])
)
def GenerateParameterList(self): def GenerateParameterList(self):
self.AddParam("Pads", "steps", self.uNatural, 4) self.AddParam("Pads", "steps", self.uInteger, 4, min_value=2)
self.AddParam("Pads", "bands", self.uNatural, 2) self.AddParam("Pads", "bands", self.uInteger, 2, min_value=1)
self.AddParam("Pads", "width", self.uMM, 10) self.AddParam("Pads", "width", self.uMM, 10)
self.AddParam("Pads", "length", self.uMM, 50) self.AddParam("Pads", "length", self.uMM, 50)
self.AddParam("Pads", "clearance", self.uMM, 1) self.AddParam("Pads", "clearance", self.uMM, 1)
@property
def pads(self):
return self.parameters['Pads']
# build a rectangular pad # build a rectangular pad
def smdRectPad(self,module,size,pos,name): def smdRectPad(self,module,size,pos,name):
pad = D_PAD(module) pad = D_PAD(module)
@ -82,18 +89,8 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
# This method checks the parameters provided to wizard and set errors # This method checks the parameters provided to wizard and set errors
def CheckParameters(self): def CheckParameters(self):
prms = self.parameters["Pads"] #TODO - implement custom checks
steps = prms["*steps"] pass
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"]
# The start pad is made of a rectangular pad plus a couple of # The start pad is made of a rectangular pad plus a couple of
# triangular pads facing tips on the middle/right of the first # triangular pads facing tips on the middle/right of the first
@ -177,18 +174,18 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
# build the footprint from parameters # build the footprint from parameters
# FIX ME: the X and Y position of the footprint can be better. # FIX ME: the X and Y position of the footprint can be better.
def BuildThisFootprint(self): def BuildThisFootprint(self):
prm = self.parameters["Pads"]
steps = int(prm["*steps"]) steps = self.pads["steps"]
bands = int(prm["*bands"]) bands = self.pads["bands"]
touch_width = prm["width"] touch_width = self.pads["width"]
touch_length = prm["length"] touch_length = self.pads["length"]
touch_clearance = prm["clearance"] touch_clearance = self.pads["clearance"]
step_length = float(touch_length) / float(steps) step_length = float(touch_length) / float(steps)
t_size = self.GetTextSize() t_size = self.GetTextSize()
w_text = self.draw.GetLineThickness() 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) self.draw.Value(0, -ypos, t_size)
ypos += t_size + w_text*2 ypos += t_size + w_text*2
self.draw.Reference(0, -ypos, t_size) self.draw.Reference(0, -ypos, t_size)
@ -197,9 +194,13 @@ class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin):
self.module.SetAttributes(MOD_CMS) self.module.SetAttributes(MOD_CMS)
# starting pad # starting pad
pos = wxPointMM(0,0)
band_width = touch_width/bands 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): for b in range(bands):
self.AddStrip(pos,steps,band_width,step_length,touch_clearance) self.AddStrip(pos,steps,band_width,step_length,touch_clearance)
pos += wxPoint(0,band_width) pos += wxPoint(0,band_width)

View File

@ -15,8 +15,7 @@
from __future__ import division from __future__ import division
import pcbnew as B import pcbnew as B
import FootprintWizardBase
import HelpfulFootprintWizardPlugin
''' '''
Created on Jan 16, 2015 Created on Jan 16, 2015
@ -49,7 +48,7 @@ class Uss39:
# Reformated text with start and end characters # 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))]) 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' GetName = lambda self: 'BARCODE USS-39'
GetDescription = lambda self: 'USS-39 Barcode' GetDescription = lambda self: 'USS-39 Barcode'
GetReferencePrefix = lambda self: 'BARCODE' GetReferencePrefix = lambda self: 'BARCODE'
@ -61,18 +60,20 @@ class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin):
self.AddParam("Barcode", "Height", self.uMM, 3.0) self.AddParam("Barcode", "Height", self.uMM, 3.0)
self.AddParam("Barcode", "Margin", self.uMM, 2.0) self.AddParam("Barcode", "Margin", self.uMM, 2.0)
self.AddParam("Barcode", "Contents", self.uString, 'BARCODE') self.AddParam("Barcode", "Contents", self.uString, 'BARCODE')
self.AddParam("Caption", "Enabled", self.uBool, True) self.AddParam("Caption", "Enabled", self.uBool, True)
self.AddParam("Caption", "Height", self.uMM, 1.2) self.AddParam("Caption", "Height", self.uMM, 1.2)
self.AddParam("Caption", "Thickness", self.uMM, 0.12) self.AddParam("Caption", "Thickness", self.uMM, 0.12)
def CheckParameters(self): def CheckParameters(self):
# Reset constants # Reset constants
self.CourtyardLineWidth = B.FromMM(0.05) self.CourtyardLineWidth = B.FromMM(0.05)
# Set bar height to the greater of 6.35mm or 0.15*L # Set bar height to the greater of 6.35mm or 0.15*L
# Set quiet width to 10*X # Set quiet width to 10*X
# User-defined parameters # User-defined parameters
# Create barcode object # 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.X = int(self.parameters['Barcode']['Pixel Width'])
self.module.Value().SetText( str(self.Barcode) ) self.module.Value().SetText( str(self.Barcode) )
self.C = len(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.draw.Circle(0, 0, B.FromMM(0.25))
self.module.Value().SetLayer(B.F_Fab) self.module.Value().SetLayer(B.F_Fab)
Uss39Wizard().register() Uss39Wizard().register()

View File

@ -17,52 +17,41 @@
from __future__ import division from __future__ import division
import pcbnew import pcbnew
import HelpfulFootprintWizardPlugin as HFPW import FootprintWizardBase
import PadArray as PA import PadArray as PA
class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): class RowedFootprint(FootprintWizardBase.FootprintWizard):
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'
def GenerateParameterList(self): def GenerateParameterList(self):
# defaults for a ZIP package # defaults for a ZIP package
self.AddParam("Pads", self.pad_count_key, self.uNatural, 24) self.AddParam("Pads", "pad count", self.uInteger, 24)
self.AddParam("Pads", self.line_count_key, self.uNatural, 2) self.AddParam("Pads", "line count", self.uInteger, 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", "silkscreen inside", self.uBool, False)
self.AddParam("Body", self.courtyard_y_margin_key, self.uMM, 1) 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): def CheckParameters(self):
self.CheckParamInt("Pads", '*' + self.pad_count_key) # TODO - implement custom checks
self.CheckParamInt("Pads", '*' + self.line_count_key) pass
# can do this internally to parameter manager?
self.CheckParamBool("Body", '*' + self.silkscreen_inside_key)
def BuildThisFootprint(self): def BuildThisFootprint(self):
pads = self.parameters["Pads"]
body = self.parameters["Body"]
pad_count = pads['*' + self.pad_count_key] pad_count = self.pads['pad count']
pad_Vsize = pads[self.pad_vertical_size_key] pad_Vsize = self.pads['pad height']
pad_Hsize = pads[self.pad_horizontal_size_key] pad_Hsize = self.pads['pad width']
line_pitch = pads[self.line_spacing_key] line_pitch = self.pads['line spacing']
pad_pitch = pads[self.pad_pitch_key] pad_pitch = self.pads['pitch']
line_count = pads['*' + self.line_count_key] line_count = self.pads['line count']
if line_count == 1: if line_count == 1:
singleline = True singleline = True
@ -74,12 +63,12 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
array = PA.PadZGridArray(pad, pad_count, line_count, line_pitch, pad_pitch) array = PA.PadZGridArray(pad, pad_count, line_count, line_pitch, pad_pitch)
array.AddPadsToModule(self.draw) array.AddPadsToModule(self.draw)
# draw the Silk Screen # draw the Silk Screen
pads_per_line = pad_count // line_count pads_per_line = pad_count // line_count
row_length = pad_pitch * (pads_per_line - 1) # fenceposts row_length = pad_pitch * (pads_per_line - 1) # fenceposts
ssx_offset = pad_Hsize / 2 + body[self.outline_x_margin_key] ssx_offset = pad_Hsize / 2 + self.body['outline x margin']
ssy_offset = pad_Vsize / 2 + body[self.outline_y_margin_key] ssy_offset = pad_Vsize / 2 + self.body['outline y margin']
pin1posX = pad_pitch * (pad_count - 1) / 2 pin1posX = pad_pitch * (pad_count - 1) / 2
pin1posY = line_pitch * (line_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. # body inside pads is possible only for 2 rows.
# for other values, there is no room # for other values, there is no room
linew = self.draw.GetLineThickness() 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 cornery = pin1posY - ssy_offset
if cornery < linew: if cornery < linew:
cornery = linew cornery = linew
@ -99,15 +88,15 @@ class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin):
self.DrawBox(leftx*2, cornery*2) self.DrawBox(leftx*2, cornery*2)
# Courtyard # Courtyard
cmarginx = body[self.courtyard_x_margin_key] cmarginx = self.body['courtyard margin']
cmarginy = body[self.courtyard_y_margin_key] cmarginy = cmarginx
self.draw.SetLayer(pcbnew.F_CrtYd) self.draw.SetLayer(pcbnew.F_CrtYd)
thick = self.draw.GetLineThickness() thick = self.draw.GetLineThickness()
sizex = (pin1posX + cmarginx) * 2 + pad_Hsize + thick sizex = (pin1posX + cmarginx) * 2 + pad_Hsize + thick
sizey = (pin1posY + cmarginy) * 2 + pad_Vsize + thick sizey = (pin1posY + cmarginy) * 2 + pad_Vsize + thick
# round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid # round size to nearest 0.1mm, rectangle will thus land on a 0.05mm grid
sizex = self.PutOnGridMM(sizex, 0.1) sizex = pcbnew.PutOnGridMM(sizex, 0.1)
sizey = self.PutOnGridMM(sizey, 0.1) sizey = pcbnew.PutOnGridMM(sizey, 0.1)
# set courtyard line thickness to the one defined in KLC # set courtyard line thickness to the one defined in KLC
self.draw.SetLineThickness(pcbnew.FromMM(0.05)) self.draw.SetLineThickness(pcbnew.FromMM(0.05))
self.draw.Box(0, 0, sizex, sizey) self.draw.Box(0, 0, sizex, sizey)
@ -152,17 +141,17 @@ class ZIPWizard(RowedFootprint):
def GenerateParameterList(self): def GenerateParameterList(self):
RowedFootprint.GenerateParameterList(self) RowedFootprint.GenerateParameterList(self)
self.AddParam("Pads", self.pad_pitch_key, self.uMM, 1.27) self.AddParam("Pads", "pitch", self.uMM, 1.27)
self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 1.2) self.AddParam("Pads", "pad width", self.uMM, 1.2)
self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 2) self.AddParam("Pads", "pad height", self.uMM, 2)
self.AddParam("Pads", self.line_spacing_key, self.uMM, 2.54) self.AddParam("Pads", "line spacing", self.uMM, 2.54)
self.AddParam("Pads", self.drill_size_key, self.uMM, 0.8) self.AddParam("Pads", "drill size", self.uMM, 0.8)
self.AddParam("Body", self.outline_x_margin_key, self.uMM, 1) self.AddParam("Body", 'outline x margin', self.uMM, 1)
self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5) self.AddParam("Body", 'outline y margin', self.uMM, 0.5)
def GetValue(self): def GetValue(self):
rows = self.parameters["Pads"]['*' + self.line_count_key] rows = self.pads['line count']
pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key] pad_cnt = self.pads['pad count']
if rows == 1: if rows == 1:
name = "SIP" name = "SIP"
@ -174,9 +163,9 @@ class ZIPWizard(RowedFootprint):
return "%s-%d" % (name, pad_cnt) return "%s-%d" % (name, pad_cnt)
def GetPad(self): def GetPad(self):
pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key] pad_Vsize = self.pads['pad height']
pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key] pad_Hsize = self.pads['pad width']
drill = self.parameters["Pads"][self.drill_size_key] drill = self.pads['drill size']
return PA.PadMaker(self.module).THPad( return PA.PadMaker(self.module).THPad(
pad_Vsize, pad_Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL) pad_Vsize, pad_Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL)
@ -192,23 +181,23 @@ class ZOICWizard(RowedFootprint):
return "ZOIC, etc, Footprint Wizard" return "ZOIC, etc, Footprint Wizard"
def GetValue(self): 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): def GenerateParameterList(self):
RowedFootprint.GenerateParameterList(self) RowedFootprint.GenerateParameterList(self)
#and override some of them #and override some of them
self.AddParam("Pads", self.pad_pitch_key, self.uMM, 0.6) self.AddParam("Pads", "pitch", self.uMM, 0.6)
self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 0.6) self.AddParam("Pads", "pad width", self.uMM, 0.6)
self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 1.8) self.AddParam("Pads", "pad height", self.uMM, 1.8)
self.AddParam("Pads", self.line_spacing_key, self.uMM, 5.2) self.AddParam("Pads", "line spacing", self.uMM, 5.2)
self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5) self.AddParam("Body", "outline x margin", self.uMM, 0.5)
self.AddParam("Body", self.outline_y_margin_key, self.uMM, 1) self.AddParam("Body", "outline y margin", self.uMM, 1)
def GetPad(self): def GetPad(self):
pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key] pad_Vsize = self.pads['pad height']
pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key] pad_Hsize = self.pads['pad width']
return PA.PadMaker(self.module).SMDPad( return PA.PadMaker(self.module).SMDPad(
pad_Vsize, pad_Hsize, shape=pcbnew.PAD_SHAPE_RECT) pad_Vsize, pad_Hsize, shape=pcbnew.PAD_SHAPE_RECT)

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo <miguelangel@ajo.es> * Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo <miguelangel@ajo.es>
* Copyright (C) 2013 KiCad Developers, see CHANGELOG.TXT for contributors. * Copyright (C) 2016 KiCad Developers, see CHANGELOG.TXT for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -237,24 +237,9 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterTypes( int aPage )
PyObject* arglist = Py_BuildValue( "(i)", aPage ); PyObject* arglist = Py_BuildValue( "(i)", aPage );
ret = CallRetArrayStrMethod( "GetParameterNames", arglist ); ret = CallRetArrayStrMethod( "GetParameterTypes", arglist );
Py_DECREF( 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; return ret;
} }
@ -284,6 +269,29 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterErrors( int aPage )
return ret; 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 ) wxString PYTHON_FOOTPRINT_WIZARD::SetParameterValues( int aPage, wxArrayString& aValues )
{ {
@ -309,6 +317,13 @@ wxString PYTHON_FOOTPRINT_WIZARD::SetParameterValues( int aPage, wxArrayString&
return res; return res;
} }
void PYTHON_FOOTPRINT_WIZARD::ResetParameters()
{
PyLOCK lock;
CallMethod( "ResetWizard", NULL );
}
// this is a SWIG function declaration -from module.i // this is a SWIG function declaration -from module.i
MODULE* PyModule_to_MODULE( PyObject* obj0 ); MODULE* PyModule_to_MODULE( PyObject* obj0 );

View File

@ -45,6 +45,7 @@ class PYTHON_FOOTPRINT_WIZARD : public FOOTPRINT_WIZARD
public: public:
PYTHON_FOOTPRINT_WIZARD( PyObject* wizard ); PYTHON_FOOTPRINT_WIZARD( PyObject* wizard );
~PYTHON_FOOTPRINT_WIZARD(); ~PYTHON_FOOTPRINT_WIZARD();
wxString GetName() override; wxString GetName() override;
wxString GetImage() override; wxString GetImage() override;
wxString GetDescription() override; wxString GetDescription() override;
@ -58,6 +59,10 @@ public:
wxString SetParameterValues( int aPage, wxArrayString& aValues ) override; wxString SetParameterValues( int aPage, wxArrayString& aValues ) override;
MODULE* GetFootprint( wxString * aMessages ) override; MODULE* GetFootprint( wxString * aMessages ) override;
void* GetObject() override; void* GetObject() override;
wxArrayString GetParameterHints( int aPage ) override;
wxArrayString GetParameterDesignators( int aPage = 0) override;
void ResetParameters() override;
}; };

View File

@ -119,23 +119,19 @@ def LoadPlugins(bundlepath=None):
if bundlepath: if bundlepath:
plugin_directories.append(bundlepath) plugin_directories.append(bundlepath)
plugin_directories.append(os.path.join(bundlepath, 'plugins')) plugin_directories.append(os.path.join(bundlepath, 'plugins'))
plugin_directories.append(os.path.join(bundlepath, 'plugins', 'wizards'))
if kicad_path: if kicad_path:
plugin_directories.append(os.path.join(kicad_path, 'scripting')) plugin_directories.append(os.path.join(kicad_path, 'scripting'))
plugin_directories.append(os.path.join(kicad_path, 'scripting', 'plugins')) plugin_directories.append(os.path.join(kicad_path, 'scripting', 'plugins'))
plugin_directories.append(os.path.join(kicad_path, 'scripting', 'plugins', 'wizards'))
if config_path: if config_path:
plugin_directories.append(os.path.join(config_path, 'scripting')) plugin_directories.append(os.path.join(config_path, 'scripting'))
plugin_directories.append(os.path.join(config_path, 'scripting', 'plugins')) plugin_directories.append(os.path.join(config_path, 'scripting', 'plugins'))
plugin_directories.append(os.path.join(config_path, 'scripting', 'plugins', 'wizards'))
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):
plugin_directories.append(os.environ['HOME']+'/.kicad_plugins/') plugin_directories.append(os.environ['HOME']+'/.kicad_plugins/')
plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/') plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/')
plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/plugins/') plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/plugins/')
plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/plugins/wizards')
for plugins_dir in plugin_directories: for plugins_dir in plugin_directories:
if not os.path.isdir(plugins_dir): if not os.path.isdir(plugins_dir):
@ -164,7 +160,6 @@ def LoadPlugins(bundlepath=None):
pass pass
class KiCadPlugin: class KiCadPlugin:
def __init__(self): def __init__(self):
pass pass
@ -203,88 +198,295 @@ class FilePlugin(KiCadPlugin):
from math import ceil, floor, sqrt 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): def __init__(self):
KiCadPlugin.__init__(self) KiCadPlugin.__init__(self)
self.defaults() self.defaults()
def defaults(self): def defaults(self):
self.module = None self.module = None
self.parameters = {} self.params = [] # List of added parameters that observes addition order
self.parameter_errors={}
self.name = "Undefined Footprint Wizard plugin" self.name = "KiCad FP Wizard"
self.description = "" self.description = "Undefined Footprint Wizard plugin"
self.image = "" self.image = ""
self.buildmessages = "" 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 return self.name
def GetImage(self): def GetImage(self): # Return the filename of the preview image associated with this wizard
return self.image return self.image
def GetDescription(self): def GetDescription(self): # Return the description text
return self.description return self.description
def GetValue(self):
raise NotImplementedError
def GetNumParameterPages(self): def GetReferencePrefix(self):
return len(self.parameters) return "REF" # Default reference prefix for any footprint
def GetParameterPageName(self,page_n): def GetParam(self, page, name): # Grab a parameter
return self.page_order[page_n] for p in self.params:
if p.page == page and p.name == name:
return p
def GetParameterNames(self,page_n): return None
name = self.GetParameterPageName(page_n)
return self.parameter_order[name]
def GetParameterValues(self,page_n): def CheckParam(self, page, name, **kwarg):
name = self.GetParameterPageName(page_n) self.GetParam(page,name).Check(**kwarg)
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 GetParameterErrors(self,page_n): def AnyErrors(self):
self.CheckParameters() return any([len(p.error_list) > 0 for p in self.params])
name = self.GetParameterPageName(page_n)
names = self.GetParameterNames(page_n)
values = [self.parameter_errors[name][n] for n in names]
return map(lambda x: str(x), values) # list elements as strings
def CheckParameters(self): @property
return "" def pages(self): # Return an (ordered) list of the available page names
page_list = []
for p in self.params:
if p.page not in page_list:
page_list.append(p.page)
def ConvertValue(self,v): return page_list
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 SetParameterValues(self,page_n,values): def GetParameterPageName(self,page_n): # Return the name of a page at a given index
name = self.GetParameterPageName(page_n) return self.pages[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 = []
def ClearErrors(self): for p in self.params:
errs={} if p.page == page_name:
params.append(p)
for page in self.parameters.keys(): return params
page_dict = self.parameters[page]
page_params = {}
for param in page_dict.keys():
page_params[param]=""
errs[page]=page_params def GetParametersByPageIndex(self, page_index): # Return an ordered list of parameters on a given page
return self.GetParametersByPageName(self.GetParameterPageName(page_index))
self.parameter_errors = errs def GetParameterDesignators(self, page_index): # Return a list of designators associated with a given page
params = self.GetParametersByPageIndex(page_index)
return [p.designator for p in params]
def GetParameterNames(self,page_index): # Return the list of names associated with a given page
params = self.GetParametersByPageIndex(page_index)
return [p.name for p in params]
def GetParameterValues(self,page_index): # Return the list of values associated with a given page
params = self.GetParametersByPageIndex(page_index)
return [str(p.raw_value) for p in params]
def GetParameterErrors(self,page_index): # Return list of errors associated with a given page
params = self.GetParametersByPageIndex(page_index)
return [str("\n".join(p.error_list)) for p in params]
def GetParameterTypes(self, page_index): # Return list of units associated with a given page
params = self.GetParametersByPageIndex(page_index)
return [str(p.units) for p in params]
def GetParameterHints(self, page_index): # Return a list of units associated with a given page
params = self.GetParametersByPageIndex(page_index)
return [str(p.hint) for p in params]
def GetParameterDesignators(self, page_index): # Return a list of designators associated with a given page
params = self.GetParametersByPageIndex(page_index)
return [str(p.designator) for p in params]
def SetParameterValues(self, page_index, list_of_values): # Update values on a given page
params = self.GetParametersByPageIndex(page_index)
for i, param in enumerate(params):
if i >= len(list_of_values):
break
param.SetValue(list_of_values[i])
def GetFootprint( self ): def GetFootprint( self ):
self.BuildFootprint() self.BuildFootprint()
@ -297,17 +499,30 @@ class FootprintWizardPlugin(KiCadPlugin):
return self.buildmessages return self.buildmessages
def Show(self): def Show(self):
print "Footprint Wizard Name: ",self.GetName() text = "Footprint Wizard Name: {name}\n".format(name=self.GetName())
print "Footprint Wizard Description: ",self.GetDescription() text += "Footprint Wizard Description: {desc}\n".format(desc=self.GetDescription())
n_pages = self.GetNumParameterPages() n_pages = self.GetNumParameterPages()
print " setup pages: ",n_pages
for page in range(0,n_pages): text += "Pages: {n}\n".format(n=n_pages)
name = self.GetParameterPageName(page)
values = self.GetParameterValues(page) for i in range(n_pages):
names = self.GetParameterNames(page) name = self.GetParameterPageName(i)
print "page %d) %s"%(page,name)
for n in range (0,len(values)): params = self.GetParametersByPageName(name)
print "\t%s\t:\t%s"%(names[n],values[n])
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): class ActionPlugin(KiCadPlugin):
def __init__(self): def __init__(self):