Skip to content

Commit 0836d12

Browse files
committed
Readme overhaul
1 parent d647cab commit 0836d12

File tree

3 files changed

+134
-44
lines changed

3 files changed

+134
-44
lines changed

README.md

+134-44
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,28 @@ Development of a new Python scripting API for KiCad
33
based on Piers Titus van der Torren work and comunity
44
feedback to create a less C++ tied API.
55

6-
This repo has been fully tested with KiCAD 5, 6, 7 and partially tested with 7.99.
7-
Note: the KiCAD/kicad-python and pointhi/kicad-python and PyPI:kicad-python are distinct projects no longer maintained.
8-
96
## Description
10-
KiCAD 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 documentation](https://docs.kicad.org/doxygen-python/namespacepcbnew.html) is 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.
7+
KiCAD 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 documentation](https://docs.kicad.org/doxygen-python/namespacepcbnew.html) is 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.
118

12-
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 using `kicad-python` instead will be backwards compatible, forwards compatible, and easier to understand for KiCAD newcomers.
9+
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 using `kicad-python` instead are backwards compatible, forwards compatible, and easier to understand for KiCAD newcomers.
1310

1411
This package is a pythonic wrapper around the various `pcbnew` APIs. 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.
1512

13+
This package has been fully tested with KiCAD 5, 6, 7 and partially tested with 7.99.
14+
1615
### An excerpt
1716
A simple pythonic script might look like this
1817
```python
19-
print([track.layer for track in board.tracks])
20-
print([track.width for track in board.tracks if track.is_selected])
18+
print([track.layer for track in pcb.tracks])
19+
print([track.width for track in pcb.tracks if track.is_selected])
2120
```
2221
which produces
2322
```
2423
[F.Cu, B.Cu, B.Cu]
2524
[0.8, 0.6]
2625
```
27-
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.
26+
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.
27+
Don't be fooled though - `track` and `board` contain no state. They use properties to give an intuition of state, but they are dynamically interacting with the underlying C++ `PCB_TRACK` and `BOARD`. You can always access the low-level objects using `track.native_obj`.
2828

2929
## Installation
3030

@@ -35,12 +35,12 @@ pip install kicad-python/.
3535
```
3636
<!-- pip install kigadgets -->
3737

38-
2. Open the pcbnew GUI application. Open its terminal ![](doc/pcbnew_terminal_icon.png) and run these commands in kicad 6+
38+
2. Open the pcbnew GUI application. Open its terminal ![](doc/pcbnew_terminal_icon.png) or ![](doc/pcbnew_terminal_icon2.png) and run these commands in kicad 6+
3939
```python
40-
>>> import pcbnew
41-
>>> pcbnew.__file__
40+
import pcbnew
41+
pcbnew.__file__
4242
# [Path A]: This will give something like "/usr/lib/python3/dist-packages/pcbnew.py"
43-
>>> pcbnew.SETTINGS_MANAGER.GetUserSettingsPath()
43+
pcbnew.SETTINGS_MANAGER.GetUserSettingsPath()
4444
# [Path B]: This will give something like "home/username/.config/kicad"
4545
```
4646
For kicad 5, replace that last command with `pcbnew.SETTINGS_MANAGER_GetUserSettingsPath()` (note the last underscore).
@@ -56,7 +56,7 @@ link_kicad_python_to_pcbnew /usr/lib/python3/dist-packages/pcbnew.py /home/usern
5656

5757
4. Try it out! Quit and reopen pcbnew application. Open its terminal, then run
5858
```python
59-
pcb.add_circle((100, 100), 20, 'F.SilkS'); pcbnew.Refresh() # F.SilkS > alias to -> F.Silkscreen for newer versions
59+
pcb.add_circle((100, 100), 20, 'F.Silkscreen'); pcbnew.Refresh()
6060
```
6161

6262
### Troubleshooting
@@ -104,57 +104,45 @@ Third, it exposes KiCad's `pcbnew.py` to your external python environment. The p
104104

105105
**Effect:** You can now use the full KiCad built-in SWIG wrapper, the `kicad-python` package, and any non-GUI plugins you are developing *outside 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.
106106

107-
### pykicad
108-
[pykicad](https://github.com/dvc94ch/pykicad) and various other packages use an approach of parsing ".kicad_pcb" files directly, without involvement of the KiCad's `pcbnew.py` library. In contrast, `kicad-python` wraps that SWIG library provided by KiCAD devs. Both packages work for batch processing but have complementary strengths appropriate for different use cases.
109-
110-
`pykicad` strengths
111-
- pure python
112-
- works out of the box without configuring environment
113-
- no KiCad installation required
114-
115-
`kicad-python` strengths
116-
- compatible with KiCad v5/6/7/(8?) and associated file standards
117-
- exposes all `pcbnew.py` functionality in headless environments
118-
- works within KiCad GUI terminal and Action Plugins
119-
- can interact with GUI states, such as selection/highlighting
107+
## Snippet examples
108+
These snippets are run in the GUI terminal. They are common automations that don't need dedicated action plugins
120109

121-
## Examples
122-
These all should work in the pcbnew 5, 6, or 7 GUI terminal on Mac/Windows/Linux.
110+
There is no preceding context; the linking step above provides `pcb` to the terminal. These all should work in pcbnew 5, 6, or 7 on Mac, Windows, or Linux.
123111

124112
### Hide silkscreen labels of selected footprints
125113
```python
126-
for m in pcb.modules:
127-
if m.is_selected:
128-
m.referenceLabel.visible = False
114+
for fp in pcb.footprints:
115+
if fp.is_selected:
116+
fp.reference_label.visible = False
129117
pcbnew.Refresh()
130118
```
119+
![](doc/simple_script.png)
131120

132121
### Move all silk labels to fab layers
133122
Instead, we can keep them on Fab layers so we can still see them while designing the PCB.
134123
```python
135124
for m in pcb.modules:
136-
ref = m.referenceLabel
125+
ref = m.reference_label
137126
if ref.layer == 'F.Silkscreen':
138127
ref.layer = 'F.Fab'
139128
elif ref.layer == 'B.Silkscreen':
140129
ref.layer = 'B.Fab'
141130
pcbnew.Refresh()
142131
```
143132

144-
### Give unique references to every footprint
145-
Making arrays of components can lead to massive reference conflicts. References should be unique, even if you don't care exactly what they are. Useful for panelization.
133+
### Select similar vias
146134
```python
147-
from collections import defaultdict
148-
counters = defaultdict(lambda: 1)
149-
for m in pcb.modules:
150-
component_class = m.reference[0]
151-
counters[component_class] += 1
152-
m.reference = '{}{:03d}'.format(component_class, counters[component_class])
135+
og_via = next(pcb.selected_items)
136+
for via2 in pcb.vias:
137+
if via2.diameter != og_via.diameter: continue
138+
if via2.drill != og_via.drill: continue
139+
via2.select()
140+
og_via.select(False)
153141
pcbnew.Refresh()
154142
```
155-
Iteration order is not guaranteed, although you could figure it out using the `Module.position` properties.
143+
There is some additional functionality for micro and blind vias.
156144

157-
### Change all drill sizes
145+
### Change all drill diameters
158146
Because planning ahead doesn't always work
159147
```python
160148
for v in pcb.vias:
@@ -168,9 +156,57 @@ Not sure why to do this besides a nice look.
168156
```python
169157
for t in pcb.tracks:
170158
new_width = t.width * 1.1
171-
pcb.add_line(t.start, t.end, 'F.SilkS' if t.layer == 'F.Cu' else 'B.Silks', new_width)
159+
pcb.add_line(t.start, t.end, 'F.SilkS' if t.layer == 'F.Cu' else 'B.SilkS', new_width)
160+
pcbnew.Refresh()
161+
```
162+
163+
### Select everything schematically connected to this footprint
164+
```python
165+
fp = next(pcb.selected_items)
166+
nets = {pad.net_name for pad in fp.pads}
167+
nets -= {'GND', '+5V'} # because these are connected to everything
168+
for mod in pcb.footprints:
169+
if any(pad.net_name in nets for pad in mod.pads):
170+
mod.select()
171+
```
172+
173+
### Import user library
174+
Suppose you wrote a file located in $KICAD_SCRIPTING_DIR/my_lib.py
175+
```python
176+
# ~/.config/kicad/scripting/my_lib.py (Linux)
177+
# ~/Library/Preferences/kicad/scripting/my_lib.py (MacOS)
178+
from kicad.pcbnew.board import Board
179+
180+
def do_something(pcb):
181+
...
182+
183+
if __name__ == '__main__':
184+
pcb = Board.load(sys.argv[1])
185+
do_something(pcb)
186+
pcb.save()
187+
```
188+
Then you can run it in the pcbnew.app terminal like
189+
```python
190+
from my_lib import do_something
191+
do_something(pcb)
192+
pcbnew.Refresh()
193+
```
194+
or from the command line like
195+
```bash
196+
python my_lib.py some_file.kicad_pcb
197+
```
198+
199+
### Keep track of live editor state
200+
```python
201+
from kicad.pcbnew.drawing import Rectangle
202+
my_rect = Rectangle((0,0), (60, 40))
203+
print(my_rect.x, my_rect.contains((1,1))) # 30 True
204+
pcb.add(my_rect)
172205
pcbnew.Refresh()
206+
# Go move the new rectangle in the editor
207+
print(my_rect.x) # 15.2 False
173208
```
209+
`kicad-python` is a stateless wrapper of objects, not just an API wrapper. It stays synchronized with the state of the underlying native objects even when they are modified elsewhere.
174210

175211
### Procedural layout
176212
Suppose you want to test various track width resistances.
@@ -188,4 +224,58 @@ for w in widths:
188224
y += 20
189225
pcbnew.Refresh()
190226
```
191-
Go ahead and try this out in the pcbnew terminal. The sky is the limit when it comes to procedural layout.
227+
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!
228+
229+
## Related packages
230+
KiCAD 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 how `kicad-python` fits in.
231+
232+
### KiKit
233+
[KiKit](https://github.com/yaqwsx/KiKit) has powerful user-side functionality for panelization, exporting, and other common fabrication tasks. Like `kicad-python`, `KiKit` has 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 here
234+
235+
| | KiKit | kicad-python |
236+
| ----------------- | ------------ | ---------------------------- |
237+
| Primary audience | users | developers |
238+
| CAD logic/state | python | C++ |
239+
| Entry points | Plugin, CLI | API, examples |
240+
| Dependencies | 8 | 0 |
241+
| Lines to maintain | 15k | 3k |
242+
| Python versions | 3.7+ | 2.\*/3.\* |
243+
| Documentation | extensive | "documents itself" (for now) |
244+
245+
**Audiences:** While `KiKit` is directed primarily to end users, `kicad-python` is directed moreso to developers and coders. It is lean: <2,800 lines of code, no constraints on python version, and **zero dependencies** besides `pcbnew.py`. Out of the box, `kicad-python` offers 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, out of the box, `KiKit` exposes highly-configurable, advanced functionality through friendly entry points in CLI and GUI action plugins.
246+
247+
**Internals:** `KiKit` performs a significant amount of internal state handling and CAD logic (via `shapely`). `kicad-python` does not store state; it is a thin wrapper around corresponding SWIG objects. While the first approach gives functionality beyond `pcbnew` built 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 use `shapely` too, they are welcome to import it.
248+
249+
> [!TIP]
250+
> 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.
251+
> While you are [learning python syntax](https://docs.python.org/3/tutorial/index.html), you can just copy the examples above and modify to suit your needs.
252+
253+
#### pcbnewTransition
254+
KiKit is based on [pcbnewTransition](https://github.com/yaqwsx/pcbnewTransition) to provide cross-version compatibility. This package unifies the APIs of v5-v7 `pcbnew` into the v7 API. Something similar is happening in `kicad/__init__.py` with a sylistic difference that `kicad-python` unifies under a wrapping API instead of patching the `pcbnew` API. One nice feature of a wrapper-style API is that the contract for cross-version compatibility ends at a clearly-defined place: the `native_obj` property.
255+
256+
### pykicad
257+
[pykicad](https://github.com/dvc94ch/pykicad) and various other packages use an approach of parsing ".kicad_pcb" files directly, without involvement of the KiCad's `pcbnew.py` library. In contrast, `kicad-python` wraps that SWIG library provided by KiCAD devs. Both packages work for batch processing. While `kicad-python` exposes all `pcbnew.py` state and functions, `pykicad` does not even require an installation of KiCAD, which is advantageous in certain use cases.
258+
259+
### The kicad-pythons
260+
This project forks KiCAD/kicad-python and maintains its complete history. The original repo has been archived. The pointhi/kicad-python repo (tied to `pip install kicad-python`) was inspired by the 2016 version of KiCAD/kicad-python but is not maintained beyond KiCAD v4.
261+
262+
### lygadgets
263+
This project adopts a philosophy similar to that of [lygadgets](https://github.com/atait/klayout-gadgets), except for PCBs instead of integrated circuits. Both attempt to harmonize between a GUI application and external python environments. Neither uses `subprocess` because who knows where that will get interpreted. Both are simple and lean with zero dependencies.
264+
265+
The overarching idea is workflow *interoperability* rather 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.
266+
267+
An example of interoperability, `kicad-python` can be lightly inserted anywhere in existing code using `wrap` and `native_obj`.
268+
```python
269+
# legacy_script.py
270+
...
271+
my_zone = get_a_zone_somewhere()
272+
# my_zone.SetClearance(my_zone.GetClearance() * 2) # This existing line will not work >v5
273+
### begin insertion
274+
from kicad.pcbnew.zone import Zone
275+
zone_tmp = Zone.wrap(my_zone)
276+
zone_tmp.clearance *= 2 # Works in all versions
277+
my_zone = zone_tmp.native_obj
278+
### end insertion
279+
do_something_else_to(my_zone)
280+
```
281+
Now this code is forwards compatible without breaking backwards compatibility.

doc/pcbnew_terminal_icon2.png

7.64 KB
Loading

doc/simple_script.png

549 KB
Loading

0 commit comments

Comments
 (0)