kicad/pcbnew/python/plugins/qrcode_footprint_wizard.py

223 lines
9.0 KiB
Python
Raw Normal View History

2017-01-03 15:01:47 +00:00
# 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.
# last change: 2021, Jul 19.
2017-01-03 15:01:47 +00:00
import pcbnew
import FootprintWizardBase
# Additional import for QRCode
# see https://github.com/kazuhikoarase/qrcode-generator/blob/master/python/qrcode.py
import kicad_qrcode as qrcode # TODO: local qrcode package is preferred, so we renamed it
2017-01-03 15:01:47 +00:00
# Max type number for the QRCode is 10, this is restricted by the upstream library
# https://github.com/kazuhikoarase/qrcode-generator
_PYTHON_QR_LIB_MAX_TYPE_NO = len(qrcode.QRUtil.MAX_LENGTH)
2017-01-03 15:01:47 +00:00
class QRCodeWizard(FootprintWizardBase.FootprintWizard):
GetName = lambda self: '2D Barcode QRCode'
GetDescription = lambda self: 'QR Code barcode generator'
2017-01-03 15:01:47 +00:00
GetReferencePrefix = lambda self: 'QR***'
GetValue = lambda self: self.module.Value().GetText()
def GenerateParameterList(self):
# TODO: add params for ECLevel and qrcode mode (num/alnum/bytes)
self.AddParam("Barcode", "Qr Pixel Width", self.uMM, 0.5)
self.AddParam(
"Barcode",
"Border Margin (Px)",
self.uInteger,
0,
min_value=0,
)
self.AddParam(
"Barcode",
"Type Number",
self.uInteger,
0,
min_value=0,
max_value=_PYTHON_QR_LIB_MAX_TYPE_NO,
hint="Set to 0 for autodetect (will use the smallest possible)",
)
2017-01-03 15:01:47 +00:00
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.0)
self.AddParam("Caption", "Width", self.uMM, 1.0)
self.AddParam("Caption", "Thickness", self.uMM, 0.15)
2017-01-03 15:01:47 +00:00
def CheckParameters(self):
self.Barcode = self.parameters['Barcode']['Contents']
self.TypeNumber = self.parameters['Barcode']['Type Number']
self.X = self.parameters['Barcode']['Qr Pixel Width']
2017-01-03 15:01:47 +00:00
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 Margin (Px)'])
2017-01-03 15:01:47 +00:00
self.textHeight = int(self.parameters['Caption']['Height'])
self.textThickness = int(self.parameters['Caption']['Thickness'])
self.textWidth = int(self.parameters['Caption']['Width'])
self.module.Value().SetText(str(self.Barcode))
2017-01-03 15:01:47 +00:00
2017-01-03 15:01:47 +00:00
# ErrorCorrectLevel: L = 7%, M = 15% Q = 25% H = 30%
self.ECLevel = qrcode.ErrorCorrectLevel.M
# Check if the content is too long
# technically we don't need this conversion (TypeNumber=0 will be
# regarded as max) but this isn't part of the documented API, so we'll
# convert to be safe
max_type_num = (
_PYTHON_QR_LIB_MAX_TYPE_NO
if self.TypeNumber == 0
else self.TypeNumber
)
max_length = qrcode.QRUtil.getMaxLength(
max_type_num,
qrcode.Mode.MODE_8BIT_BYTE,
self.ECLevel
)
if len(self.Barcode) > max_length:
self.GetParam("Barcode", "Contents").AddError(
f"Content too long ({len(self.Barcode)} > {max_length}) for"
" the provided parameters."
)
2017-01-03 15:01:47 +00:00
def drawPixelSquareArea( self, layer, size, xposition, yposition):
# creates a PCB_SHAPE of rectangle type. The rectangle is square
rectangle = pcbnew.PCB_SHAPE(self.module)
rectangle.SetShape(pcbnew.S_RECT)
rectangle.SetWidth( 0 )
rectangle.SetFilled( True )
rectangle.SetLayer(layer)
halfsize = int(size/2)
rectangle.SetStartX( -halfsize+xposition )
rectangle.SetStartY( -halfsize+yposition )
rectangle.SetEndX( halfsize+xposition )
rectangle.SetEndY( halfsize+yposition )
return rectangle
def _drawQrPixel(self, xposition, yposition):
# build a rectangular pad as a dot on copper layer,
# and a rectangle (a square) on silkscreen
2017-01-03 15:01:47 +00:00
if self.UseCu:
2020-11-14 01:06:05 +00:00
pad = pcbnew.PAD(self.module)
pad.SetSize(pcbnew.VECTOR2I(self.X, self.X))
pad_pos = pcbnew.VECTOR2I(xposition,yposition)
pad.SetPosition(pad_pos)
pad.SetShape(pcbnew.PAD_SHAPE_RECT)
pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD)
pad.SetName("")
layerset = pcbnew.LSET()
2017-01-03 15:01:47 +00:00
layerset.AddLayer(pcbnew.F_Cu)
pad.SetLayerSet( layerset )
self.module.Add(pad)
2017-01-03 15:01:47 +00:00
if self.UseSilkS:
rectangle=self.drawPixelSquareArea(pcbnew.F_SilkS, self.X, xposition, yposition)
self.module.Add(rectangle)
2017-01-03 15:01:47 +00:00
def BuildThisFootprint(self):
# Build QRCode
# Auto detect type number
if self.TypeNumber == 0:
self.qr = qrcode.QRCode.getMinimumQRCode(self.Barcode, self.ECLevel)
# Manually specified type number
else:
self.qr = qrcode.QRCode()
self.qr.setTypeNumber(self.TypeNumber)
self.qr.setErrorCorrectLevel(self.ECLevel)
self.qr.addData(self.Barcode)
self.qr.make()
# render QRCode
2017-01-03 15:01:47 +00:00
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
2017-01-03 15:01:47 +00:00
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
area_width = arrayToDraw.__len__()*self.X + self.border*2
2017-01-03 15:01:47 +00:00
# Center position of QrCode
yposition = - int(half_number_of_elements * self.X) + int(self.X/2)
area_height = arrayToDraw.__len__()*self.X + self.border*2
2017-01-03 15:01:47 +00:00
for line in arrayToDraw:
xposition = - int(half_number_of_elements * self.X) + int(self.X/2)
2017-01-03 15:01:47 +00:00
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._drawQrPixel(xposition, yposition)
2017-01-03 15:01:47 +00:00
xposition += self.X
yposition += self.X
# Add value field
2017-01-03 15:01:47 +00:00
textPosition = int((self.textHeight) + ((1 + half_number_of_elements) * self.X))
pos = pcbnew.VECTOR2I(0, - textPosition)
self.module.Value().SetPosition(pos)
self.module.Value().SetTextHeight(self.textHeight)
self.module.Value().SetTextWidth(self.textWidth)
self.module.Value().SetTextThickness(self.textThickness)
# Add Reference field
pos = pcbnew.VECTOR2I(0, textPosition)
self.module.Reference().SetPosition(pos)
self.module.Reference().SetTextHeight(self.textHeight)
self.module.Reference().SetTextWidth(self.textWidth)
self.module.Reference().SetTextThickness(self.textThickness)
2017-01-03 15:01:47 +00:00
self.module.Value().SetLayer(pcbnew.F_SilkS)
#build the footprint courtyard
self.draw.SetLayer( pcbnew.F_CrtYd )
self.draw.SetLineThickness( pcbnew.FromMM( 0.05 ) ) #Default per KLC F5.1 as of 12/2018
cr_margin = pcbnew.FromMM( 0.1 )
self.draw.Box(0, 0, area_width + cr_margin*2, area_height + cr_margin*2)
#build the footprint solder mask: the solder mask covers all copper pads
if self.UseCu:
self.draw.SetLineThickness( 0 )
sm_margin = pcbnew.FromMM( 0.05 )
rectangle=self.drawPixelSquareArea(pcbnew.F_Mask, area_width + sm_margin*2, 0, 0)
self.module.Add(rectangle)
2017-01-03 15:01:47 +00:00
QRCodeWizard().register()