# -*- coding:utf-8 -*- ################################ # excel 表格分级下沉填充 # 任意一个 1 级列的值发生变化,则 2级列停止下沉, # 同理 任意一个 2级列的值发生了变化时, 3级列停止下沉,以此类推 # # # ################################ import os import sys import xlrd import xlwt from xlutils.copy import copy import tkinter as tk from tkinter import filedialog, dialog import tkinter.messagebox from tkinter import ttk class MergeWindows(object): """ 配置项 key """ INCLUDE_ROW_HEAD = 'include_row_head' INCLUDE_ROW_TAIL = 'include_row_tail' INCLUDE_ROW_NAME = 'include_row_name' INCLUDE_COL_HEAD = 'include_col_head' INCLUDE_COL_TAIL = 'include_col_tail' INCLUDE_COL_NAME = 'include_col_name' EXCLUDE_ROW_HEAD = 'exclude_row_head' EXCLUDE_ROW_TAIL = 'exclude_row_tail' EXCLUDE_ROW_NAME = 'exclude_row_name' EXCLUDE_COL_HEAD = 'exclude_col_head' EXCLUDE_COL_TAIL = 'exclude_col_tail' EXCLUDE_COL_NAME = 'exclude_col_name' def __init__(self, master: tk.Frame = None): super().__init__() self.__master: tk.Frame = master self.__conf_type: tk.IntVar = tk.IntVar() # 配置方式 self.__conf_type.set(0) self.__f_auto_conf: tk.Frame = tk.Frame() self.__f_handle_conf: tk.Frame = tk.Frame() self.__conf_text_widgets: dict[str, tk.Text] = {} self.__input_files_list: list[str] = [] self.__output_file: str = None # 显示变量 self.__select_files_mess: tk.StringVar = tk.StringVar() self.__select_files_mess.set('请选择要合并的文件') self.__output_file_mess: tk.StringVar = tk.StringVar() self.__output_file_mess.set('选择输出文件') def __conf_type_selection(self): conf_type = self.__conf_type.get() if conf_type == 0: self.__f_handle_conf.grid_remove() self.__f_auto_conf.grid() elif conf_type == 1: self.__f_auto_conf.grid_remove() self.__f_handle_conf.grid() def __select_files(self): file_list = filedialog.askopenfilenames(title=u'选择文件', initialdir=(os.path.expanduser('~/')), filetypes=[('Excel xls', '*.xls')]) if len(file_list) != 0: self.__select_files_mess.set('选中' + str(len(file_list)) + '个文件') else: self.__select_files_mess.set('请选择要合并的文件') self.__input_files_list = file_list def __save_file_path(self): output_file_path = filedialog.asksaveasfilename(title=u'保存文件') if len(output_file_path) > 0: if (not str(output_file_path).endswith('.xls')) & (not str(output_file_path).endswith('.xlsx')): output_file_path = output_file_path + '.xls' self.__output_file = output_file_path self.__output_file_mess.set(os.path.split(output_file_path)[1]) def __clear_conf(self): for w_key in self.__conf_text_widgets.keys(): self.__conf_text_widgets.get(w_key).delete("0.0", "end") self.__input_files_list = [] self.__output_file = None self.__select_files_mess.set('请选择要合并的文件') self.__output_file_mess.set('选择输出文件') self.__conf_type.set(0) def __start(self): if self.__conf_type == 0: pass elif self.__conf_type == 1: pass def create_windows(self): self.__widget_row_col_configure(self.__master, {2: 1}, {0: 1}) """ 顶端功能按钮 """ f_info = tk.Frame(self.__master, height=50, bd=1, bg='gainsboro') f_info.grid(row=0, column=0, padx=5, pady=5, ipadx=2, ipady=2, sticky=tk.NSEW) """ 文件选择栏 """ f_files = tk.Frame(self.__master, height=100, bd=1, bg='gainsboro') f_files.grid(row=1, column=0, padx=5, pady=5, sticky=tk.EW) """ 配置栏 """ f_config = tk.Frame(self.__master, bd=1, bg='gainsboro') f_config.grid(row=2, column=0, padx=5, pady=5, sticky=tk.NSEW) f_config.rowconfigure(1, weight=1) f_config.columnconfigure(0, weight=1) """ 顶端功能按钮控件布局 """ f_info.columnconfigure(0, weight=1) l_massage = ttk.Label(f_info, text='请选择文件') l_massage.grid(row=0, column=0, sticky=tk.EW) b_start = ttk.Button(f_info, text="开始") b_start.grid(row=0, column=1, padx=5, sticky=tk.E) b_cancel = ttk.Button(f_info, text="取消") b_cancel.grid(row=0, column=2, padx=5, sticky=tk.E) b_reset = ttk.Button(f_info, text="重置", command=self.__clear_conf) b_reset.grid(row=0, column=3, padx=5, sticky=tk.E) b_help = ttk.Button(f_info, text="帮助") b_help.grid(row=0, column=4, padx=5, sticky=tk.E) """ 文件选择栏控件布局 """ self.__widget_row_col_configure(f_files, {}, {0: 1, 1: 1}) f_input = tk.Frame(f_files, bg='gainsboro') f_input.grid(row=0, column=0, sticky=tk.NSEW) f_output = tk.Frame(f_files, bg='gainsboro') f_output.grid(row=0, column=1, sticky=tk.NSEW) b_in_select = ttk.Button(f_input, text="选择要合并的文件", command=self.__select_files) b_in_select.grid(row=0, column=0) l_in_select = ttk.Label(f_input, textvariable=self.__select_files_mess) l_in_select.grid(row=1, column=0) b_out_select = ttk.Button(f_output, text="选择输出文件", command=self.__save_file_path) b_out_select.grid(row=0, column=0) l_out_select = ttk.Label(f_output, textvariable=self.__output_file_mess) l_out_select.grid(row=1, column=0) """ 配置规则控件布局 """ self.__f_auto_conf = tk.Frame(f_config, bd=1, bg='silver') self.__f_auto_conf.grid(row=1, column=0, sticky=tk.NSEW) self.__f_handle_conf = tk.Frame(f_config, bd=1, bg='silver') self.__f_handle_conf.grid(row=1, column=0, padx=5, pady=5, sticky=tk.NSEW) self.__widget_row_col_configure(self.__f_handle_conf, {0: 1}, {0: 1, 1: 1}) self.__f_handle_conf.grid_remove() f_config_radio = tk.Frame(f_config, bd=1, height=50, bg='silver') f_config_radio.grid(row=0, column=0, padx=5, pady=5, sticky=tk.NW) r_auto = ttk.Radiobutton(f_config_radio, variable=self.__conf_type, text='自动合并', value=0, command=self.__conf_type_selection) r_auto.grid(row=0, column=0, sticky=tk.W) r_handle = ttk.Radiobutton(f_config_radio, variable=self.__conf_type, text='手动配置', value=1, command=self.__conf_type_selection) r_handle.grid(row=0, column=1, sticky=tk.W) t_auto_info = tk.Label(self.__f_auto_conf, bg='silver', text='自动判断列名并合并相同列数据') t_auto_info.grid(row=0, column=0, sticky=tk.NSEW) # 手动配置框 f_include_conf = tk.LabelFrame(self.__f_handle_conf, text='包含', padx=5, pady=5, bg='silver') f_include_conf.grid(row=0, column=0, padx=3, pady=3, sticky=tk.NSEW) self.__widget_row_col_configure(f_include_conf, {2: 1, 4: 1, 6: 1, 10: 1, 12: 1, 14: 1}, {0: 1}) l_include_0 = tk.Label(f_include_conf, text='行设置:(不填写代表包含全部行)', bg='silver') l_include_0.grid(row=0, column=0, columnspan=2, ) self.__create_text(f_include_conf, self.INCLUDE_ROW_HEAD, '正数第N行', 1, 0) self.__create_text(f_include_conf, self.INCLUDE_ROW_TAIL, '倒数第N行', 3, 0) self.__create_text(f_include_conf, self.INCLUDE_ROW_NAME, '行名(取第一列数据作为行名)', 5, 0) sep_0 = ttk.Separator(f_include_conf, orient='horizontal') sep_0.grid(row=7, column=0, columnspan=2, padx=5, pady=5, sticky=tk.EW) l_include_3 = tk.Label(f_include_conf, text='列设置:(不填写代表包含全部列)', bg='silver') l_include_3.grid(row=8, column=0, columnspan=2) self.__create_text(f_include_conf, self.INCLUDE_COL_HEAD, '正数第N列', 9, 0) self.__create_text(f_include_conf, self.INCLUDE_COL_TAIL, '倒数第N列', 11, 0) self.__create_text(f_include_conf, self.INCLUDE_COL_NAME, '列名(取第一行数据作为列名)', 13, 0) f_exclude_conf = tk.LabelFrame(self.__f_handle_conf, text='不包含', padx=5, pady=5, bg='silver') f_exclude_conf.grid(row=0, column=1, padx=3, pady=3, sticky=tk.NSEW) self.__widget_row_col_configure(f_exclude_conf, {2: 1, 4: 1, 6: 1, 10: 1, 12: 1, 14: 1}, {0: 1}) l_exclude_0 = tk.Label(f_exclude_conf, text='行设置:(不填写代表没有需要排除的行)', bg='silver') l_exclude_0.grid(row=0, column=0, columnspan=2) self.__create_text(f_exclude_conf, self.EXCLUDE_ROW_HEAD, '正数第N行', 1, 0) self.__create_text(f_exclude_conf, self.EXCLUDE_ROW_TAIL, '倒数第N行', 3, 0) self.__create_text(f_exclude_conf, self.EXCLUDE_ROW_NAME, '行名(取第一列数据作为行名)', 5, 0) sep_1 = ttk.Separator(f_exclude_conf, orient='horizontal') sep_1.grid(row=7, column=0, columnspan=2, padx=5, pady=5, sticky=tk.EW) l_exclude_3 = tk.Label(f_exclude_conf, text='列设置:(不填写代表没有需要排除的列)', bg='silver') l_exclude_3.grid(row=8, column=0, columnspan=2) self.__create_text(f_exclude_conf, self.EXCLUDE_COL_HEAD, '正数第N列', 9, 0) self.__create_text(f_exclude_conf, self.EXCLUDE_COL_TAIL, '倒数第N列', 11, 0) self.__create_text(f_exclude_conf, self.EXCLUDE_COL_NAME, '列名(取第一行数据作为列名)', 13, 0) def __widget_row_col_configure(self, root: tk.Widget, rows: dict[int, int], columns: dict[int, int]): for row in rows.keys(): root.rowconfigure(row, weight=rows[row]) for col in columns.keys(): root.columnconfigure(col, weight=columns[col]) def __create_text(self, root: tk.Widget, name: str, label_text: str, r: int, c: int): label = tk.Label(root, text=label_text, bg='silver') label.grid(row=r, column=c, columnspan=2, sticky=tk.W) scroll_y = ttk.Scrollbar(root, orient=tk.VERTICAL) text = tk.Text(root, yscrollcommand=scroll_y.set, undo=True, wrap=tk.NONE) text.grid(row=r + 1, column=c, sticky=tk.NSEW) scroll_y.grid(row=r + 1, column=c + 1, ) scroll_y.config(command=text.yview) self.__conf_text_widgets[name] = text def main(): root = tk.Tk() root.title('Excel 文件合并工具') root.geometry('720x640') root.minsize(720, 640) root.rowconfigure(0, weight=1) root.columnconfigure(0, weight=1) master_frame = tk.Frame(root) master_frame.grid(row=0, column=0, padx=5, pady=5, sticky=tk.NSEW) w = MergeWindows(master_frame) w.create_windows() root.mainloop() if __name__ == '__main__': main()