225 lines
11 KiB
Python
225 lines
11 KiB
Python
# -*- 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()
|