# KiCad Python API (kipy) 快速入門指南
:::info
**最後更新日期:** 2025年4月25日
**適用版本:** KiCad 9.0+
:::
這份快速入門指南旨在幫助您快速開始使用 KiCad Python API (kipy) 進行開發。通過幾個簡單的示例,您將學習如何連接到 KiCad、操作電路板並執行常見任務。
## 目錄
- [KiCad Python API 簡介](#kicad-python-api-簡介)
- [基礎設置](#基礎設置)
- [連接到 KiCad](#示例-1-連接到-kicad-並獲取版本信息)
- [操作電路板](#示例-2-獲取當前電路板信息)
- [分析封裝](#示例-3-分析封裝和焊盤)
- [修改電路板](#示例-4-修改電路板---添加文本)
- [創建元素](#示例-5-創建導線和過孔)
- [創建插件](#示例-6-創建一個簡單的-kicad-插件)
- [進階 API 使用技巧](#進階-api-使用技巧)
- [與圖形用戶界面集成](#與圖形用戶界面集成)
- [常見問題解答](#常見問題解答-faq)
- [性能優化技巧](#性能優化技巧)
- [實用範例與實際應用案例](#實用範例與實際應用案例)
- [核心概念與架構](#核心概念與架構)
- [API 參考](#api-參考)
## KiCad Python API 簡介
KiCad 9.0 引入了全新的 IPC API(進程間通信應用程式介面),這是一個穩定的介面,旨在替代舊版的 SWIG Python 綁定。這個新 API 的主要特點有:
- **穩定性**:設計為穩定的介面,不會因為 KiCad 內部重構而變化
- **語言無關**:支持與 Python 以外的其他語言編寫的軟體互操作
- **進程獨立**:使用 Protocol Buffers 和 NNG 透過 UNIX 套接字在進程間傳輸消息
- **易於使用**:提供了 kipy 這個官方的 Python 綁定庫,使得與 IPC API 的交互更加容易

:::warning
注意:SWIG 綁定在 KiCad 9.0 中已被標記為棄用,計劃在 KiCad 10.0(預計2026年2月發布)中被完全移除。建議新的開發工作使用新的 IPC API。
:::
## 基礎設置
在開始之前,請確保:
1. 您已安裝 KiCad 9.0 或更高版本
2. 在 KiCad 中啟用了 API 服務器(首選項 > 插件中啟用)
3. 已安裝 kipy(使用 `pip install kicad-python`)
### 啟用 API 服務器
要使用 kipy,您需要在 KiCad 中啟用 API 服務器:
1. 打開 KiCad
2. 進入 **首選項 > 插件**
3. 勾選 **啟用 API 服務器**
4. 重啟 KiCad 使設置生效
### 安裝 kipy 包
在命令行中運行以下命令安裝 kipy 包:
```bash
pip install kicad-python
```
:::info
如果您需要特定版本或開發版本,可以使用以下命令:
```bash
pip install kicad-python==0.1.0 # 安裝特定版本
# 或者
pip install git+https://gitlab.com/kicad/code/kicad-python.git # 安裝開發版本
```
:::
## 核心概念與架構
kipy 的核心架構基於以下幾個關鍵概念:
1. **KiCad 類** - 主要的入口點,用於連接到運行中的 KiCad 實例
2. **Board 類** - 代表一個 KiCad PCB 電路板,允許您查詢和修改電路板上的物件
3. **Project 類** - 代表一個 KiCad 項目,提供對項目級設置的訪問
4. **Wrapper 類** - 大多數 kipy 物件都繼承自 Wrapper,提供對底層 protobuf 消息的訪問
5. **幾何類** - 提供 Vector2、Angle、Box2 等用於操作座標和幾何形狀的類
## 示例 1: 連接到 KiCad 並獲取版本信息

```python
from kipy.kicad import KiCad
# 創建 KiCad 實例並連接到運行中的 KiCad
kicad = KiCad()
# 獲取 KiCad 版本
version = kicad.get_version()
print(f"連接到 KiCad 版本: {version}")
# 獲取 API 版本
api_version = kicad.get_api_version()
print(f"API 版本: {api_version}")
# 檢查版本兼容性
try:
kicad.check_version()
print("版本兼容!")
except Exception as e:
print(f"版本不兼容: {e}")
```
## 示例 2: 獲取當前電路板信息
```python
from kipy.kicad import KiCad
kicad = KiCad()
# 獲取當前活動文檔
active_doc = kicad.get_active_document()
if active_doc and active_doc.type == 2: # 2 = DOCTYPE_BOARD
# 獲取電路板
board = kicad.get_board(active_doc)
# 獲取基本信息
print(f"電路板名稱: {board.name}")
# 獲取軌道數量
tracks = board.get_tracks()
print(f"軌道數量: {len(tracks)}")
# 獲取過孔數量
vias = board.get_vias()
print(f"過孔數量: {len(vias)}")
# 獲取封裝數量
footprints = board.get_footprints()
print(f"封裝數量: {len(footprints)}")
# 獲取網絡數量
nets = board.get_nets()
print(f"網絡數量: {len(nets)}")
else:
print("當前沒有打開的電路板")
```
## 示例 3: 分析封裝和焊盤
```python
from kipy.kicad import KiCad
from kipy.util.units import to_mm
kicad = KiCad()
# 獲取當前電路板
active_doc = kicad.get_active_document()
board = kicad.get_board(active_doc)
# 獲取所有封裝
footprints = board.get_footprints()
for fp in footprints:
ref = fp.reference_field.text.value
val = fp.value_field.text.value
x_mm = to_mm(fp.position.x)
y_mm = to_mm(fp.position.y)
print(f"{ref} ({val}) 位於 ({x_mm:.2f}mm, {y_mm:.2f}mm)")
# 獲取焊盤
pads = fp.definition.pads
print(f" 焊盤數量: {len(pads)}")
# 列出焊盤信息
for pad in pads:
pad_num = pad.number
net_name = pad.net.name if pad.net.name else "無網絡"
print(f" 焊盤 {pad_num}: 連接到網絡 '{net_name}'")
print() # 空行
```
## 示例 4: 修改電路板 - 添加文本
```python
from kipy.kicad import KiCad
from kipy.board_types import BoardText
from kipy.geometry import Vector2
from kipy.util.units import from_mm
from kipy.proto.board.board_types_pb2 import BoardLayer
from kipy.proto.common.types.enums_pb2 import HorizontalAlignment, VerticalAlignment
from datetime import datetime
kicad = KiCad()
active_doc = kicad.get_active_document()
board = kicad.get_board(active_doc)
# 開始一個提交事務
commit = board.begin_commit()
# 創建文本
text = BoardText()
text.value = "添加於 " + datetime.now().strftime("%Y-%m-%d")
text.position = Vector2.from_xy(from_mm(100), from_mm(100))
text.layer = BoardLayer.BL_F_SilkS
# 設置文本屬性
text.attributes.size = Vector2.from_xy(from_mm(1.5), from_mm(1.5))
text.attributes.horizontal_alignment = HorizontalAlignment.HA_CENTER
text.attributes.vertical_alignment = VerticalAlignment.VA_CENTER
text.attributes.bold = True
# 添加到電路板
created_items = board.create_items(text)
print(f"添加了 {len(created_items)} 個新項目")
# 提交更改
board.push_commit(commit, "添加文本標籤")
print("更改已提交")
```
## 示例 5: 創建導線和過孔
```python
from kipy.kicad import KiCad
from kipy.board_types import Track, Via
from kipy.geometry import Vector2
from kipy.util.units import from_mm
from kipy.proto.board.board_types_pb2 import BoardLayer, ViaType
kicad = KiCad()
active_doc = kicad.get_active_document()
board = kicad.get_board(active_doc)
# 開始一個提交事務
commit = board.begin_commit()
# 創建網絡(這裡使用現有網絡)
nets = board.get_nets()
if len(nets) > 0:
net = nets[0] # 使用第一個網絡
# 創建一個軌道
track = Track()
track.start = Vector2.from_xy(from_mm(100), from_mm(100))
track.end = Vector2.from_xy(from_mm(120), from_mm(100))
track.width = from_mm(0.25) # 0.25mm 寬
track.layer = BoardLayer.BL_F_Cu # 頂層銅箔
track.net = net
# 添加軌道到電路板
board.create_items(track)
# 創建一個過孔
via = Via()
via.position = Vector2.from_xy(from_mm(120), from_mm(100))
via.type = ViaType.VT_THROUGH
via.diameter = from_mm(0.8)
via.drill_diameter = from_mm(0.4)
via.net = net
# 添加過孔到電路板
board.create_items(via)
# 提交更改
board.push_commit(commit, "添加軌道和過孔")
print("添加了軌道和過孔")
else:
board.drop_commit(commit)
print("沒有可用的網絡")
```
## 示例 6: 創建一個簡單的 KiCad 插件
```python
import os
import pcbnew
from kipy.kicad import KiCad
from kipy.board_types import BoardText
from kipy.geometry import Vector2
from kipy.util.units import from_mm
from kipy.proto.board.board_types_pb2 import BoardLayer
import datetime
class DateStampPlugin(pcbnew.ActionPlugin):
def __init__(self):
super().__init__()
self.name = "添加日期戳記"
self.category = "修改 PCB"
self.description = "在電路板上添加當前日期"
self.show_toolbar_button = True
# 插件圖標路徑
self.icon_file_name = os.path.join(os.path.dirname(__file__), "date_icon.png")
def Run(self):
# 連接到 KiCad
kicad = KiCad()
# 獲取當前電路板
active_doc = kicad.get_active_document()
board = kicad.get_board(active_doc)
# 開始提交事務
commit = board.begin_commit()
# 創建日期文本
date_text = BoardText()
date_text.value = "生成日期: " + datetime.datetime.now().strftime("%Y-%m-%d")
date_text.position = Vector2.from_xy(from_mm(150), from_mm(150))
date_text.layer = BoardLayer.BL_F_SilkS
# 設置文本屬性
date_text.attributes.size = Vector2.from_xy(from_mm(1.5), from_mm(1.5))
# 添加到電路板
board.create_items(date_text)
# 提交更改
board.push_commit(commit, "添加日期戳記")
# 通知用戶
print("已添加日期戳記到電路板")
# 注冊插件
DateStampPlugin().register()
```
保存上面的代碼為 `date_stamp.py`,然後將其放置在 KiCad 插件目錄中:
- Windows: `%APPDATA%\kicad\9.0\scripting\plugins\`
- Linux: `~/.local/share/kicad/9.0/scripting/plugins/`
- macOS: `~/Library/Application Support/kicad/9.0/scripting/plugins/`
## 示例 7: 透過pytho繪製舉矩形
```python
from kipy import KiCad
from kipy.board_types import BoardRectangle, BoardLayer
from kipy.geometry import Vector2
from kipy.util.units import from_mm
from kipy.common_types import GraphicAttributes, Color
# 連線到 KiCad
kicad = KiCad()
board = kicad.get_board()
commit = board.begin_commit()
# 定義要繪製的層
layers = [
BoardLayer.BL_F_Cu, # 頂層銅箔
BoardLayer.BL_B_Cu, # 底層銅箔
BoardLayer.BL_F_SilkS, # 頂層絲印
BoardLayer.BL_B_SilkS, # 底層絲印
BoardLayer.BL_F_Mask, # 頂層阻焊
BoardLayer.BL_B_Mask, # 底層阻焊
BoardLayer.BL_Edge_Cuts, # 邊緣切割
BoardLayer.BL_Dwgs_User, # 用戶圖形層
]
# 獲取圖形元素預設設置
graphics_defaults = board.get_graphics_defaults()
# 設置方框的基本參數
center_x = from_mm(100) # 中心點 X 座標
center_y = from_mm(100) # 中心點 Y 座標
max_size = from_mm(50) # 最大方框的寬度/高度
min_size = from_mm(10) # 最小方框的寬度/高度
num_rectangles = 5 # 每一層繪製的方框數量
# 在每一層繪製同心方形
for layer_index, layer in enumerate(layers):
# 方框間的大小差異
size_step = (max_size - min_size) // (num_rectangles - 1)
# 在當前層繪製多個同心方形
for i in range(num_rectangles):
# 計算當前方框的大小
current_size = max_size - i * size_step
# 計算方框的左上角和右下角座標
half_size = current_size // 2
top_left = Vector2.from_xy(center_x - half_size, center_y - half_size)
bottom_right = Vector2.from_xy(center_x + half_size, center_y + half_size)
# 創建方框
rect = BoardRectangle()
rect.top_left = top_left
rect.bottom_right = bottom_right
rect.layer = layer
# 設置線寬
rect.attributes.stroke.width = from_mm(0.2)
# 根據層和方框大小設置不同的填充
# 偶數層和偶數方框使用實心填充,其他使用無填充
if (layer_index % 2 == 0) and (i % 2 == 0):
rect.attributes.fill.mode = 1 # 實心填充
else:
rect.attributes.fill.mode = 0 # 無填充
# 添加到電路板
board.create_items(rect)
# 提交變更
board.push_commit(commit, message="在不同層繪製同心方形")
print("在多個層上繪製了同心方形")
```


## 常見任務快速參考
### 單位轉換
```python
from kipy.util.units import from_mm, to_mm
# 毫米轉換為 KiCad 內部單位(納米)
position_nm = from_mm(10) # 10mm 轉換為納米
# KiCad 內部單位轉換為毫米
size_mm = to_mm(1000000) # 1,000,000 納米轉換為毫米
```
### 獲取層名稱
```python
from kipy.proto.board.board_types_pb2 import BoardLayer
from kipy.util.board_layer import canonical_name
# 獲取層的標準名稱
layer = BoardLayer.BL_F_Cu
layer_name = canonical_name(layer) # 返回 "F.Cu"
```
### 獲取選定的項目
```python
# 獲取選定的項目
selected_items = board.get_selection()
print(f"選定了 {len(selected_items)} 個項目")
# 添加項目到選擇
board.add_to_selection(some_item)
# 清除選擇
board.clear_selection()
```
### 保存電路板
```python
# 保存當前電路板
board.save()
# 另存為新文件
board.save_as("/path/to/new/board.kicad_pcb")
```
## 進階 API 使用技巧
### 處理事務提交
在 KiCad Python API 中,所有對電路板的修改都應該通過事務提交進行。這不僅可以確保修改被正確應用,還允許用戶撤銷操作。
```python
from kipy.kicad import KiCad
kicad = KiCad()
board = kicad.get_board()
# 開始一個提交事務
commit = board.begin_commit()
# 進行修改...
# 例如創建、更新或刪除項目
try:
# 提交更改並提供說明信息(顯示在撤銷/重做菜單中)
board.push_commit(commit, "我的腳本修改")
print("修改已成功提交")
except Exception as e:
# 如果出現錯誤,放棄提交
board.drop_commit(commit)
print(f"修改失敗: {e}")
```
### 批量處理項目
當需要處理大量項目時,將它們批量處理可以提高效率:
```python
from kipy.kicad import KiCad
from kipy.board_types import BoardText
from kipy.util.units import from_mm
from kipy.proto.board.board_types_pb2 import BoardLayer
kicad = KiCad()
board = kicad.get_board()
commit = board.begin_commit()
# 創建多個文本項目
texts = []
for i in range(10):
text = BoardText()
text.value = f"項目 {i}"
text.position = Vector2.from_xy(from_mm(100 + i*10), from_mm(100))
text.layer = BoardLayer.BL_F_SilkS
texts.append(text)
# 批量創建項目
created_items = board.create_items(texts)
print(f"批量創建了 {len(created_items)} 個項目")
# 提交更改
board.push_commit(commit, "批量添加文本項目")
```
### 自定義插件目錄結構
對於複雜的插件,建議使用以下目錄結構:
```
my_plugin/
├── plugin.json # 插件配置文件
├── icon.png # 插件圖標
├── __init__.py # 初始化文件
├── main.py # 主要入口點
├── dialog.py # 對話框 UI
└── utils/ # 工具函數
├── __init__.py
└── helpers.py
```
plugin.json 文件示例:
```json
{
"$schema": "https://go.kicad.org/api/schemas/v1",
"identifier": "com.example.my-plugin",
"name": "我的 KiCad 插件",
"description": "一個實用的 KiCad PCB 編輯器插件",
"version": "1.0.0",
"author": {
"name": "您的名字",
"contact": {
"web": "https://example.com"
}
},
"runtime": {
"type": "python",
"min_version": "3.9"
},
"actions": [
{
"identifier": "my-action",
"name": "執行我的功能",
"description": "示範插件功能",
"show-button": true,
"scopes": ["pcb"],
"entrypoint": "main.py"
}
]
}
```
## 與圖形用戶界面集成
KiCad Python API 可以與 wxPython 結合,創建與 KiCad 風格一致的用戶界面:
```python
import wx
from kipy.kicad import KiCad
class MyDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, title="我的插件對話框")
# 創建控件
self.text_ctrl = wx.TextCtrl(self)
self.button = wx.Button(self, label="確定")
# 設置佈局
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(wx.StaticText(self, label="請輸入文本:"), 0, wx.ALL, 5)
sizer.Add(self.text_ctrl, 0, wx.EXPAND|wx.ALL, 5)
sizer.Add(self.button, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
self.SetSizerAndFit(sizer)
# 綁定事件
self.button.Bind(wx.EVT_BUTTON, self.on_button_click)
def on_button_click(self, event):
text = self.text_ctrl.GetValue()
print(f"用戶輸入: {text}")
self.EndModal(wx.ID_OK)
def run_plugin():
kicad = KiCad()
board = kicad.get_board()
# 創建對話框
app = wx.App.Get()
with MyDialog(wx.GetApp().GetTopWindow()) as dlg:
if dlg.ShowModal() == wx.ID_OK:
print("對話框確認")
# 在這裡執行操作
else:
print("對話框取消")
```
## 常見問題解答 (FAQ)
### Q: 新的 IPC API 與舊的 SWIG 綁定有什麼區別?
A: 新的 IPC API 與舊版 SWIG 綁定相比有以下主要區別:
1. **穩定性**:IPC API 設計為穩定接口,不會隨著 KiCad 內部代碼重構而改變
2. **進程分離**:IPC API 在單獨的進程中運行,通過 IPC 機制與 KiCad 通信,而 SWIG 綁定直接在 KiCad 進程中運行
3. **語言無關**:IPC API 可以從多種編程語言訪問,而不僅僅是 Python
4. **更現代的 API 設計**:提供了更一致、更易於使用的接口
5. **穩定的 ABI**:插件不需要針對每個 KiCad 版本重新編譯
### Q: 如何調試 kipy 腳本?
A: 您可以使用標準的 Python 調試技術:
1. 使用 print 語句輸出調試信息
2. 使用 Python 的 logging 模塊記錄信息
3. 使用 VSCode 或 PyCharm 等 IDE 的調試器進行交互式調試
4. 使用 try-except 塊捕獲並打印詳細的錯誤信息
```python
import logging
# 設置日誌記錄
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='kipy_debug.log')
try:
# 您的代碼
kicad = KiCad()
board = kicad.get_board()
logging.info(f"成功獲取電路板: {board.name}")
except Exception as e:
logging.error(f"發生錯誤: {e}", exc_info=True)
```
### Q: 我的 kipy 腳本無法連接到 KiCad,可能的原因是什麼?
A: 常見的連接問題包括:
1. KiCad 中沒有啟用 API 服務器(首選項 > 插件中啟用)
2. KiCad 版本與 kipy 版本不兼容
3. 未運行 KiCad 或運行了多個 KiCad 實例
4. 系統防火牆或安全軟件阻止了進程間通信
首先確保 KiCad 正在運行,並在設置中啟用了 API 服務器。然後嘗試重新安裝與您 KiCad 版本兼容的 kipy 版本。
### Q: 如何創建使用 kipy 的獨立工具(非插件)?
A: 您可以創建直接使用 kipy 與運行中的 KiCad 通信的獨立 Python 腳本:
```python
#!/usr/bin/env python3
from kipy.kicad import KiCad
def main():
try:
# 連接到運行中的 KiCad 實例
kicad = KiCad()
print(f"已連接到 KiCad {kicad.get_version()}")
# 獲取當前電路板
board = kicad.get_board()
if not board:
print("未找到打開的電路板")
return
# 執行您的操作
# ...
except Exception as e:
print(f"錯誤: {e}")
if __name__ == "__main__":
main()
```
運行這樣的腳本時,確保 KiCad 已經在運行並且已經打開了電路板。
### Q: 我可以使用 kipy 創建完整的電路板嗎?
A: 是的,您可以從頭開始創建電路板,添加所有的元件和軌道。然而,通常更實用的做法是從現有的電路板開始,然後修改它。
### Q: 我如何處理錯誤和例外?
A: kipy 會抛出 `ApiError` 和 `ConnectionError` 等異常。您應該處理這些異常以確保腳本在出現問題時能够正常處理:
```python
from kipy.errors import ApiError, ConnectionError
try:
# kipy 代碼
kicad = KiCad()
# ...
except ConnectionError as e:
print(f"無法連接到 KiCad: {e}")
except ApiError as e:
print(f"API 錯誤: {e}")
```
## 性能優化技巧
使用 KiCad Python API 處理大型電路板時,以下是一些提高性能的技巧:
1. **批量處理**:一次性提交多個更改,而不是逐個提交
2. **限制重繪**:在批量操作期間禁用重繪,完成後再恢復
3. **使用適當的數據結構**:例如使用 `dict` 建立網絡名稱到網絡對象的映射
4. **避免不必要的查詢**:緩存經常訪問的數據,而不是反复查詢
以下是優化批量更新操作的示例:
```python
from kipy.kicad import KiCad
kicad = KiCad()
board = kicad.get_board()
commit = board.begin_commit()
# 預先獲取所有需要的數據
tracks = board.get_tracks()
nets = {net.name: net for net in board.get_nets()}
# 批量處理項目
to_update = []
for track in tracks:
if track.width < from_mm(0.2): # 找出寬度小於 0.2mm 的軌道
track.width = from_mm(0.2) # 設置為 0.2mm
to_update.append(track)
# 一次性更新所有修改的軌道
if to_update:
board.update_items(to_update)
board.push_commit(commit, f"將 {len(to_update)} 條軌道寬度更新為 0.2mm")
print(f"已更新 {len(to_update)} 條軌道")
else:
board.drop_commit(commit)
print("沒有需要更新的軌道")
```
## 實用範例與實際應用案例
### 自動化設計規則檢查
以下範例展示如何使用 kipy 進行設計規則檢查(DRC)並生成報告:
```python
from kipy.kicad import KiCad
from kipy.util.units import to_mm
import csv
import datetime
def check_track_clearance(board, min_clearance_mm=0.2):
"""檢查軌道之間的最小間距"""
tracks = board.get_tracks()
violations = []
# 這裡只是一個簡化的示例
# 實際的間距檢查需要更複雜的算法
for i, track1 in enumerate(tracks):
for track2 in tracks[i+1:]:
if track1.layer == track2.layer and track1.net != track2.net:
# 這裡應有實際計算兩條軌道之間最小距離的代碼
distance_mm = 0.1 # 假設值,實際需要計算
if distance_mm < min_clearance_mm:
violations.append({
'track1': f"({to_mm(track1.start.x):.2f}, {to_mm(track1.start.y):.2f}) - ({to_mm(track1.end.x):.2f}, {to_mm(track1.end.y):.2f})",
'track2': f"({to_mm(track2.start.x):.2f}, {to_mm(track2.start.y):.2f}) - ({to_mm(track2.end.x):.2f}, {to_mm(track2.end.y):.2f})",
'distance': f"{distance_mm:.2f}mm",
'required': f"{min_clearance_mm:.2f}mm"
})
return violations
def export_drc_report(violations, filename):
"""將違規導出為 CSV 報告"""
with open(filename, 'w', newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=['track1', 'track2', 'distance', 'required'])
writer.writeheader()
for v in violations:
writer.writerow(v)
print(f"報告已保存至 {filename}")
def main():
kicad = KiCad()
board = kicad.get_board()
print(f"正在檢查電路板: {board.name}")
violations = check_track_clearance(board)
if violations:
print(f"發現 {len(violations)} 個間距違規")
filename = f"drc_report_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
export_drc_report(violations, filename)
else:
print("未發現間距違規")
if __name__ == "__main__":
main()
```
### 自動生成裝配圖層
這個範例展示如何使用 kipy 在電路板上生成裝配圖層標記,用於製造和裝配:
```python
from kipy.kicad import KiCad
from kipy.board_types import BoardText, BoardCircle
from kipy.geometry import Vector2
from kipy.util.units import from_mm, to_mm
from kipy.proto.board.board_types_pb2 import BoardLayer
import math
def create_fiducial_marks(board, layer=BoardLayer.BL_F_SilkS):
"""在電路板角落創建基準標記"""
commit = board.begin_commit()
# 獲取電路板邊界
board_outline = board.get_board_outline()
bb = board_outline.bounding_box
# 計算角落位置(留出 5mm 邊距)
margin = from_mm(5)
corners = [
Vector2.from_xy(bb.min_x + margin, bb.min_y + margin), # 左下
Vector2.from_xy(bb.max_x - margin, bb.min_y + margin), # 右下
Vector2.from_xy(bb.max_x - margin, bb.max_y - margin), # 右上
Vector2.from_xy(bb.min_x + margin, bb.max_y - margin) # 左上
]
# 創建基準標記(十字加圓)
marks = []
for i, pos in enumerate(corners):
# 創建圓
circle = BoardCircle()
circle.center = pos
circle.radius = from_mm(1)
circle.layer = layer
marks.append(circle)
# 創建標籤
text = BoardText()
text.value = f"FID{i+1}"
text.position = Vector2.from_xy(pos.x, pos.y + from_mm(2))
text.layer = layer
marks.append(text)
# 創建元素並提交
board.create_items(marks)
board.push_commit(commit, "添加裝配基準標記")
return len(marks)
def generate_assembly_labels(board):
"""為每個封裝生成裝配標籤"""
commit = board.begin_commit()
footprints = board.get_footprints()
labels = []
for fp in footprints:
ref = fp.reference_field.text.value
val = fp.value_field.text.value
# 在元件上方2mm處創建標籤
text = BoardText()
text.value = f"{ref}:{val}"
# 計算位置,考慮元件旋轉
angle_rad = math.radians(fp.orientation.degrees)
offset_x = -from_mm(2) * math.sin(angle_rad)
offset_y = from_mm(2) * math.cos(angle_rad)
text.position = Vector2.from_xy(fp.position.x + offset_x, fp.position.y + offset_y)
text.layer = BoardLayer.BL_F_Fab # 放在製造層
labels.append(text)
board.create_items(labels)
board.push_commit(commit, "添加裝配標籤")
return len(labels)
def main():
kicad = KiCad()
board = kicad.get_board()
num_fiducials = create_fiducial_marks(board)
print(f"已添加 {num_fiducials} 個基準標記")
num_labels = generate_assembly_labels(board)
print(f"已添加 {num_labels} 個裝配標籤")
if __name__ == "__main__":
main()
```
### PCB 自動佈局輔助工具
下面是一個簡單的工具,用於自動排列電路板上的某些元件:
```python
from kipy.kicad import KiCad
from kipy.geometry import Vector2
from kipy.util.units import from_mm, to_mm
import re
def arrange_components_in_grid(board, ref_pattern, rows, cols, spacing_mm):
"""將匹配模式的元件按網格排列"""
commit = board.begin_commit()
# 查找匹配的元件
footprints = board.get_footprints()
matching_fps = []
pattern = re.compile(ref_pattern)
for fp in footprints:
ref = fp.reference_field.text.value
if pattern.match(ref):
matching_fps.append(fp)
if not matching_fps:
print(f"找不到匹配 '{ref_pattern}' 的元件")
return 0
# 最多處理 rows*cols 個元件
count = min(len(matching_fps), rows * cols)
# 設置起始位置為第一個匹配元件的位置
start_x = matching_fps[0].position.x
start_y = matching_fps[0].position.y
# 計算間距
spacing_x = from_mm(spacing_mm)
spacing_y = from_mm(spacing_mm)
# 按網格排列元件
for i in range(count):
row = i // cols
col = i % cols
fp = matching_fps[i]
fp.position = Vector2.from_xy(
start_x + col * spacing_x,
start_y + row * spacing_y
)
# 更新元件位置
board.update_items(matching_fps[:count])
board.push_commit(commit, f"按網格排列 {count} 個元件")
return count
def main():
kicad = KiCad()
board = kicad.get_board()
# 例如,將所有 LED 元件排成 3x4 網格,間距 10mm
count = arrange_components_in_grid(board, r"LED\d+", 3, 4, 10)
print(f"已排列 {count} 個元件")
if __name__ == "__main__":
main()
```
### 自動創建PCB測試點
以下範例展示如何為特定網絡自動添加測試點:
```python
from kipy.kicad import KiCad
from kipy.board_types import Via
from kipy.geometry import Vector2
from kipy.util.units import from_mm
from kipy.proto.board.board_types_pb2 import ViaType
def add_test_points(board, net_names, diameter_mm=1.0, drill_mm=0.5):
"""為指定網絡添加測試點"""
commit = board.begin_commit()
# 獲取所有網絡
nets = board.get_nets()
net_dict = {net.name: net for net in nets if net.name}
# 查找匹配的網絡
test_points = []
for net_name in net_names:
if net_name in net_dict:
net = net_dict[net_name]
# 獲取這個網絡的軌道,找到一個合適的位置
tracks = [t for t in board.get_tracks() if t.net.code == net.code]
if tracks:
# 使用第一條軌道的中點作為測試點位置
track = tracks[0]
pos_x = (track.start.x + track.end.x) / 2
pos_y = (track.start.y + track.end.y) / 2
# 創建測試點(使用過孔)
via = Via()
via.position = Vector2.from_xy(pos_x, pos_y)
via.type = ViaType.VT_THROUGH
via.diameter = from_mm(diameter_mm)
via.drill_diameter = from_mm(drill_mm)
via.net = net
test_points.append(via)
print(f"為網絡 '{net_name}' 添加測試點")
else:
print(f"網絡 '{net_name}' 沒有軌道,無法添加測試點")
else:
print(f"找不到網絡 '{net_name}'")
if test_points:
board.create_items(test_points)
board.push_commit(commit, f"添加 {len(test_points)} 個測試點")
return len(test_points)
else:
board.drop_commit(commit)
return 0
def main():
kicad = KiCad()
board = kicad.get_board()
# 指定要添加測試點的網絡名稱
net_names = ["GND", "VCC", "RST", "SCL", "SDA"]
count = add_test_points(board, net_names)
print(f"共添加了 {count} 個測試點")
if __name__ == "__main__":
main()
```
## 與其他工具集成
### 與 Git 版本控制集成
這個範例展示如何使用 kipy 實現 KiCad 與 Git 版本控制的集成:
```python
import os
import subprocess
from kipy.kicad import KiCad
import datetime
def git_commit_pcb_changes(board, commit_message=None):
"""保存電路板並將更改提交到 Git"""
# 獲取電路板文件路徑
board_path = board.filename
if not os.path.isfile(board_path):
print("電路板尚未保存,無法提交到 Git")
return False
# 保存電路板
board.save()
print(f"已保存電路板到: {board_path}")
# 獲取 Git 倉庫根目錄
try:
repo_root = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"],
cwd=os.path.dirname(board_path),
text=True
).strip()
except subprocess.CalledProcessError:
print("當前目錄不是 Git 倉庫")
return False
# 獲取相對路徑
rel_path = os.path.relpath(board_path, repo_root)
# 添加到暫存區
try:
subprocess.run(
["git", "add", rel_path],
cwd=repo_root,
check=True
)
# 創建提交信息
if not commit_message:
commit_message = f"更新電路板設計 {datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}"
# 提交更改
subprocess.run(
["git", "commit", "-m", commit_message],
cwd=repo_root,
check=True
)
print(f"已將電路板更改提交到 Git: {commit_message}")
return True
except subprocess.CalledProcessError as e:
print(f"Git 操作失敗: {e}")
return False
def main():
kicad = KiCad()
board = kicad.get_board()
# 進行一些修改...
# 保存並提交修改
git_commit_pcb_changes(board, "添加新的電源元件和軌道")
if __name__ == "__main__":
main()
```
### 與 BOM 管理系統集成
以下範例展示如何生成物料清單(BOM)並與外部系統集成:
```python
from kipy.kicad import KiCad
import csv
import json
import requests
def generate_bom(board):
"""生成電路板的物料清單"""
footprints = board.get_footprints()
# 按值和封裝分組元件
components = {}
for fp in footprints:
ref = fp.reference_field.text.value
val = fp.value_field.text.value
pkg = fp.definition.identifier.lib_id.name if hasattr(fp.definition, 'identifier') else "Unknown"
key = f"{val}|{pkg}"
if key not in components:
components[key] = {
'value': val,
'package': pkg,
'references': [],
'quantity': 0
}
components[key]['references'].append(ref)
components[key]['quantity'] += 1
# 轉換為列表
bom_list = list(components.values())
# 為每個項目添加引用字符串
for item in bom_list:
item['references_str'] = ", ".join(sorted(item['references']))
return bom_list
def export_bom_csv(bom_list, filename):
"""將 BOM 導出為 CSV 文件"""
with open(filename, 'w', newline='') as csvfile:
writer = csv.DictWriter(
csvfile,
fieldnames=['value', 'package', 'references_str', 'quantity'],
extrasaction='ignore'
)
writer.writeheader()
writer.writerows(bom_list)
print(f"BOM 已導出至 {filename}")
return filename
def upload_bom_to_system(bom_list, api_url, api_key):
"""將 BOM 上傳到外部系統"""
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {api_key}'
}
payload = {
'project_name': 'My KiCad Project',
'components': bom_list
}
try:
response = requests.post(api_url, headers=headers, json=payload)
response.raise_for_status()
print(f"BOM 已成功上傳: {response.json().get('message', '')}")
return True
except requests.exceptions.RequestException as e:
print(f"上傳失敗: {e}")
return False
def main():
kicad = KiCad()
board = kicad.get_board()
# 生成 BOM
bom_list = generate_bom(board)
# 導出為 CSV
export_bom_csv(bom_list, "bom_export.csv")
# 上傳到外部系統(需要實際的 API 端點和密鑰)
# upload_bom_to_system(bom_list, "https://api.example.com/bom", "your_api_key")
if __name__ == "__main__":
main()
```
## API 參考
### 主要類和模塊
- **kipy.kicad**:包含 KiCad 類,用於連接到 KiCad 實例
- **kipy.board_types**:包含 Board、Track、Via、Footprint 等類
- **kipy.geometry**:包含 Vector2、Angle、Box2 等幾何類
- **kipy.util**:包含單位轉換和其他實用功能
- **kipy.proto**:包含底層 protobuf 類型定義
- **kipy.errors**:包含異常類型
### 常用常數
- **BoardLayer**:定義電路板層(如 BL_F_Cu、BL_B_Cu、BL_F_SilkS)
- **ViaType**:定義過孔類型(如 VT_THROUGH、VT_BLIND_BURIED)
- **DocumentType**:定義文檔類型(如 DOCTYPE_BOARD、DOCTYPE_SCHEMATIC)
### 幾何操作
- **Vector2**:表示二維向量或點
- **Angle**:表示角度,提供度和弧度之間的轉換
- **Box2**:表示二維軸對齊框
## 下一步
- 瀏覽 [KiCad 開發文檔](https://dev-docs.kicad.org/en/apis-and-binding/ipc-api/) 了解更詳細的 API 信息
- 查看 [kipy 源代碼](https://gitlab.com/kicad/code/kicad-python) 以深入了解實現細節
- 加入 [KiCad 論壇](https://forum.kicad.info/) 與社區討論 API 使用
- 探索 [kipy 示例目錄](https://gitlab.com/kicad/code/kicad-python/-/tree/master/examples) 中的更多示例