Package: kigadgets
kicad-python: atait forkDevelopment of a new Python scripting API for KiCad
based on Piers Titus van der Torren work and comunity
feedback to create a less C++ tied API.DescriptionKiCAD and pcbnew expose a python API that allows plugins and other procedural processing of PCB layouts. There are limitations of using this API directly:its documentationis empty (v7 does not exist yet); it is a clunky SWIG/C-style API with custom datatypes for things like lists; its API changes for every KiCAD version; and it exposes too much functionality on equal footing.Even if the perfect built-in KiCAD python API came tomorrow, new plugins written on that API would not work in v4-v7, and old plugins would no longer work. Plugins written usingkicad-pythoninstead are backwards compatible, forwards compatible, and easier to understand for KiCAD newcomers.This package is a pythonic wrapper around the variouspcbnewAPIs. It implements patterns such as objects, properties, and iterables. It performs more intuitive unit and layer handling. It only exposes functionality most relevant to editing boards, the idea being that native functionality can always be accessed through the wrapped objects if needed.This package has been fully tested with KiCAD 5, 6, 7 and partially tested with 7.99.[!CAUTION]
The atait fork is undergoing a refactor that will result in new package imports.
Instances offrom kicad.pcbnew.board import Boardmust be replaced byfrom kigadgets.board import Boardby version 0.5.0An excerptA simple pythonic script might look like thisprint([track.layerfortrackinpcb.tracks])print([track.widthfortrackinpcb.tracksiftrack.is_selected])which produces[F.Cu, B.Cu, B.Cu]
[0.8, 0.6]This simple interface is not possible with the C++ SWIG API. The python wrapper is handling things like calling the (sometimes hard to find) function names, sanitizing datatypes, looking up layers, and enabling the generator pattern.
Don't be fooled though -trackandboardcontain no state. They use properties to give an intuition of state, but they are dynamically interacting with the underlying C++PCB_TRACKandBOARD. You can always access the low-level objects usingtrack.native_obj.Installation via PyPIpip install kigadgetsOpen the pcbnew GUI application. Open its terminalorand run this command in kicad 6+importpcbnew;print(pcbnew.__file__,pcbnew.SETTINGS_MANAGER.GetUserSettingsPath())This will print 2 paths.Copy that entire line.For kicad 5, replace that last command withpcbnew.SETTINGS_MANAGER_GetUserSettingsPath()(note the last underscore).Go back to your external command line or Terminal shell, and run this command, replacing [paste] with what you copiedlink_kicad_python_to_pcbnew[paste]For example,link_kicad_python_to_pcbnew/usr/lib/python3/dist-packages/pcbnew.py/home/username/.config/kicadTry it out! Quit and reopen pcbnew application. Open its terminal, then runpcb.add_circle((100,100),20,'F.Silkscreen');pcbnew.Refresh()Troubleshooting[cannot write to package directory] Step 3 attempts to write a file in the installation ofkicad-python. If that fails because you don't have file permissions or something, you can instead set the environment variable "PCBNEW_PATH" to the first path to Path A. Put this line in your .bashrc or .zshrc# In general: export PCBNEW_PATH="[Path A]"exportPCBNEW_PATH=/usr/lib/python3/dist-packages/pcbnew.py# For example[python version errors] Some external libraries might be compiled.pcbnew.pydoes depend on compiled code (called_pcbnew.so). That means not all versions of python work. You may get errors in your terminal that say "version `GLIBCXX_3.4.30' not found". To fix this, determine the version used in KiCad with this command in the GUI terminal>>>importsys;sys.version_info# sys.version_info(major=3, minor=10, ...)Then, in your external terminal, create a conda environment with that same python version. Run the shell commands again, and do the rest of your batch processing within this conda environment. Note, sometimes python 3.8 so-files will work with 3.10, but matching these versions is the best way to guarantee compatibility.[Upgrading kicad] User configuration directories are different for versions 6 and 7. You may not want to keep multiple copies of script code. One approach is to keep all 3rd party code in~/.config/kicad/scripting(Linux), and then symbolic link that into the specific version directory.ln-s~/.config/kicad/scripting~/.config/kicad/7.0/scriptingInStep 3above, you can then use either path for Path B: ".../kicad" or ".../kicad/7.0".What islink_kicad_python_to_pcbnewdoing for you?As long as the above procedure works, you do not have to read this part.The KiCad application comes with its own isolated version of python. It is not designed to install any new packages like this one. Furthermore, its python API is not installed in a place that your external python or pip can find.link_kicad_python_to_pcbnewcreates a bidirectional link, tellingkicad-python(this package) andpcbnew.py(their builtin C++ wrapper) where to find each other. The script all does this for you.First, it writes an initialization script for the pcbnew GUI's application terminal. It runs automatically when the shell opens and looks like this# File (for example): /home/myself/.config/kicad/PyShell_pcbnew_startup.pyimportsyssys.path.append("/path/to/your/kicad-python/")fromkicad.pcbnew.boardimportBoardpcb=Board.from_editor()# pcb is now a global variable in the terminalEffect:You can now usekicad-pythonfeatures in your GUI terminal. Quick 3-line scripts can be quite useful (examples below).Second, the script exposeskicad-pythonto the pcbnew GUI action plugin environment. It does this by linking this package into the "kicad/scripting/plugins" directory.Effect:You can now usekicad-pythonwhen developing action plugins.Third, it exposes KiCad'spcbnew.pyto your external python environment. The path is stored in a file called.path_to_pcbnew_module, which is located in thekicad-pythonpackage installation. Since it is a file, it persists after the first time. You can override this in an environment variablePCBNEW_PATH.Effect:You can now use the full KiCad built-in SWIG wrapper, thekicad-pythonpackage, and any non-GUI plugins you are developingoutside of the pcbnew application. It is useful for batch processing, remote computers, procedural layout, continuous integration, and use in other software such as FreeCAD and various autorouters.Snippet examplesThese snippets are run in the GUI terminal. They are common automations that aren't worth making dedicated action plugins. There is no preceding context; the linking step above providespcbto the terminal. These all should work in pcbnew 5, 6, or 7 on Mac, Windows, or Linux.Hide silkscreen labels of selected footprintsforfpinpcb.footprints:iffp.is_selected:fp.reference_label.visible=Falsepcbnew.Refresh()Move all silk labels to fab layersInstead, we can keep them on Fab layers so we can still see them while designing the PCB.forminpcb.modules:ref=m.reference_labelifref.layer=='F.Silkscreen':ref.layer='F.Fab'elifref.layer=='B.Silkscreen':ref.layer='B.Fab'pcbnew.Refresh()Select similar viasThis snippet assumes you have selected one viaog_via=next(pcb.selected_items)forvia2inpcb.vias:ifvia2.diameter!=og_via.diameter:continueifvia2.drill!=og_via.drill:continuevia2.select()og_via.select(False)pcbnew.Refresh()The functionnextis used becausepcb.itemsis a generator, not a list. Turn it into a list using thelistfunction if desired.Seevia.pyfor additional functionality related to micro and blind vias.Change all drill diametersBecause planning ahead doesn't always workforvinpcb.vias:ifv.drill>0.4andv.drill<0.6:v.drill=0.5pcbnew.Refresh()Put silkscreen over tracksNot sure why to do this besides a nice look.fortinpcb.tracks:new_width=t.width*1.1pcb.add_line(t.start,t.end,'F.SilkS'ift.layer=='F.Cu'else'B.SilkS',new_width)pcbnew.Refresh()Select everything schematically connected to this footprintfp=next(pcb.selected_items)nets={pad.net_nameforpadinfp.pads}nets-={'GND','+5V'}# because these are connected to everythingformodinpcb.footprints:ifany(pad.net_nameinnetsforpadinmod.pads):mod.select()Import user library for GUI/CLISuppose you wrote a file located in $KICAD_SCRIPTING_DIR/my_lib.py# ~/.config/kicad/scripting/my_lib.py (Linux)# ~/Library/Preferences/kicad/scripting/my_lib.py (MacOS)fromkicad.pcbnew.boardimportBoarddefdo_something(pcb):...if__name__=='__main__':pcb=Board.load(sys.argv[1])do_something(pcb)newname=pcb.filename.split('.')[0]+'-proc.kicad_pcb'# Prevent overwrite of source filepcb.save(newname)Then you can run it in the pcbnew.app terminal likefrommy_libimportdo_somethingdo_something(pcb)pcbnew.Refresh()or from the command line likepythonmy_lib.pysome_file.kicad_pcbKeep track of live editor statefromkicad.pcbnew.drawingimportRectanglemy_rect=Rectangle((0,0),(60,40))pcb.add(my_rect)pcbnew.Refresh()print(my_rect.x,my_rect.contains((1,1)))# 30 True# Go move the new rectangle in the editorprint(my_rect.x,my_rect.contains((1,1)))# 15.2 Falsekicad-pythonstays synchronized with the state of the underlying native objects even when they are modified elsewhere because it is wrapping the C++ state rather than holding a Python state.Procedural layoutSuppose you want to test various track width resistances.y=0length=50widths=[.12,.24,.48,.96]r_contact=5forwinwidths:pcb.add_track([(0,y),(length,y)],'F.Cu',width=w)forlayin['F.Cu','F.Mask']:forxin[0,length]:pcb.add_circle((x,y),r_contact/2,lay,r_contact)pcb.add_text((length/2,y-2),'width ={:.2f}mm'.format(w),'F.SilkS')y+=20pcbnew.Refresh()Go ahead and try this out in the pcbnew terminal, although this type of thing is better to stick in a user library (see above). The sky is the limit when it comes to procedural layout!Related packagesKiCAD has a rich landscape of user-developed tools, libraries, and plugins. They have complementary approaches that are optimized for different use cases. It is worth understanding this landscape in order to use the right tool for the job. This is howkicad-pythonfits in.KiKitKiKithas powerful user-side functionality for panelization, exporting, and other common fabrication tasks. Likekicad-python,KiKithas applications spanning GUI and batch environments; they create cross-version compatibility by modifying SWIG API; they expose libraries usable in other plugin development. Some differences are summarized hereKiKitkicad-pythonPrimary audienceusersdevelopersCAD logic/statepythonC++Entry pointsPlugin, CLIAPIDependencies80Lines to maintain15k3kPython versions3.7+2.*/3.*Documentationextensive"documents itself" for nowAudiences:WhileKiKitis directed primarily to end users,kicad-pythonis directed moreso to developers and coders. It is lean: <2,800 lines of code, no constraints on python version, andzero dependenciesbesidespcbnew.py. Out of the box,kicad-pythonoffers very little to the end user who doesn't want to code. It has no entry points, meaning the user must do some coding to write 10-line snippets, action plugins, and/or batch entry points. In contrast,KiKitcomes with batteries included. It exposes highly-configurable, advanced functionality through friendly entry points in CLI and GUI action plugins.Internals:KiKitperforms a significant amount of internal state handling and CAD logic (viashapely).kicad-pythondoes not store state; it is a thin wrapper around corresponding SWIG objects. While the first approach gives functionality beyondpcbnewbuilt into KiKit, the second exposes the key functionality of underlying objects, leaving the state and logic to C++. It requires a coder to do things with those objects. If that dev wants to useshapelytoo, they are welcome to import it.[!TIP]
If you don't view yourself as a coder, you can become one! Have a look at the snippets above - do you understand what they are doing? If so, you can code.
While you arelearning python syntax, you can just copy the examples above and modify to suit your needs.pcbnewTransitionKiKit is based onpcbnewTransitionto provide cross-version compatibility. This package unifies the APIs of v5-v7pcbnewinto the v7 API. Something similar is happening inkicad/__init__.pywith a sylistic difference thatkicad-pythonunifies under a wrapping API instead of patching thepcbnewAPI. One nice feature of a wrapper-style API is that the contract for cross-version compatibility ends at a clearly-defined place: thenative_objproperty.pykicadpykicadand various other packages use an approach of parsing ".kicad_pcb" files directly, without involvement of the KiCad'spcbnew.pylibrary. In contrast,kicad-pythonwraps that SWIG library provided by KiCAD devs. Both packages work for batch processing. Whilekicad-pythonexposes allpcbnew.pystate and functions,pykicaddoes not even require an installation of KiCAD, which is advantageous in certain use cases.The kicad-pythonsThis project forks KiCAD/kicad-python and maintains its complete history. The original repo has been archived. The pointhi/kicad-python repo (tied topip install kicad-python) was inspired by the 2016 version of KiCAD/kicad-python but is not maintained beyond KiCAD v4.lygadgetsThis project adopts a philosophy similar to that oflygadgets, except for PCBs instead of integrated circuits. Both attempt to harmonize between a GUI application and external python environments. Neither usessubprocessbecause who knows where that will get interpreted. Both are simple and lean with zero dependencies.The overarching idea is workflowinteroperabilityrather than uniformity. I think this works better for open source because everybody has their existing workflows, and there is no central authority to impose "the best" API or - more generally - to tell you how to do your thing.An example of interoperability,kicad-pythoncan be delicately inserted anywhere in existing code usingwrapandnative_obj.# file: legacy_script.py...my_zone=get_a_zone_somewhere()# my_zone.SetClearance(my_zone.GetClearance() * 2)  # This existing line will not work >v5### begin insertionfromkicad.pcbnew.zoneimportZonezone_tmp=Zone.wrap(my_zone)# Intake from any versionzone_tmp.clearance*=2# Version independentmy_zone=zone_tmp.native_obj# Outlet to correct version### end insertiondo_something_else_to(my_zone)Now this code is forwards compatible without breaking backwards compatibility.