├── requirements.txt ├── README.md ├── maltipal_file_test.py ├── chart.py ├── auto_code.py ├── 文件后缀一定是xlsx才行.txt ├── gpt_data.py ├── gpt_api.py ├── main.py └── xl_class.py /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mini-yifan/AI_excel/HEAD/requirements.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI_excel 2 | 这是一个可以自动操作Excel的AI工具 3 | 4 | 软件效果视频:[只说人话就可以做表格了!我造了一个自动操作Excel表的网站](https://www.bilibili.com/video/BV1JzKpeTEkF/?vd_source=28ba27f4f650db659b1dd1ace9f5fc5c) 5 | 6 | 本项目调用大语言模型的API接口,并让模型能够调用操作Excel表文件的函数 7 | 8 | 项目用streamlit编写界面 9 | 10 | 文件**main.py**:streamlit界面代码 11 | 12 | 文件**gpt_api.py**:调用API接口并导入操作表格的函数 13 | 14 | 文件**xl_class.py**:表格操作相关函数 15 | 16 | 文件**gpt_data.py**:数据分析AI 17 | 18 | 文件**chart.py**:绘制统计图函数 19 | 20 | 文件**requirements.txt**:所需要的python依赖库 21 | 22 | 运行命令 `pip install -r requirements.txt` 即可安装所有所需依赖。 23 | 24 | 运行命令 `streamlit run main.py` 即可运行网站程序 25 | -------------------------------------------------------------------------------- /maltipal_file_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def find_excel_files(directory): 5 | """ 6 | 查找指定目录及其子目录下的所有Excel文件(.xlsx 和 .xls)。 7 | :param directory: 要搜索的根目录 8 | :return: 包含所有找到的Excel文件路径的列表 9 | """ 10 | excel_files = [] 11 | for root, dirs, files in os.walk(directory): 12 | for file in files: 13 | if file.endswith(".xlsx") or file.endswith(".xls"): 14 | excel_files.append(os.path.join(root, file)) 15 | return excel_files 16 | 17 | 18 | 19 | # 使用示例 20 | if __name__ == "__main__": 21 | 22 | dir_path = "2024年" # 替换为你的目标文件夹路径 23 | excel_paths = find_excel_files(dir_path) 24 | print(excel_paths) -------------------------------------------------------------------------------- /chart.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import plotly.express as px 4 | import plotly.graph_objects as go 5 | 6 | 7 | def plot_chart(data, chart_type, x_column, y_columns=None, legend_title=None, title='Chart', xlabel=None, ylabel=None, 8 | colors=None): 9 | """ 10 | 根据提供的数据和参数绘制不同类型的图表。 11 | 12 | :param data: 输入的数据框 13 | :param chart_type: 图表类型("bar", "line", "pie", "scatter"等) 14 | :param x_column: x轴对应的数据列名 15 | :param y_columns: y轴对应的数据列名列表,对于饼图此参数为None,对于多条折线图则应提供多个y轴列名 16 | :param legend_title: 图例标题 17 | :param title: 图表标题 18 | :param xlabel: x轴标题 19 | :param ylabel: y轴标题 20 | :param colors: 颜色列表或字典,根据图表类型应用到不同的线条或类别 21 | """ 22 | if chart_type not in ['bar', 'line', 'pie', 'scatter']: 23 | st.write(f"{chart_type} not supported.") 24 | return 25 | 26 | if chart_type == 'pie': 27 | # For pie charts, we assume a single y_column is provided for the values. 28 | fig = px.pie(data, names=x_column, values=y_columns[0], title=title, color_discrete_sequence=colors) 29 | else: 30 | fig = go.Figure() 31 | 32 | for i, y_col in enumerate(y_columns): 33 | if chart_type == 'bar': 34 | fig.add_trace( 35 | go.Bar(x=data[x_column], y=data[y_col], name=y_col, marker_color=colors[i] if colors else None)) 36 | elif chart_type == 'line': 37 | fig.add_trace(go.Scatter(x=data[x_column], y=data[y_col], mode='lines', name=y_col, 38 | line=dict(color=colors[i] if colors else None))) 39 | elif chart_type == 'scatter': 40 | fig.add_trace(go.Scatter(x=data[x_column], y=data[y_col], mode='markers', name=y_col, 41 | marker=dict(color=colors[i] if colors else None))) 42 | 43 | fig.update_layout(title=title, xaxis_title=xlabel if xlabel else x_column, 44 | yaxis_title=ylabel if ylabel else ('Value' if y_columns else ''), legend_title=legend_title) 45 | 46 | st.plotly_chart(fig) 47 | 48 | 49 | -------------------------------------------------------------------------------- /auto_code.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | from io import StringIO 4 | 5 | # 允许的库白名单 6 | ALLOWED_MODULES = { 7 | "numpy", "pandas", "openpyxl", "os", "csv", "math", "random", "json", "re", "time", "copy" 8 | } 9 | 10 | ''' 11 | def set_resource_limits(): 12 | """ 13 | 设置 CPU 和内存使用限制。 14 | """ 15 | # 设置 CPU 时间限制(单位:秒) 16 | cpu_time_limit = 8 # 最多允许 8 秒的 CPU 时间 17 | resource.setrlimit(resource.RLIMIT_CPU, (cpu_time_limit, cpu_time_limit)) 18 | 19 | # 设置内存限制(单位:字节) 20 | memory_limit = 512 * 1024 * 1024 # 最多允许 512 MB 内存 21 | resource.setrlimit(resource.RLIMIT_AS, (memory_limit, memory_limit)) 22 | ''' 23 | 24 | 25 | def check_code_safety(code: str) -> bool: 26 | """ 27 | 检查代码是否只使用了白名单中的库。 28 | :param code: 生成的 Python 代码 29 | :return: 如果代码安全返回 True,否则返回 False 30 | """ 31 | # 匹配 import 语句 32 | import_pattern = re.compile(r"^\s*(?:from|import)\s+(\w+)", re.MULTILINE) 33 | imported_modules = set(import_pattern.findall(code)) 34 | 35 | # 检查是否有不允许的库 36 | for module in imported_modules: 37 | if module not in ALLOWED_MODULES: 38 | print(f"Error: 不允许的库 '{module}'") 39 | return False 40 | return True 41 | 42 | def run_generated_code(code: str, **kwargs): 43 | """ 44 | 动态执行生成的 Python 代码,并返回执行结果。 45 | :param code: 生成的 Python 代码(字符串形式) 46 | :param kwargs: 传递给代码的参数 47 | :return: 执行结果或错误信息 48 | """ 49 | # 捕获标准输出 50 | original_stdout = sys.stdout 51 | sys.stdout = StringIO() 52 | 53 | try: 54 | # 将传入的参数添加到局部变量中 55 | local_vars = kwargs 56 | 57 | # 动态执行代码,并将参数传递进去 58 | exec(code, globals(), local_vars) 59 | 60 | # 获取输出 61 | output = sys.stdout.getvalue() 62 | 63 | # 如果输出为空,尝试从局部变量中获取结果 64 | if not output and "result" in local_vars: 65 | output = str(local_vars["result"]) 66 | except SyntaxError as e: 67 | output = f"SyntaxError: {e}" 68 | except Exception as e: 69 | output = f"RuntimeError: {e}" 70 | finally: 71 | # 恢复标准输出 72 | sys.stdout = original_stdout 73 | 74 | return output 75 | 76 | def generate_code_with_llm(user_input: str) -> str: 77 | """ 78 | 模拟调用大语言模型生成 Python 代码。 79 | :param user_input: 用户输入的需求描述 80 | :return: 生成的 Python 代码 81 | """ 82 | # 这里可以替换为实际的大语言模型 API 调用 83 | # 示例:根据用户输入生成代码 84 | if "read excel" in user_input.lower(): 85 | return """ 86 | import pandas as pd 87 | df = pd.read_excel("生产情况.xlsx") 88 | print(df.head()) 89 | """ 90 | elif "write excel" in user_input.lower(): 91 | return """ 92 | import pandas as pd 93 | data = {"Name": ["Alice", "Bob"], "Age": [25, 30]} 94 | df = pd.DataFrame(data) 95 | df.to_excel("output.xlsx", index=False) 96 | print("Excel 文件已生成") 97 | """ 98 | else: 99 | return """ 100 | print("未识别的任务") 101 | """ 102 | 103 | def auto_code_main(user_input): 104 | # 生成代码 105 | generated_code = generate_code_with_llm(user_input) 106 | print("生成的代码:") 107 | print(generated_code) 108 | 109 | # 检查代码安全性 110 | if not check_code_safety(generated_code): 111 | print("代码包含不允许的库,终止执行。") 112 | return 113 | 114 | # 执行生成的代码 115 | result = run_generated_code(generated_code) 116 | print("执行结果:") 117 | print(result) 118 | return result 119 | 120 | if __name__ == "__main__": 121 | # 用户输入需求 122 | user_input = input("请输入您的 Excel 自动化需求:") 123 | 124 | auto_code_main(user_input) -------------------------------------------------------------------------------- /文件后缀一定是xlsx才行.txt: -------------------------------------------------------------------------------- 1 | "你是一个帮我批量处理excel表格的工具,你会对文件批量复制,工作表格批量创建,删除文件,批量修改文件内容等。你可以调用我写好的代码函数" 2 | "所有和excel表操作相关的回答你要以JSON字符串格式输出,输出内容只有JSON字符串,不要出现'''和json字样,字符串直接以{开始" 3 | "'def_name'是一个列表,列表内容有'get_current_date()'、'delete_excel_file()'、'create_excel_with_multiple_sheets()'、'copy_first_sheet_to_all_sheets()'、'modify_sheet_cell_value()'、'copy_and_rename_excel_files()'、'excel_cells_to_list()'、'format_excel_cell_range()'、'merge_excel_cells()'等,'def_name'列表中一次可以包含个函数名;'text'的内容为我向你提问的问题,'response'是你对问题的回答" 4 | 'get_current_date():功能: 获取当前日期和时间,格式为年_月_日_时_分_秒。调用提示词: "获取当前日期和时间。"' 5 | 'delete_excel_file(file_path):功能: 删除指定路径的Excel文件。调用提示词: "删除Excel文件,文件路径为[file_path]。"' 6 | 'create_excel_with_multiple_sheets(file_path, sheets_num=1, sheets_data=None, time_list=[], copy=False, split_y="", split_m="", split_d=""):功能: 创建一个包含多个工作表的Excel文件,工作表名称可以是日期、月份、年份或自定义名称。调用提示词: "创建包含多个工作表的Excel文件,文件路径为[file_path],创建工作表数量为[sheets_num],工作表数据为[sheets_data],时间列表为[time_list],是否复制[copy],年份分隔符为[split_y],月份分隔符为[split_m],日期分隔符为[split_d]。"' 7 | 'copy_first_sheet_to_all_sheets(src_file_path, dest_file_path, sheet_i=0):功能: 将源Excel文件的第一个工作表复制到目标Excel文件的所有工作表中。调用提示词: "将源Excel文件的第 sheet_i 工作表复制到目标Excel文件的所有工作表中,源文件路径为[src_file_path],目标文件路径为[dest_file_path],原文件的工作表[sheet_i],sheet_i为从0开始的整数。"' 8 | 'modify_sheet_cell_value(file_path, cell=\'K1\', sheet_id=[0], new_value=None, time_list=[], split_y=\'年\', split_m=\'月\', split_d=\'日\'):功能: 修改指定Excel文件中指定工作表的单元格值,可以根据时间列表自动生成工作表名称。调用提示词: "修改Excel文件中指定工作表的单元格值,文件路径为[file_path],单元格为[cell],工作表ID为[sheet_id],新值为[new_value],时间列表为[time_list],年份分隔符为[split_y],月份分隔符为[split_m],日期分隔符为[split_d],sheet_id的列表从1开始,当sheet_id为[0]时对所有的工作表进行操作。第几个工作表对应的sheet_id就是几,从1开始,如第4个工作表,sheet_id=[4]"' 9 | 'copy_and_rename_excel_files(file_path, num_copies=1, time_list=[], split_y="", split_m="", split_d=""):功能: 复制指定数量的Excel文件,并重命名,可以选择是否以时间为后缀。调用提示词: "复制并重命名Excel文件,文件路径为[file_path],复制数量为[num_copies],时间列表为[time_list],年份分隔符为[split_y],月份分隔符为[split_m],日期分隔符为[split_d]。"' 10 | 'excel_cells_to_list(src_file_path, dest_file_path, cell_range, sheet=0, cell=\'E10\'):功能:从一个 Excel 文件的指定工作表中提取特定单元格范围的内容,并将这些内容写入另一个 Excel 文件的指定单元格中,注意这个是到指定的格子中。函数的核心功能包括:提取单元格内容:从源 Excel 文件的指定工作表中提取指定单元格范围(如 A1:A5)的值。支持提取公式计算后的值(通过 data_only=True 实现)。写入目标文件:将提取的值逐个写入目标 Excel 文件的指定单元格中。依赖外部函数 modify_sheet_cell_value 来实现写入操作。src_file_path 是源 Excel 文件的路径,dest_file_path 是目标 Excel 文件的路径,cell_range 是要提取的单元格范围(例如 \'A1:A5\'),sheet 是工作表的名称或索引(默认为 0,即第一个工作表),cell 是目标文件中要修改的单元格地址(例如\'E10\')。' 11 | 'format_excel_cell_range(file_path, cell_address, sheet_id=[0], alignment=None, width=None, height=None)函数用于在 Excel 文件中设置指定单元格的格式(如对齐方式、列宽、行高等),并支持对单个或多个工作表进行操作。该函数首先会加载指定的 Excel 文件,然后根据传入的参数选择要操作的工作表。如果未指定工作表索引(即 `sheet_id=[0]`),则默认对所有工作表进行操作;如果传入了一个工作表索引列表(如 `sheet_id=[1, 2, 3]`),则仅对指定的工作表进行操作。对于每个选中的工作表,函数会调用 `format_excel_cell` 函数来设置指定单元格的格式,包括对齐方式(左对齐、居中对齐、右对齐)、列宽和行高。完成所有操作后,函数会保存并关闭 Excel 文件。调用该函数时,需要提供以下参数:`file_path` 是 Excel 文件的路径,`cell_address` 是要设置格式的单元格地址(如 `\'E6\'`),`sheet_id` 是可选的工作表索引列表(从 1 开始计数,默认值为 `[0]`,表示操作所有工作表),`alignment` 是可选的对齐方式(支持 `\'left\'`、`\'center\'`、`\'right\'`),`width` 是可选的列宽值,`height` 是可选的行高值。' 12 | 'merge_excel_cells(file_path, start_cell, end_cell, sheet_id=[0]) 函数用于合并 Excel 文件中指定区域的单元格。函数接受四个参数:file_path 是 Excel 文件的路径;start_cell 是合并区域的起始单元格(例如 \'A2\');end_cell 是合并区域的结束单元格(例如 \'B2\');sheet_id 是一个列表,用于指定在哪些工作表中执行合并操作,默认值为 [0],表示在所有工作表中合并指定区域,如果传入其他列表(如 [1, 2, 3]),则仅在对应 ID 的工作表中进行合并。函数会加载 Excel 文件,根据参数选择工作表并合并指定区域的单元格,最后保存修改后的文件。' 13 | '' 14 | "当询问一些我没有设定的函数功能的时候,用你自己的专业能力进行回答,且不需要严格按照JSON格式输出,注意当可执行我写好的函数时就一定要按JSON格式输出指定内容" '' 15 | '' 16 | '如向你提要求:将“表2.xlsx“文件的内容复制到”表1.xlsx“文件的所有工作表中。你以JSON格式回答:{"def_name":["copy_first_sheet_to_all_sheets(src_file_path=\'表2.xlsx\', dest_file_path=\'表1.xlsx\')"], "text":"将“表2.xlsx“文件的内容复制到”表1.xlsx“文件的所有工作表中", "responce":""} ' 17 | '如向你提要求:将“表2.xlsx“文件第3个工作表的内容复制到”表1.xlsx“文件的所有工作表中。你以JSON格式回答:{"def_name":["copy_first_sheet_to_all_sheets(src_file_path=\'表2.xlsx\', dest_file_path=\'表1.xlsx\', sheet_i=2)"], "将“表2.xlsx“文件第3个工作表的内容复制到”表1.xlsx“文件的所有工作表中。", "responce":""} ' 18 | '' 19 | '如向你提要求:把文件复制10次,你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, num_copies=10)"], "text":"将文件复制10次", "responce":""} ' 20 | '如向你提要求:把文件按2024年2月25号到2024年3月2号为后缀进行复制,年月日之间以汉字进行分割。你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, time_list=[2024, 2, 25, 2024, 3, 2], split_y=\'年\', split_m=\'月\', split_d=\'日\')"], "text":"把文件按2024年2月25号到2024年3月2号为后缀进行复制,年月日之间以汉字进行分割", "responce":""} ' 21 | '如向你提要求:把文件按2024年10月到2025年2月为后缀进行复制,年月日之间以_进行分割。你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, time_list=[2024, 10, 0, 2025, 2, 0], split_y=\'_\', split_m=\'_\', split_d=\'_\')"], "text":"把文件按2024年10月到2025年2月为后缀进行复制,年月日之间以/进行分割。", "responce":""} ' 22 | '如向你提要求:把文件按2024年到2026年为后缀进行复制。你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, time_list=[2024, 0, 0, 2025, 0, 0])"], "text":"把文件按2024年到2026年为后缀进行复制。", "responce":""} ' 23 | '如向你提要求:把文件生成2024年2月25号到2024年3月2号的工作表,年月日之间以汉字进行分割。你以JSON格式回答:{"def_name":["create_excel_with_multiple_sheets(file_path, time_list=[2024, 2, 25, 2024, 3, 2], split_y=\'年\', split_m=\'月\', split_d=\'日\')"], "text":"把文件生成2024年2月25号到2024年3月2号的工作表,年月日之间以汉字进行分割。", "responce":""} ' 24 | '如向你提要求:把文件生成2024年10月到2025年2月的工作表,年月日之间以—进行分割。你以JSON格式回答:{"def_name":["create_excel_with_multiple_sheets(file_path, time_list=[2024, 10, 0, 2025, 2, 0], split_y=\'-\', split_m=\'-\', split_d=\'-\')"], "text":"把文件生成2024年10月到2025年2月的工作表,年月日之间以—进行分割。", "responce":""} ' 25 | '如向你提要求:把文件生成10个工作表。你以JSON格式回答:{"def_name":["create_excel_with_multiple_sheets(file_path, sheets_num=10)"], "text":"把文件生成10个工作表。", "responce":""} ' 26 | '' 27 | '如向你提要求:把文件生成2024年到2026的工作表。你以JSON格式回答:{"def_name":["create_excel_with_multiple_sheets(file_path, time_list=[2024, 0, 0, 2026, 0, 0])"], "text":"把文件生成2024年到2026的工作表。", "responce":""} ' 28 | '如向你提要求:修改所有工作表的 A1 单元格内容为“你好”。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A1\',new_value=\'你好\')"], "text":"修改所有工作表的 A1 单元格内容为“你好", "responce":""} ' 29 | '如向你提要求:修改第 1 和第 2 个工作表的 A1 单元格内容为“测试”。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A1\', sheet_id=[1, 2],new_value=\'测试\')"])"], "text":"修改第 1 和第 2 个工作表的 A1 单元格内容为“测试”。", "responce":""} ' 30 | '如向你提要求:将A12单元格内容进行修改,内容为“时间日期”加从2024年2月24日到2024年3月1日,年月日中间无分割。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A12\', new_value=\'时间日期\', time_list=[2024, 2, 24, 2024, 3, 1], split_y=\'\', split_m=\'\', split_d=\'\')"])"], "text":"将A12单元格内容进行修改,内容为“时间日期”加从2024年2月24日到2024年3月1日,年月日中间无分割。", "responce":""} ' 31 | '如向你提要求:将A12单元格内容进行修改,内容为从2024年2月24日到2024年3月1日,年月日中间汉字分割。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A12\', time_list=[2024, 2, 24, 2024, 3, 1], split_y=\'年\', split_m=\'月\', split_d=\'日\')"])"], "text":"将A12单元格内容进行修改,内容为从2024年2月24日到2024年3月1日,年月日中间汉字分割。", "responce":""} ' 32 | '如向你提要求:在问文件的第3个工作表中的A2格子写入“第二次测试“。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A2\', new_value=\'第二次测试\', sheet_id=[3])"], "text":"在问文件的第3个工作表中的A2格子写入“第二次测试“。", "responce":""} ' 33 | '' 34 | '如向你提要求:将所有工作表中 E6 单元格的对齐方式设置为右对齐。你以JSON格式回答:{"def_name":["format_excel_cell_range(file_path, cell_address=\'E6\', alignment=\'right\')"], "text":"将所有工作表中 E6 单元格的对齐方式设置为右对齐。“。", "responce":""} ' 35 | '如向你提要求:将第 1 和第 3 个工作表中 E6 单元格的列宽设置为 20,行高设置为 30。你以JSON格式回答:{"def_name":["format_excel_cell_range(file_path, cell_address=\'E6\', sheet_id=[1, 3], width=20, height=30)"], "text":"将第 1 和第 3 个工作表中 E6 单元格的列宽设置为 20,行高设置为 30。“。", "responce":""} ' 36 | '如向你提要求:把文件复制10次,然后把文件生成2024年到2026年的工作表,修改所有工作表的 A1 单元格内容为“你好”。你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, num_copies=10)", "create_excel_with_multiple_sheets(file_path, time_list=[2024, 0, 0, 2026, 0, 0])", "modify_sheet_cell_value(file_path, cell=\'A1\',new_value=\'你好\')"], "text":"把文件复制10次,然后把文件生成2024年到2026年的工作表,修改所有工作表的 A1 单元格内容为“你好”", "responce":""} ' 37 | 38 | 'JSON格式中"def_name"中可以添加多个函数' 39 | '' -------------------------------------------------------------------------------- /gpt_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from openai import OpenAI 3 | from xl_class import * 4 | import json 5 | import re 6 | import base64 7 | 8 | file_path = 'C:/files/python_files/gpt_excel/入库单.xlsx' 9 | 10 | # 本地模型名称 11 | model_local = "deepseek-r1:1.5b" 12 | 13 | 14 | 15 | def get_image_base64(image_file): 16 | """Convert an image file to a base64 string.""" 17 | if image_file is not None: 18 | encoded_string = base64.b64encode(image_file.read()).decode() # 编码并转换为可读字符串 19 | return encoded_string 20 | return None 21 | 22 | 23 | def llm_model2(content, model=None, API_key=None, image_file=None): 24 | if model is None: 25 | model = "deepseek-v3-volcengine" 26 | else: 27 | model = model 28 | 29 | if model == "本地模型": 30 | base_url = 'http://localhost:11434/v1/' 31 | api_key = 'ollama' 32 | model = model_local 33 | else: 34 | base_url = 'https://api.mindcraft.com.cn/v1/' 35 | #base_url = 'https://api.siliconflow.cn/v1' 36 | api_key = API_key 37 | client = OpenAI(base_url=base_url, api_key=api_key) 38 | 39 | if image_file is not None: 40 | params = { 41 | "model": "Doubao-1.5-vision-pro-32k", 42 | "message": [ 43 | { 44 | "role": "system", 45 | "content": "你是一个数据分析AI,对图片中的数据进行提取,分析,总结,归纳。" 46 | "你可以绘制图表,当需要绘制图表时,用JSON格式回答,其他情况正常回答" 47 | "不要出现python代码" 48 | "'def_name'是一个列表,列表内容有'plot_chart(),write_df_to_excel(output_filename, df)','text'的内容为我向你提问的问题,'response'是你对问题的回答" 49 | "plot_chart(data, chart_type, x_column, y_columns=None, legend_title=None, title='Chart', xlabel=None, ylabel=None,colors=None)函数用于根据给定的数据和参数绘制不同类型的统计图表,支持条形图、多条折线图、饼图和散点图等,接受包括数据框、图表类型、x轴列名、y轴列名列表(对于饼图此参数为None)、图例标题、图表标题、x轴和y轴标签以及颜色在内的多个参数,其中除了数据、图表类型和x轴列名是必需的之外,其余参数均具有默认值以提供灵活性和便捷性,例如你可以通过指定不同的颜色来区分图表中的不同类别或系列,并且可以通过设置图例标题和坐标轴标签来增强图表的可读性和解释力。" 50 | "plot_chart的函数,利用Plotly Express和Streamlit库根据提供的数据框、图表类型及其它参数生成饼图,当指定图表类型为\"pie\"时,它使用数据框中的分类信息(x_column)作为饼图各扇区的标签,以及对应数值信息(y_columns的第一个元素)决定各扇区大小,同时允许自定义标题、颜色等样式,并最终通过Streamlit展示生成的饼图。" 51 | "绘制饼图时plot_chart函数的使用方法如下:plot_chart(data=data = pd.DataFrame({'Fruits': ['Apple', 'Banana', 'Cherry'],'Sales': [55, 45, 30]}), chart_type='pie', x_column='Fruits', y_columns=['Sales'], title='Fruit Sales Distribution')" 52 | "" 53 | "如向你提要求:来绘制一个展示销售和支出随年份变化的折线图,其中数据包含'Year'、'Sales'和'Expenses'三列,分别代表年份、销售额和支出额;通过设置chart_type为'line'指定绘制折线图,x_column选择'Year'作为x轴,y_columns包括['Sales', 'Expenses']以同时展示销售与支出两条折线,legend_title定义图例标题为'Indicator',整体图表标题设为\"Sales and Expenses Over Years\",并自定义了x轴标签为\"Year\",y轴标签为\"Amount\",最后通过colors参数设置了每条折线的颜色分别为'#636EFA'和'#EF553B'," 54 | '你用JSON格式回答:{"def_name":["plot_chart(data=pd.DataFrame({\'Year\': [\'2021\', \'2022\', \'2023\', \'2024\'],\'Sales\': [500, 700, 800, 600],\'Expenses\': [400, 450, 500, 550]}), chart_type=\'line\', x_column=\'Year\', y_columns=[\'Sales\', \'Expenses\'],legend_title=\'Indicator\', title="Sales and Expenses Over Years", xlabel="Year", ylabel="Amount",colors=[\'#636EFA\', \'#EF553B\'])"]}' 55 | '当让你生成多个统计图时,可以根据需求写多个JSON字符串,但每一个JSON字符串要严格按我给你的格式书写,不要在一个JSON字符串里写多个"plot_chart"函数' 56 | '函数write_df_to_excel(output_filename, df)用于将pandas DataFrame数据简便而高效地导出到Excel文件中,其中output_filename为包含路径的输出文件名称,df则是要导出的数据内容' 57 | '向你询将图片数据存入excel表文件中等相关问题,你以JSON格式回答,用JSON格式,格式如下:{"def_name":["write_df_to_excel(output_filename, df=pd.DataFrame({\'Year\': [\'2021\', \'2022\', \'2023\', \'2024\'],\'Sales\': [500, 700, 800, 600],\'Expenses\': [400, 450, 500, 550]})"]}' 58 | '' 59 | 60 | 61 | }, 62 | { 63 | "role": "user", 64 | "content": [ 65 | # 使用 base64 编码传输 66 | { 67 | 'type': 'image', 68 | 'source': { 69 | 'data': get_image_base64(image_file) 70 | }, 71 | }, 72 | { 73 | 'type': 'text', 74 | 'text': content, 75 | }, 76 | ] 77 | } 78 | 79 | ], 80 | "temperature": 0, 81 | "max_tokens": 8000, 82 | "stream": True 83 | } 84 | 85 | else: 86 | params = { 87 | "model": model, 88 | "message": [ 89 | { 90 | "role": "system", 91 | "content": "你是一个数据分析AI,对数据进行提取,分析,总结,归纳。" 92 | "你可以绘制图表,当需要绘制图表时,用JSON格式回答" 93 | '在写JSON格式中不要用df代替参数,用JSON格式调用函数时要直接将DataFrame数据写在里面,不要用变量代替。像这样写:data=pd.DataFrame({\'Year\': [\'2021\', \'2022\', \'2023\', \'2024\'],\'Sales\': [500, 700, 800, 600],\'Expenses\': [400, 450, 500, 550]})' 94 | '你的输出中不能同时出现JSON字符串和python代码' 95 | '你的输出中不能同时出现JSON字符串和python代码' 96 | '你的输出中不能同时出现JSON字符串和python代码' 97 | "" 98 | "" 99 | "'def_name'是一个列表,列表内容有'plot_chart()','text'的内容为我向你提问的问题,'response'是你对问题的回答" 100 | "plot_chart(data, chart_type, x_column, y_columns=None, legend_title=None, title='Chart', xlabel=None, ylabel=None,colors=None)函数用于根据给定的数据和参数绘制不同类型的统计图表,支持条形图、多条折线图、饼图和散点图等,接受包括数据框、图表类型、x轴列名、y轴列名列表(对于饼图此参数为None)、图例标题、图表标题、x轴和y轴标签以及颜色在内的多个参数,其中除了数据、图表类型和x轴列名是必需的之外,其余参数均具有默认值以提供灵活性和便捷性,例如你可以通过指定不同的颜色来区分图表中的不同类别或系列,并且可以通过设置图例标题和坐标轴标签来增强图表的可读性和解释力。" 101 | "plot_chart的函数,利用Plotly Express和Streamlit库根据提供的数据框、图表类型及其它参数生成饼图,当指定图表类型为\"pie\"时,它使用数据框中的分类信息(x_column)作为饼图各扇区的标签,以及对应数值信息(y_columns的第一个元素)决定各扇区大小,同时允许自定义标题、颜色等样式,并最终通过Streamlit展示生成的饼图。" 102 | "绘制饼图时plot_chart函数的使用方法如下:plot_chart(data=data = pd.DataFrame({'Fruits': ['Apple', 'Banana', 'Cherry'],'Sales': [55, 45, 30]}), chart_type='pie', x_column='Fruits', y_columns=['Sales'], title='Fruit Sales Distribution')" 103 | "" 104 | "如向你提要求:来绘制一个展示销售和支出随年份变化的折线图,其中数据包含'Year'、'Sales'和'Expenses'三列,分别代表年份、销售额和支出额;通过设置chart_type为'line'指定绘制折线图,x_column选择'Year'作为x轴,y_columns包括['Sales', 'Expenses']以同时展示销售与支出两条折线,legend_title定义图例标题为'Indicator',整体图表标题设为\"Sales and Expenses Over Years\",并自定义了x轴标签为\"Year\",y轴标签为\"Amount\",最后通过colors参数设置了每条折线的颜色分别为'#636EFA'和'#EF553B'," 105 | '你用JSON格式回答:{"def_name":["plot_chart(data=pd.DataFrame({\'Year\': [\'2021\', \'2022\', \'2023\', \'2024\'],\'Sales\': [500, 700, 800, 600],\'Expenses\': [400, 450, 500, 550]}), chart_type=\'line\', x_column=\'Year\', y_columns=[\'Sales\', \'Expenses\'],legend_title=\'Indicator\', title="Sales and Expenses Over Years", xlabel="Year", ylabel="Amount",colors=[\'#636EFA\', \'#EF553B\'])"]}' 106 | '当让你生成多个统计图时,可以根据需求写多个JSON字符串,但每一个JSON字符串要严格按我给你的格式书写,不要在一个JSON字符串里写多个"plot_chart"函数' 107 | '' 108 | '如果向你提问绘制图表和保存数据之外的一些问题,且涉及到计算、查找、保存等任务,你可以自己决定编写相应的python代码来实现,且要对代码进行一定的解释,如果涉及到保存文件要说明保存到哪个文件里,把文件地址说清楚。' 109 | '你编写的python代码里只能使用"numpy", "pandas", "openpyxl", "os", "csv", "math", "random", "json", "re", "time", "copy"库,其他库不要用。' 110 | '所有数据数值相关的python代码,在写代码的过程中必须要进行如下步骤:1.提取有效数据行(排除首行空值和最后合计行);2.筛选所需列并重命名;3.转换数据类型并筛选。步骤必须至少有这三步,可有其他步骤但是这三步必须有。' 111 | '所有数据数值相关的python代码,在写代码的过程中必须要进行如下步骤:1.提取有效数据行(排除首行空值和最后合计行);2.筛选所需列并重命名;3.转换数据类型并筛选。步骤必须至少有这三步,可有其他步骤但是这三步必须有。' 112 | '你接收到的数据与pandas的DataFrame格式的数据样式相同,在python代码中就以df参数代替传给你的那部分数据的DataFrame格式,即你写的python代码中将传给你的数据就以pandas的DataFrame格式的df参数代替即可' 113 | 'python代码要清晰完整,思路明了,一定要写完整引入的库。' 114 | '一些简单的数据分析和数据提取问题,可以选择不用python代码,只输出文本和调用JSON字符串中的函数即可' 115 | '所有数据相关的要求,在写代码的过程中要对格子的数据类型进行判断,如写代码时出现”a>400“时要判断a是否是int或者是flout类型,保证代码的正常运行' 116 | '所有数据相关的要求,在写代码的过程中要对格子的数据类型进行判断,如写代码时出现”a>400“时要判断a是否是int或者是flout类型,保证代码的正常运行' 117 | '所有数据数值相关的python代码,在写代码的过程中必须要进行如下步骤:1.提取有效数据行(排除首行空值和最后合计行);2.筛选所需列并重命名;3.转换数据类型并筛选。步骤必须至少有这三步,可有其他步骤但是这三步必须有。' 118 | '你接收到的数据是pandas的DataFrame格式的数据,在python代码中就以df参数代替,数据相关的表格要求代码中要首先筛选出所有数值' 119 | '在写JSON格式中不要用df代替参数,用JSON格式调用函数时要直接将DataFrame数据写在里面,不要用变量代替。像这样写:data=pd.DataFrame({\'Year\': [\'2021\', \'2022\', \'2023\', \'2024\'],\'Sales\': [500, 700, 800, 600],\'Expenses\': [400, 450, 500, 550]})' 120 | '' 121 | '非必要时刻,不运用python代码' 122 | '非必要时刻,不写python代码' 123 | '' 124 | '' 125 | 126 | }, 127 | { 128 | "role": "user", 129 | "content": content 130 | } 131 | 132 | ], 133 | "temperature": 0, 134 | "max_tokens": 8000, 135 | "stream": True 136 | } 137 | 138 | 139 | response = client.chat.completions.create( 140 | model=params.get("model"), 141 | messages=params.get("message"), 142 | temperature=params.get("temperature"), 143 | max_tokens=params.get("max_tokens"), 144 | stream=params.get("stream"), 145 | ) 146 | return response 147 | 148 | 149 | def llm_text2(response): 150 | text = '' 151 | for i in response: 152 | content = i.choices[0].delta.content 153 | if not content: 154 | if i.usage: 155 | print('\n请求花销usage:', i.usage) 156 | continue 157 | print(content, end='', flush=True) 158 | text += content 159 | #text_to_speech(content) 160 | else: 161 | print() 162 | return text 163 | 164 | 165 | # 连接其他函数 166 | def link_llm2(text): 167 | # 使用正则表达式查找{'def_name' 168 | # 正则表达式 169 | pattern = r'\{[^{}]*\}' 170 | 171 | # 使用正则表达式匹配 172 | match = re.findall(pattern, text) 173 | print(match) 174 | 175 | if match: 176 | for i_n in match: 177 | print(i_n) 178 | try: 179 | # 解析JSON数据 180 | json_data = json.loads(i_n) 181 | datas = json_data['def_name'] 182 | except: 183 | print("解析JSON出错") 184 | # 执行函数 185 | for data in datas: 186 | try: 187 | print(data) 188 | exec(data) 189 | except: 190 | str_text = "不能执行此动作" 191 | print(str_text) 192 | return str_text 193 | else: 194 | return text 195 | 196 | 197 | def AI_run2(content, model, API_key): 198 | response = llm_model2(content, model, API_key) 199 | text = llm_text2(response) 200 | return text 201 | 202 | 203 | if __name__ == '__main__': 204 | try: 205 | while True: 206 | content = input("写入需求:") 207 | text = AI_run2(content, model=None, API_key='1') 208 | #link_llm2(text) 209 | except KeyboardInterrupt: 210 | print("程序出错已退出。") -------------------------------------------------------------------------------- /gpt_api.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from openai import OpenAI 3 | from xl_class import * 4 | import json 5 | import re 6 | 7 | file_path = 'C:/files/python_files/gpt_excel/入库单.xlsx' 8 | 9 | # 本地模型名称 10 | model_local = "deepseek-r1:1.5b" 11 | 12 | def llm_model(content, model=None, API_key=None): 13 | if model is None: 14 | model = "GLM-4-Air" 15 | else: 16 | model = model 17 | 18 | if model == "本地模型": 19 | base_url = 'http://localhost:11434/v1/' 20 | api_key = 'ollama' 21 | model = model_local 22 | else: 23 | base_url = 'https://api.mindcraft.com.cn/v1/' 24 | api_key = API_key 25 | 26 | client = OpenAI(base_url=base_url, api_key=api_key) 27 | params = { 28 | "model": model, 29 | "message": [ 30 | { 31 | "role": "system", 32 | "content": "你是一个帮我批量处理excel表格的工具,你会对文件批量复制,工作表格批量创建,删除文件,批量修改文件内容等。你可以调用我写好的代码函数" 33 | "所有和excel表操作相关的回答你要以JSON字符串格式输出。" 34 | "当有询问你功能相关的指令是,不需要用JSON,正常回答你的功能就好。" 35 | "'def_name'是一个列表,列表内容有'get_current_date()'、'delete_excel_file()'、'create_excel_with_multiple_sheets()'、'copy_first_sheet_to_all_sheets()'、'modify_sheet_cell_value()'、'copy_and_rename_excel_files()'、'excel_cells_to_list()'、'format_excel_cell_range()'、'merge_excel_cells()'、'process_excel_files()'等,'def_name'列表中一次可以包含个函数名;'text'的内容为我向你提问的问题,'response'是你对问题的回答" 36 | 'get_current_date():功能: 获取当前日期和时间,格式为年_月_日_时_分_秒。调用提示词: "获取当前日期和时间。"' 37 | 'delete_excel_file(file_path):功能: 删除指定路径的Excel文件。调用提示词: "删除Excel文件,文件路径为[file_path]。"' 38 | 'create_excel_with_multiple_sheets(file_path, sheets_num=1, sheets_data=None, time_list=[], copy=False, split_y="", split_m="", split_d=""):功能: 创建一个包含多个工作表的Excel文件,工作表名称可以是日期、月份、年份或自定义名称。调用提示词: "创建包含多个工作表的Excel文件,文件路径为[file_path],创建工作表数量为[sheets_num],工作表数据为[sheets_data],时间列表为[time_list],是否复制[copy],年份分隔符为[split_y],月份分隔符为[split_m],日期分隔符为[split_d]。如果没有特殊要求,copy不需要传入参数,保持默认即可"' 39 | 'copy_first_sheet_to_all_sheets(src_file_path, dest_file_path, sheet_i=0):功能: 将源Excel文件的第一个工作表复制到目标Excel文件的所有工作表中。调用提示词: "将源Excel文件的第 sheet_i 工作表复制到目标Excel文件的所有工作表中,源文件路径为[src_file_path],目标文件路径为[dest_file_path],原文件的工作表[sheet_i],sheet_i为从0开始的整数。"' 40 | 'modify_sheet_cell_value(file_path, cell=\'K1\', sheet_id=[0], new_value=None, time_list=[], split_y=\'年\', split_m=\'月\', split_d=\'日\'):功能: 修改指定Excel文件中指定工作表的单元格值,可以根据时间列表自动生成工作表名称。调用提示词: "修改Excel文件中指定工作表的单元格值,文件路径为[file_path],单元格为[cell],工作表ID为[sheet_id],新值为[new_value],时间列表为[time_list],年份分隔符为[split_y],月份分隔符为[split_m],日期分隔符为[split_d],sheet_id的列表从1开始,当sheet_id为[0]时对所有的工作表进行操作。第几个工作表对应的sheet_id就是几,从1开始,如第4个工作表,sheet_id=[4]"' 41 | 'copy_and_rename_excel_files(file_path, num_copies=1, time_list=[], split_y="", split_m="", split_d=""):功能: 复制指定数量的Excel文件,并重命名,可以选择是否以时间为后缀。调用提示词: "复制并重命名Excel文件,文件路径为[file_path],复制数量为[num_copies],时间列表为[time_list],年份分隔符为[split_y],月份分隔符为[split_m],日期分隔符为[split_d]。"' 42 | 'excel_cells_to_list(src_file_path, dest_file_path, cell_range, sheet=0, cell=\'E10\'):功能:从一个 Excel 文件的指定工作表中提取特定单元格范围的内容,并将这些内容写入另一个 Excel 文件的指定单元格中,注意这个是到指定的格子中。函数的核心功能包括:提取单元格内容:从源 Excel 文件的指定工作表中提取指定单元格范围(如 A1:A5)的值。支持提取公式计算后的值(通过 data_only=True 实现)。写入目标文件:将提取的值逐个写入目标 Excel 文件的指定单元格中。依赖外部函数 modify_sheet_cell_value 来实现写入操作。src_file_path 是源 Excel 文件的路径,dest_file_path 是目标 Excel 文件的路径,cell_range 是要提取的单元格范围(例如 \'A1:A5\'),sheet 是工作表的名称或索引(默认为 0,即第一个工作表),cell 是目标文件中要修改的单元格地址(例如\'E10\')。' 43 | 'format_excel_cell_range(file_path, cell_address, sheet_id=[0], alignment=None, width=None, height=None)函数用于在 Excel 文件中设置指定单元格的格式(如对齐方式、列宽、行高等),并支持对单个或多个工作表进行操作。该函数首先会加载指定的 Excel 文件,然后根据传入的参数选择要操作的工作表。如果未指定工作表索引(即 `sheet_id=[0]`),则默认对所有工作表进行操作;如果传入了一个工作表索引列表(如 `sheet_id=[1, 2, 3]`),则仅对指定的工作表进行操作。对于每个选中的工作表,函数会调用 `format_excel_cell` 函数来设置指定单元格的格式,包括对齐方式(左对齐、居中对齐、右对齐)、列宽和行高。完成所有操作后,函数会保存并关闭 Excel 文件。调用该函数时,需要提供以下参数:`file_path` 是 Excel 文件的路径,`cell_address` 是要设置格式的单元格地址(如 `\'E6\'`),`sheet_id` 是可选的工作表索引列表(从 1 开始计数,默认值为 `[0]`,表示操作所有工作表),`alignment` 是可选的对齐方式(支持 `\'left\'`、`\'center\'`、`\'right\'`),`width` 是可选的列宽值,`height` 是可选的行高值。' 44 | 'merge_excel_cells(file_path, start_cell, end_cell, sheet_id=[0]) 函数用于合并 Excel 文件中指定区域的单元格。函数接受四个参数:file_path 是 Excel 文件的路径;start_cell 是合并区域的起始单元格(例如 \'A2\');end_cell 是合并区域的结束单元格(例如 \'B2\');sheet_id 是一个列表,用于指定在哪些工作表中执行合并操作,默认值为 [0],表示在所有工作表中合并指定区域,如果传入其他列表(如 [1, 2, 3]),则仅在对应 ID 的工作表中进行合并。函数会加载 Excel 文件,根据参数选择工作表并合并指定区域的单元格,最后保存修改后的文件。' 45 | 'process_excel_files(file_list, keyword=None, cell_position=None, operation=\'read\', direction=None, num_of_cells=1,output_file=\'output_m.xlsx\', extract_formula=False, transpose=False)函数通过file_list指定待处理的Excel文件列表,当需要查找特定内容时,可使用keyword参数指定关键字(如keyword="销售额")或通过cell_position参数直接定位单元格(如cell_position="B3"),并通过operation参数选择操作类型(默认为\'read\'读取,\'modify\'修改),若为读取操作,则需用direction指定方向(如\'right\'右侧,\'left\'左侧,\'up\'上侧,\'down\'下侧)、num_of_cells指定提取相邻单元格数量(如num_of_cells=3提取3个单元格),extract_formula控制是否提取公式(如True提取公式而非值),transpose决定结果是否转置(如True行列互换),最终结果会保存到output_file指定的文件(默认为output_m.xlsx);例如:process_excel_files(["data.xlsx"], keyword="总计", direction="right", num_of_cells=3)会从所有文件的“总计”单元格右侧提取3个值,而process_excel_files(["file.xlsx"], cell_position="C5", direction="up", num_of_cells=2, extract_formula=True, transpose=True)会提取C5单元格上方2个单元格的公式并转置输出。' 46 | '' 47 | "当询问一些我没有设定的函数功能的时候,用你自己的专业能力进行回答,且不需要严格按照JSON格式输出,注意当可执行我写好的函数时就一定要按JSON格式输出指定内容" '' 48 | '' 49 | '如向你提要求:将“表2.xlsx“文件的内容复制到”表1.xlsx“文件的所有工作表中。你以JSON格式回答:{"def_name":["copy_first_sheet_to_all_sheets(src_file_path=\'表2.xlsx\', dest_file_path=\'表1.xlsx\')"], "text":"将“表2.xlsx“文件的内容复制到”表1.xlsx“文件的所有工作表中", "responce":""} ' 50 | '如向你提要求:将“表2.xlsx“文件第3个工作表的内容复制到”表1.xlsx“文件的所有工作表中。你以JSON格式回答:{"def_name":["copy_first_sheet_to_all_sheets(src_file_path=\'表2.xlsx\', dest_file_path=\'表1.xlsx\', sheet_i=2)"], "将“表2.xlsx“文件第3个工作表的内容复制到”表1.xlsx“文件的所有工作表中。", "responce":""} ' 51 | '' 52 | '如向你提要求:把文件复制10次,你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, num_copies=10)"], "text":"将文件复制10次", "responce":""} ' 53 | '如向你提要求:把文件按2024年2月25号到2024年3月2号为后缀进行复制,年月日之间以汉字进行分割。你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, time_list=[2024, 2, 25, 2024, 3, 2], split_y=\'年\', split_m=\'月\', split_d=\'日\')"], "text":"把文件按2024年2月25号到2024年3月2号为后缀进行复制,年月日之间以汉字进行分割", "responce":""} ' 54 | '如向你提要求:把文件按2024年10月到2025年2月为后缀进行复制,年月日之间以_进行分割。你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, time_list=[2024, 10, 0, 2025, 2, 0], split_y=\'_\', split_m=\'_\', split_d=\'_\')"], "text":"把文件按2024年10月到2025年2月为后缀进行复制,年月日之间以/进行分割。", "responce":""} ' 55 | '如向你提要求:把文件按2024年到2026年为后缀进行复制。你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, time_list=[2024, 0, 0, 2025, 0, 0])"], "text":"把文件按2024年到2026年为后缀进行复制。", "responce":""} ' 56 | '如向你提要求:把文件生成2024年2月25号到2024年3月2号的工作表,年月日之间以汉字进行分割。你以JSON格式回答:{"def_name":["create_excel_with_multiple_sheets(file_path, time_list=[2024, 2, 25, 2024, 3, 2], split_y=\'年\', split_m=\'月\', split_d=\'日\')"], "text":"把文件生成2024年2月25号到2024年3月2号的工作表,年月日之间以汉字进行分割。", "responce":""} ' 57 | '如向你提要求:把文件生成2024年10月到2025年2月的工作表,年月日之间以—进行分割。你以JSON格式回答:{"def_name":["create_excel_with_multiple_sheets(file_path, time_list=[2024, 10, 0, 2025, 2, 0], split_y=\'-\', split_m=\'-\', split_d=\'-\')"], "text":"把文件生成2024年10月到2025年2月的工作表,年月日之间以—进行分割。", "responce":""} ' 58 | '如向你提要求:把文件生成10个工作表。你以JSON格式回答:{"def_name":["create_excel_with_multiple_sheets(file_path, sheets_num=10)"], "text":"把文件生成10个工作表。", "responce":""} ' 59 | '' 60 | '如向你提要求:把文件生成2024年到2026的工作表。你以JSON格式回答:{"def_name":["create_excel_with_multiple_sheets(file_path, time_list=[2024, 0, 0, 2026, 0, 0])"], "text":"把文件生成2024年到2026的工作表。", "responce":""} ' 61 | '如向你提要求:修改所有工作表的 A1 单元格内容为“你好”。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A1\',new_value=\'你好\')"], "text":"修改所有工作表的 A1 单元格内容为“你好", "responce":""} ' 62 | '如向你提要求:修改第 1 和第 2 个工作表的 A1 单元格内容为“测试”。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A1\', sheet_id=[1, 2],new_value=\'测试\')"])"], "text":"修改第 1 和第 2 个工作表的 A1 单元格内容为“测试”。", "responce":""} ' 63 | '如向你提要求:将A12单元格内容进行修改,内容为“时间日期”加从2024年2月24日到2024年3月1日,年月日中间无分割。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A12\', new_value=\'时间日期\', time_list=[2024, 2, 24, 2024, 3, 1], split_y=\'\', split_m=\'\', split_d=\'\')"])"], "text":"将A12单元格内容进行修改,内容为“时间日期”加从2024年2月24日到2024年3月1日,年月日中间无分割。", "responce":""} ' 64 | '如向你提要求:将A12单元格内容进行修改,内容为从2024年2月24日到2024年3月1日,年月日中间汉字分割。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A12\', time_list=[2024, 2, 24, 2024, 3, 1], split_y=\'年\', split_m=\'月\', split_d=\'日\')"])"], "text":"将A12单元格内容进行修改,内容为从2024年2月24日到2024年3月1日,年月日中间汉字分割。", "responce":""} ' 65 | '如向你提要求:在问文件的第3个工作表中的A2格子写入“第二次测试“。你以JSON格式回答:{"def_name":["modify_sheet_cell_value(file_path, cell=\'A2\', new_value=\'第二次测试\', sheet_id=[3])"], "text":"在问文件的第3个工作表中的A2格子写入“第二次测试“。", "responce":""} ' 66 | '' 67 | '如向你提要求:将所有工作表中 E6 单元格的对齐方式设置为右对齐。你以JSON格式回答:{"def_name":["format_excel_cell_range(file_path, cell_address=\'E6\', alignment=\'right\')"], "text":"将所有工作表中 E6 单元格的对齐方式设置为右对齐。“。", "responce":""} ' 68 | '如向你提要求:将第 1 和第 3 个工作表中 E6 单元格的列宽设置为 20,行高设置为 30。你以JSON格式回答:{"def_name":["format_excel_cell_range(file_path, cell_address=\'E6\', sheet_id=[1, 3], width=20, height=30)"], "text":"将第 1 和第 3 个工作表中 E6 单元格的列宽设置为 20,行高设置为 30。“。", "responce":""} ' 69 | '如向你提要求:把文件复制10次,然后把文件生成2024年到2026年的工作表,修改所有工作表的 A1 单元格内容为“你好”。你以JSON格式回答:{"def_name":["copy_and_rename_excel_files(file_path, num_copies=10)", "create_excel_with_multiple_sheets(file_path, time_list=[2024, 0, 0, 2026, 0, 0])", "modify_sheet_cell_value(file_path, cell=\'A1\',new_value=\'你好\')"], "text":"把文件复制10次,然后把文件生成2024年到2026年的工作表,修改所有工作表的 A1 单元格内容为“你好”", "responce":""} ' 70 | '如向你提要求:查找Excel文件中包含“销售额”关键字的单元格,并读取其右侧3个单元格的值,保存到output.xlsx文件中。你以JSON格式回答:{"def_name":["process_excel_files(file_list=[\'data.xlsx\', \'data2.xlsx\'], keyword=\'销售额\', direction=\'right\', num_of_cells=3, output_file=\'output.xlsx\')"], "text":"查找Excel文件中包含“销售额”关键字的单元格,并读取其右侧3个单元格的值,保存到output.xlsx文件中。", "responce":""}' 71 | '如向你提要求:查找Excel文件中A1单元格上方2个单元格的公式,并将结果转置保存到output_formula.xlsx文件中。你以JSON格式回答:{"def_name":["process_excel_files(file_list=[\'file.xlsx\', \'file2.xlsx\'], cell_position=\'A1\', direction=\'up\', num_of_cells=2, extract_formula=True, transpose=True, output_file=\'output_formula.xlsx\')"], "text":"查找Excel文件中A1单元格上方2个单元格的公式,并将结果转置保存到output_formula.xlsx文件中。", "responce":""}' 72 | '如向你提要求:查找Excel文件中B3单元格左侧1个单元格的值,并将结果保存到output_data.xlsx文件中。你以JSON格式回答:{"def_name":["process_excel_files(file_list=[\'file.xlsx\'], cell_position=\'B3\', direction=\'left\', num_of_cells=1, output_file=\'output_data.xlsx\')"], "text":"查找Excel文件中B3单元格左侧1个单元格的值,并将结果保存到output_data.xlsx文件中。", "responce":""}' 73 | '如向你提要求:查找Excel文件中所有包含“利润”关键字的单元格,并读取其下方5个单元格的值,保存到output_profit.xlsx文件中。你以JSON格式回答:{"def_name":["process_excel_files(file_list=[\'data.xlsx\'], keyword=\'利润\', direction=\'down\', num_of_cells=5, output_file=\'output_profit.xlsx\')"], "text":"查找Excel文件中所有包含“利润”关键字的单元格,并读取其下方5个单元格的值,保存到output_profit.xlsx文件中。", "responce":""}' 74 | '' 75 | 'JSON格式中"def_name"中可以添加多个函数' 76 | '' 77 | '' 78 | }, 79 | { 80 | "role": "user", 81 | "content": content 82 | } 83 | ], 84 | "temperature": 0, 85 | "max_tokens": 5000, 86 | "stream": True 87 | } 88 | 89 | response = client.chat.completions.create( 90 | model=params.get("model"), 91 | messages=params.get("message"), 92 | temperature=params.get("temperature"), 93 | max_tokens=params.get("max_tokens"), 94 | stream=params.get("stream"), 95 | ) 96 | return response 97 | 98 | 99 | def llm_text(response): 100 | text = '' 101 | for i in response: 102 | content = i.choices[0].delta.content 103 | if not content: 104 | if i.usage: 105 | print('\n请求花销usage:', i.usage) 106 | continue 107 | print(content, end='', flush=True) 108 | text += content 109 | #text_to_speech(content) 110 | else: 111 | print() 112 | return text 113 | 114 | 115 | # 连接其他函数 116 | def link_llm(text, file_path): 117 | file_path = file_path 118 | # 使用正则表达式查找{'def_name' 119 | # 正则表达式 120 | pattern = r'\{[^{}]*\}' 121 | 122 | # 使用正则表达式匹配 123 | match = re.findall(pattern, text) 124 | print(match) 125 | 126 | if match: 127 | for i_n in match: 128 | print(i_n) 129 | try: 130 | # 解析JSON数据 131 | json_data = json.loads(i_n) 132 | datas = json_data['def_name'] 133 | except: 134 | print("解析JSON出错") 135 | # 执行函数 136 | for data in datas: 137 | try: 138 | print(data) 139 | exec(data) 140 | except: 141 | str_text = "不能执行此动作" 142 | print(str_text) 143 | continue 144 | else: 145 | return text 146 | 147 | def AI_run(content, model, API_key): 148 | response = llm_model(content, model, API_key) 149 | text = llm_text(response) 150 | return text 151 | 152 | if __name__ == '__main__': 153 | try: 154 | while True: 155 | content = input("写入需求:") 156 | text = AI_run(content, model=None, API_key="MC-9B63B87358C74B41B9365B9F3188070F") 157 | link_llm(text, file_path) 158 | except KeyboardInterrupt: 159 | print("程序出错已退出。") -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | 4 | from xl_class import * 5 | from gpt_api import AI_run, link_llm 6 | from gpt_data import llm_model2, llm_text2 7 | import time 8 | import copy 9 | import json 10 | import plotly.express as px 11 | import plotly.graph_objects as go 12 | import os 13 | from maltipal_file_test import find_excel_files 14 | from auto_code import * 15 | 16 | 17 | def plot_chart(data, chart_type, x_column, y_columns=None, legend_title=None, title='Chart', xlabel=None, ylabel=None, 18 | colors=None): 19 | """ 20 | 根据提供的数据和参数绘制不同类型的图表。 21 | 22 | :param data: 输入的数据框 23 | :param chart_type: 图表类型("bar", "line", "pie", "scatter"等) 24 | :param x_column: x轴对应的数据列名 25 | :param y_columns: y轴对应的数据列名列表,对于饼图此参数为None,对于多条折线图则应提供多个y轴列名 26 | :param legend_title: 图例标题 27 | :param title: 图表标题 28 | :param xlabel: x轴标题 29 | :param ylabel: y轴标题 30 | :param colors: 颜色列表或字典,根据图表类型应用到不同的线条或类别 31 | """ 32 | if chart_type not in ['bar', 'line', 'pie', 'scatter']: 33 | st.write(f"{chart_type} not supported.") 34 | return 35 | 36 | if chart_type == 'pie': 37 | # For pie charts, we assume a single y_column is provided for the values. 38 | fig = px.pie(data, names=x_column, values=y_columns[0], title=title, color_discrete_sequence=colors) 39 | else: 40 | fig = go.Figure() 41 | 42 | for i, y_col in enumerate(y_columns): 43 | if chart_type == 'bar': 44 | fig.add_trace( 45 | go.Bar(x=data[x_column], y=data[y_col], name=y_col, marker_color=colors[i] if colors else None)) 46 | elif chart_type == 'line': 47 | fig.add_trace(go.Scatter(x=data[x_column], y=data[y_col], mode='lines', name=y_col, 48 | line=dict(color=colors[i] if colors else None))) 49 | elif chart_type == 'scatter': 50 | fig.add_trace(go.Scatter(x=data[x_column], y=data[y_col], mode='markers', name=y_col, 51 | marker=dict(color=colors[i] if colors else None))) 52 | 53 | fig.update_layout(title=title, xaxis_title=xlabel if xlabel else x_column, 54 | yaxis_title=ylabel if ylabel else ('Value' if y_columns else ''), legend_title=legend_title) 55 | st.plotly_chart(fig) 56 | 57 | 58 | def save_list_to_txt(file_path, data_list): 59 | """ 60 | 将列表存入txt文件 61 | :param file_path: 文件路径 62 | :param data_list: 要存储的列表 63 | """ 64 | with open(file_path, 'w', encoding='utf-8') as file: 65 | for item in data_list: 66 | file.write(str(item) + '\n') # 每个元素写入一行 67 | 68 | 69 | def check_path_type(path): 70 | """ 71 | 判断给定的路径是指向一个文件还是一个目录。 72 | 参数:path (str): 要检查的路径。 73 | 返回:str: 返回描述路径类型的字符串信息。 74 | """ 75 | if os.path.isfile(path): 76 | return 1 # 文件 77 | elif os.path.isdir(path): 78 | return 0 # 文件夹 79 | else: 80 | return 400 81 | 82 | 83 | def list_to_string(input_list, separator=', '): 84 | """ 85 | 将列表转换为字符串,支持包含任意类型的元素。 86 | 参数:input_list (list): 要转换的列表。 87 | separator (str): 元素之间的分隔符,默认为', '。 88 | 返回:str: 转换后的字符串。 89 | """ 90 | # 将列表中的每个元素转换为字符串 91 | str_list = [str(element) for element in input_list] 92 | 93 | # 使用指定的分隔符连接所有字符串元素 94 | return separator.join(str_list) 95 | 96 | 97 | # 获取当前日期 98 | time_c = get_current_date() 99 | 100 | # 初始化状态变量,用于存储历史回答 101 | if 'history' not in st.session_state: 102 | st.session_state.history = [] 103 | 104 | st.title("Excel自动化AI工具") 105 | 106 | col3_1, col3_2 = st.columns([16, 1]) 107 | 108 | with col3_1: 109 | input_str = st.text_input("请输入你的Excel文件路径(文件名或文件夹名):") 110 | file_path = process_path_or_filename(input_str) 111 | # 检查文件是否存在 112 | exist_file = os.path.exists(file_path) 113 | if file_path: 114 | if exist_file: 115 | path_n = check_path_type(file_path) 116 | if path_n == 1: 117 | st.success(f"文件 '{file_path}' 存在!") 118 | elif path_n == 0: 119 | st.success(f"文件夹 '{file_path}' 存在!") 120 | files_list = find_excel_files(file_path) 121 | #st.write(files_list) 122 | else: 123 | st.error(f"文件或文件夹 '{file_path}' 不存在!请重新输入文件路径!") 124 | 125 | with col3_2: 126 | if file_path: 127 | st.write(f"### 💡") 128 | 129 | 130 | tab1, tab2, tab3 = st.tabs(["AI脚本生成", "自定义脚本", "数据分析"]) 131 | 132 | 133 | with tab1: 134 | col4_1, col4_2, col4_3 = st.columns([6, 8, 2]) 135 | with col4_1: 136 | # 定义主流大语言模型列表 137 | models1 = ["deepseek-v3-aliyun", "本地模型", "deepseek-r1-aliyun", "deepseek-v3-baiduyun", "deepseek-r1-baiduyun", "deepseek-v3-volcengine", "deepseek-r1-volcengine", "deepseek-chat", "deepseek-reasoner", "GLM-4-Air", "GLM-4-Flash", "Doubao-1.5-lite-32k", "Doubao-1.5-pro-32k", "deepseek-reasoner", "deepseek-coder", "qwen-turbo-latest", "qwen-plus-latest", "qwen-coder-plus-latest"] 138 | # 创建下拉列表,默认选择 DeepSeek 139 | selected_model1 = st.selectbox( 140 | "选择一个大语言模型", 141 | models1, 142 | index=models1.index("deepseek-v3-aliyun") # 设置默认选项为 DeepSeek 143 | ) 144 | with col4_2: 145 | API_key_1 = st.text_input("输入API密钥", type="password") 146 | with col4_3: 147 | st.markdown("[如何获得API密钥](https://apifox.com/apidoc/shared-0fd7ea54-919e-4c93-b673-c60219bc82e0/doc-4739665)", ) 148 | 149 | query = st.text_area("请输入关于你上传文件路径下对该文件的文件的指令,或关于上传文件的需求:") 150 | if file_path: 151 | if exist_file: 152 | if path_n == 0: 153 | st.write(files_list) 154 | query = query + "\n文件夹下有如下Excel文件:" + list_to_string(files_list) 155 | #st.write(query) 156 | 157 | 158 | col1_1, col1_2 = st.columns([1, 2]) 159 | with col1_1: 160 | button = st.button("执行任务") 161 | with col1_2: 162 | if file_path: 163 | if exist_file: 164 | if path_n == 1: 165 | checked = st.checkbox(f"复制 {file_path} 文件备份") 166 | 167 | if button: 168 | if not file_path: 169 | st.error("请先输入文件路径") 170 | elif not query: 171 | st.error("请输入指令或需求") 172 | elif not exist_file: 173 | st.error("文件不存在,请重新输入文件路径") 174 | elif not API_key_1: 175 | st.error("请输入API密钥") 176 | else: 177 | with st.spinner("AI思考中,请稍等..."): 178 | time_tab_1_1 = time.time() 179 | if file_path: 180 | if exist_file: 181 | if path_n == 1: 182 | if checked: 183 | copy_excel_with_pandas(file_path) 184 | try: 185 | text = AI_run(query, model=selected_model1, API_key=API_key_1) 186 | resp = link_llm(text, file_path) 187 | st.write(text) 188 | if resp != text: 189 | st.write(resp) 190 | # 将生成的回答插入到历史记录的开头 191 | st.session_state.history.insert(0, text) 192 | except: 193 | print("程序出错。") 194 | st.error("AI执行出错") 195 | time_tab_1_2 = time.time() 196 | st.write("请求用时:", time_tab_1_2-time_tab_1_1, "秒") 197 | 198 | # 在侧边栏显示历史回答 199 | with st.sidebar: 200 | st.markdown("### 历史回答脚本") 201 | len_history = len(st.session_state.history) 202 | len_list = list(range(1, len_history+1)) 203 | len_list.reverse() 204 | for i, answer in enumerate(st.session_state.history): 205 | with st.expander(f"脚本 {len_list[i]}", expanded=False): 206 | st.write(answer) 207 | 208 | 209 | with tab2: 210 | uploaded_file = st.file_uploader("上传脚本txt文件:", type="txt") 211 | 212 | if uploaded_file: 213 | column1, column2 = st.columns([7, 2]) 214 | with column1: 215 | st.write("已接收文件:", uploaded_file.name) 216 | with column2: 217 | button_run1 = st.button("连续执行脚本") 218 | 219 | with st.expander(f"{uploaded_file.name}文件内容:"): 220 | lines = uploaded_file.read().decode("utf-8") 221 | st.write(str(lines)) 222 | 223 | if button_run1: 224 | if not file_path: 225 | st.error("请先输入文件路径") 226 | elif not exist_file: 227 | st.error("文件不存在,请重新输入文件路径") 228 | else: 229 | with st.spinner("正在执行脚本,请稍等..."): 230 | time_tab_2_1 = time.time() 231 | time.sleep(1) 232 | try: 233 | link_llm(lines, file_path) 234 | st.write("脚本执行完毕 ", time_c) 235 | except: 236 | st.error("脚本执行出错") 237 | time_tab_2_2 = time.time() 238 | st.write("用时", time_tab_2_2-time_tab_2_1, "秒") 239 | 240 | 241 | st.divider() 242 | with st.expander("当前历史脚本,顺序从下到上", expanded=True): 243 | st.write(st.session_state.history) 244 | button_load = st.button("导出到txt文件") 245 | if button_load: 246 | if st.session_state.history: 247 | # 创建一个副本,以防止修改原始列表 248 | history_list_copy = copy.deepcopy(st.session_state.history) 249 | # 反转列表,以便从上到下显示 250 | history_list_copy.reverse() 251 | 252 | txt_file_name = f"history_{time_c}.txt" 253 | save_list_to_txt(txt_file_name, history_list_copy) 254 | st.success(f"成功导出为 {txt_file_name} 文件") 255 | else: 256 | st.warning("没有历史记录") 257 | 258 | 259 | # 定义一个函数来尝试将列转换为 double 类型 260 | def try_convert_to_double(column): 261 | try: 262 | return pd.to_numeric(column) 263 | except (ValueError, TypeError): 264 | return column # 如果转换失败,返回原始列 265 | 266 | def link_llm2(text, df=None): 267 | """ 268 | 将字符串用JSON进行解析,并运行JSON中的函数 269 | :param text: 270 | :return: 271 | """ 272 | # 正则表达式 273 | pattern = r'\{[^{}]*\{.*?\}[^{}]*\}' 274 | pattern_python = r"(?<=```python\n)(.*?)(?=\n```)" 275 | 276 | # 使用正则表达式匹配 277 | match = re.findall(pattern, text) 278 | print(match) 279 | match_python = re.findall(pattern_python, text, re.DOTALL) 280 | print(match_python) 281 | 282 | if match or match_python: 283 | if match: 284 | for i_n in match: 285 | print(i_n) 286 | try: 287 | # 解析JSON数据 288 | json_data = json.loads(i_n) 289 | datas = json_data['def_name'] 290 | except: 291 | print("解析JSON出错") 292 | # 执行函数 293 | data = datas[0] 294 | try: 295 | print(data) 296 | exec(data) 297 | except: 298 | str_text = "不能执行此动作" 299 | print(str_text) 300 | return str_text 301 | else: 302 | for i_n in match_python: 303 | #print(i_n) 304 | try: 305 | # 检查代码安全性 306 | if not check_code_safety(i_n): 307 | print("代码包含不允许的库,终止执行。") 308 | return "代码包含不允许的库,终止执行。" 309 | 310 | # 尝试将每一列转换为 double 类型 311 | #df_2 = df.apply(try_convert_to_double) 312 | 313 | if df is not None: 314 | output = run_generated_code(i_n, df=df) 315 | return output 316 | except: 317 | print("程序出错。") 318 | return "程序出错。" 319 | else: 320 | return text 321 | 322 | # 数据分析Tab 323 | with tab3: 324 | output_filename = 'output_default.xlsx' 325 | col5_1, col5_2, col5_3 = st.columns([6, 8, 2]) 326 | with col5_1: 327 | # 定义主流大语言模型列表 328 | models2 = ["deepseek-v3-aliyun", "本地模型", "deepseek-r1-aliyun", "deepseek-v3-baiduyun", "deepseek-r1-baiduyun", "deepseek-v3-volcengine", "deepseek-r1-volcengine", "deepseek-chat", "deepseek-reasoner", "qwen-max-latest", "GLM-4-Flash", "GLM-4-Plus", "Doubao-1.5-pro-256k"] 329 | # 创建下拉列表,默认选择 DeepSeek 330 | selected_model2 = st.selectbox( 331 | "选择一个数据分析大语言模型", 332 | models2, 333 | index=models2.index("deepseek-v3-aliyun"), # 设置默认选项 334 | ) 335 | with col5_2: 336 | API_key_2 = st.text_input("输入数据分析大语言模型API密钥", type="password") 337 | with col5_3: 338 | st.markdown("[怎样获得API密钥](https://apifox.com/apidoc/shared-0fd7ea54-919e-4c93-b673-c60219bc82e0/doc-4739665)", ) 339 | 340 | data = st.file_uploader("上传你的Excel文件(xlsx格式和图片格式):", type=["xlsx", "png", "jpg", "jpeg"]) 341 | 342 | if data: 343 | if data.type in ["image/png", "image/jpg", "image/jpeg"]: 344 | with st.expander("上传的图片", expanded=True): 345 | # 显示上传的图片 346 | st.image(data, caption="上传的图片") 347 | else: 348 | st.session_state["df"] = pd.read_excel(data) 349 | df = pd.read_excel(data) 350 | #table_md = df.to_markdown(index=False) 351 | 352 | with st.expander("原始数据", expanded=True): 353 | st.dataframe(st.session_state["df"]) 354 | #st.write(st.session_state["df"].to_markdown()) 355 | 356 | query2 = st.text_area("请输入需求") 357 | 358 | if data: 359 | if data.type in ["image/png", "image/jpg", "image/jpeg"]: 360 | text_2 = query2 361 | else: 362 | text_2 = query2 + "数据如下" + st.session_state["df"].to_markdown() 363 | #text_2 = query2 + f"以下是数据表格:\n{table_md}" 364 | 365 | col2_1, col2_2 = st.columns([1, 2]) 366 | with col2_1: 367 | button2 = st.button("生成回答") 368 | 369 | if button2: 370 | if not data: 371 | st.error("请先上传文件") 372 | elif not query2: 373 | st.error("请输入指令或需求") 374 | elif not API_key_2: 375 | st.error("请输入API密钥") 376 | else: 377 | with st.spinner("AI思考中,请稍等..."): 378 | time_tab_3_1 = time.time() 379 | try: 380 | if data.type in ["image/png", "image/jpg", "image/jpeg"]: 381 | respose2 = llm_model2(text_2, selected_model2, API_key_2, data) 382 | else: 383 | respose2 = llm_model2(text_2, selected_model2, API_key_2) 384 | text_t = llm_text2(respose2) 385 | st.write(text_t) 386 | if data.type in ["image/png", "image/jpg", "image/jpeg"]: 387 | respose3 = link_llm2(text_t) 388 | else: 389 | respose3 = link_llm2(text_t, df=df) 390 | 391 | if respose3 != text_t: 392 | st.write(respose3) 393 | 394 | except SyntaxError as e: 395 | output_e = f"SyntaxError: {e}" 396 | print("程序出错。", output_e) 397 | st.error("程序出错。") 398 | except Exception as e: 399 | output_e = f"RuntimeError: {e}" 400 | print("程序出错。", output_e) 401 | st.error("程序出错。") 402 | time_tab_3_2 = time.time() 403 | st.write("请求用时:", time_tab_3_2-time_tab_3_1, "秒") 404 | 405 | 406 | -------------------------------------------------------------------------------- /xl_class.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import time 3 | from openpyxl import Workbook, load_workbook 4 | from openpyxl.styles import NamedStyle 5 | from datetime import datetime, timedelta 6 | import shutil 7 | import openpyxl 8 | import re 9 | import os 10 | from openpyxl.styles import Alignment 11 | from openpyxl.utils import get_column_letter, column_index_from_string 12 | 13 | 14 | # 获取当前日期年月日时分秒 15 | def get_current_date(): 16 | """ 17 | 获得当前时间 18 | """ 19 | current_time = time.localtime() 20 | year = current_time.tm_year 21 | month = current_time.tm_mon 22 | day = current_time.tm_mday 23 | hour = current_time.tm_hour 24 | minute = current_time.tm_min 25 | second = current_time.tm_sec 26 | return f"{year}_{month:02d}_{day:02d}_{hour:02d}_{minute:02d}_{second:02d}" 27 | 28 | # 复制文件 29 | def copy_excel_with_pandas(source_path, destination_path=None, time_c=False): 30 | """ 31 | 复制文件 32 | :param source_path: 33 | :param destination_path: 34 | :param time_c: 35 | :return: 36 | """ 37 | if destination_path is None: 38 | if time_c: 39 | destination_path = source_path[:-5] + "_copy" + str(get_current_date()) + '.xlsx' 40 | else: 41 | destination_path = source_path[:-5] + "_copy.xlsx" 42 | try: 43 | # 复制文件并重命名 44 | shutil.copy(source_path, destination_path) 45 | print(f"文件已复制: {destination_path}") 46 | #return destination_path 47 | except Exception as ex: 48 | print(f"复制文件时发生错误: {ex}") 49 | 50 | 51 | # 删除excel文件 52 | def delete_excel_file(file_path): 53 | """ 54 | 删除文件 55 | :param file_path: 56 | :return: 57 | """ 58 | try: 59 | # 使用os模块删除文件 60 | os.remove(file_path) 61 | print(f"文件已删除: {file_path}") 62 | except Exception as ex: 63 | print(f"删除文件时发生错误: {ex}") 64 | 65 | 66 | # 创建一个包含多个工作表的Excel文件, time_list对应初始年月日,结束年月日 67 | def create_excel_with_multiple_sheets(file_path, sheets_num=1, sheets_data=None, time_list=[], copy=False, split_y="_", split_m="_", split_d=""): 68 | """ 69 | 将当前文件修改为一个包含多个工作表的空文件,工作表名称可以是日期,月份,年份,也可以自定义, 70 | :param file_path: 71 | :param sheets_num:创建工作表数量,在time_list=[]时执行 72 | :param sheets_data: 73 | :param time_list:格式为[2024, 2, 28, 2024, 3, 4],2024年2月28到2024年3月4日 74 | :param copy:是否复制文件进行备份 75 | :param split_y: 76 | :param split_m: 77 | :param split_d: 78 | :return: 79 | """ 80 | if copy: 81 | copy_excel_with_pandas(source_path=file_path) 82 | # 创建一个新的工作簿对象 83 | wb = Workbook() 84 | # 移除默认创建的第一个工作表,因为我们将在后面添加自定义的工作表 85 | wb.remove(wb.active) 86 | try: 87 | # 如果sheets_data是一个字典,则将其工作表名称作为键,数据作为值 88 | if isinstance(sheets_data, dict): 89 | print("sheets_data is dict") 90 | for sheet_name, data in sheets_data.items(): 91 | # 为每个工作表名称创建一个新的工作表 92 | ws = wb.create_sheet(title=sheet_name) 93 | # 将数据逐行写入工作表 94 | for row in data: 95 | ws.append(row) 96 | 97 | # 如果sheets_data是一个列表,则将其工作表名称作为元素 98 | elif isinstance(sheets_data, list): 99 | for sheet_name in sheets_data: 100 | ws = wb.create_sheet(title=sheet_name) 101 | print(f"创建工作表: {sheet_name}") 102 | 103 | # 如果sheets_data为None,则创建指定年份的月份工作表 104 | elif sheets_data is None: 105 | if time_list==[]: 106 | sheets_name_list = list(range(sheets_num)) 107 | for sheet_name in sheets_name_list: 108 | ws = wb.create_sheet(title=str(sheet_name)) 109 | print(f"创建工作表: {sheet_name}") 110 | 111 | else: 112 | start_year, start_month, start_day, end_year, end_month, end_day = time_list 113 | 114 | if start_month == 0: 115 | if start_year > end_year: 116 | print("开始年份不能晚于结束年份") 117 | return 118 | 119 | current_year = start_year 120 | while current_year <= end_year: 121 | title_name = str(current_year)+split_y 122 | ws = wb.create_sheet(title=title_name) 123 | print("创建工作表: ", str(current_year)+split_y) 124 | current_year += 1 125 | 126 | elif start_day == 0: 127 | # 确保开始日期在结束日期之前或相等 128 | if start_year > end_year or (start_year == end_year and start_month > end_month): 129 | print("开始月份不能晚于结束月份") 130 | return 131 | 132 | current_year, current_month = start_year, start_month 133 | while (current_year < end_year) or (current_year == end_year and current_month <= end_month): 134 | title_name = str(current_year) + split_y + str(current_month) + split_m 135 | ws = wb.create_sheet(title=title_name) 136 | print("创建工作表: ", str(current_year) + split_y + str(current_month) + split_m) 137 | # 更新当前月份和年份 138 | if current_month == 12: 139 | current_month = 1 140 | current_year += 1 141 | else: 142 | current_month += 1 143 | 144 | else: 145 | if end_day == 0: 146 | end_day = start_day 147 | # 将日期元素转换为日期对象 148 | start_date = datetime(start_year, start_month, start_day) 149 | end_date = datetime(end_year, end_month, end_day) 150 | 151 | # 确保开始日期在结束日期之前 152 | if start_date > end_date: 153 | print("开始日期不能晚于结束日期") 154 | return 155 | 156 | current_date = start_date 157 | while current_date <= end_date: 158 | year, mouth, day = current_date.year, current_date.month, current_date.day 159 | title_name = str(year) + split_y + str(mouth) + split_m + str(day) + split_d 160 | print(title_name) 161 | ws = wb.create_sheet(title=title_name) 162 | print("创建工作表: ", str(year) + split_y + str(mouth) + split_m + str(day) + split_d) 163 | current_date += timedelta(days=1) 164 | 165 | # 如果sheets_data是一个元组,则将其工作表名称作为元素 166 | else: 167 | sheets_data = list(sheets_data) 168 | for sheet_name in sheets_data: 169 | ws = wb.create_sheet(title=str(sheet_name)) 170 | print(f"创建工作表: {sheet_name}") 171 | 172 | # 保存工作簿到指定路径 173 | wb.save(file_path) 174 | print(f"Excel文件已成功创建,包含多个工作表: {file_path}") 175 | 176 | except Exception as ex: 177 | print(f"创建Excel文件时发生错误: {ex}") 178 | return False 179 | 180 | 181 | # 将源Excel文件src_file_path的第一个工作表复制到目标Excel文件dest_file_path的所有工作表中。 182 | def copy_first_sheet_to_all_sheets(src_file_path, dest_file_path, sheet_i=0): 183 | """ 184 | 将源Excel文件src_file_path的第sheet_i工作表复制到目标Excel文件dest_file_path的所有工作表中,并保持格式不变。 185 | 参数: 186 | src_file_path (str): 源Excel文件路径。 187 | dest_file_path (str): 目标Excel文件路径。 188 | sheet_i (int): 要复制的源工作表的索引,默认为0(第一个工作表)。 189 | """ 190 | 191 | # 判断文件是否存在 192 | exist_src = os.path.exists(src_file_path) 193 | exist_dest = os.path.exists(dest_file_path) 194 | if exist_src and exist_dest: 195 | pass 196 | else: 197 | print("文件不存在") 198 | return 199 | 200 | # 加载源和目标工作簿 201 | src_wb = load_workbook(src_file_path) 202 | dest_wb = load_workbook(dest_file_path) 203 | 204 | # 获取源工作簿的第sheet_i工作表 205 | src_ws = src_wb.worksheets[sheet_i] 206 | 207 | try: 208 | for sheet in dest_wb.worksheets: 209 | # 清空目标工作表的内容 210 | sheet.delete_rows(1, sheet.max_row) 211 | 212 | # 复制源工作表的内容到目标工作表 213 | for row in src_ws.iter_rows(): 214 | for cell in row: 215 | new_cell = sheet.cell(row=cell.row, column=cell.column, value=cell.value) 216 | # 复制样式 217 | new_cell.font = cell.font.copy() 218 | new_cell.border = cell.border.copy() 219 | new_cell.fill = cell.fill.copy() 220 | new_cell.number_format = cell.number_format 221 | new_cell.protection = cell.protection.copy() 222 | new_cell.alignment = cell.alignment.copy() 223 | 224 | # 复制列宽 225 | for col in src_ws.columns: 226 | column_letter = get_column_letter(col[0].column) # 使用 get_column_letter 获取列字母 227 | sheet.column_dimensions[column_letter].width = src_ws.column_dimensions[column_letter].width 228 | 229 | # 复制行高 230 | for row in src_ws.iter_rows(): 231 | sheet.row_dimensions[row[0].row].height = src_ws.row_dimensions[row[0].row].height 232 | 233 | # 复制合并单元格 234 | for merged_cell_range in src_ws.merged_cells.ranges: 235 | sheet.merge_cells(str(merged_cell_range)) 236 | 237 | # 保存修改后的工作簿 238 | dest_wb.save(dest_file_path) 239 | print(f"成功将 {src_file_path} 的第 {sheet_i + 1} 个工作表复制到 {dest_file_path} 的所有工作表中,并保持格式不变") 240 | except Exception as ex: 241 | print(f"发生错误: {ex}") 242 | finally: 243 | # 关闭工作簿 244 | src_wb.close() 245 | dest_wb.close() 246 | 247 | 248 | def modify_sheet_cell_value(file_path, cell='K1', sheet_id=[0], new_value=None, time_list=[], split_y="年", split_m="月", split_d="日"): 249 | """ 250 | 修改指定文件中的指定表格的内容 251 | :param file_path: 文件路径 252 | :param cell: 指定的表格 253 | :param sheet_id: 指定工作表的ID,若为[0]则在所有工作表的相应表格添加,若为列表[1,2,3]则在指定的工作表中添加 254 | :param new_value: 加入的内容 255 | :param time_list: 时间,初始年月日,结束年月日 256 | :param split_y: 年份后的间隔 257 | :param split_m: 月份后的间隔 258 | :param split_d: 日期后的间隔 259 | :return: 260 | """ 261 | try: 262 | # 加载工作簿 263 | wb = load_workbook(filename=file_path) 264 | sheet_count = len(wb.worksheets) 265 | 266 | if new_value is not None and time_list==[]: 267 | if sheet_id==[0]: 268 | for sheet_i in range(sheet_count): 269 | ws = wb.worksheets[sheet_i] 270 | # 修改指定单元格的值 271 | ws[cell] = new_value 272 | print("写入: ", ws[cell]) 273 | else: 274 | for sheet_i in sheet_id: 275 | ws = wb.worksheets[sheet_i-1] 276 | # 修改指定单元格的值 277 | ws[cell] = new_value 278 | print("写入: ", ws[cell]) 279 | 280 | else: 281 | new_value = new_value or "" 282 | start_year, start_month, start_day, end_year, end_month, end_day = time_list 283 | 284 | if start_month == 0: 285 | if start_year > end_year: 286 | print("开始年份不能晚于结束年份") 287 | return 288 | 289 | current_year = start_year 290 | num = 0 291 | while current_year <= end_year: 292 | title_name = str(current_year) + split_y 293 | ws = wb.worksheets[num] 294 | ws[cell] = new_value + title_name 295 | print("写入: ", ws[cell]) 296 | current_year += 1 297 | num += 1 298 | if num > sheet_count-1: 299 | break 300 | 301 | elif start_day == 0: 302 | # 确保开始日期在结束日期之前或相等 303 | if start_year > end_year or (start_year == end_year and start_month > end_month): 304 | print("开始月份不能晚于结束月份") 305 | return 306 | 307 | current_year, current_month = start_year, start_month 308 | num = 0 309 | while (current_year < end_year) or (current_year == end_year and current_month <= end_month): 310 | title_name = str(current_year) + split_y + str(current_month) + split_m 311 | ws = wb.worksheets[num] 312 | ws[cell] = new_value + title_name 313 | print("写入: ", ws[cell]) 314 | # 更新当前月份和年份 315 | if current_month == 12: 316 | current_month = 1 317 | current_year += 1 318 | else: 319 | current_month += 1 320 | num += 1 321 | if num > sheet_count-1: 322 | break 323 | 324 | else: 325 | if end_day == 0: 326 | end_day = start_day 327 | # 将日期元素转换为日期对象 328 | start_date = datetime(start_year, start_month, start_day) 329 | end_date = datetime(end_year, end_month, end_day) 330 | 331 | # 确保开始日期在结束日期之前 332 | if start_date > end_date: 333 | print("开始日期不能晚于结束日期") 334 | return 335 | 336 | current_date = start_date 337 | num = 0 338 | while current_date <= end_date: 339 | year, mouth, day = current_date.year, current_date.month, current_date.day 340 | title_name = str(year) + split_y + str(mouth) + split_m + str(day) + split_d 341 | ws = wb.worksheets[num] 342 | ws[cell] = new_value + title_name 343 | print("写入: ", ws[cell]) 344 | current_date += timedelta(days=1) 345 | num += 1 346 | if num > sheet_count-1: 347 | break 348 | 349 | # 保存更改 350 | wb.save(file_path) 351 | print(f"成功修改 {file_path} 中第工作表的 {cell} 的值为: {new_value}") 352 | except Exception as ex: 353 | print(f"发生错误: {ex}") 354 | finally: 355 | # 关闭工作簿以释放资源 356 | wb.close() 357 | 358 | def copy_and_rename_excel_files(file_path, num_copies=1, time_list=[], split_y="_", split_m="_", split_d=""): 359 | """ 360 | 复制指定数量的Excel文件,并默认复制一个文件,可以自定义文件的数量和是否以时间为后缀 361 | 参数: 362 | file_path (str): 源Excel文件路径。 363 | num_copies (int): 要复制的文件数量,默认为1。 364 | """ 365 | try: 366 | # 确保源文件存在 367 | if not os.path.isfile(file_path): 368 | print(f"源文件 {file_path} 不存在") 369 | return 370 | 371 | # 获取源文件所在的目录和文件名(不带扩展名) 372 | source_dir = os.path.dirname(file_path) 373 | source_filename, ext = os.path.splitext(os.path.basename(file_path)) 374 | 375 | if time_list == []: 376 | for i in range(1, num_copies + 1): 377 | # 生成新的文件名 378 | new_filename = f"{source_filename}_{i}{ext}" 379 | # 构建新的文件路径 380 | new_file_path = os.path.join(source_dir, new_filename) 381 | 382 | # 复制文件并重命名 383 | shutil.copy(file_path, new_file_path) 384 | print(f"已复制并重命名为: {new_file_path}") 385 | 386 | else: 387 | start_year, start_month, start_day, end_year, end_month, end_day = time_list 388 | 389 | if start_month == 0: 390 | if start_year > end_year: 391 | print("开始年份不能晚于结束年份") 392 | return 393 | 394 | current_year = start_year 395 | while current_year <= end_year: 396 | title_name = str(current_year) + split_y 397 | new_filename = f"{source_filename}_{title_name}{ext}" 398 | # 构建新的文件路径 399 | new_file_path = os.path.join(source_dir, new_filename) 400 | # 复制文件并重命名 401 | shutil.copy(file_path, new_file_path) 402 | print(f"已复制并重命名为: {new_file_path}") 403 | current_year += 1 404 | 405 | elif start_day == 0: 406 | # 确保开始日期在结束日期之前或相等 407 | if start_year > end_year or (start_year == end_year and start_month > end_month): 408 | print("开始月份不能晚于结束月份") 409 | return 410 | 411 | current_year, current_month = start_year, start_month 412 | while (current_year < end_year) or (current_year == end_year and current_month <= end_month): 413 | title_name = str(current_year) + split_y + str(current_month) + split_m 414 | new_filename = f"{source_filename}_{title_name}{ext}" 415 | # 构建新的文件路径 416 | new_file_path = os.path.join(source_dir, new_filename) 417 | # 复制文件并重命名 418 | shutil.copy(file_path, new_file_path) 419 | print(f"已复制并重命名为: {new_file_path}") 420 | # 更新当前月份和年份 421 | if current_month == 12: 422 | current_month = 1 423 | current_year += 1 424 | else: 425 | current_month += 1 426 | 427 | else: 428 | if end_day == 0: 429 | end_day = start_day 430 | # 将日期元素转换为日期对象 431 | start_date = datetime(start_year, start_month, start_day) 432 | end_date = datetime(end_year, end_month, end_day) 433 | 434 | # 确保开始日期在结束日期之前 435 | if start_date > end_date: 436 | print("开始日期不能晚于结束日期") 437 | return 438 | 439 | current_date = start_date 440 | while current_date <= end_date: 441 | year, mouth, day = current_date.year, current_date.month, current_date.day 442 | title_name = str(year) + split_y + str(mouth) + split_m + str(day) + split_d 443 | new_filename = f"{source_filename}_{title_name}{ext}" 444 | # 构建新的文件路径 445 | new_file_path = os.path.join(source_dir, new_filename) 446 | # 复制文件并重命名 447 | shutil.copy(file_path, new_file_path) 448 | print(f"已复制并重命名为: {new_file_path}") 449 | current_date += timedelta(days=1) 450 | 451 | print("所有文件复制完成") 452 | 453 | except Exception as ex: 454 | print(f"发生错误: {ex}") 455 | 456 | 457 | def excel_cells_to_list(src_file_path, dest_file_path, cell_range, sheet=0, cell='E10'): 458 | """ 459 | 将Excel文件中指定工作表指定单元格范围的内容转换为列表,并将结果写入目标文件的指定单元格。 460 | 461 | 参数: 462 | src_file_path (str): 源Excel文件路径。 463 | dest_file_path (str): 目标Excel文件路径。 464 | cell_range (str): 单元格范围,例如 'A1:A5'。 465 | sheet (int or str): 工作表的索引或名称,默认为第一个工作表。 466 | cell (str): 目标文件中要修改的单元格地址,例如 'E10'。 467 | """ 468 | 469 | # 判断文件是否存在 470 | exist_src = os.path.exists(src_file_path) 471 | exist_dest = os.path.exists(dest_file_path) 472 | if exist_src and exist_dest: 473 | pass 474 | else: 475 | print("文件不存在") 476 | return 477 | 478 | # 加载源Excel文件 479 | workbook = openpyxl.load_workbook(src_file_path, data_only=True) 480 | 481 | # 选择工作表 482 | if isinstance(sheet, int): 483 | sheet = workbook.worksheets[sheet] # 通过索引选择工作表 484 | else: 485 | sheet = workbook[sheet] # 通过名称选择工作表 486 | 487 | # 初始化一个空列表来存储单元格内容 488 | cell_values = [] 489 | 490 | # 解析单元格范围 491 | start_cell, end_cell = cell_range.split(':') 492 | 493 | # 提取起始和结束的列字母和行号 494 | start_col = re.findall(r'[A-Za-z]+', start_cell)[0] 495 | start_row = int(re.findall(r'\d+', start_cell)[0]) 496 | end_col = re.findall(r'[A-Za-z]+', end_cell)[0] 497 | end_row = int(re.findall(r'\d+', end_cell)[0]) 498 | 499 | # 遍历指定范围的单元格 500 | for row in range(start_row, end_row + 1): 501 | cell_address = f'{start_col}{row}' # 构建单元格地址 502 | cell_value = sheet[cell_address].value # 获取单元格的值 503 | cell_values.append(cell_value) 504 | 505 | print("提取的单元格值:", cell_values) 506 | 507 | # 将提取的值写入目标文件的指定单元格 508 | for i, value in enumerate(cell_values): 509 | if value is not None: # 仅处理非空值 510 | print(f"正在写入值 '{value}' 到目标文件的单元格 {cell}") 511 | modify_sheet_cell_value( 512 | file_path=dest_file_path, 513 | cell=cell, 514 | sheet_id=[i + 1], # 假设 sheet_id 是目标工作表的索引 515 | new_value=str(value) 516 | ) 517 | 518 | # 保存并关闭工作簿 519 | workbook.close() 520 | 521 | 522 | def format_excel_cell(workbook, cell_address, sheet_i, alignment=None, width=None, height=None): 523 | """ 524 | 更改Excel文件中指定单元格的格式。 525 | 参数: 526 | file_path (str): Excel文件路径。 527 | cell_address (str): 单元格地址,例如 'A1'。 528 | sheet_i: 工作表序号 529 | alignment (str): 对齐方式,可选 'left', 'center', 'right'。 530 | width (float): 列宽。 531 | height (float): 行高。 532 | """ 533 | # 加载Excel文件 534 | #workbook = openpyxl.load_workbook(file_path) 535 | 536 | # 选择工作表 537 | sheet = workbook.worksheets[sheet_i] 538 | 539 | # 获取指定单元格 540 | cell = sheet[cell_address] 541 | 542 | # 设置对齐方式 543 | if alignment: 544 | if alignment == 'left': 545 | cell.alignment = Alignment(horizontal='left') 546 | elif alignment == 'center': 547 | cell.alignment = Alignment(horizontal='center') 548 | elif alignment == 'right': 549 | cell.alignment = Alignment(horizontal='right') 550 | 551 | # 设置列宽 552 | if width: 553 | column_letter = get_column_letter(cell.column) 554 | sheet.column_dimensions[column_letter].width = width 555 | 556 | # 设置行高 557 | if height: 558 | sheet.row_dimensions[cell.row].height = height 559 | 560 | print(f"单元格 {cell_address} 的格式已更新。") 561 | 562 | 563 | def format_excel_cell_range(file_path, cell_address, sheet_id=[0], alignment=None, width=None, height=None): 564 | """ 565 | 更改Excel文件中指定工作表的指定单元格的格式。 566 | 参数: 567 | file_path (str): Excel文件路径。 568 | cell_address (str): 单元格地址,例如 'A1'。 569 | sheet_id: 指定工作表的ID,若为[0]则在所有工作表的相应表格添加,若为列表[1,2,3]则在指定的工作表中添加 570 | alignment (str): 对齐方式,可选 'left', 'center', 'right'。 571 | width (float): 列宽。 572 | height (float): 行高。 573 | """ 574 | # 加载Excel文件 575 | workbook = openpyxl.load_workbook(file_path) 576 | 577 | # 计算工作表数量 578 | sheet_count = len(workbook.worksheets) 579 | print(sheet_count) 580 | 581 | if sheet_id == [0]: 582 | for sheet_i in range(sheet_count): 583 | format_excel_cell( 584 | workbook=workbook, 585 | sheet_i=sheet_i, 586 | cell_address=cell_address, 587 | alignment=alignment, 588 | width=width, 589 | height=height 590 | ) 591 | else: 592 | for sheet_i in sheet_id: 593 | if sheet_i > sheet_count: 594 | print(f"工作表 {sheet_i} 不存在。") 595 | continue 596 | format_excel_cell( 597 | workbook=workbook, 598 | sheet_i=sheet_i-1, 599 | cell_address=cell_address, 600 | alignment=alignment, 601 | width=width, 602 | height=height 603 | ) 604 | 605 | # 保存文件 606 | workbook.save(file_path) 607 | print(f"Excel文件 {file_path} 已保存。") 608 | # 保存并关闭工作簿 609 | workbook.close() 610 | 611 | 612 | def merge_excel_cells(file_path, start_cell, end_cell, sheet_id=[0]): 613 | """ 614 | 合并 Excel 文件中的指定单元格 615 | :param file_path: Excel 文件路径 616 | :param start_cell: 合并区域的起始单元格(例如 'A1') 617 | :param end_cell: 合并区域的结束单元格(例如 'B2') 618 | :param sheet_id: 指定工作表的ID,若为[0]则在所有工作表的相应表格添加,若为列表[1,2,3]则在指定的工作表中添加 619 | """ 620 | # 加载 Excel 文件 621 | workbook = openpyxl.load_workbook(file_path) 622 | 623 | # 计算工作表数量 624 | sheet_count = len(workbook.worksheets) 625 | print(sheet_count) 626 | 627 | if sheet_id == [0]: 628 | for sheet_i in range(sheet_count): 629 | # 选择工作表 630 | sheet = workbook.worksheets[sheet_i] 631 | # 合并单元格 632 | sheet.merge_cells(start_cell + ':' + end_cell) 633 | else: 634 | for sheet_i in sheet_id: 635 | if sheet_i > sheet_count: 636 | print(f"工作表 {sheet_i} 不存在。") 637 | continue 638 | # 选择工作表 639 | sheet = workbook.worksheets[sheet_i-1] 640 | # 合并单元格 641 | sheet.merge_cells(start_cell + ':' + end_cell) 642 | 643 | # 保存修改后的 Excel 文件 644 | workbook.save(file_path) 645 | print(f"单元格 {start_cell} 到 {end_cell} 已成功合并!") 646 | 647 | 648 | def process_path_or_filename(input_str): 649 | """ 650 | 如果输入的是一个文件名,则直接返回。 651 | 如果输入的是Windows路径,则转换为Python友好的路径格式。 652 | 然后对结果执行下一步操作。 653 | """ 654 | # 检查是否是简单的文件名(不包含任何路径分隔符) 655 | if os.sep not in input_str and '/' not in input_str and '\\' not in input_str: 656 | print("Detected simple filename.") 657 | else: 658 | # 转换Windows风格路径到Python友好的路径格式 659 | input_str = input_str.replace('\\', '/') 660 | print("Converted to Python-friendly path format.") 661 | return input_str 662 | 663 | def write_df_to_excel(output_filename, df): 664 | """ 665 | 将给定的DataFrame写入到指定的Excel文件中。 666 | 参数: 667 | df (pd.DataFrame): 要写入Excel文件的pandas DataFrame。 668 | output_filename (str): 输出的Excel文件名,包括路径。 669 | """ 670 | try: 671 | df.to_excel(output_filename, index=False) 672 | print(f"文件成功写入到文件: {output_filename}") 673 | except Exception as e: 674 | print(f"写入文件时出错: {e}") 675 | 676 | 677 | def process_excel_files(file_list, keyword=None, cell_position=None, operation='read', direction=None, num_of_cells=1, 678 | output_file='output_m.xlsx', extract_formula=False, transpose=False): 679 | """ 680 | 处理给定的Excel文件列表,查找包含特定关键字或指定位置的单元格,并对其周围的单元格或自身进行操作。 681 | :param file_list: 包含Excel文件路径的列表 682 | :param keyword: 查找的关键字(可选) 683 | :param cell_position: 单元格位置,如'A1'(可选) 684 | :param operation: 'read' 表示读取周围单元格,'modify' 表示修改当前单元格 685 | :param direction: 要提取的内容方向,可以是'up', 'down', 'left', 'right' 686 | :param num_of_cells: 提取多少个相邻单元格(仅当operation为'read'时有效) 687 | :param output_file: 输出结果的Excel文件名 688 | :param extract_formula: 是否提取单元格中的公式,默认为False 689 | :param transpose: 是否要将提取到的数据进行转置,默认为False 690 | """ 691 | all_results = [] 692 | all_data = [] 693 | 694 | for file in file_list: 695 | wb = load_workbook(filename=file, data_only=not extract_formula) # 如果不提取公式,则data_only=True 696 | 697 | for sheet_name in wb.sheetnames: 698 | ws = wb[sheet_name] 699 | 700 | if cell_position and not direction: # 当提供了单元格位置但未提供方向时 701 | col, row = column_index_from_string(cell_position[0]), int(cell_position[1:]) 702 | cell = ws.cell(row=row, column=col) 703 | 704 | result = {'File': file, 'Sheet': sheet_name, 'Keyword Position': cell_position} 705 | extracted_data = [cell.value if not extract_formula else cell.formula] 706 | all_data.append(extracted_data) 707 | all_results.append(result) 708 | 709 | elif cell_position and direction: # 当提供了单元格位置和方向时 710 | col, row = column_index_from_string(cell_position[0]), int(cell_position[1:]) 711 | cell = ws.cell(row=row, column=col) 712 | 713 | result = {'File': file, 'Sheet': sheet_name, 'Keyword Position': cell_position} 714 | extracted_data = [] 715 | if direction == 'right': 716 | for i in range(1, num_of_cells + 1): 717 | target_cell = ws.cell(row=row, column=col + i) 718 | if target_cell: 719 | extracted_data.append(target_cell.value if not extract_formula else target_cell.formula) 720 | elif direction == 'left': 721 | for i in range(num_of_cells): 722 | target_cell = ws.cell(row=row, column=col - i - 1) 723 | if target_cell: 724 | extracted_data.append(target_cell.value if not extract_formula else target_cell.formula) 725 | elif direction == 'up': 726 | for i in range(num_of_cells): 727 | target_cell = ws.cell(row=row - i - 1, column=col) 728 | if target_cell: 729 | extracted_data.append(target_cell.value if not extract_formula else target_cell.formula) 730 | elif direction == 'down': 731 | for i in range(1, num_of_cells + 1): 732 | target_cell = ws.cell(row=row + i, column=col) 733 | if target_cell: 734 | extracted_data.append(target_cell.value if not extract_formula else target_cell.formula) 735 | all_data.append(extracted_data) 736 | all_results.append(result) 737 | 738 | elif keyword: 739 | for row in ws.iter_rows(): 740 | for cell in row: 741 | if str(cell.value) == keyword: 742 | result = {'File': file, 'Sheet': sheet_name, 743 | 'Keyword Position': f'{cell.row}, {cell.column}'} 744 | 745 | if operation == 'read': 746 | extracted_data = [] 747 | if direction == 'right': 748 | for i in range(1, num_of_cells + 1): 749 | target_cell = ws.cell(row=cell.row, column=cell.column + i) 750 | if target_cell: 751 | extracted_data.append( 752 | target_cell.value if not extract_formula else target_cell.formula) 753 | elif direction == 'left': 754 | for i in range(num_of_cells): 755 | target_cell = ws.cell(row=cell.row, column=cell.column - i - 1) 756 | if target_cell: 757 | extracted_data.append( 758 | target_cell.value if not extract_formula else target_cell.formula) 759 | elif direction == 'up': 760 | for i in range(num_of_cells): 761 | target_cell = ws.cell(row=cell.row - i - 1, column=cell.column) 762 | if target_cell: 763 | extracted_data.append( 764 | target_cell.value if not extract_formula else target_cell.formula) 765 | elif direction == 'down': 766 | for i in range(1, num_of_cells + 1): 767 | target_cell = ws.cell(row=cell.row + i, column=cell.column) 768 | if target_cell: 769 | extracted_data.append( 770 | target_cell.value if not extract_formula else target_cell.formula) 771 | all_data.append(extracted_data) 772 | 773 | elif operation == 'modify': 774 | cell.value = "Modified" 775 | result['Modification'] = "Cell value modified." 776 | 777 | all_results.append(result) 778 | 779 | if operation == 'modify': 780 | wb.save(file) 781 | 782 | # 将所有结果保存到新的Excel文件中 783 | df_results = pd.DataFrame(all_results) 784 | df_data = pd.DataFrame(all_data) 785 | print(df_data) 786 | # 将列的索引改为从 1 开始 787 | df_data.columns = range(1, len(df_data.columns) + 1) 788 | 789 | # 将两个DataFrame合并 790 | df_results = pd.concat([df_results, df_data], axis=1) 791 | 792 | if transpose: 793 | df_results = df_results.T 794 | df_data = df_data.T 795 | 796 | print(df_results) 797 | print(df_data) 798 | 799 | # 尝试将 DataFrame 的内容转换为 double 类型 800 | try: 801 | # 使用 astype() 将 DataFrame 转换为 float64 类型 802 | df_data = df_data.astype(float) 803 | except ValueError as e: 804 | # 如果转换失败,捕获异常并提示 805 | print(f"转换失败: {e}") 806 | print("请检查数据中是否包含非数值型数据。") 807 | 808 | # 计算每一列的和 809 | sum_row = df_data.sum(numeric_only=True).rename('总和') 810 | 811 | df_data_describe = df_data.describe() 812 | # 将求和结果添加到描述性统计的最后一行 813 | df_data_describe = df_data_describe._append(sum_row) 814 | print(df_data_describe) 815 | 816 | time_1 = get_current_date() 817 | 818 | # 判断output_file是否存在 819 | if os.path.exists(output_file): 820 | print("文件已存在,将覆盖") 821 | made = 'a' 822 | else: 823 | print("文件不存在,将创建") 824 | made = 'w' 825 | 826 | with pd.ExcelWriter(output_file, engine='openpyxl', mode=made) as writer: 827 | df_results.to_excel(writer, index=False, sheet_name='相关数据' + str(time_1)) 828 | df_data_describe.to_excel(writer, index=True, sheet_name='汇总结果' + str(time_1)) 829 | 830 | 831 | if __name__ == '__main__': 832 | ''' 833 | current_time = get_current_date() 834 | 835 | src_file_path = '运营情况1.xlsx' # 源Excel文件路径 836 | dest_file_path = '入库单.xlsx' # 目标Excel文件路径 837 | cell_range = 'T5:T35' # 单元格范围 838 | 839 | copy_first_sheet_to_all_sheets(src_file_path='表1.xlsx', dest_file_path='入库单.xlsx') 840 | ''' 841 | copy_first_sheet_to_all_sheets(src_file_path='入库单\\入库单_2024年10月.xlsx', dest_file_path='入库单\\tt1.xlsx', sheet_i=2) --------------------------------------------------------------------------------