While KiCad offers a powerful platform for PCB design, finding resources on creating custom plugins specifically for KiCad 8 can be a challenge. This scarcity of examples can hinder creative exploration and automation possibilities.
This article dives into the world of Python scripting for KiCad 8, showcasing the development of a plugin named phy_plugin.py
. This plugin tackles this gap by enabling the generation of phyllotaxis patterns, those captivating spiral arrangements seen in nature. We'll delve into the script's functionality, user interface, and how it leverages KiCad's features to automate pattern creation.
By following along, you'll gain insights into Python scripting for KiCad 8 and discover how to create visually stunning and space-optimized PCB layouts using the power of nature's design principles.
1. pcbnew.ActionPlugin:This class serves as the foundation for creating action plugins in KiCad version 8 (and tested with version 7). When you develop a KiCad plugin that interacts with the PCB layout and needs to be triggered by a user action, you inherit from this class.
Here's what the pcbnew.ActionPlugin
class offers:
- Structure: It defines specific methods that your plugin needs to implement. These methods handle functionalities like defining plugin properties:(name, category, description) and what happens when the user activates the plugin.
- Registration: The
register()
method within this class allows you to register your plugin with KiCad, making it accessible from the user interface.
import wx
import pcbnew
import math
class GreetingPlugin(pcbnew.ActionPlugin):
def defaults(self):
self.name = "Phyllotaxis Plugin"
self.category = "General"
self.description = "Phyllotactic patterns to generate designs."
def Run(self):
# your code will be here
# Register the plugin
GreetingPlugin().register()
2. User Input and dialog:The script presents a dialog box where users can input various parameter
- Number of LEDs: This defines the number of components to be placed in the pattern.
- Angle: The golden angle (approximately 137.5 degrees) is the default value, but it can be customized.
- Cspread: This controls the spiral's "tightness" and spacing between elements.
- LED Rotation: This allows individual component rotation for added design flexibility.
- Angle Offset: This enables fine-tuning of the starting angle for the spiral.
Dialog Creation: The script utilizes the wxPython library to create a graphical dialog box with input fields and buttons.
import wx
import pcbnew
import math
class GreetingPlugin(pcbnew.ActionPlugin):
def defaults(self):
self.name = "Phyllotaxis Plugin"
self.category = "General"
self.description = "Phyllotactic patterns to generate designs."
def Run(self):
# Create a custom dialog
app = wx.App(False)
dialog = wx.Dialog(None, title="Phy", size=(600, 500))
# Create a grid sizer with 2 columns
sizer = wx.GridSizer(rows=0, cols=2, hgap=10, vgap=15)
self.t_label = wx.StaticText(dialog, label="Enter number of LEDs:")
self.t_input = wx.TextCtrl(dialog, value="104")
sizer.Add(self.t_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.t_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.angle_label = wx.StaticText(dialog, label="Enter angle (137.508):")
self.angle_input = wx.TextCtrl(dialog, value="137.508")
sizer.Add(self.angle_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.angle_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.cspread_label = wx.StaticText(dialog, label="Enter cspread:")
self.cspread_input = wx.TextCtrl(dialog, value="2")
sizer.Add(self.cspread_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.cspread_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.rot_label = wx.StaticText(dialog, label="Enter Led Rotation:")
self.rot_input = wx.TextCtrl(dialog, value="90")
sizer.Add(self.rot_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.rot_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.angvalue_label = wx.StaticText(dialog, label="Enter angle offset (negative):")
self.angvalue_input = wx.TextCtrl(dialog, value="-1")
sizer.Add(self.angvalue_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.angvalue_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
dialog.SetSizer(sizer)
# Show the dialog
dialog.ShowModal()
dialog.Destroy()
app.MainLoop()
# Register the plugin
GreetingPlugin().register(
3.Button Functionality:- Run Script: Upon clicking this button, the script retrieves user-entered values and calls the
PhyllotacticPattern
function. - Close Window: This button gracefully closes the dialog box.
import wx
import pcbnew
import math
class GreetingPlugin(pcbnew.ActionPlugin):
def defaults(self):
self.name = "Phyllotaxis Plugin"
self.category = "General"
self.description = "Phyllotactic patterns to generate designs."
def Run(self):
# Create a custom dialog
app = wx.App(False)
dialog = wx.Dialog(None, title="Phy", size=(600, 500))
# Create a grid sizer with 2 columns
sizer = wx.GridSizer(rows=0, cols=2, hgap=10, vgap=15)
self.t_label = wx.StaticText(dialog, label="Enter number of LEDs:")
self.t_input = wx.TextCtrl(dialog, value="104")
sizer.Add(self.t_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.t_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.angle_label = wx.StaticText(dialog, label="Enter angle (137.508):")
self.angle_input = wx.TextCtrl(dialog, value="137.508")
sizer.Add(self.angle_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.angle_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.cspread_label = wx.StaticText(dialog, label="Enter cspread:")
self.cspread_input = wx.TextCtrl(dialog, value="2")
sizer.Add(self.cspread_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.cspread_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.rot_label = wx.StaticText(dialog, label="Enter Led Rotation:")
self.rot_input = wx.TextCtrl(dialog, value="90")
sizer.Add(self.rot_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.rot_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.angvalue_label = wx.StaticText(dialog, label="Enter angle offset (negative):")
self.angvalue_input = wx.TextCtrl(dialog, value="-1")
sizer.Add(self.angvalue_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.angvalue_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
# Add buttons
ok_button = wx.Button(dialog, label="Run Script")
close_button = wx.Button(dialog, label="Close Window")
def on_close_button_click(event):
dialog.Close()
ok_button.Bind(wx.EVT_BUTTON, self.on_ok_button_click)
close_button.Bind(wx.EVT_BUTTON, on_close_button_click)
# Add buttons to the sizer
sizer.Add(close_button, 0, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 10)
sizer.Add(ok_button, 0, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 10)
dialog.SetSizer(sizer)
# Show the dialog
dialog.ShowModal()
dialog.Destroy()
app.MainLoop()
def on_ok_button_click(self, event):
t = int(self.t_input.GetValue())
angle = float(self.angle_input.GetValue())
cspread = float(self.cspread_input.GetValue())
rot = int(self.rot_input.GetValue())
angvalue = float(self.angvalue_input.GetValue())
PhyllotacticPattern(t, angle, cspread, rot, angvalue)
# Register the plugin
GreetingPlugin().register()
4.Phyllotactic Pattern Generation:In the last step we are adding the function to move LEDs to the calculated positions. The PhyllotacticPattern
function takes the user-specified parameters and performs the following:
- Calculates the spiral arm length and angle for each component based on the golden angle and user-defined parameters.
- Iterates through the number of LEDs, finding their corresponding footprints in the PCB design.
- Positions each component at the calculated coordinates using the
SetPosition
method. - Sets the desired rotation for each component using the
SetOrientationDegrees
method. - Refreshes the PCB view to reflect the changes.
pcbnew.Refresh()
This function plays a crucial role in updating the PCB layout view within KiCad. When your plugin modifies the PCB design by placing components, changing their positions or orientations, calling pcbnew.Refresh()
ensures that these changes are reflected visually in the software's interface.
In the context of phy_plugin.py
, the script utilizes pcbnew.Refresh()
after generating the phyllotaxis pattern. This ensures that the newly placed and rotated components become visible in the PCB layout view, allowing the user to see the generated pattern.
import wx
import pcbnew
import math
class GreetingPlugin(pcbnew.ActionPlugin):
def defaults(self):
self.name = "Phyllotaxis Plugin"
self.category = "General"
self.description = "Phyllotactic patterns to generate designs."
def Run(self):
# Create a custom dialog
app = wx.App(False)
dialog = wx.Dialog(None, title="Phy", size=(600, 500))
# Create a grid sizer with 2 columns
sizer = wx.GridSizer(rows=0, cols=2, hgap=10, vgap=15)
self.t_label = wx.StaticText(dialog, label="Enter number of LEDs:")
self.t_input = wx.TextCtrl(dialog, value="104")
sizer.Add(self.t_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.t_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.angle_label = wx.StaticText(dialog, label="Enter angle (137.508):")
self.angle_input = wx.TextCtrl(dialog, value="137.508")
sizer.Add(self.angle_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.angle_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.cspread_label = wx.StaticText(dialog, label="Enter cspread:")
self.cspread_input = wx.TextCtrl(dialog, value="2")
sizer.Add(self.cspread_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.cspread_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.rot_label = wx.StaticText(dialog, label="Enter Led Rotation:")
self.rot_input = wx.TextCtrl(dialog, value="90")
sizer.Add(self.rot_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.rot_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.angvalue_label = wx.StaticText(dialog, label="Enter angle offset (negative):")
self.angvalue_input = wx.TextCtrl(dialog, value="-1")
sizer.Add(self.angvalue_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.angvalue_input, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
# Add buttons
ok_button = wx.Button(dialog, label="Run Script")
close_button = wx.Button(dialog, label="Close Window")
def on_close_button_click(event):
dialog.Close()
ok_button.Bind(wx.EVT_BUTTON, self.on_ok_button_click)
close_button.Bind(wx.EVT_BUTTON, on_close_button_click)
# Add buttons to the sizer
sizer.Add(close_button, 0, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 10)
sizer.Add(ok_button, 0, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 10)
dialog.SetSizer(sizer)
# Show the dialog
dialog.ShowModal()
dialog.Destroy()
app.MainLoop()
def on_ok_button_click(self, event):
t = int(self.t_input.GetValue())
angle = float(self.angle_input.GetValue())
cspread = float(self.cspread_input.GetValue())
rot = int(self.rot_input.GetValue())
angvalue = float(self.angvalue_input.GetValue())
PhyllotacticPattern(t, angle, cspread, rot, angvalue)
# Register the plugin
GreetingPlugin().register()
def PhyllotacticPattern(t, angle = 137.508, cspread = 4, rot = 90, angvalue = -1):
phi = angle * ( math.pi / 180.0 )
xcenter = 100.0
ycenter = 100.0
pcb = pcbnew.GetBoard()
for n in range (0,t):
r = cspread * math.sqrt(n)
theta = (n) * phi
part = pcb.FindFootprintByReference("D" + str(n+1))
x = r * math.cos(theta) + xcenter
y = r * math.sin(theta) + ycenter
d = (n*angvalue*angle)+rot
part.SetPosition(pcbnew.VECTOR2I_MM(x, y))
part.SetOrientationDegrees(d)
pcbnew.Refresh()
Next:
- Open KiCad and go to the PCB Editor (PCBnew).
- Navigate to the Tools menu at the top and select External Plugins.
- Within the External Plugins submenu, click on Open Plugin Directory.
This will automatically open the directory where KiCad expects to find external plugins. You can then place your phy_plugin.py
script in this directory.
Next click on Refresh Plugins. KiCad will now scan the plugin directory and update the list of available plugins. If the phy_plugin.py
script was placed in the correct location, it should now be visible in the list. You will be able to run it from the pulldown menu.
Comments
Please log in or sign up to comment.