Esta vez me he puesto a trastear con PyPb, el bridge (todavía en beta) de Appeon que deja llamar a Python directamente desde PowerBuilder 2025 R2, sin montar APIs web, sin escribir una línea de C# y, lo mejor de todo, sin que el cliente tenga que instalar Python. Os cuento cómo ha ido.
Un ejemplo didáctico, en modo solución, de cómo integrar Python en PowerBuilder
usando PyPb (el bridge de Appeon sobre
Python.NET). La idea central es una clase reutilizable, n_cst_pyton, que
convierte "hablar con Python" en algo tan simple como of_import + of_invoke:
importar módulos, llamar funciones, instanciar clases y recoger resultados.
Y para que no se quede en teoría, lo validamos con un caso ejecutable: exportar un DataWindow a Excel, comparando dos caminos:
- El export nativo que estrena PB 2025:
dw_1.SaveDisplayedDataAs(…, XLSX!). - El camino Python + openpyxl, que va un paso más allá y genera un
.xlsxestilizado (cabecera con color, filas zebra, formato moneda €, totales, autofiltro y cabecera fija).
Pero ojo: el Excel es solo la excusa. Lo importante es lo cómodo que resulta llamar a Python una vez tienes
n_cst_pytonmontada. A partir de ahí, integrar pandas, numpy, openpyxl, OpenCV… es cuestión de dos líneas.
Es una fachada fina en PowerScript sobre PyPb, con manejo de errores al estilo
ERP (0/-1 + of_lasterror()) y sin lanzar excepciones hacia fuera. Fijaos en
lo poco que hace falta para arrancar Python y pedirle la versión:
n_cst_pyton lnv_py
string ls_ver
lnv_py = CREATE n_cst_pyton
lnv_py.of_init("…\python.runtime\python313.dll") // arranca el runtime
lnv_py.of_invoke("platform", "python_version", ls_ver) // import + llamada
// ls_ver -> "3.13.1"
DESTROY lnv_py
| Método | Para qué |
|---|---|
of_init(dll) |
Arranca (o reutiliza) el runtime de Python |
of_import(modulo, ref mod) |
Importa un módulo (con caché) |
of_invoke(modulo, func, ref res) |
Atajo: importa + llama función → string |
of_run(sentencia, ref res) |
Ejecuta una expresión Python suelta |
of_exec_req(sentencia, req, ref res) |
Ejecuta Python con variables locales |
of_lasterror() |
Último error capturado |
| Botón | Qué hace |
|---|---|
| Probar Python | Arranca el runtime e imprime la versión (platform.python_version()). |
| Crear Excel (openpyxl) | Crea un .xlsx básico con openpyxl vía n_cst_pyton_excel. |
| Recargar facturas (JSON) | Carga data2026.json (300 facturas) en un DataWindow. |
| Excel nativo (PB XLSX) | La nueva función de PB 2025: dw_1.SaveDisplayedDataAs(…, XLSX!). |
| Excel Python (openpyxl) | Exporta el grid a un .xlsx con estilo: cabecera color, filas zebra, moneda €, totales, autofiltro y cabecera fija. |
n_cst_pyton_excel es un objeto de negocio de ejemplo construido encima de
n_cst_pyton: enseña el patrón de envolver una librería Python en una clase PB
de uso cómodo.
n_cst_pyton— la fachada reutilizable (lo importante del repo).n_cst_pyton_excel— ejemplo de objeto de negocio sobre openpyxl.python.runtime/— Python 3.13 embeddable con openpyxl incluido (pip install … -t python.runtime). Viaja junto a la app; nada que instalar.bin.pypb.appeon/— los assemblies .NET de PyPb (se despliegan con el EXE).export_facturas.py— exportador estilizado data-driven: PB le pasa las columnas visibles del DataWindow para que el Excel refleje lo que se ve.
- PowerBuilder 2022 R3 / 2025 / 2025 R2
- Python 3.11–3.13, misma arquitectura que la app (aquí 64-bit)
- .NET 8.0
- Abre el workspace (
PyPbExample.pbsln) en el IDE de PowerBuilder. - Asegúrate de que
pypblib.pblestá en la library list del target. - Compila y ejecuta.
Se entrega un Python embeddable portable dentro del proyecto:
- Descarga el Python Embeddable Package (misma versión que en desarrollo).
- Extráelo en
python.runtime/. - Instala ahí las dependencias:
python.exe -m pip install openpyxl -t python.runtime. - Apunta el runtime con una ruta relativa al EXE.
- Distribuye
python.runtime/ybin.pypb.appeon/junto al EXE.
Gotcha: en el embeddable, descomenta
import siteenpythonXXX._pth.
Al probar los dos exports me topé con algo llamativo:
- Python → nativo: funciona. Y a partir de ahí, en cualquier orden.
- Nativo → Python: el botón de Python falla con
Could not create instance of .NET Assembly: Load bin.pypb.appeon\…dll failed.
La causa: PowerBuilder carga el runtime .NET una sola vez por proceso. El export
XLSX nativo (SaveDisplayedDataAs) y PyPb usan .NET, y manda quien lo inicialice
primero. Si el nativo va primero, PyPb ya no puede cargar su assembly.
Mi primer intento fue restaurar el directorio de trabajo tras el export (PyPb carga su DLL por ruta relativa) — pero no era eso: no era el directorio, era el orden de inicialización.
La solución: pre-cargar PyPb al arrancar la app (en el open del objeto
aplicación, antes de abrir la ventana), para que su .NET se inicialice el primero:
n_cst_pyton lnv_pyinit
lnv_pyinit = CREATE n_cst_pyton
lnv_pyinit.of_init(gs_appdir + "\python.runtime\python313.dll")
DESTROY lnv_pyinit
El contexto de Python es global y persiste tras el DESTROY, así que después los
botones funcionan en cualquier orden.
of_executestatementusaeval()de Python → solo EXPRESIONES. Una asignación (ws.title = x) lanzaSyntaxError; usasetattr(...)o métodos (of_exec_req).of_set(prop, string)pasa unSystem.Stringde .NET, no unstrde Python → rompe el código que valida tipos conre/isinstance(str)(p. ej. openpyxl).- PyPb mantiene un único contexto de Python por proceso y lo reutiliza.
- Para pasar los datos del grid a Python se usa
dw_1.ExportJson()(en esta versiónExportString(JSON!)no existe), así el Excel sale fiel al DataWindow.
PyPb es una característica beta de Appeon. Este repositorio es un ejemplo de aprendizaje, no código de producción.
Tenéis el ejemplo publicado en modo solución (clonáis y compila): https://github.com/rasanfe/PyPbExample
- PyPb es un proyecto de Appeon: https://github.com/Appeon/PyPb
- Excel estilizado con openpyxl.
¡Nos vemos en el próximo artículo! Y recuerda: en PowerBuilder, los límites solo están en nuestra imaginación. 🚀
📨 Blog: https://rsrsystem.blogspot.com/