excel-tools/workbooks_merge.py
2021-11-29 18:19:58 +08:00

225 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- 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()