Replay Oriented Instrumentation
ArabelaTso/Skills-4-SE
Instruments programs to record execution information for deterministic replay debugging.
A skill your agent uses when integrating Python scripts with C++ in a DAWorkbench plugin — calling Python from C++, Python multithreading without UI conflicts, Python-to-C++ data exchange, Python UI…
$ npx skills add czyt1988/data-workbench --skill python-script-integration -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install czyt1988/data-workbench python-script-integration --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/czyt1988/data-workbench.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/python-script-integration .claude/skills/python-script-integration && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "python-script-integration" agent skill from https://github.com/czyt1988/data-workbench/tree/master/skills/python-script-integration into .claude/skills/python-script-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-script-integration", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/czyt1988/data-workbench/tree/master/skills/python-script-integrationType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add czyt1988/data-workbench --skill python-script-integration -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install czyt1988/data-workbench python-script-integration --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/czyt1988/data-workbench.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/python-script-integration .agents/skills/python-script-integration && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "python-script-integration" agent skill from https://github.com/czyt1988/data-workbench/tree/master/skills/python-script-integration into .agents/skills/python-script-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-script-integration", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add czyt1988/data-workbench --skill python-script-integration -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install czyt1988/data-workbench python-script-integration --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/czyt1988/data-workbench.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/python-script-integration .cursor/skills/python-script-integration && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "python-script-integration" agent skill from https://github.com/czyt1988/data-workbench/tree/master/skills/python-script-integration into .cursor/skills/python-script-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-script-integration", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/czyt1988/data-workbench.git --path skills/python-script-integration--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add czyt1988/data-workbench --skill python-script-integration -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install czyt1988/data-workbench python-script-integration --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/czyt1988/data-workbench.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/python-script-integration .gemini/skills/python-script-integration && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "python-script-integration" agent skill from https://github.com/czyt1988/data-workbench/tree/master/skills/python-script-integration into .gemini/skills/python-script-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-script-integration", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install czyt1988/data-workbench python-script-integrationInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add czyt1988/data-workbench --skill python-script-integration -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/czyt1988/data-workbench.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/python-script-integration .github/skills/python-script-integration && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "python-script-integration" agent skill from https://github.com/czyt1988/data-workbench/tree/master/skills/python-script-integration into .github/skills/python-script-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-script-integration", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add czyt1988/data-workbench --skill python-script-integration -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install czyt1988/data-workbench python-script-integration --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/czyt1988/data-workbench.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/python-script-integration .opencode/skills/python-script-integration && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "python-script-integration" agent skill from https://github.com/czyt1988/data-workbench/tree/master/skills/python-script-integration into .opencode/skills/python-script-integration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "python-script-integration", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
python-script-integrationA skill your agent uses when integrating Python scripts with C++ in a DAWorkbench plugin — calling Python from C++, Python multithreading without UI conflicts, Python-to-C++ data exchange, Python UI…
Python Script Integration is an agent skill from czyt1988/data-workbench. Use when integrating Python scripts with C++ in a DAWorkbench plugin — calling Python from C++, Python multithreading without UI conflicts, Python-to-C++ data exchange, Python UI dialog invocation, threadstatusmanager usage, GIL management. Triggers: Python call, python thread, DAPyModule, pybind11, GIL, callInMainThread, threadstatusmanager, python script, data exchange, python UI.
Its SKILL.md is about 5.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Data & Analytics, covering Async programming. It works with Python and C++. The repository describes itself as: AI Agent-driven data analysis workbench built on C++17/Qt: directed-graph workflow engine, embedded Python (pandas/numpy), interactive publication-quality charts, C++ & Python… The licence is LGPL-3.0.
4 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit cf3bbda. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
pythonFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Python Script Integration loads about 5.5k tokens when it runs. Until then it costs about 104 tokens; SKILL.md has 451 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from czyt1988/data-workbench at commit cf3bbda, republished under its LGPL-3.0 licence (© czyt1988). 451 words, ~5,529 tokens.
.claude/skills/python-script-integration/SKILL.md (or your agent's skills folder).本技能指导在 DAWorkbench 插件中实现 Python 脚本调用、Python 多线程、Python 与 C++ 界面的数据交互和跨线程通信。
你要做什么?
├── C++ 调用 Python 函数 → 场景 A:Python 模块导入与函数调用
├── Python 后台线程处理数据,不阻塞 UI → 场景 B:Python 多线程
├── Python 线程向 C++ 主线程推送数据 → 场景 C:跨线程数据回传
├── Python 脚本访问 C++ 数据管理器 → 场景 D:Python 访问 C++ 数据
├── Python 脚本弹出 UI 对话框 → 场景 E:Python 调用 UI
├── Python 脚本向日志窗口输出 → 场景 F:Python 日志通道
└── Python 脚本部署与更新 → 场景 G:脚本部署┌──────── C++ 主线程(Qt 事件循环) ────────┐
│ │
│ MyWorker (C++ Worker) │
│ ├ DAPyModule m_pyModule │
│ │ └ import("MyPackage") │
│ ├ DAPyModule m_pyDataAnalysisModule │
│ │ └ = m_pyModule.attr("data_analysis")│
│ ├ DAPyModule m_threadStatusMgrModule │
│ │ └ = DAWorkbench.DAPyBase │
│ │ .attr("thread_status_manager") │
│ │ │
│ ├ 调用 Python 函数 (同步) │
│ │ └ m_pyDataAnalysisModule->attr( │
│ │ "process_zip_data_thread")(path) │
│ │ → 返回 taskid │
│ │ │
│ └ QTimer::singleShot 轮询线程状态 │
│ └ get_task_status(taskid) │
│ → 返回 dict {is_running, progress, message, ...} │
│ │
└───────────────────────────────────────────┘
↑ callInMainThread ↑ QTimer 轮询
│ (Qt 信号槽跨线程) │ (C++ 主动查询)
┌───────── Python 后台线程 ──────────────────┐
│ │
│ threading.Thread(target=process_zip_data)│
│ ├ 处理数据(pandas/numpy) │
│ ├ status.update_progress(p, msg) │
│ ├ status.update_custom_data(k, v) │
│ ├ status.finish(success, msg) │
│ └ 回调函数: signal_handler │
│ .callInMainThread(add_data_func) │
│ → 在主线程中操作 DataManager │
│ │
└───────────────────────────────────────────┘pybind11::gil_scoped_acquire / pybind11::gil_scoped_releasepybind11::error_already_set 异常必须在 GIL 作用域内 catch 并消费callInMainThread#include "DAPyModule.h"
#include "DAPybind11InQt.h" // 必须在所有 pybind11 头文件之前
#include "DAPybind11QtCaster.hpp"
bool MyWorker::initializePythonEnv()
{
try {
// 1. 导入插件自己的 Python 包
m_pyModule = std::make_unique<DA::DAPyModule>();
if (!m_pyModule->import("MyPackage")) {
daCritical << "Failed to import MyPackage";
return false;
}
// 2. 获取子模块(包内的 .py 文件)
m_pyDataModule = std::make_unique<DA::DAPyModule>();
*m_pyDataModule = m_pyModule->attr("data_analysis");
// 3. 导入平台提供的线程状态管理器
DA::DAPyModule daWorkbench("DAWorkbench.DAPyBase");
m_threadStatusMgr = std::make_unique<DA::DAPyModule>();
*m_threadStatusMgr = daWorkbench.attr("thread_status_manager");
m_isPythonValid = true;
return true;
} catch (const std::exception& e) {
m_pyModule.reset();
m_pyDataModule.reset();
qCritical() << e.what();
return false;
}
}#include "DAPybind11QtCaster.hpp" // Qt ↔ Python 类型转换
void MyWorker::callPythonFunction()
{
try {
// 获取函数对象
auto func = m_pyDataModule->attr("my_function");
if (func.is_none()) {
qCritical() << "function not found";
return;
}
// 传递 QString 参数(DAPybind11QtCaster 自动转换)
auto arg = DA::PY::toPyObject(QString("some_path"));
auto result = func(arg);
// 获取返回值
std::string taskid = result.cast<std::string>();
m_taskID = taskid;
} catch (const std::exception& e) {
qCritical() << e.what();
}
}PyScripts/
└── MyPackage/ ← 包名(C++ import 的名字)
├── __init__.py ← 包入口,导出子模块
├── data_analysis.py ← 数据分析脚本
└── utils.py ← 工具函数# __init__.py
from . import data_analysis, utils# data_analysis.py
def my_function(path: str) -> str:
"""C++ 通过 attr("my_function") 调用此函数"""
import pandas as pd
df = pd.read_csv(path)
return f"processed_{len(df)}_rows"| C++ 类型 | Python 类型 | 说明 |
|---|---|---|
QString | str | 自动 UTF-8 转换 |
QList<T> | list | 泛型容器 |
QHash<K,V> / QMap<K,V> | dict | 字典转换 |
QVariant | Any | 支持 numpy 数组/标量 |
QDateTime | datetime.datetime | 支持 pandas.Timestamp |
QColor | tuple | (r,g,b) 或 (r,g,b,a) |
手动转换辅助函数:
// QString → pybind11::object
auto pyStr = DA::PY::toPyObject(QString("hello"));
// pybind11::dict → QVariant
QVariant var = DA::PY::fromPyVariant(pyDict);
// 判断能否转为 QDateTime
bool canConvert = DA::PY::canCastToQDateTime(pyObj);# data_analysis.py
import threading
import DAWorkbench.DAPyBase.thread_status_manager as tsm
def process_zip_data_thread(zip_path: str) -> str:
"""
主入口:启动后台线程处理数据
返回: taskid(字符串),空字符串表示启动失败
"""
try:
# 创建任务状态追踪器
taskid, status = tsm.create_task_with_status("process zip data")
# 启动后台线程
thread = threading.Thread(
target=_process_data_internal,
args=(zip_path, status),
daemon=True
)
thread.start()
return taskid
except Exception as e:
logger.error(f"启动线程失败: {e}")
return ""
def _process_data_internal(zip_path: str, status: tsm.ProcessingStatus):
"""后台线程的实际处理函数"""
try:
status.start()
status.update_progress(0, "开始处理")
status.update_custom_data("zip_path", zip_path)
# ... 数据处理逻辑 ...
for i, file in enumerate(files):
status.update_progress(
(i / len(files)) * 100,
f"处理文件 {i}/{len(files)}: {file}"
)
# ... 处理每个文件 ...
status.update_custom_data("result_count", len(results))
status.finish(True, f"完成,共处理 {len(results)} 个文件")
except Exception as e:
status.finish(False, f"失败: {e}")void MyWorker::startBackgroundTask()
{
if (!isPythonValid()) return;
try {
// 调用 Python 函数启动后台线程
auto func = m_pyDataModule->attr("process_zip_data_thread");
auto arg = DA::PY::toPyObject(m_zipPath);
auto result = func(arg);
if (result.is_none()) return;
m_taskID = result.cast<std::string>();
// 显示进度条
DA::DAStatusBarInterface* statusBar = m_ui->getStatusBar();
statusBar->showProgressBar();
statusBar->setProgressText(QString(u8"正在处理..."));
// 启动轮询
checkTaskStatus();
} catch (const std::exception& e) {
qCritical() << e.what();
}
}
void MyWorker::checkTaskStatus()
{
try {
// 释放 GIL,让 Python 后台线程能运行
{
pybind11::gil_scoped_release release;
QThread::sleep(1); // 短暂休眠
}
// 调用 Python 获取任务状态
auto get_status = m_threadStatusMgr->attr("get_task_status");
pybind11::dict status = get_status(m_taskID);
bool is_running = status["is_running"].cast<bool>();
if (is_running) {
// 更新进度
double progress = status["progress"].cast<double>();
std::string message = status["message"].cast<std::string>();
double elapsed = status["elapsed_seconds"].cast<double>();
DA::DAStatusBarInterface* statusBar = m_ui->getStatusBar();
statusBar->setProgress(progress);
statusBar->setProgressText(
QString::fromStdString(message) +
QString(u8" 已用时: %1:%2")
.arg(static_cast<int>(elapsed) / 60, 2, 10, QChar('0'))
.arg(static_cast<int>(elapsed) % 60, 2, 10, QChar('0'))
);
// 继续轮询(20ms 后再次检查)
QTimer::singleShot(20, this, &MyWorker::checkTaskStatus);
} else {
// 任务完成
bool is_success = status["is_success"].cast<bool>();
QString msg = QString::fromStdString(status["message"].cast<std::string>());
DA::DAStatusBarInterface* statusBar = m_ui->getStatusBar();
statusBar->hideProgressBar();
statusBar->setBusy(false);
if (is_success) {
statusBar->showMessage(QString(u8"完成!"));
// 获取自定义数据
pybind11::dict custom_data = status["custom_data"];
QString zip_path = QString::fromStdString(
custom_data["zip_path"].cast<std::string>());
// 处理完成后的逻辑
} else {
statusBar->showMessage(QString(u8"失败! ") + msg);
}
QApplication::processEvents();
}
} catch (const std::exception& e) {
qCritical() << e.what();
}
}get_task_status(taskid) 返回的字典包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
task_id | str | 任务唯一 ID |
task_name | str | 任务名称 |
is_running | bool | 是否正在运行(未暂停、未取消、未完成) |
is_paused | bool | 是否暂停 |
is_canceled | bool | 是否取消 |
is_success | bool | 是否成功完成 |
current_stage | str | 当前阶段描述 |
progress | float | 进度百分比 (0-100) |
elapsed_seconds | float | 已用时间(秒) |
message | str | 当前状态消息 |
custom_data | dict | 自定义数据副本 |
# 创建任务
taskid, status = tsm.create_task_with_status("task name")
# 状态控制
status.start() # 开始
status.update_progress(50, "处理中") # 更新进度
status.pause() # 暂停
status.resume() # 恢复
status.cancel("用户取消") # 取消
status.finish(True, "完成消息") # 完成(成功/失败)
# 自定义数据
status.update_custom_data("key", value)
value = status.get_custom_data("key", default)
# 查询
info = status.get_status() # 返回状态字典
is_active = status.is_active() # 是否活跃
is_finished = status.is_finished() # 是否已结束Python 后台线程处理完数据后,需要将结果写入 C++ 的 DataManager。但不能直接操作 Qt 界面,必须通过 DAPythonSignalHandler::callInMainThread 在主线程执行。
# data_analysis.py
import da_app, da_data
def process_zip_data_thread(zip_path: str) -> str:
taskid, status = tsm.create_task_with_status("process zip data")
def internal_callback(result_dict):
"""后台线程完成后的回调,通过 callInMainThread 在主线程写入数据"""
if result_dict is not None:
signal_handler = da_app.getCore().getPythonSignalHandler()
if signal_handler:
def add_data_in_main_thread():
"""此函数在 Qt 主线程中执行"""
datamanager = da_app.getCore().getDataManagerInterface()
for name, df in result_dict.items():
data = da_data.DAData(df)
data.setName(name)
data.setDescribe(name)
datamanager.addData(data)
signal_handler.callInMainThread(add_data_in_main_thread)
thread = threading.Thread(
target=_process_data,
args=(zip_path, internal_callback, status),
daemon=True
)
thread.start()
return taskid// 在 Worker 初始化时连接数据添加信号
void MyWorker::initialize(DA::DACoreInterface* core, MyUI* ui)
{
m_core = core;
m_ui = core->getUiInterface();
DA::DADataManagerInterface* datamgr = m_core->getDataManagerInterface();
// 连接数据添加信号
connect(datamgr, &DA::DADataManagerInterface::dataAdded,
this, &MyWorker::onDataAdded);
}
void MyWorker::onDataAdded()
{
// 数据已通过 Python 线程 → callInMainThread → DataManager::addData 添加
// 在这里重新分析数据
analysisDatas();
Q_EMIT datasAdded(); // 通知 UI 更新
}Python 后台线程 C++ 主线程(Qt 事件循环)
│ │
├ signal_handler.callInMainThread( │
│ lambda: add_data()) │
│ └ 函数包装为 FunctionWrapper │
│ └ 存入 m_functionMap (mutex) │
│ └ emit executeRequested(id) ──────┼──→ Qt 跨线程队列连接
│ │
│ (Python 线程继续执行) ├ onExecuteRequested(id)
│ │ └ 从 map 取出 FunctionWrapper
│ │ └ 执行函数(操作 DataManager)
│ │import da_app, da_data
def my_python_function():
"""Python 脚本中访问 C++ DataManager"""
datamanager = da_app.getCore().getDataManagerInterface()
# 按通配符查找数据表
datas = datamanager.findDatas("*module*")
for d in datas:
name = d.getName()
df = d.toDataFrame() # 转为 pandas DataFrame
# 处理 DataFrame...
# 获取特定名称的数据
datas = datamanager.findDatas("*module_8")
if datas:
df = datas[0].toDataFrame()import da_app, da_data
import pandas as pd
def write_data_to_manager():
"""将 DataFrame 写入 DataManager(必须在主线程调用)"""
signal_handler = da_app.getCore().getPythonSignalHandler()
signal_handler.callInMainThread(lambda: _do_write())
def _do_write():
datamanager = da_app.getCore().getDataManagerInterface()
df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
data = da_data.DAData(df)
data.setName("my_data_table")
data.setDescribe("description")
datamanager.addData(data)import da_data
# DAData 包装 pandas DataFrame
data = da_data.DAData(df) # 从 DataFrame 创建
df = data.toDataFrame() # 转回 DataFrame
data.setName("name") # 设置名称
data.setDescribe("desc") # 设置描述
name = data.getName() # 获取名称
is_null = data.isNull() # 是否为空
data_id = data.id() # 获取 IDimport da_app
def my_function():
ui = da_app.getCore().getUiInterface()
# 选择文件夹
path = ui.getExistingDirectory("选择保存路径")
if len(path) == 0:
return # 用户取消
# 选择文件
# file_path = ui.getOpenFileName("选择文件", "CSV (*.csv)")平台提供 FormBuilder 用于构建参数配置对话框,C++ 端通过 getConfigValues 弹出对话框。
from DAWorkbench.DAPyBase.form_builder import FormBuilder, option
def show_config_dialog():
ui = da_app.getCore().getUiInterface()
config = (FormBuilder("参数设置")
.group("basic", "基本设置")
.enum("machine", label="机型",
default="GMV-224WM/S",
options=[option("GMV-224WM/S", "GMV-224WM/S"),
option("GMV-252WM/S", "GMV-252WM/S")],
description="选择机型")
.bool("save_word", label="是否生成报告",
default=True,
description="勾选将生成 Word 报告")
.end_group()
.build()
)
result = ui.getConfigValues(config, "my_config_cache_key")
if len(result) == 0:
return # 用户取消
machine = result["machine"]
save_word = result["save_word"]| 方法 | 用途 |
|---|---|
FormBuilder(title) | 创建表单构建器 |
.group(id, title) | 开始一个分组 |
.end_group() | 结束当前分组 |
.str(name, label, default, description) | 字符串输入 |
.int(name, label, default, description) | 整数输入 |
.float(name, label, default, description) | 浮点输入 |
.bool(name, label, default, description) | 布尔选择 |
.enum(name, label, default, options, description) | 下拉枚举 |
.option(value, label) | 枚举选项(在 options 列表中) |
.build() | 构建 JSON 配置 |
import da_app
def my_function():
ui = da_app.getCore().getUiInterface()
# 第二个参数 showInStatusBar:
# True = 同时显示在状态栏(仅主线程安全)
# False = 仅写入日志窗口(后台线程安全)
ui.addInfoLogMessage("信息消息", False)
ui.addWarningLogMessage("警告消息", False)
ui.addCriticalLogMessage("错误消息", False)!!!danger 后台线程中必须传 showInStatusBar=False
Python 后台线程中调用日志方法时,第二个参数必须为 False,否则会触发 GUI 操作导致崩溃。False 表示仅走 spdlog 通道(线程安全),不触碰状态栏等 GUI 控件。
import logging
from loguru import logger
try:
import da_app
_has_da_app = True
except Exception:
_has_da_app = False
def emit_ui_message(msg: str, level: str = "warning") -> None:
"""线程安全的 UI 日志输出"""
if not _has_da_app:
return
try:
ui = da_app.getCore().getUiInterface()
if level == "critical":
ui.addCriticalLogMessage(msg, False)
elif level == "info":
ui.addInfoLogMessage(msg, False)
else:
ui.addWarningLogMessage(msg, False)
except Exception:
pass # UI 推送失败不应影响数据处理主流程# src/CMakeLists.txt
# Python 脚本在 CMake 配置阶段自动复制到安装目录
file(COPY "${CMAKE_CURRENT_LIST_DIR}/PyScripts/MyPackage"
DESTINATION ${DAWorkbench_INSTALL_DIR}/bin/PyScripts)
install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/PyScripts/MyPackage"
DESTINATION ${CMAKE_INSTALL_BINDIR}/PyScripts)bin_Release_qtX.Y.Z_MSVC_x64/bin/
├── PyScripts/
│ ├── DAWorkbench/ ← 平台内置 Python 包
│ │ └── DAPyBase/
│ │ └── thread_status_manager.py
│ └── MyPackage/ ← 插件 Python 包
│ ├── __init__.py
│ └── data_analysis.py
├── plugins/
│ └── MyPlugin.dll ← 插件 DLL
└── DAWorkbench.exe ← 主程序!!!danger 修改 .py 文件后必须重新部署并重启应用
DAWorkbench 嵌入式 Python 在启动时加载脚本到内存,修改 .py 文件后:
1. 重新运行 CMake configure(触发 file(COPY) 复制到 bin 目录),或手动复制 .py 到 bin/PyScripts/MyPackage/
2. 重启 DAWorkbench 主程序(Python 解释器重新初始化)
3. 使用 DAPyModule::reload() 可在运行时重新加载模块,但仅对已导入的模块有效
以下是一个完整的"C++ 触发 → Python 后台处理 → C++ 轮询进度 → Python 回传数据 → C++ 更新界面"的链路:
1. 用户点击 Ribbon Action
→ C++ MyUI::onActionTriggered()
→ MyWorker::importZipData()
2. C++ 调用 Python 启动后台线程
→ m_pyDataAnalysisModule->attr("process_zip_data_thread")(zipPath)
→ Python: threading.Thread(target=_process_data).start()
→ 返回 taskid
3. C++ 启动 QTimer 轮询
→ checkZipDataImportStatus()
→ 释放 GIL: pybind11::gil_scoped_release + QThread::sleep(1)
→ 获取状态: get_task_status(taskid) → dict
→ 更新状态栏: statusBar->setProgress(progress)
→ QTimer::singleShot(20, continue)
4. Python 后台线程处理数据
→ pandas 读取 CSV → 清洗 → 时间对齐 → 统计计算
→ status.update_progress(x, "处理中")
→ status.update_custom_data("key", value)
5. Python 线程完成,回调函数通过 callInMainThread 写入数据
→ signal_handler.callInMainThread(add_data_func)
→ Qt 信号 → 主线程执行: datamanager.addData(data)
6. C++ 收到 dataAdded 信号
→ onDataAdded() → analysisDatas()
→ Q_EMIT datasAdded()
7. UI 收到 datasAdded 信号
→ MyUI::onDataAdded()
→ 更新设备选择列表
→ 创建默认绘图// 成员变量声明(使用 unique_ptr 延迟初始化)
std::unique_ptr<DA::DAPyModule> m_pyModule;
std::unique_ptr<DA::DAPyModule> m_pyDataModule;
std::unique_ptr<DA::DAPyModule> m_threadStatusMgr;
// 初始化
m_pyModule = std::make_unique<DA::DAPyModule>();
m_pyModule->import("MyPackage");
// 获取子模块
m_pyDataModule = std::make_unique<DA::DAPyModule>();
*m_pyDataModule = m_pyModule->attr("data_analysis");
// 调用函数
auto func = m_pyDataModule->attr("my_function");
auto result = func(arg1, arg2);
// 从 dict 读取值
pybind11::dict status = get_status(taskid);
bool running = status["is_running"].cast<bool>();
double progress = status["progress"].cast<double>();
std::string message = status["message"].cast<std::string>();
pybind11::dict custom_data = status["custom_data"];// 1. Qt 头文件(正常引入)
#include <QObject>
#include <QString>
// 2. pybind11 桥接头(必须在这些之前引入所有 Qt 头文件)
#include "DAPybind11InQt.h" // slots 宏冲突解决
#include "DAPybind11QtCaster.hpp" // Qt ↔ Python 类型转换
// 3. DA Python 封装
#include "DAPyModule.h"
// 4. 其他
#include "DALog.h"callInMainThreadgil_scoped_release)showInStatusBar — 后台线程必须传 FalseDAPybind11InQt.h 必须第一个引入 — 解决 Qt slots 宏与 Python.h 的冲突DAWidgets 需要显式 find_package — DAGui 依赖它但不自动加载pybind11::error_already_set 的析构需要 GILDAPyModule 继承自 DAPyObjectWrapper(非 QObject) — 不能使用 Qt 信号槽| 文件 | 说明 |
|---|---|
src/MyWorker.cpp | C++ 调用 Python + QTimer 轮询线程状态完整示例 |
src/PyScripts/MyPackage/data_analysis.py | Python 多线程 + callInMainThread 回传数据完整示例 |
src/PyScripts/MyPackage/zip_csv_file_handle.py | 后台线程处理数据 + thread_status_manager 使用示例 |
src/PyScripts/MyPackage/plot_map.py | Python 写入 DataManager + 生成 Word 报告示例 |
data-workbench/src/DAPyBindQt/DAPyModule.h | DAPyModule 基类 API |
data-workbench/src/DAPyBindQt/DAPybind11InQt.h | slots 宏冲突解决 |
data-workbench/src/DAPyBindQt/DAPybind11QtCaster.hpp | Qt ↔ Python 类型转换器 |
data-workbench/src/DAPyBindQt/DAPythonSignalHandler.h | 跨线程通信机制 |
data-workbench/src/DAInterface/DACoreInterface.h | 核心接口(含 getPythonSignalHandler) |
data-workbench/src/PyScripts/DAWorkbench/DAPyBase/thread_status_manager.py | 线程状态管理器完整 API |
data-workbench/skills/add-python-binding/SKILL.md | 添加 Python 绑定(暴露 C++ 给 Python)的技能 |
© czyt1988, LGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/python-script-integration of czyt1988/data-workbench.
Open the folder on GitHubat commit cf3bbda
Python Script Integration next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Python Script Integration this skillczyt1988/data-workbench | 102 | — | ~5.5k | Automated safety check: Pass | LGPL-3.0 | |
| Replay Oriented InstrumentationArabelaTso/Skills-4-SE | 253 | — | ~2.5k | Automated safety check: Pass | Apache-2.0 | |
| Onnxtxtonnx/onnx | 22k | — | ~1.3k | Automated safety check: Pass | Apache-2.0 | |
| Smooth Cpp Sharedopenforecast-org/smooth | 107 | — | ~1.1k | Automated safety check: Pass | LGPL-2.1 | |
| Elodin DBelodin-sys/elodin | 547 | — | ~2.8k | Automated safety check: Pass | Apache-2.0 | |
| GeomasterLeonChaoX/qinyan-academic-skills | 937 | 1 repos | ~2.9k | Automated safety check: Pass | MIT |
ArabelaTso/Skills-4-SE
Instruments programs to record execution information for deterministic replay debugging.
onnx/onnx
Read or write ONNX text format ("onnxtxt"). An agent skill from onnx/onnx.
openforecast-org/smooth
Work on the C++ layer that the R package and the Python port share — the headers under src/headers/, the Rcpp bindings in src/, the pybind11 bindings in src/python/, and the two build systems that…
elodin-sys/elodin
Work with Elodin-DB, the time-series telemetry database. An agent skill from elodin-sys/elodin.
LeonChaoX/qinyan-academic-skills
Comprehensive geospatial science skill covering remote sensing, GIS, spatial analysis, machine learning for earth observation, and 30+ scientific domains.
CVCUDA/CV-CUDA
Verify a new CV-CUDA operator against the deterministic final regression checklist (the /make-op done-gate).
czyt1988/data-workbench
A skill your agent uses when adding new Python bindings (exposing C++ classes/functions to Python) in the DAWorkBench project, or modifying existing bindings.
czyt1988/data-workbench
A skill your agent uses when creating property panels or configuration UI in the data-workbench Qt/C++ project.
czyt1988/data-workbench
A skill your agent uses when creating a new C++ plugin DLL for the data-workbench platform, or modifying an existing plugin's structure, CMake, UI integration, or data access.
Categories
A skill your agent uses when integrating Python scripts with C++ in a DAWorkbench plugin — calling Python from C++, Python multithreading without UI conflicts, Python-to-C++ data exchange, Python UI…. Python Script Integration is an agent skill from czyt1988/data-workbench. Use when integrating Python scripts with C++ in a DAWorkbench plugin — calling Python from C++, Python multithreading without UI conflicts, Python-to-C++ data exchange, Python UI dialog invocation, threadstatusmanager usage, GIL management.
Python Script Integration fits situations like: integrating Python scripts with C++ in a DAWorkbench plugin — calling Python from C++; Python multithreading without UI conflicts; Python-to-C++ data exchange; Python UI dialog invocation.
Run `npx skills add czyt1988/data-workbench --skill python-script-integration -a claude-code`. Or copy the skill folder (skills/python-script-integration in czyt1988/data-workbench) into .claude/skills/python-script-integration in your project. Claude Code loads it when a task matches its description.
Run `npx skills add czyt1988/data-workbench --skill python-script-integration -a codex`. Or copy the skill folder (skills/python-script-integration in czyt1988/data-workbench) into .agents/skills/python-script-integration in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add czyt1988/data-workbench --skill python-script-integration -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/python-script-integration, .gemini/skills/python-script-integration, .github/skills/python-script-integration and .opencode/skills/python-script-integration in your project.
Going by SKILL.md and its folder, Python Script Integration needs the command-line tools its instructions call (python). Our summary lists: Python 3.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Python Script Integration is published under the LGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.5k tokens (SKILL.md is roughly 22k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Python Script Integration: Replay Oriented Instrumentation (ArabelaTso/Skills-4-SE, 253 stars), Onnxtxt (onnx/onnx, 22k stars), Smooth Cpp Shared (openforecast-org/smooth, 107 stars) and Elodin DB (elodin-sys/elodin, 547 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
czyt1988 (a GitHub user) maintains it in czyt1988/data-workbench, which has 102 GitHub stars. The repository holds 4 skills in this directory. The repository was last updated on September 12, 2026.
Source: czyt1988/data-workbench on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.