Adding GUI elements to pcbnew is what this post is about.1. The code I’m talking about can be found here.
The pcbnew GUI is written using wxWidgets/wxPython. If you’re not familiar with wxPython, here is my favorite tutorial so far. All of its python APIs work fine for me in pcbnew so far. I’ve found there are two important things to keep in mind:
- You don’t need to do the normal app = MyApp(0); app.MainLoop() 2
- If you do print from callback (for debugging) the text will not be in the python window. Instead, you can find it in the terminal where you invoked kicad. Keep this in mind if you usually invoke from the kicad project manager or from you OS task launcher.
As usual, you’ll need some imports:
import wx import pcbnewTo create a new window, you’ll want to create a new class derived from wx.Frame3. The initializer/constructor will create the new GUI elements and you’ll want some methods to use as event callbacks. To determine the placement of the new widgets, I’m using BoxSizer, but there are a bunch of other options.
Here are some of the high level points for when your reading the code below:
- All widgets automagically get a unique negative id. You can pick your own numbers if you’d like. You’ll need the id when binding callbacks, given a widget pointer, just call GetId().
- wx.StaticText is a simple text label.
- wx.Button is a button. duh
- wx.ComboBox gives you a scrollable list selection. I use it to pick a net or module.
- wx.BoxSizer is just for layout. Line things up vertically or horizontally
- To tell wx about your callback functions, use the bind method
[scroll-box]
class SimpleGui(wx.Frame): def __init__(self, parent, board): wx.Frame.__init__(self, parent, title="this is the title") self.panel = wx.Panel(self) label = wx.StaticText(self.panel, label = "Hello World") button = wx.Button(self.panel, label="Button label", id=1) nets = board.GetNetsByName() self.netnames = [] for netname, net in nets.items(): if (str(netname) == ""): continue self.netnames.append(str(netname)) netcb = wx.ComboBox(self.panel, choices=self.netnames) netcb.SetSelection(0) netsbox = wx.BoxSizer(wx.HORIZONTAL) netsbox.Add(wx.StaticText(self.panel, label="Nets:")) netsbox.Add(netcb, proportion=1) modules = board.GetModules() self.modulenames = [] for mod in modules: self.modulenames.append("{}({})".format(mod.GetReference(), mod.GetValue())) modcb = wx.ComboBox(self.panel, choices=self.modulenames) modcb.SetSelection(0) modsbox = wx.BoxSizer(wx.HORIZONTAL) modsbox.Add(wx.StaticText(self.panel, label="Modules:")) modsbox.Add(modcb, proportion=1) box = wx.BoxSizer(wx.VERTICAL) box.Add(label, proportion=0) box.Add(button, proportion=0) box.Add(netsbox, proportion=0) box.Add(modsbox, proportion=0) self.panel.SetSizer(box) self.Bind(wx.EVT_BUTTON, self.OnPress, id=1) self.Bind(wx.EVT_COMBOBOX, self.OnSelectNet, id=netcb.GetId()) self.Bind(wx.EVT_COMBOBOX, self.OnSelectMod, id=modcb.GetId()) def OnPress(self, event): print("in OnPress") def OnSelectNet(self, event): item = event.GetSelection() print("Net {} was selected".format(self.netnames[item])) def OnSelectMod(self, event): item = event.GetSelection() print("Module {} was selected".format(self.modulenames[item]))[/scroll-box]
Now that we have the derived GUI class, it’s a simple matter of instantiating it.
def InitSimpleGui(board): sg = SimpleGui(None, board) sg.Show(True) return sg sg = InitSimpleGui(pcbnew.GetBoard())And that’s it. If you find this useful and if you end up creating something for pcbnew, I’d love to hear about it.
adding GUI elements in pcbnew