使用Xlwings通过Excel按钮运行Python脚本遇ModuleNotFoundError求助
我是来自哥伦比亚的医生,近期正在学习Python并将其应用于工作中。我正在构建一个肥胖治疗的成本效益模型,第一步是通过代码设定减重目标(%TWL):在Excel表格的输入区域填写数据,脚本基于这些数据计算治疗目标(%TWL)。
该代码在PyCharm中运行完全正常,医学逻辑没问题,已实现将计算得到的目标写入Excel文件的F2单元格。
但当我尝试在Excel中添加按钮,通过Xlwings点击运行脚本以根据输入区域的值更新F2单元格的目标时,出现错误,报错信息为:
Traceback (most recent call last):
File "", line 1, in
ModuleNotFoundError: No module named 'Error'
我使用的设备是MacBook Air M2,已安装最新版Python及Xlwings相关组件和Excel插件,尝试修改VBA代码、主脚本、文件路径等方法均未解决问题,特此求助。
我的Python代码
import pandas as pd import xlwings as xw def main (): # Cargar el archivo Excel ruta_archivo = "/FULL ROUTE/COBESE.xlsm" df = pd.read_excel(ruta_archivo, sheet_name='Input') # Antropometría antropometria_peso = df.loc[4, "C"] # Peso en kg antropometria_talla = df.loc[5, "C"] # Talla en metros print() print("Antropometría:") print(f"Peso: {antropometria_peso}, Talla: {antropometria_talla}") print() # Comorbilidades # Diabetes diabetes_diagnostico = df.loc[10, "C"] == "si" # ¿El paciente tiene diabetes? diabetes_duracion = df.loc[11, "C"] # Duración de la diabetes en años diabetes_num_medicamentos = df.loc[12, "C"] # Número de medicamentos para diabetes diabetes_usa_insulina = df.loc[13, "C"] == "si" # ¿El paciente usa insulina? print("Comorbilidades:") print( f"Diabetes: {diabetes_diagnostico}, Duración diabetes: {diabetes_duracion}, " f"Num. medicamentos diabetes: {diabetes_num_medicamentos}, Usa insulina: {diabetes_usa_insulina}" ) # Hígado graso higado_graso_diagnostico = df.loc[24, "C"] == "si" # ¿El paciente tiene hígado graso? higado_graso_grado = df.loc[25, "C"] # Grado de hígado graso print(f"Hígado graso: {higado_graso_diagnostico}, Grado hígado graso: {higado_graso_grado}") # Hipertensión arterial (HTA) hta_diagnostico = df.loc[15, "C"] == "si" # ¿El paciente tiene HTA? hta_num_medicamentos = df.loc[16, "C"] # Número de medicamentos para HTA print(f"HTA: {hta_diagnostico}, Num. medicamentos HTA: {hta_num_medicamentos}") # Dislipidemia dislipidemia_diagnostico = df.loc[18, "C"] == "si" # ¿El paciente tiene Dislipidemia? dislipidemia_num_medicamentos = df.loc[19, "C"] # Número de medicamentos para Dislipidemia print(f"Dislipidemia: {dislipidemia_diagnostico}, Num. Medicamentos: {dislipidemia_num_medicamentos}") print() def clasificar_diabetes(duracion, num_medicamentos, usa_insulina): if duracion < 3 and num_medicamentos < 3 and not usa_insulina: return 'Leve' elif 3 <= duracion <= 5 and num_medicamentos >= 3 and not usa_insulina: return 'Moderada' elif duracion > 5 and num_medicamentos > 4 or usa_insulina: return 'Severa' else: return 'Sin clasificar' def clasificar_hta(num_medicamentos): if num_medicamentos <= 2: return 'Leve' elif 2 < num_medicamentos <= 4: return 'Moderada' elif num_medicamentos > 4: return 'Severa' else: return 'Sin clasificar' def clasificar_dislipidemia(num_medicamentos2): if num_medicamentos2 <= 2: return 'Leve' elif 2 < num_medicamentos2 <= 4: return 'Moderada' elif num_medicamentos2 > 4: return 'Severa' else: return 'Sin clasificar' # Clasificar pacientes por comorbilidades clasificacion_diabetes = '' clasificacion_hta = '' clasificacion_dislipidemia = '' if diabetes_diagnostico: clasificacion_diabetes = clasificar_diabetes(diabetes_duracion, diabetes_num_medicamentos, diabetes_usa_insulina) if hta_diagnostico: clasificacion_hta = clasificar_hta(hta_num_medicamentos) if dislipidemia_diagnostico: clasificacion_dislipidemia = clasificar_dislipidemia(dislipidemia_num_medicamentos) # Cálculo de la meta de TWL def calcular_meta(peso, talla, diabetes, grado_higado_graso, hta, dislipidemia): # Convertir la severidad de la diabetes y la HTA en un número enfermedad_mapping = {'Leve': {'Diabetes': 10, 'HTA': 5, 'Dislipidemia': 5}, 'Moderada': {'Diabetes': 20, 'HTA': 12.5, 'Dislipidemia': 5}, 'Severa': {'Diabetes': 35, 'HTA': 20, 'Dislipidemia': 10}} diabetes_severidad = enfermedad_mapping.get(diabetes, {}).get('Diabetes', 0) hta_severidad = enfermedad_mapping.get(hta, {}).get('HTA', 0) dislipidemia_severidad = enfermedad_mapping.get(dislipidemia, {}).get('Dislipidemia', 0) # Verificar si los diagnósticos son True o False if not diabetes_diagnostico: diabetes_severidad = 0 if not hta_severidad: hta_severidad = 0 if not dislipidemia_diagnostico: dislipidemia_severidad = 0 # Calcular el peso necesario para alcanzar un IMC de 30 peso_objetivo_imc30 = 30 * (talla ** 2) # Calcular la meta de %TWL para alcanzar un IMC de 30 twl_para_imc30 = ((peso - peso_objetivo_imc30) / peso) * 100 # Mapear el grado de hígado graso a su correspondiente meta if not higado_graso_diagnostico: twl_para_higado_graso = 0 else: higado_graso_mapping = {"Grado 1": 10, "Grado 2": 20, "Grado 3": 40} twl_para_higado_graso = higado_graso_mapping.get(grado_higado_graso, 0) # Escoger la meta más grande return max(twl_para_imc30, diabetes_severidad, twl_para_higado_graso, hta_severidad, dislipidemia_severidad) meta = calcular_meta( antropometria_peso, antropometria_talla, clasificacion_diabetes, higado_graso_grado, clasificacion_hta, clasificacion_dislipidemia ) print(f"La meta de este paciente debe ser TWL: {meta:.2f}%.") print() # Obtén la aplicación Excel y abre el archivo app = xw.apps.active wb = app.books.open(ruta_archivo) # Selecciona la hoja de cálculo donde deseas escribir el resultado sheet = wb.sheets['Input'] # Escribe el resultado en una celda específica formateado como cadena sheet.range('F2').value = "{:.2f}".format(meta).replace('.', ',') # Guarda los cambios y cierra el archivo wb.save() wb.close() app.quit() # Llamar a la función main() if __name__ == '__main__': main()
我的VBA代码
Public Sub Python() RunPython "import Error; Error.main ()" End Sub
问题根源及解决方法
1. 核心错误修复
VBA代码里的import Error是错误的——这里的Error应该替换成你的Python脚本实际文件名(不含.py后缀)。比如如果你的Python脚本叫twl_calculator.py,修改后的VBA代码应为:
Public Sub Python() RunPython "import twl_calculator; twl_calculator.main ()" End Sub
2. 脚本优化建议
移除硬编码路径:原脚本中固定的文件路径容易出错,建议直接通过Xlwings获取当前活动工作簿,避免手动指定路径:
def main(): # 获取当前活动的Excel应用和工作簿 app = xw.apps.active wb = app.books.active sheet = wb.sheets['Input'] # 直接从工作表读取数据(注意:pandas的df.loc[4]对应Excel第5行,因为pandas索引从0开始) antropometria_peso = sheet.range('C5').value antropometria_talla = sheet.range('C6').value # 后续所有df.loc[...]都替换为对应的Excel单元格,比如df.loc[10, "C"] → sheet.range('C11').value这样修改后,脚本会直接操作当前打开的Excel文件,无需依赖固定路径。
避免关闭Excel程序:原代码中的
app.quit()会关闭整个Excel窗口,这在Excel内运行脚本时非常不友好,建议删除wb.close()和app.quit(),只保留wb.save()即可。
3. Mac环境配置检查
- 确认Xlwings Excel插件已正确安装,在Excel的「工具」菜单中能看到Xlwings选项。
- 打开Excel → 工具 → Xlwings → Settings,检查Python路径是否指向你安装的最新版Python(比如虚拟环境路径或系统Python路径)。
内容的提问来源于stack exchange,提问作者Dr.Mateo95

