# 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 的交互更加容易 ![截圖 2025-04-25 晚上10.25.43](https://hackmd.io/_uploads/ryZ42Gtkel.png) :::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 並獲取版本信息 ![截圖 2025-04-25 晚上10.35.58](https://hackmd.io/_uploads/SJyxRMK1xg.png) ```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("在多個層上繪製了同心方形") ``` ![截圖 2025-04-25 晚上10.28.10](https://hackmd.io/_uploads/HJJB2zFyle.png) ![截圖 2025-04-25 晚上10.28.31](https://hackmd.io/_uploads/BysEhGK1gx.png) ## 常見任務快速參考 ### 單位轉換 ```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) 中的更多示例