High-performance OpenCV + wxWidgets integration for Python
Project description
wxCvModule 使用者指南
版本:v1.5 最後更新:2026-02-19
目錄
1. 簡介
wxCvModule 是一個高效能的 C++ + Python 混合函式庫,將 OpenCV 影像處理與 wxWidgets GUI 無縫整合進 Python wxPython 應用程式。
主要特色
| 特色 | 說明 |
|---|---|
| 高效能渲染 | 直接 C++ 渲染管線,處理大圖不卡頓 |
| 豐富的 ROI 工具 | 支援矩形、旋轉矩形、圓形、環形、多邊形、點、線段 |
| 互動操作 | Ctrl+滾輪縮放、滾輪平移、中鍵拖曳、右鍵選單 |
| Python 友好 | 直接傳遞 NumPy array,零拷貝共享記憶體 |
| 事件委派 | 右鍵點擊、滑鼠移動、雙擊事件可委派給 Python 處理 |
| 跨平台 | Windows、Linux、macOS 均支援 |
2. 安裝
2.1 透過 pip 安裝(推薦)
pip install wxcvmodule
安裝必要的 Python 依賴:
pip install wxPython numpy opencv-python
2.2 從原始碼編譯
若要從原始碼編譯,請參閱 CLAUDE.md 中的編譯指令,以及各平台對應的 Build Guide:
- Windows:
docs/Windows_Local_Build_Guide.md - Linux:
docs/Linux_Wheel_Build_Guide.md - macOS:CLAUDE.md 的 macOS 編譯章節
編譯完成後,可透過以下指令打包 Wheel:
pip install build scikit-build-core
python -m build --wheel
3. 各平台注意事項
此章節是使用 wxCvModule 前最重要的閱讀內容,特別是 macOS 使用者。
3.1 Windows
Windows 是最簡單的平台,沒有特殊限制。
- Import 順序:無限制,
import wx與import wxCvModule順序不影響功能。 - 面板座標(x, y):建議使用
-1(讓系統自動決定)。 - 依賴管理:Wheel 已包含所有 DLL(OpenCV、wxWidgets),安裝後即可使用,不需額外安裝任何套件。
- 路徑格式:
LoadImage()完整支援中文等 Unicode 路徑(內部使用std::wstring)。 - GUI 後端:Win32 API(
__WXMSW__)
典型初始化範例:
import wx
import wxCvModule # Windows 上 import 順序不影響
handle = container.GetHandle()
cv_panel = wxCvModule.wxCvROIAdvPanel(handle, wx.ID_ANY, -1, -1, width, height)
3.2 Linux
Linux 使用 GTK3 後端,需要系統預先安裝 GTK3 執行環境。
- Import 順序:無限制。
- 面板座標(x, y):使用
-1。 - 系統依賴:GTK3 必須存在於系統中。
安裝 GTK3(若尚未安裝):
# Ubuntu / Debian
sudo apt install libgtk-3-0
# Fedora / RHEL
sudo dnf install gtk3
# Arch Linux
sudo pacman -S gtk3
- 路徑格式:
LoadImage()使用 UTF-8 字串處理路徑,支援 Unicode 路徑。 - Wheel 大小:約 11–12 MB。OpenCV 與 wxWidgets 靜態連結進
.so,GTK3 則使用系統版本。 - GUI 後端:GTK3 (
__WXGTK__) - 圖像編解碼:HEIF(
.heic)與 JPEG XL(.jxl)需要 libheif / libjxl(Wheel 已包含)。
典型初始化範例:
import wx
import wxCvModule
handle = container.GetHandle()
cv_panel = wxCvModule.wxCvROIAdvPanel(handle, wx.ID_ANY, -1, -1, width, height)
3.3 macOS(重要差異)
macOS 與 Windows/Linux 有根本性的架構差異,使用前務必閱讀以下說明。
⚠️ 關鍵規則:Import 順序
# ✅ 正確:先 import wx,再 import wxCvModule
import wx
import wxCvModule
# ❌ 錯誤:會拋出 RuntimeError
import wxCvModule # wx 尚未載入,crash!
import wx
原因:macOS 版本的 wxCvModule 使用 -undefined dynamic_lookup 技術,wx 符號在執行時才從 wxPython 的 dylib 解析。若 wxPython 未先載入,C++ 端的 wxAppConsole::GetInstance() 找不到 wxApp,會拋出明確的 RuntimeError。
⚠️ 面板座標必須為 (0, 0)
# ✅ macOS 正確:使用 0, 0
cv_panel = wxCvModule.wxCvROIAdvPanel(handle, wx.ID_ANY, 0, 0, width, height)
# ❌ macOS 錯誤:使用 -1, -1 可能導致面板無法正確嵌入
cv_panel = wxCvModule.wxCvROIAdvPanel(handle, wx.ID_ANY, -1, -1, width, height)
⚠️ 關閉視窗時必須手動釋放 C++ panel
def on_close(self, event):
self.cv_panel = None # 觸發 C++ 析構 → 避免 wxApp 結束卡住
self.roi_panel = None
self.Destroy()
原因:macOS 使用隱藏框架(hidden wxFrame)作為 C++ panel 的臨時父視窗。若不釋放,wxApp 結束時會因 top-level window 仍存在而卡住不退出。
平台比較摘要
| 項目 | Windows | Linux | macOS |
|---|---|---|---|
| Import 順序限制 | 無 | 無 | 必須先 import wx |
| 面板 x, y 參數 | -1, -1 |
-1, -1 |
0, 0 |
| 關閉時需釋放 panel | 建議 | 建議 | 必須 |
| wxWidgets 連結方式 | 動態(獨立 DLL) | 靜態(內嵌) | dynamic_lookup(共用 wxPython) |
| GUI 後端 | Win32 | GTK3 | Cocoa |
| 路徑 Unicode 支援 | std::wstring |
UTF-8 | UTF-8 |
跨平台最佳寫法
若要讓同一份程式碼在三個平台都能正確執行,建議:
import sys
import os
# 自動搜尋 wxCvModule 位置
_dir = os.path.dirname(os.path.abspath(__file__))
for _p in [_dir, os.path.join(_dir, "..", "build"),
os.path.join(_dir, "..", "build", "Release")]:
if os.path.isdir(_p) and _p not in sys.path:
sys.path.insert(0, os.path.normpath(_p))
# wx 必須在 wxCvModule 之前(macOS 強制要求,其他平台無影響)
import wx
import wxCvModule
# 面板座標:macOS 用 0,0;其他平台用 -1,-1
_X = 0 if sys.platform == "darwin" else -1
_Y = 0 if sys.platform == "darwin" else -1
# 建立面板
cv_panel = wxCvModule.wxCvROIAdvPanel(handle, wx.ID_ANY, _X, _Y, w, h)
4. 快速開始
4.1 基本圖像顯示(wxCvPanel)
import sys
import wx
import numpy as np
import wxCvModule
_X = 0 if sys.platform == "darwin" else -1
_Y = 0 if sys.platform == "darwin" else -1
class BasicViewerFrame(wx.Frame):
def __init__(self):
super().__init__(None, title="wxCvModule Basic Viewer", size=(800, 600))
self.cv_panel = None
# 建立容器 Panel
self.container = wx.Panel(self)
self.container.SetBackgroundColour(wx.Colour(40, 40, 40))
# 綁定事件
self.Bind(wx.EVT_SHOW, self.on_show)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.container.Bind(wx.EVT_SIZE, self.on_resize)
self.Centre()
def on_show(self, event):
if event.IsShown() and self.cv_panel is None:
wx.CallAfter(self.init_panel)
event.Skip()
def on_close(self, event):
self.cv_panel = None # macOS 必要
self.Destroy()
def init_panel(self):
handle = self.container.GetHandle()
size = self.container.GetSize()
self.cv_panel = wxCvModule.wxCvPanel(
handle, wx.ID_ANY, _X, _Y, size.width, size.height
)
self.cv_panel.SetCenterImageEnable(True)
# 建立測試圖像
img = np.zeros((480, 640, 3), dtype=np.uint8)
img[:, :, 0] = 100 # B channel
self.cv_panel.SetMat(img)
self.cv_panel.SetZoomToFit()
def on_resize(self, event):
if self.cv_panel and self.cv_panel.IsOk():
sz = self.container.GetSize()
self.cv_panel.SetSize(sz.width, sz.height)
self.cv_panel.Refresh()
event.Skip()
if __name__ == "__main__":
app = wx.App()
BasicViewerFrame().Show()
app.MainLoop()
4.2 ROI 編輯(wxCvROIAdvPanel)
class ROIEditorFrame(wx.Frame):
def __init__(self):
super().__init__(None, title="ROI Editor", size=(900, 650))
self.roi_panel = None
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.container = wx.Panel(panel)
sizer.Add(self.container, 1, wx.EXPAND | wx.ALL, 4)
# ROI 模式選擇
mode_sizer = wx.BoxSizer(wx.HORIZONTAL)
mode_sizer.Add(wx.StaticText(panel, label="ROI Mode:"), 0,
wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 6)
self.mode_choice = wx.Choice(panel, choices=[
"Nothing", "Point", "Line", "Rectangle",
"RotatedRect", "Circle", "Annulus", "Polygon"
])
self.mode_choice.SetSelection(0)
self.mode_choice.Bind(wx.EVT_CHOICE, self.on_mode_change)
mode_sizer.Add(self.mode_choice)
sizer.Add(mode_sizer, 0, wx.ALL, 4)
panel.SetSizer(sizer)
self.Bind(wx.EVT_SHOW, self.on_show)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.container.Bind(wx.EVT_SIZE, self.on_resize)
self.Centre()
def on_show(self, event):
if event.IsShown() and self.roi_panel is None:
wx.CallAfter(self.init_panel)
event.Skip()
def on_close(self, event):
self.roi_panel = None # macOS 必要
self.Destroy()
def init_panel(self):
handle = self.container.GetHandle()
size = self.container.GetSize()
self.roi_panel = wxCvModule.wxCvROIAdvPanel(
handle, wx.ID_ANY, _X, _Y, size.width, size.height
)
# 啟用所有 ROI 工具及右鍵選單
self.roi_panel.SetFuncEnable(True, True, True)
self.roi_panel.SetMenuROIEnable(True, True, True, True, True, True, True)
self.roi_panel.SetCenterImageEnable(True)
# 設定 Crop 回調:使用者完成 ROI 後觸發
self.roi_panel.SetOnCropCallback(self.on_crop)
# 載入圖像
img = np.zeros((480, 640, 3), dtype=np.uint8)
self.roi_panel.SetMat(img)
self.roi_panel.SetZoomToFit()
def on_mode_change(self, event):
if self.roi_panel:
self.roi_panel.SetROIMode(self.mode_choice.GetSelection())
def on_resize(self, event):
if self.roi_panel and self.roi_panel.IsOk():
sz = self.container.GetSize()
self.roi_panel.SetSize(sz.width, sz.height)
self.roi_panel.Refresh()
event.Skip()
def on_crop(self, rect):
# rect = (x, y, width, height),原圖座標
wx.CallAfter(print, f"ROI Crop: {rect}")
4.3 純邏輯引擎(wxCvEngine,無需 GUI)
wxCvEngine 適合批次影像處理或命令列工具,不需要 wxPython 也不需要顯示視窗。
import wxCvModule
import numpy as np
engine = wxCvModule.wxCvEngine()
# 設定圖像
img = np.zeros((480, 640, 3), dtype=np.uint8)
engine.SetMat(img)
print(f"Image size: {engine.GetImageSize()}") # (width, height)
# 影像處理操作
engine.ConvertToGray()
engine.Resize(320, 240)
engine.GaussianBlur(5, 1.5)
edges = engine.Canny(50, 150) # 直接回傳結果 numpy array
# 取得處理後的圖像
result = engine.GetMat() # 回傳 numpy.ndarray
# 從檔案載入 / 儲存(支援 Unicode 路徑)
engine.LoadImage("/path/to/image.png")
engine.SaveImage("/output/result.jpg")
engine.LoadImage("/path/with/unicode/圖片.png", wxCvModule.IMREAD_GRAYSCALE)
5. 核心類別說明
5.1 wxCvEngine — 純邏輯影像引擎
不依賴 GUI,適用於影像前處理、批次轉換等場景。
| 方法 | 說明 |
|---|---|
SetMat(img) |
設定圖像(NumPy BGR/BGRA/Gray array) |
GetMat() |
取得當前圖像(NumPy array) |
HasImage() |
是否有圖像 |
GetImageSize() |
回傳 (width, height) |
LoadImage(path, flag?) |
從檔案載入(支援 Unicode 路徑) |
SaveImage(path) |
儲存到檔案 |
ConvertToGray() |
轉灰階 |
Resize(w, h) |
縮放 |
GaussianBlur(ksize, sigma) |
高斯模糊 |
Canny(t1, t2) |
Canny 邊緣偵測,回傳結果圖像 |
Clear() |
清除圖像 |
imread flag 常數:
wxCvModule.IMREAD_COLOR # 彩色(預設)
wxCvModule.IMREAD_GRAYSCALE # 灰階
wxCvModule.IMREAD_UNCHANGED # 保留 Alpha 通道
5.2 wxCvPanel — 基本圖像顯示面板
繼承 wxScrolledCanvas,提供縮放、平移、置中顯示功能。
| 方法 | 說明 |
|---|---|
SetMat(img) |
設定顯示圖像 |
GetMat() |
取得當前圖像 |
LoadImage(path) |
從檔案載入(支援 Unicode 路徑) |
SetZoomToFit() |
縮放至適合視窗 |
SetOriginal() |
恢復 1:1 原始大小 |
SetZoomIn() |
放大 |
SetZoomOut() |
縮小 |
SetCenterImageEnable(bool) |
啟用圖像置中顯示 |
SetCanvasBgColor(r, g, b) |
設定畫布背景顏色 |
SetSize(w, h) |
調整面板大小 |
Refresh() |
強制重新繪製 |
IsOk() |
面板是否正常初始化 |
GetHandle() |
取得原生視窗 Handle |
5.2.1 滑鼠操作快捷鍵
| 操作 | 行為 |
|---|---|
| 滾輪上 / 下 | 畫面垂直捲動(圖像放大時生效) |
| 水平傾斜滾輪 | 畫面水平捲動(支援水平滾輪的滑鼠) |
| Ctrl + 滾輪上 | 以滑鼠位置為中心放大圖像 |
| Ctrl + 滾輪下 | 以滑鼠位置為中心縮小圖像 |
| 中鍵按住拖曳 | 自由平移畫面(圖像放大時生效) |
| 右鍵點擊 | 開啟 ROI 右鍵選單(或觸發 Python 回調) |
此設計符合 CVAT、LabelMe 等主流標注軟體的操作慣例。
5.3 wxCvROIAdvPanel — 進階 ROI 編輯面板
繼承 wxCvPanel,增加完整的 ROI 工具集與事件系統。
除了繼承 wxCvPanel 的所有方法外,還提供:
| 方法 | 說明 |
|---|---|
SetFuncEnable(menu, crop, move) |
啟用右鍵選單 / Crop 功能 / 移動功能 |
SetMenuROIEnable(...) |
控制右鍵選單中各 ROI 工具的顯示 |
SetROIMode(mode) |
設定當前 ROI 工具(0–7) |
GetROIMode() |
取得當前 ROI 工具 |
GetRect() |
取得 ROI 邊界框 (x, y, w, h) |
GetPolygonPoints() |
取得多邊形頂點 [(x,y), ...] |
GetRotateAngle() |
取得旋轉角度 |
GetInnerRadius() |
取得環形內半徑 |
GetOuterRadius() |
取得環形外半徑 |
GetStartAngle() |
取得環形起始角度 |
GetEndAngle() |
取得環形結束角度 |
GetLeftMouseDownPoint() |
取得拖曳起點(原圖座標) |
GetLeftMouseUpPoint() |
取得拖曳終點(原圖座標) |
SetEditingROI(mode, pts, angle) |
以程式設定 ROI |
AppendOverlay(mode, pts, color, size, angle?) |
新增疊加層 |
AppendMaskOverlay(mask, color, alpha) |
新增遮罩疊加層 |
ClearOverlay() |
清除所有疊加層 |
SetDisplayOverlay(bool) |
顯示 / 隱藏疊加層 |
UpdateDrawImage(bool) |
強制更新渲染(加上 True 重建快取) |
ConvertMaskToPolygon(mask) |
將二值遮罩轉為多邊形點集 |
6. ROI 工具詳解
6.1 ROI 模式編號
| 編號 | 名稱 | 說明 | 互動方式 |
|---|---|---|---|
| 0 | Nothing | 無工具(清除 ROI) | — |
| 1 | Point | 點 | 左鍵點擊 |
| 2 | Line | 線段 | 左鍵拖曳 |
| 3 | Rectangle | 矩形 | 左鍵拖曳 |
| 4 | RotatedRect | 旋轉矩形 | 左鍵拖曳,拖曳邊緣旋轉 |
| 5 | Circle | 圓形 | 左鍵拖曳 |
| 6 | Annulus | 環形(扇環) | 左鍵拖曳 |
| 7 | Polygon | 多邊形 | 左鍵逐點點擊,雙擊結束(或點擊第一個頂點閉合) |
roi_panel.SetROIMode(3) # 切換到矩形工具
6.2 ROI 參數格式
以下是 SetEditingROI 與 AppendOverlay 使用的點集格式(非常重要,格式不正確會靜默失敗):
| 模式 | SetEditingROI pts 格式 | AppendOverlay pts 格式 | 備註 |
|---|---|---|---|
| Point (1) | [(x, y)] |
[(x, y), ...] 多點 |
— |
| Line (2) | [(x1, y1), (x2, y2)] |
同左 | — |
| Rectangle (3) | [(左上x, 左上y), (寬, 高)] |
同左 | — |
| RotatedRect (4) | [(左上x, 左上y), (寬, 高)] + angle 參數 |
同左 + angle 參數 |
— |
| Circle (5) | [(cx, cy), (radius, 0)] |
同左 | GetRect 回傳的 (x,y) 是圓心 |
| Annulus (6) | [(cx, cy), (outer_r, 0), (inner_r, 0), (start, end)] |
[(cx, cy), (0, 0), (inner_r, outer_r), (start, end)] |
⚠️ 兩者格式略有不同 |
| Polygon (7) | [(x1, y1), (x2, y2), ..., (xn, yn)] |
同左 | — |
環形(Annulus)注意:
SetEditingROI與AppendOverlay的 pts 格式在 pts[1], pts[2] 欄位順序不同,使用前請仔細確認。
6.3 取得 ROI 資料
mode = roi_panel.GetROIMode()
rect = roi_panel.GetRect() # (x, y, w, h)
if mode == 3: # Rectangle
x, y, w, h = rect
print(f"矩形: ({x}, {y}) 寬={w} 高={h}")
elif mode == 4: # RotatedRect
x, y, w, h = rect
angle = roi_panel.GetRotateAngle()
print(f"旋轉矩形: ({x}, {y}) 寬={w} 高={h} 角度={angle:.1f}°")
elif mode == 5: # Circle
# 注意:Circle 的 GetRect 回傳 (cx, cy, ?, ?),x,y 是圓心
cx, cy = rect[0], rect[1]
radius = roi_panel.GetOuterRadius()
print(f"圓形: 圓心=({cx}, {cy}) 半徑={radius}")
elif mode == 6: # Annulus
cx, cy = rect[0], rect[1]
inner_r = roi_panel.GetInnerRadius()
outer_r = roi_panel.GetOuterRadius()
start = roi_panel.GetStartAngle()
end = roi_panel.GetEndAngle()
print(f"環形: 圓心=({cx}, {cy}) 內徑={inner_r} 外徑={outer_r} "
f"角度={start:.1f}°~{end:.1f}°")
elif mode == 7: # Polygon
pts = roi_panel.GetPolygonPoints()
print(f"多邊形: {len(pts)} 個頂點")
for i, (x, y) in enumerate(pts):
print(f" [{i}] ({x:.1f}, {y:.1f})")
6.4 以程式設定 ROI(SetEditingROI)
# 設定矩形 ROI(左上角 100,80,寬 200,高 150)
roi_panel.SetROIMode(3)
roi_panel.SetEditingROI(3, [(100, 80), (200, 150)], 0.0)
# 設定圓形 ROI(圓心 320,240,半徑 80)
roi_panel.SetROIMode(5)
roi_panel.SetEditingROI(5, [(320, 240), (80, 0.1)], 0.0)
# 注意:第二個點的 y 值用 0.1 而非 0,避免 C++ 有效性檢查拒絕
# 設定多邊形 ROI(五芒星)
import math
pts = []
for j in range(10):
a = -math.pi/2 + j * math.pi/5
r = 100 if j % 2 == 0 else 40
pts.append((320 + r*math.cos(a), 240 + r*math.sin(a)))
roi_panel.SetROIMode(7)
roi_panel.SetEditingROI(7, pts, 0.0)
7. 疊加層系統(Overlay)
Overlay 系統允許在圖像上疊加多個 ROI 形狀,每個可指定不同顏色,用於同時顯示多個標注結果。
7.1 基本使用
# 清除所有既有的 overlay
roi_panel.ClearOverlay()
# 顏色格式為 OpenCV BGR:(Blue, Green, Red)
red = (0, 0, 255)
green = (0, 255, 0)
blue = (255, 0, 0)
yellow = (0, 255, 255)
# AppendOverlay(mode, points, color, line_width, angle=0.0)
# 矩形:[(左上x, 左上y), (寬, 高)]
roi_panel.AppendOverlay(3, [(50, 50), (200, 100)], red, 2)
# 圓形:[(圓心x, 圓心y), (半徑, 0)]
roi_panel.AppendOverlay(5, [(320, 240), (80, 0)], green, 2)
# 旋轉矩形:帶 angle 參數
roi_panel.AppendOverlay(4, [(150, 150), (180, 90)], blue, 2, 30.0)
# 環形:[(圓心), (0, 0), (inner_r, outer_r), (start_angle, end_angle)]
roi_panel.AppendOverlay(6, [(400, 300), (0, 0), (40, 80), (30, 210)], yellow, 2)
# 顯示 overlay
roi_panel.SetDisplayOverlay(True)
# 更新畫面(True = 重建快取)
roi_panel.UpdateDrawImage(True)
7.2 遮罩疊加(Mask Overlay)
適合顯示語義分割模型的輸出結果(如 SAM 分割遮罩):
import numpy as np
import cv2
# 建立二值遮罩(0 = 背景,255 = 前景)
mask = np.zeros((480, 640), dtype=np.uint8)
cv2.fillPoly(mask, [np.array([(100,100),(300,100),(300,300),(100,300)])], 255)
# AppendMaskOverlay(mask, color_bgr, alpha)
# alpha: 0.0 = 完全透明,1.0 = 完全不透明
roi_panel.AppendMaskOverlay(mask, (0, 0, 255), 0.5) # 半透明紅色
roi_panel.SetDisplayOverlay(True)
roi_panel.UpdateDrawImage(True)
7.3 遮罩轉多邊形
# 將 AI 模型輸出的 pixel mask 轉換為可編輯的多邊形
polygon_pts = roi_panel.ConvertMaskToPolygon(mask)
# 設定為可編輯的 ROI
roi_panel.SetEditingROI(7, polygon_pts, 0.0)
roi_panel.SetROIMode(7)
8. 事件回調
8.1 Crop 事件(使用者完成 ROI)
使用者拖曳完成 ROI 後觸發:
def on_crop(rect):
# rect = (x, y, w, h),原圖座標
x, y, w, h = rect
print(f"ROI 完成: x={x:.0f} y={y:.0f} w={w:.0f} h={h:.0f}")
roi_panel.SetOnCropCallback(on_crop)
注意:回調在 C++ 執行緒觸發,若要更新 GUI(如
wx.TextCtrl),必須使用wx.CallAfter:def on_crop(rect): wx.CallAfter(self.info_label.SetLabel, f"Rect: {rect}")
8.2 右鍵點擊委派(REQ-006)
攔截右鍵點擊,由 Python 自定義行為:
def on_right_click(pt, hit_index):
"""
pt : (x, y) 原圖座標(浮點數)
hit_index : 點擊到的 overlay 索引;-1 表示沒有點到任何 ROI
return True → 攔截(隱藏 C++ 原生右鍵選單)
return False → 不攔截(顯示 C++ 原生右鍵選單)
"""
x, y = pt
if hit_index >= 0:
print(f"點擊到第 {hit_index} 個 ROI,座標: ({x:.1f}, {y:.1f})")
# 在此實作自定義選單
return True # 攔截原生選單
return False # 未點到 ROI,顯示原生選單
roi_panel.SetOnRightClickCallback(on_right_click)
# 恢復 C++ 原生選單
roi_panel.SetOnRightClickCallback(None)
8.3 滑鼠移動與雙擊(REQ-010)
即時追蹤滑鼠在圖像上的座標,以及雙擊事件(適合 SAM 互動標注、快速刪除等):
def on_mouse_move(pt):
"""pt = (x, y) 原圖座標,高頻觸發"""
# 使用 CallAfter 更新 GUI,避免跨執行緒問題
wx.CallAfter(status_bar.SetStatusText, f"座標: ({pt[0]:.1f}, {pt[1]:.1f})")
def on_left_dclick(pt):
"""左鍵雙擊,適合快速選取 / SAM 前景點"""
wx.CallAfter(print, f"左鍵雙擊: ({pt[0]:.1f}, {pt[1]:.1f})")
def on_right_dclick(pt):
"""右鍵雙擊,適合快速刪除 / SAM 背景點"""
wx.CallAfter(print, f"右鍵雙擊: ({pt[0]:.1f}, {pt[1]:.1f})")
roi_panel.SetOnMouseMoveCallback(on_mouse_move)
roi_panel.SetOnLeftDClickCallback(on_left_dclick)
roi_panel.SetOnRightDClickCallback(on_right_dclick)
# 取消回調
roi_panel.SetOnMouseMoveCallback(None)
效能說明:
on_mouse_move為高頻回調,只在有註冊回調時才有 Python GIL 開銷。未註冊時完全無效能影響。
9. 視窗嵌入與 Resize
9.1 Resize 手動同步
由於 C++ panel 使用原生 API 嵌入(非 wxSizer 管理),不會自動跟隨父容器縮放,需手動同步:
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(None, size=(800, 600))
self.container = wx.Panel(self)
self.cv_panel = None
# 綁定容器的 resize 事件(注意是 container,不是 frame)
self.container.Bind(wx.EVT_SIZE, self.on_container_resize)
def on_container_resize(self, event):
if self.cv_panel and self.cv_panel.IsOk():
sz = self.container.GetSize()
self.cv_panel.SetSize(sz.width, sz.height)
self.cv_panel.Refresh()
event.Skip() # 必須呼叫,讓 sizer 繼續處理
9.2 延遲初始化(必須在視窗顯示後)
C++ panel 需要有效的原生 Handle 才能初始化。Handle 在視窗顯示後才穩定可用,因此推薦使用 EVT_SHOW + wx.CallAfter:
def on_show(self, event):
if event.IsShown() and self.cv_panel is None:
wx.CallAfter(self.init_panel) # 延遲一個事件循環,確保 Handle 有效
event.Skip()
10. API 快速參考
顯示控制
| 方法 | 說明 |
|---|---|
SetMat(img) |
設定 NumPy array 圖像(BGR/BGRA/Gray) |
GetMat() |
取得圖像 |
LoadImage(path) |
從檔案載入(支援 Unicode 路徑) |
SetZoomToFit() |
縮放至適合 |
SetOriginal() |
1:1 原始大小 |
SetZoomIn() |
放大 |
SetZoomOut() |
縮小 |
SetCenterImageEnable(bool) |
啟用圖像置中 |
SetCanvasBgColor(r, g, b) |
設定背景顏色 |
ROI 控制
| 方法 | 說明 |
|---|---|
SetROIMode(mode) |
設定工具(0–7) |
GetROIMode() |
取得當前工具 |
SetFuncEnable(m, c, mv) |
啟用選單 / Crop / 移動 |
SetMenuROIEnable(...) |
控制選單中各工具可見性(7個 bool) |
SetEditingROI(mode, pts, angle) |
以程式設定 ROI |
GetRect() |
取得邊界框 (x, y, w, h) |
GetPolygonPoints() |
取得多邊形頂點 |
GetRotateAngle() |
取得旋轉角度 |
GetInnerRadius() |
取得環形內半徑 |
GetOuterRadius() |
取得環形外半徑 |
GetStartAngle() |
取得環形起始角度 |
GetEndAngle() |
取得環形結束角度 |
Overlay 控制
| 方法 | 說明 |
|---|---|
AppendOverlay(mode, pts, color, size, angle?) |
新增疊加形狀 |
AppendMaskOverlay(mask, color, alpha) |
新增遮罩疊加 |
ClearOverlay() |
清除所有疊加 |
SetDisplayOverlay(bool) |
顯示 / 隱藏疊加 |
UpdateDrawImage(True) |
強制更新渲染 |
ConvertMaskToPolygon(mask) |
遮罩轉多邊形 |
回調設定
| 方法 | 說明 |
|---|---|
SetOnCropCallback(fn) |
ROI 完成回調 fn(rect) |
SetOnRightClickCallback(fn) |
右鍵回調 fn(pt, hit_index) → bool |
SetOnMouseMoveCallback(fn) |
滑鼠移動回調 fn(pt) |
SetOnLeftDClickCallback(fn) |
左鍵雙擊回調 fn(pt) |
SetOnRightDClickCallback(fn) |
右鍵雙擊回調 fn(pt) |
11. 常見問題
Q1:macOS 出現 RuntimeError: wxCvModule on macOS requires 'import wx' before 'import wxCvModule'
原因:macOS 版本的 wxCvModule 使用 dynamic_lookup 在執行時從 wxPython 解析 wx 符號。若 wxPython 未先載入,C++ 找不到 wxApp 實例。
解決:確保程式碼中 import wx 在 import wxCvModule 之前執行,包含所有間接匯入路徑。
Q2:面板建立後空白(白色或黑色畫面)
常見原因與解決方式:
| 症狀 | 可能原因 | 解決方式 |
|---|---|---|
| 白色畫面(macOS) | 未使用 Reparent,直接 NSView 嵌入 | 使用 wxCvModule 提供的 API,不要自行做 Cocoa 嵌入 |
| 黑色畫面(macOS) | setWantsLayer:YES 衝突 |
不要對容器 NSView 手動設定 layer |
| 空白(所有平台) | init_panel 在視窗顯示前執行 |
使用 EVT_SHOW + wx.CallAfter 延遲初始化 |
| 空白(所有平台) | Handle 取得時機過早 | 確保 GetHandle() 在 Show() 之後才呼叫 |
Q3:視窗關閉後 Python 程式不退出(macOS)
原因:C++ 隱藏框架(hidden wxFrame)是 top-level window,會阻止 wxApp 結束。
解決:在 EVT_CLOSE 中手動釋放 panel:
def on_close(self, event):
self.cv_panel = None # 觸發 C++ 析構 → 銷毀隱藏框架
self.roi_panel = None
self.Destroy()
Q4:Resize 後圖像沒有跟著縮放
C++ panel 不使用 wxSizer 管理,必須手動同步:
def on_resize(self, event):
if self.cv_panel and self.cv_panel.IsOk():
sz = self.container.GetSize()
self.cv_panel.SetSize(sz.width, sz.height)
self.cv_panel.Refresh()
event.Skip()
Q5:Linux 上出現 libgtk-3.so: cannot open shared object file
安裝 GTK3 執行時函式庫:
sudo apt install libgtk-3-0 # Ubuntu/Debian
sudo dnf install gtk3 # Fedora/RHEL
sudo pacman -S gtk3 # Arch Linux
Q6:回調函數更新 GUI 時出現 Assertion 或崩潰
C++ 回調可能在非 GUI 執行緒觸發。使用 wx.CallAfter 確保 GUI 更新在主執行緒執行:
def on_mouse_move(pt):
# ❌ 直接更新可能崩潰
# self.label.SetLabel(f"{pt}")
# ✅ 透過 CallAfter 安全更新
wx.CallAfter(self.label.SetLabel, f"({pt[0]:.0f}, {pt[1]:.0f})")
Q7:AppendOverlay 沒有效果
常見原因:
- 忘記呼叫
SetDisplayOverlay(True)開啟 overlay 顯示。 - 忘記呼叫
UpdateDrawImage(True)觸發重繪。 - 點集格式不正確(請對照第 6.2 節的格式表)。
roi_panel.ClearOverlay()
roi_panel.AppendOverlay(3, [(100, 100), (200, 150)], (0, 0, 255), 2)
roi_panel.SetDisplayOverlay(True) # 必須!
roi_panel.UpdateDrawImage(True) # 必須!
本指南對應 wxCvModule v1.5 及以上版本。
完整範例程式請參閱 examples/python_demo.py。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wxcvmodule-0.1.0-cp314-cp314-win_amd64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp314-cp314-win_amd64.whl
- Upload date:
- Size: 36.4 MB
- Tags: CPython 3.14, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86be909708298cdba06a6c00ab152bdcff7bd18ebdefc448c8861e319dc44a61
|
|
| MD5 |
9023b510948ab050ed962af4ac724aa5
|
|
| BLAKE2b-256 |
7e2e5d5c116fec293e0bcad3c676dc11a09748f5772ac4aadc7d85bbb7a85351
|
File details
Details for the file wxcvmodule-0.1.0-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 36.4 MB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5d088b140e33caf778c6e7a1cf2d82cccdc48ace304a0c361838dc4d2c1558c
|
|
| MD5 |
2cced0c014aa92735ef3c1191c5844a2
|
|
| BLAKE2b-256 |
c2d01d0534bee3d72fee41c91c7b50825d0b7ba97865c0460c8cffb6d49e2ab4
|
File details
Details for the file wxcvmodule-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 12.2 MB
- Tags: CPython 3.13, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b77c05e74977986b67d9512dc94c4591cbcbdf679541e9bb365eaeb7fe8f094
|
|
| MD5 |
97817510088002a0e4834c5278fcceca
|
|
| BLAKE2b-256 |
533cfe3a3cc5e10f58cce81816aabb210f7d3b15feb881e30c42e58c14cb12ab
|
File details
Details for the file wxcvmodule-0.1.0-cp313-cp313-macosx_26_0_arm64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp313-cp313-macosx_26_0_arm64.whl
- Upload date:
- Size: 32.8 MB
- Tags: CPython 3.13, macOS 26.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94bc72c5ecb099cd465dffa8542a006eb3fafe5df77fc44d8502daa653ceb244
|
|
| MD5 |
b5bf10273b34e7e43f72aa884b13c554
|
|
| BLAKE2b-256 |
b9fa2962e78751c6ccf756bc33968a16c4c8a9137d050999738470a2897f33bb
|
File details
Details for the file wxcvmodule-0.1.0-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 36.4 MB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4f3206ea3770058d7d5f0e83cf5d4aa52ec8e0b559d653c25a37d2d91b424a4
|
|
| MD5 |
5243f7e066cad66222139c4cde10cedf
|
|
| BLAKE2b-256 |
7f647b0a4576d64d939c8156914d04c5eaef116399763601ca1ae0f070d22614
|
File details
Details for the file wxcvmodule-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 12.2 MB
- Tags: CPython 3.12, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
325e7834eb51310ffa2ed39ecd2d3cc46a730648adcbaebf82c031b7d1d5be48
|
|
| MD5 |
c69246d5159946a38c14b2511d76de33
|
|
| BLAKE2b-256 |
2a2bb1a640b0de9bd0983cdd0b68cace3078d4816e4d39ffa3d3755792f12a9f
|
File details
Details for the file wxcvmodule-0.1.0-cp312-cp312-macosx_26_0_arm64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp312-cp312-macosx_26_0_arm64.whl
- Upload date:
- Size: 32.8 MB
- Tags: CPython 3.12, macOS 26.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db077c7b2a44f81928c990fada5d40125fc86235ce7e28c85a3a57cf28ce081b
|
|
| MD5 |
6ed32e92edfb0209a63ac6e86f7bd0a5
|
|
| BLAKE2b-256 |
07b7a9f3320a4b228474fbd7780680b8c40e11b8f86e931ec6cdd589dc7b00d2
|
File details
Details for the file wxcvmodule-0.1.0-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 36.4 MB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97670892fbf4f231b8cf58e05d5f7e28ed775456807965c88d73e7991ea946a9
|
|
| MD5 |
defb38bc98eb99b855537f42724f358f
|
|
| BLAKE2b-256 |
8e90629a70fcc765d6b93af2b2a5247d049be5dbe1a93f07a10060a70045c05c
|
File details
Details for the file wxcvmodule-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 12.2 MB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca558f45a480c2d9885db17b640b884e4200fbb307c4b7607f7e9f444ab3268b
|
|
| MD5 |
c17bfb925d880ec0c3c7c5436a9a5900
|
|
| BLAKE2b-256 |
d93cd107c3378e4d60288fe7bc6e4913630cfd002029b79f345361f19e16c0bb
|
File details
Details for the file wxcvmodule-0.1.0-cp311-cp311-macosx_26_0_arm64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp311-cp311-macosx_26_0_arm64.whl
- Upload date:
- Size: 32.8 MB
- Tags: CPython 3.11, macOS 26.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfacb8b048674819e3f71b3724c5fd2b805a8a13cebf06fe76c2a4e1569e42c1
|
|
| MD5 |
f47cee029ed903c2008569eed5eabad2
|
|
| BLAKE2b-256 |
ab845c5172e739da0c0d11287e9b631733f923bcc8bf35e9c3a8b3f795230e8a
|
File details
Details for the file wxcvmodule-0.1.0-cp310-cp310-win_amd64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp310-cp310-win_amd64.whl
- Upload date:
- Size: 36.4 MB
- Tags: CPython 3.10, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
126074a687881df337f9758062523e4e2e5d761db5217869edc513524d81a6d0
|
|
| MD5 |
0857b66e4395411ef794dc2df1d66ebc
|
|
| BLAKE2b-256 |
7d44c14094327b7b950b0e79694e3ce92eec33524a95c3894389bd829222001d
|
File details
Details for the file wxcvmodule-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 12.2 MB
- Tags: CPython 3.10, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e50f282d6165900d0752840616a71fda26d713e273ded881c19dc58d3ca0d325
|
|
| MD5 |
9eed279ebabd597a05bf4f6461bdba1f
|
|
| BLAKE2b-256 |
e796fe46d4977b6830f7db287dcc0bd847468d388e1ef57480733c94a84a31d4
|
File details
Details for the file wxcvmodule-0.1.0-cp310-cp310-macosx_26_0_arm64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp310-cp310-macosx_26_0_arm64.whl
- Upload date:
- Size: 32.7 MB
- Tags: CPython 3.10, macOS 26.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c907f6969229f17e8a2d8347ba060ab19d610da75f2a94bc6a70f12539f3ec1
|
|
| MD5 |
3b2f4e066f5e2c680291b0718fddf507
|
|
| BLAKE2b-256 |
ad9f779a4c71b0d44e5c6ff072b738e49b65b44d11f35c301a56e429cec5d455
|
File details
Details for the file wxcvmodule-0.1.0-cp39-cp39-win_amd64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp39-cp39-win_amd64.whl
- Upload date:
- Size: 36.4 MB
- Tags: CPython 3.9, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adffc8ba100f64e99ac2acc1d0f9a9e7cb52f548f6af8856d287d488f44d5572
|
|
| MD5 |
ee91da3b6244887b50cc101a99cbcb1f
|
|
| BLAKE2b-256 |
9e28e954d6cc8813433248671ef294b0fde636d05b733474d2d692f34b7bfdc9
|
File details
Details for the file wxcvmodule-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 12.2 MB
- Tags: CPython 3.9, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43af7e7fc9aea0ebd1c6f0c66ddc4bf232c158fdadaea1482bfb59cf22a4b875
|
|
| MD5 |
785c2dc2438f759874d425df0054a70c
|
|
| BLAKE2b-256 |
5092cd35795676890e2d9a5caca8d74081da891eb3cf76b9a6055de00f2a3e07
|
File details
Details for the file wxcvmodule-0.1.0-cp39-cp39-macosx_26_0_arm64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp39-cp39-macosx_26_0_arm64.whl
- Upload date:
- Size: 32.8 MB
- Tags: CPython 3.9, macOS 26.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0c6b0d34eb6f02c720ebfbf97ec74121d9e6eee4560473b4bbef40a0c38c5b0
|
|
| MD5 |
accf6b43dbe0c3a177c373264ee1383c
|
|
| BLAKE2b-256 |
c9e0614b549b763b850d4780869b358e0c6d4700cacce06d6fbb00434b45e89f
|
File details
Details for the file wxcvmodule-0.1.0-cp38-cp38-win_amd64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp38-cp38-win_amd64.whl
- Upload date:
- Size: 36.4 MB
- Tags: CPython 3.8, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1cf2a5d23ecfa8a9eb62e77118e3bf074b1d6b86ef8d19ec232261eb8b85536
|
|
| MD5 |
bdd79bc9cd91b50824b571953048ed5e
|
|
| BLAKE2b-256 |
30ba011aba2a3212f5fe5833210e8eb68eb633949994e9d22e7002c56e517a90
|
File details
Details for the file wxcvmodule-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: wxcvmodule-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 12.2 MB
- Tags: CPython 3.8, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e8e4e093c8c0b00abc9b10c16b655af2d522a95349b2d54fc7963ac410223a5
|
|
| MD5 |
5d743b029a2db8f7ab9ba744e07e7457
|
|
| BLAKE2b-256 |
53855203f2154e6aebf6cb65c70d11ce4f59362cbb87093f9f7b3a7d321f93f6
|