modules.widgets 模块帮助
本章节包含 modules.widgets 包中常用「小部件 / 辅助模块」的使用说明和示例。
PythonCoder
模块简介与适用场景
PythonCoder是一个通用「脚本执行」模块,用于在 Pipeline 中调用本地 Python 脚本中的 本地函数 (local function)。它通过 动态端口 接收任意类型的数据(如
TableData、TableCollection、普通dict/list等),执行时会把 模块实例 module 作为本地函数的第一个参数传入;本地函数从module.dynamic_ports_in[...]读取输入,并以dict形式返回输出,由 PythonCoder 写回到动态输出端口。动态 输出 端口可通过
add_dynamic_ports_out(..., ptype=...)显式指定PortType``(见 ``gdisdk.pipeline.portTypes),使return_results_config/ 前端拿到的端口类型不再是默认的General,从而正确选择文本、图表、文件等展示方式。典型适用场景:
已有一段较为独立的业务逻辑(例如复杂统计、报表拼装、特殊格式转换),不希望为其单独封装一个
PipeModule子类;需要在不改动主代码库的情况下,为某些项目/模板增加「可插拔」的计算逻辑;
希望让熟悉 Python 的工程师以「写脚本 + 定义函数」的方式扩展 Pipeline 能力。
端口说明
输入端口 - (无固定端口):需要通过
add_dynamic_ports_in(...)动态声明输入端口 - 动态输入端口命名要求:端口名必须以"Input"开头(例如InputTable、InputTables) - 在本地函数中读取方式:module.dynamic_ports_in["InputX"].data输出端口 - (无固定端口):需要通过
add_dynamic_ports_out(...)动态声明输出端口 - 动态输出端口命名要求:端口名必须以"Output"开头(例如OutputTable、OutputResultModel) - 写出机制:本地函数必须返回一个dict,其中 key 为输出端口名、value 为要写入端口的数据;PythonCoder 会将其写回到对应的动态输出端口 - 端口类型(``ptype``):add_dynamic_ports_out可选参数ptype指定该端口的PortType;省略时使用模块默认dynamic_ports_out_type``(PythonCoder 为 ``PortType.General)。上传 GDIM 且端口出现在return_results_config中时,应按实际返回数据指定类型(结构化结果优先PortType.ResultModel,另常见PortType.PlotData、PortType.Text等;勿对新流程使用已逐步淘汰的PortType.SingleResult)。否则前端可能仍视为General而无法正确渲染。也可传入PortType列表表示联合类型;指定ptype后,写入端口时会按类型校验数据。
Note
推荐使用
gdisdk.pipeline.nameSpace.local_function装饰器来定义本地函数(文件方式 / 字符串函数名)。Inline 方式**(直接传入可调用对象)**不需要
@local_function装饰器。旧方案
LocalFunctionProvider``(类 + ``run_function)仅用于兼容历史代码,未来版本将逐步停止使用;本文档示例统一采用@local_function新方案。
快速上手示例:计算工程地质纵剖面总长度
下面以「计算工程地质纵剖面总长度」为例,展示一个完整的 PythonCoder 使用流程。该示例基于真实案例的实际代码进行了简化。
1)在 Pipeline 中创建并配置 PythonCoder
from gdisdk.modules.readers import GdimTableReader
from gdisdk.modules.filters import TableSelector
from gdisdk.modules.widgets import PythonCoder
from gdisdk.pipeline.pipeline import PipeLine
from gdisdk.pipeline.portTypes import PortType
# 1. 创建 Pipeline,并指定本地函数脚本路径
pipeline = PipeLine(
app_name="SectionLengthDemo",
app_title="剖面总长度计算示例",
version="1.0.0",
)
pipeline.update_gdim_state(token="你的GDIM Token", proj_id="你的GDIM项目ID")
pipeline.workspace = "test"
# 关键:指定本地函数脚本路径
# (相对于运行该脚本的工作目录;也可以使用绝对路径)
# 通过pipeline属性指定可以避免有多个PythonCoder时每个PythonCoder都要设置脚本路径
pipeline.local_functions_path = (
"test/Doc1LocationOverview.py"
)
# 2. 前置模块:读取「剖面数据表」
read_tables = GdimTableReader(mname="ReadTables")
read_tables.table_fields = ["剖面数据表"]
section_lines_data_table = TableSelector(mname="SectionLinesDataTable")
section_lines_data_table.table_name = "剖面数据表"
# 3. 创建 PythonCoder 模块,并指定要调用的本地函数名(脚本中的函数名字符串)
section_lines_length = PythonCoder(mname="SectionLinesLength")
section_lines_length.local_function_name = "get_sections_length"
# 4. 声明动态输入 / 输出端口
# 端口名称需要与脚本中本地函数读取/返回的端口名保持一致
section_lines_length.add_dynamic_ports_in("InputTable")
section_lines_length.add_dynamic_ports_out(
"OutputResultModel",
ptype=PortType.ResultModel,
)
# 5. 像普通模块一样连线并运行
links = (
read_tables.OutputTables >> section_lines_data_table.InputTables
| section_lines_data_table.OutputTable >> section_lines_length.InputTable
)
pipeline.add_links(links)
result = pipeline.run()
print(section_lines_length.OutputResultModel.data)
要点:
pipeline.local_functions_path指向包含本地函数定义的.py脚本;section_lines_length.local_function_name指定要调用的函数名(例如get_sections_length);add_dynamic_ports_in("InputTable")/add_dynamic_ports_out("OutputResultModel", ptype=PortType.ResultModel)负责在模块上创建动态端口;名称需与本地函数读取 / 返回的键一致,ptype需与返回数据及 GDIM 展示方式一致。
2)在脚本中定义本地函数(@local_function)
与上文 pipeline.local_functions_path 对应的脚本中,需要定义一个使用 @local_function 装饰的函数,并确保 第一个参数为 module。例如:
import numpy as np
from gdisdk.dataclass.results import ResultModel, UnitField
from gdisdk.dataclass.tables import TableData
from gdisdk.dataclass.terminologies import Units
from gdisdk.pipeline.nameSpace import local_function
@local_function
def get_sections_length(module, **kwargs) -> dict[str, ResultModel | None]:
"""计算工程地质纵剖面总长度(供 PythonCoder 调用)"""
# 重要:PythonCoder 访问动态输入端口数据应使用 dynamic_ports_in
table: TableData | None = module.dynamic_ports_in["InputTable"].data
if table is None:
return {"OutputResultModel": None}
sections_length = 0.0
for _, group_df in table.groupby("剖面编号"):
dx = group_df["x_coordinate"].diff()
dy = group_df["y_coordinate"].diff()
distances = np.sqrt(dx**2 + dy**2)
sections_length += float(distances.sum())
result = ResultModel.from_dict(
{"sections_length": sections_length},
model_name="SectionLinesLengthResult",
fields_meta={
"sections_length": UnitField(
title="工程地质纵剖面总长度",
unit=Units.m,
),
},
)
# 返回值必须是一个 dict:
# key 为 PythonCoder 动态输出端口名称,
# value 为要写入该端口的数据
return {"OutputResultModel": result}
在运行时,PythonCoder 内部会通过 gdisdk.pipeline.nameSpace.load_local_function():
动态加载上述
get_sections_length函数(要求使用@local_function装饰;支持调试模式debug_mode);调用
get_sections_length(module)获取返回字典;将返回字典中的各个键写入对应的动态输出端口。
Note
本地函数第一个参数必须是 ``module``(即 PythonCoder 模块实例)。
你可以通过
module访问/修改的不仅仅是端口数据,还包括:模块属性:例如
module.threshold、module.some_flag(也可以在函数内setattr(module, ...)动态修改);Pipeline 上下文:
module.pipeline(可进一步module.pipeline.get_module("OtherModuleName")访问其它模块实例,或遍历module.pipeline.modules);端口数据:推荐从动态端口读取输入:
module.dynamic_ports_in["InputX"].data。
参数说明
参数名 |
类型 |
默认值 |
说明 |
|---|---|---|---|
|
|
|
本地函数脚本路径。若 pipeline 已设置 |
|
|
|
要调用的本地函数:
|
|
|
|
(可选)用于生成 UI Schema 的本地函数(字符串或可调用对象)。若设置,则会在
|
|
|
|
调试模式。为 |
此外,还需要通过以下方法声明端口:
方法 |
说明 |
|---|---|
|
添加一个动态输入端口。 |
|
添加一个动态输出端口。 |
除了端口相关方法外,PythonCoder 还提供了一个便捷的 属性配置方法:
add_attributes(**kwargs)- 为模块实例动态增加属性,相当于对当前模块执行一系列setattr(self, key, value)操作。 - 这些属性可以在两个地方被使用:在脚本侧:本地函数可以直接读取
module.<attr>``(例如 ``module.threshold);在 UI 侧:如果你通过
ui_schema_function_name定义了 UI Schema,UI schema 本地函数可以直接读取这些属性,或者配合 Pipeline 的add_attribute将其暴露给前端。
一个简单示例:
from gdisdk.modules.widgets import PythonCoder from gdisdk.pipeline.portTypes import PortType python_coder = PythonCoder(mname="MyCoder") python_coder.local_function_name = "MyFunc" python_coder.add_dynamic_ports_in("InputTable") python_coder.add_dynamic_ports_out("OutputText", ptype=PortType.Text) # 通过属性传递额外参数给脚本中的本地函数 python_coder.add_attributes( threshold=0.5, description="自定义分析说明", )
上传 GDIM 前检查
是否需要特别处理:是。
必须检查:
local_function_name上传前应使用 字符串函数名,不要直接传入 inline callable;执行函数应定义在独立
.py脚本中,并使用@local_function装饰;本地函数脚本路径应优先在 pipeline 层 提供;上传平台后通常由平台注入或替换,而不是依赖写死在
.pipe中的本机路径。本地函数
import的第三方包必须出现在requirements.txt或requirements-pythoncoder.txt中(后者仅 GDIM 生产镜像默认安装;本地需自行pip install -r requirements-pythoncoder.txt)。不要使用仅 Windows 可用的包(如pyodbc)。
建议检查:
ui_schema_function_name也优先使用字符串函数名,而不是 inline callable;动态端口名与本地函数中读取 / 返回的键完全一致,避免上传后因端口名不匹配而失败;
需在 GDIM 展示的动态输出端口应设置
ptype,并与return_results_config中配置的端口一致。
若遗漏,常见现象:
本地脚本运行正常,但保存为
.pipe后,GDIM 无法还原 inline 函数对象,导致模块加载或执行失败。本地因额外
pip install能跑通,上传 GDIM 后ImportError。
几种常见用法补充
1)Inline 本地函数 vs 文件本地函数(GDIM 上传要求)
PythonCoder 支持 Inline 方式:直接把
local_function_name设为一个可调用对象(函数)。但如果你要把 pipeline 保存为
.pipe并上传 GDIM 平台运行,必须使用文件方式 (即把函数定义在独立.py脚本中,并用字符串指定函数名)。
原因是:Inline 的函数对象无法被序列化到 .pipe 文件中,GDIM 侧也无法反序列化还原该可调用对象。
一个 Inline 的简化示例:
from typing import Any
from gdisdk.modules.widgets import PythonCoder
from gdisdk.pipeline.portTypes import PortType
def inline_func(module, **kwargs) -> dict[str, Any]:
table = module.dynamic_ports_in["InputTable"].data
return {"OutputText": f"rows={0 if table is None else len(table.data)}"}
coder = PythonCoder(mname="MyCoder")
coder.local_function_name = inline_func
coder.add_dynamic_ports_in("InputTable")
coder.add_dynamic_ports_out("OutputText", ptype=PortType.Text)
# coder.InputTable = my_table
coder.execute()
text = coder.OutputText.data
2)@local_function 的 module 参数可以做什么
使用 @local_function 装饰的函数时,第一个参数必须是 module。你可以通过 module:
读取输入端口数据:
module.dynamic_ports_in["InputX"].data(推荐写法)写入输出:通过返回字典
{"OutputY": data}(由 PythonCoder 写回端口)读取/修改模块属性:
module.some_attr/setattr(module, "some_attr", value)访问 pipeline 与其它模块:
module.pipeline获取当前 Pipelinemodule.pipeline.get_module("OtherModuleName")获取其它模块实例并读取/修改其属性
Warning
在本地函数里修改其它模块属性会影响后续执行行为,属于 “高级用法” 。建议只在你明确需要动态联动配置时使用,并保持修改可追踪、可回滚。
Note
为了让本地函数在未来版本「可能增加额外参数」时仍保持兼容,建议你采用更稳健的函数签名:
执行函数(execute):
def my_func(module, **kwargs): ...UI schema 函数:
def my_ui_schema(module, reset: bool = False, **kwargs): ...
3)为动态输出端口指定 ``ptype``(GDIM 前端展示)
PythonCoder 默认把所有动态输出端口记为 PortType.General。若某端口会通过 return_results_config 回传 GDIM 前端,应在声明端口时显式传入 ptype,使 get_return_results_info() / get_attribute_info() 中的 port_type 与内置模块一致。
from gdisdk.modules.widgets import PythonCoder
from gdisdk.pipeline.portTypes import PortType
plotter = PythonCoder(mname="Plot3dSurface")
plotter.local_function_name = "settlement_3d_surface"
plotter.add_dynamic_ports_in("InputPointInfo")
plotter.add_dynamic_ports_in("InputSettlementData")
plotter.add_dynamic_ports_out("OutputPlotData", ptype=PortType.PlotData)
converter = PythonCoder(mname="GenerateGeo5Tables")
converter.add_dynamic_ports_in("InputTables")
converter.add_dynamic_ports_out("OutputTables", ptype=PortType.TableCollection)
常见 ptype 与 GDIM 展示方式的对照,参见 用户指南 (User Guide) 中「如何上传 Pipeline 至 GDIM」一节的 PortType 说明;完整枚举见 pipeline.portTypes。
4)UI Schema 的本地函数与 reset 参数
PythonCoder 的 update_ui_schema 会把 reset 参数传给 UI schema 本地函数:
Inline 方式:调用
ui_schema_function_name(module, reset)文件方式:调用
ui_schema_function_name(module, reset=reset)
因此 建议 UI schema 本地函数签名包含 ``reset: bool = False``,并额外接收 ``**kwargs``,例如:
from gdisdk.pipeline.nameSpace import local_function
from gdisdk.pipeline.pipeData import StringAttributeSchema
@local_function
def my_ui_schema(module, reset: bool = False, **kwargs):
return {
"prompt": StringAttributeSchema(
title="提示词",
default=getattr(module, "prompt", ""),
)
}
5)兼容 gdi / gdisdk 的包导入写法
若本地函数脚本需要在 gdi``(源码环境)和 ``gdisdk``(编译发布包)两种环境下都能正常运行。推荐在脚本顶部使用 ``try/except 兼容导入:
try:
from gdi.pipeline.nameSpace import local_function
from gdi.dataclass.tables import TableData
except ImportError:
from gdisdk.pipeline.nameSpace import local_function
from gdisdk.dataclass.tables import TableData
按需导入实际使用到的类即可。
6)本地函数可用的第三方包
本地函数可 import 的第三方包来自两份清单(GDIM 生产镜像会安装全部):
requirements.txt:gdi / gdisdk 自身运行依赖。本地pip install -r requirements.txt后即可使用。requirements-pythoncoder.txt:仅供 PythonCoder 使用的扩展包。gdi 本身不 import 这些包;GDIM 生产环境会安装。本地若要与 GDIM 对齐,需额外执行pip install -r requirements-pythoncoder.txt。
请以这两份文件的当前内容为准,不要 import 清单之外的包——缺失的包在 GDIM 上会导致运行失败。requirements.txt 中的传递依赖(例如 pandas 引入的 numpy、seaborn 引入的 matplotlib)可以在本地函数中直接使用。
gdi / gdisdk 运行依赖中,本地函数常用的包包括:
数据处理与科学计算
pandas— DataFrame 操作(import pandas as pd)numpy— 数值计算(由 pandas 依赖引入,可直接使用)scipy— 统计检验、插值、信号处理scikit-learn— 机器学习(import sklearn)numba— JIT 数值加速
数据建模
pydantic— 数据验证与结构化对象(BaseModel,要求 v2+)
文档与 Office
pymupdf— PDF 读写(import fitz)docxtpl— Word 模板渲染docxcompose— Word 文档合并openpyxl— Excel 读写pylatexenc— LaTeX 编码 / 转 Unicode 等pypandoc_binary— 文档格式转换(import pypandoc)
数据库连接
pyodbc— ODBC(仅 Windows;GDIM 生产为 Linux,上传 GDIM 的脚本勿依赖此包)
图像与可视化
pillow— 图像处理(from PIL import Image)plotly— 交互式图表kaleido— Plotly 静态图导出(常与plotly配合)seaborn— 统计图表(基于matplotlib)
GIS / 地理空间
rasterio— 栅格地理数据pyproj— 坐标投影与转换shapely— 几何运算(点、线、面)
AI / LLM / RAG
pandasai— AI 驱动的 DataFrame 查询(v3+)pydantic-ai— Agent 框架(import pydantic_ai)pydantic-ai-skills— Skills 工具集(import pydantic_ai_skills)chromadb— 向量数据库
PythonCoder 扩展(``gdi/requirements-pythoncoder.txt``)
pygimli— 地球物理反演与建模(import pygimli)。仅在该文件中声明;GDIM 会安装,默认本地pip install -r requirements.txt不包含。生产为 Linux manylinux;无官方 Windows cp311 wheel,Windows 本地可能无法安装。
Note
matplotlib 等未在 requirements.txt 中显式列出,但作为 seaborn 等包的传递依赖,在环境中通常可用。
完整依赖名与版本请直接查看 gdisdk/requirements.txt 与 gdisdk/requirements-pythoncoder.txt。
进阶用法与配置示例(可选)
使用
ui_schema_function_name为 PythonCoder 的自定义属性生成 UI Schema,从而在 GDIM / 自定义前端中暴露参数;在脚本的本地函数中读取多个输入端口(例如
InputTable1、InputTable2),并返回多个输出端口(如OutputTable、OutputResultModel);配合
gdisdk.pipeline.pipeline.PipeLine的add_attribute,可将 PythonCoder 的自定义属性暴露为 Pipeline 属性,实现高度可配置的「脚本模块」。
在 pipeline 中的使用方式小结
与其他模块的最大区别在于: 计算逻辑在外部脚本中,通过函数实现;
在 Pipeline 中,你只需要:
设置
local_functions_path和local_function_name;通过
add_dynamic_ports_in/add_dynamic_ports_out声明好端口;像普通模块一样通过
>>/|将其接入数据流;(可选)通过 Pipeline 属性将 PythonCoder 的参数暴露给前端。
更多信息