当前位置:网站首页>合并两个excel表格工具
合并两个excel表格工具
2022-08-02 22:43:00 【Andy Dennis】
前言
我们有时候需要合并两个相关的表格。例如表格1有“考生号, 姓名, 语文成绩”字段,表格2有“考生号, 姓名, 数学成绩”,那么此时就需要将两个表格合并为 “考生号, 姓名, 语文成绩, 数学成绩”。
示例
原来a, b表格
导出后:
代码
main.py
""" Author: Andy Dennis Date: 2022.7.29 Details: 其中ComBoPicker代码来自https://blog.csdn.net/weixin_45774074/article/details/123293411 """
from tkinter import *
import tkinter as tk
from ComBoPicker import Combopicker# 导入自定义下拉多选框
import tkinter.font as tf
from tkinter import filedialog, messagebox
import traceback
import pandas as pd
main_xlsx = ''
aux_xlsx = ''
join_df = None
# 选择主表
def select_main_xlsx():
global main_xlsx
global join_df
fp = filedialog.askopenfilename(title=u'请选择主表')
print(fp)
try:
if fp is not None and len(fp) > 0:
if fp.split('.')[-1] not in ['xls', 'xlsx']:
tk.messagebox.showerror('error', '请选择xls/xlsx的文件')
return
main_xlsx = fp
lb1['text'] = fp
join_df = None
print('main_xlsx: ', main_xlsx)
lb5['text'] = '暂无提示'
# tk.messagebox.showinfo('success', '选择原始目录成功')
else:
tk.messagebox.showerror('error', '请选择主表失败')
except:
tk.messagebox.showerror('error', '请选择主表失败')
print(traceback.format_exc())
# 选择副表
def select_aux_xlsx():
global aux_xlsx
global join_df
fp = filedialog.askopenfilename(title=u'请选择副表')
print(fp)
try:
if fp is not None and len(fp) > 0:
if fp.split('.')[-1] not in ['xls', 'xlsx']:
tk.messagebox.showerror('error', '请选择xls/xlsx的文件')
return
aux_xlsx = fp
lb2['text'] = fp
join_df = None
print('aux_xlsx: ', aux_xlsx)
lb5['text'] = '暂无提示'
# tk.messagebox.showinfo('success', '选择原始目录成功')
else:
tk.messagebox.showerror('error', '请选择副表失败')
except:
tk.messagebox.showerror('error', '请选择副表失败')
print(traceback.format_exc())
# 删除下拉框并新建一个
def destroy_and_new_one(values):
global COMBOPICKER1
global window
COMBOPICKER1.destroy()
COMBOPICKER1 = Combopicker(window, values=values, entrywidth=50)
COMBOPICKER1.place(x=100, y=160)
def join_xlsx():
global COMBOPICKER1
global join_df
if main_xlsx == '' or aux_xlsx == '':
tk.messagebox.showerror('error', '请先选择好主表和副表')
return
main_df = pd.read_excel(main_xlsx)
aux_df = pd.read_excel(aux_xlsx)
main_df_cols = main_df.columns
aux_df_cols = aux_df.columns
print('main_df_cols: ', main_df_cols)
print('aux_df_cols: ', aux_df_cols)
join_cols_set = set(main_df_cols) & set(aux_df_cols)
# 主键, 这里我直接让相同的字段名(列名)作为主键了
key_cols = [item for item in main_df_cols if item in join_cols_set]
print('key_cols: ', key_cols)
union_cols = [item for item in main_df_cols]
# 添加附表其他列
for item in aux_df_cols:
if item not in union_cols:
union_cols.append(item)
print('union_cols: ', union_cols)
join_df = pd.merge(main_df, aux_df, on=key_cols, how='left')
print('join_df:\n', join_df)
destroy_and_new_one(union_cols)
lb5['text'] = '暂无提示'
def save_a_excel(save_df):
fp = filedialog.asksaveasfilename(defaultextension='合并后的文件',filetypes=[("excel文件", ".xlsx")])
print(fp)
try:
if fp is not None and len(fp) > 0:
if fp.split('.')[-1] not in ['xls', 'xlsx']:
tk.messagebox.showerror('error', '请保存为xls/xlsx格式的文件')
return
save_df.to_excel(fp, index=False,header=True)
# tk.messagebox.showinfo('success', '选择原始目录成功')
else:
tk.messagebox.showerror('error', '保存合并的表格失败')
except:
tk.messagebox.showerror('error', '保存合并的表格失败')
print(traceback.format_exc())
def save_xlsx():
global join_df
if main_xlsx == '' or aux_xlsx == '':
tk.messagebox.showerror('error', '请先选择好主表和副表')
return
if join_df is None:
tk.messagebox.showerror('error', '请先点击合并表格按钮')
return
choose_items = COMBOPICKER1.get()
print(choose_items)
if len(choose_items) == 0:
tk.messagebox.showerror('error', '请选择要导出的列名')
return
else:
choose_items = choose_items.split(',')
union_cols = join_df.columns
save_cols = [item for item in union_cols if item in choose_items]
print('导出列: ', save_cols)
lb5['text'] = '导出列: ' + ' '.join(save_cols)
save_df = join_df[save_cols]
print(save_df)
save_a_excel(save_df)
if __name__ == "__main__":
window = Tk()
# 标题
window.title('合并XLSX小软件')
window.geometry("500x300")
# 不允许重新设置大小
window.resizable('true', 'true')
bt1 = tk.Button(window, text='选择主表', width=12, height=1, font=tf.Font(size=12), command=select_main_xlsx)
bt1.place(x=60, y=24)
bt2 = tk.Button(window, text='选择副表', width=12, height=1, font=tf.Font(size=12), command=select_aux_xlsx)
bt2.place(x=220, y=24)
lb1 = tk.Label(window, text='未选择主表', font=tf.Font(size=10))
lb1.place(x=10, y=68)
lb2 = tk.Label(window, text='未选择副表', font=tf.Font(size=10))
lb2.place(x=10, y=90)
bt3 = tk.Button(window, text='合并表格', width=18, height=1, font=tf.Font(size=12), command=join_xlsx)
bt3.place(x=120, y=120)
lb4 = tk.Label(window, text='导出字段:', font=tf.Font(size=10))
lb4.place(x=10, y=160)
COMBOPICKER1 = Combopicker(window, values=['请读取表格', '读取表格'], entrywidth=50)
# COMBOPICKER1.pack(anchor="w")
COMBOPICKER1.place(x=100, y=160)
bt4 = tk.Button(window, text='导出合并后的表格', width=18, height=1, font=tf.Font(size=12), command=save_xlsx)
bt4.place(x=120, y=200)
lb5 = tk.Label(window, text='暂无提示', font=tf.Font(size=10))
lb5.place(x=10, y=240)
window.mainloop()
ComBoPicker.py
''' 引用自https://blog.csdn.net/weixin_45774074/article/details/123293411 自定义多选下拉列表 '''
import tkinter.font as tkFont
import tkinter.ttk as ttk
from tkinter import *
class Picker(ttk.Frame):
def __init__(self, master=None,activebackground='#b1dcfb',values=[],entry_wid=None,activeforeground='black', selectbackground='#003eff', selectforeground='white', command=None, borderwidth=1, relief="solid"):
self._selected_item = None
self._values = values
self._entry_wid = entry_wid
self._sel_bg = selectbackground
self._sel_fg = selectforeground
self._act_bg = activebackground
self._act_fg = activeforeground
self._command = command
ttk.Frame.__init__(self, master, borderwidth=borderwidth, relief=relief)
self.bind("<FocusIn>", lambda event:self.event_generate('<<PickerFocusIn>>'))
self.bind("<FocusOut>", lambda event:self.event_generate('<<PickerFocusOut>>'))
self._font = tkFont.Font()
self.dict_checkbutton = {
}
self.dict_checkbutton_var = {
}
self.dict_intvar_item = {
}
for index,item in enumerate(self._values):
self.dict_intvar_item[item] = IntVar()
self.dict_checkbutton[item] = ttk.Checkbutton(self, text = item, variable=self.dict_intvar_item[item],command=lambda ITEM = item:self._command(ITEM))
self.dict_checkbutton[item].grid(row=index, column=0, sticky=NSEW)
self.dict_intvar_item[item].set(0)
class Combopicker(ttk.Entry, Picker):
def __init__(self, master, values= [] ,entryvar=None, entrywidth=None, entrystyle=None, onselect=None,activebackground='#b1dcfb', activeforeground='black', selectbackground='#003eff', selectforeground='white', borderwidth=1, relief="solid"):
self.values=values
if entryvar is not None:
self.entry_var = entryvar
else:
self.entry_var = StringVar()
entry_config = {
}
if entrywidth is not None:
entry_config["width"] = entrywidth
if entrystyle is not None:
entry_config["style"] = entrystyle
ttk.Entry.__init__(self, master, textvariable=self.entry_var, **entry_config, state = "readonly")
self._is_menuoptions_visible = False
self.picker_frame = Picker(self.winfo_toplevel(), values=values,entry_wid = self.entry_var,activebackground=activebackground, activeforeground=activeforeground, selectbackground=selectbackground, selectforeground=selectforeground, command=self._on_selected_check)
self.bind_all("<1>", self._on_click, "+")
self.bind("<Escape>", lambda event: self.hide_picker())
@property
def current_value(self):
try:
value = self.entry_var.get()
return value
except ValueError:
return None
@current_value.setter
def current_value(self, INDEX):
self.entry_var.set(self.values.index(INDEX))
def _on_selected_check(self, SELECTED):
value = []
if self.entry_var.get() != "" and self.entry_var.get() != None:
temp_value = self.entry_var.get()
value = temp_value.split(",")
if str(SELECTED) in value:
value.remove(str(SELECTED))
else:
value.append(str(SELECTED))
value.sort()
temp_value = ""
for index,item in enumerate(value):
if item!= "":
if index != 0:
temp_value += ","
temp_value += str(item)
self.entry_var.set(temp_value)
def _on_click(self, event):
str_widget = str(event.widget)
if str_widget == str(self):
if not self._is_menuoptions_visible:
self.show_picker()
else:
if not str_widget.startswith(str(self.picker_frame)) and self._is_menuoptions_visible:
self.hide_picker()
def show_picker(self):
if not self._is_menuoptions_visible:
self.picker_frame.place(in_=self, relx=0, rely=1, relwidth=1 )
self.picker_frame.lift()
self._is_menuoptions_visible = True
def hide_picker(self):
if self._is_menuoptions_visible:
self.picker_frame.place_forget()
self._is_menuoptions_visible = False
边栏推荐
- MySql查询某个时间段内的数据(前一周、前三个月、前一年等)
- threejs 动态调整相机位置,使相机正好能看到对象
- JS Date 时间戳 getTune data.parse 倒计时小程序
- Technology Sharing | How to do assertion verification for xml format in interface automation testing?
- centos7安装mysql8
- Yocto系列讲解[实战篇]85 - 制作ubi镜像和自动挂载ubifs文件系统
- 一个很少见但很有用的SQL功能
- 第十章 时序与延迟
- No-code development platform form styling steps introductory course
- 你离「TDengine 开发者大会」只差一条 SQL 语句!
猜你喜欢
随机推荐
HCIP(17)
No-code development platform form styling steps introductory course
The CTF command execution subject their thinking
基于两级分解和长短时记忆网络的短期风速多步组合预测模型
Technology Sharing | How to do assertion verification for xml format in interface automation testing?
技术分享 | 接口自动化测试中如何对xml 格式做断言验证?
GameStop NFT 市场分析
MySQL 与InnoDB 下的锁做朋友 (四)行锁/记录锁
CWE4.8:2022年危害最大的25种软件安全问题
用大白话解释“什么是ERP?” 看完这篇就全明白了
Week 7 - Distributional Representations
Image recognition from zero to write DNF script key points
Yocto系列讲解[实战篇]85 - 制作ubi镜像和自动挂载ubifs文件系统
在软件测试行业近20年的我,再来和大家谈谈今日的软件测试
RuoYi-App Startup Tutorial
浅读一下dotenv的主干逻辑的源码
微信小程序(一)
虚拟内存 virualmemory
The only way to go from a monthly salary of 10k to 30k: automated testing
Cholesterol-PEG-Acid,胆固醇-聚乙二醇-羧基保持在干燥、低温环境下