Footprint Wizards Update
This commit is contained in:
parent
d1278a48b6
commit
66ee44cb1f
|
@ -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>" );
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"]
|
|
@ -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)
|
|
@ -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
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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() ) )
|
|
@ -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()
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue