Skip to content

Commit 98619f3

Browse files
author
FIo-dev
authored
Add files via upload
1 parent bb74bef commit 98619f3

File tree

10 files changed

+307
-0
lines changed

10 files changed

+307
-0
lines changed

Images/Pic 1.png

3.08 KB
Loading

Images/Pic 2.png

3.27 KB
Loading

Images/Tips.png

2.56 KB
Loading

Images/add_elem.png

474 Bytes
Loading

Images/base_ico.png

1.25 KB
Loading

Images/folder_pic.png

395 Bytes
Loading

Images/play_icon.png

1.04 KB
Loading

Images/pymerger.ico

3.46 KB
Binary file not shown.

Images/recycle_bin.png

537 Bytes
Loading

Py-merger-GUI.py

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
from tkinter import*
2+
from tkinter.ttk import Separator, Style
3+
from os import startfile
4+
from os.path import basename
5+
from openpyxl import load_workbook, Workbook
6+
from openpyxl.styles import Font
7+
from tkinter.filedialog import askopenfiles, asksaveasfilename
8+
from ctypes import windll
9+
from threading import Thread
10+
from time import sleep
11+
12+
windll.shcore.SetProcessDpiAwareness(1)
13+
win = Tk()
14+
win.title('Py-Merger GUI by Flo')
15+
win.iconbitmap('Images/Py-Merger/pymerger.ico')
16+
win.geometry(f'750x500+{round(win.winfo_screenwidth()/4)}+{round(win.winfo_screenheight()/4)}')
17+
win.minsize(650, 458)
18+
19+
docs = {'btn_merge': 'Merge selected sheets.', 'btn_import': 'Import Excel Files.', 'btn_bin':'Delete selected items.', 'btn_add':'Add to merge list.', 'btn_base':'Set sheet as base.', 'btn_play':'Start selected workbook.', 'btn_find':'Locate in explorer.'}
20+
workbooks = {}
21+
worksheets = {}
22+
wb_path = {}
23+
selection_keys = []
24+
base = 0
25+
26+
"""
27+
def manage_data():
28+
while 1:
29+
wb = list(workbooks.keys())
30+
ws = list(worksheets.keys())
31+
upt = selection_keys
32+
bs = base
33+
sleep(1)
34+
if wb != list(workbooks.keys()) or ws != list(worksheets.keys()) or upt != selection_keys or bs != base:
35+
print(f'Workbooks: {list(workbooks.keys())}')
36+
print(f'Worksheets: {list(worksheets.keys())}')
37+
print(f'Updates: {selection_keys}')
38+
print(f'Base: {base}\n')
39+
40+
Thread(target=manage_data, daemon=True).start()
41+
"""
42+
43+
def start_wb(*args):
44+
active = workbooks_list.curselection()
45+
if active != ():
46+
fn = wb_path[workbooks_list.get(active[0])]
47+
if args == ():
48+
startfile(fn)
49+
else:
50+
startfile(fn.replace(basename(fn), ''))
51+
52+
def import_wb(*args):
53+
global base
54+
if args != ():
55+
filenames = list(args)
56+
else:
57+
filenames = [elem.name for elem in askopenfiles(filetypes=(("Excel File","*.xlsx"),))]
58+
for filename in filenames:
59+
alias_name = basename(filename)
60+
wb_path[alias_name] = filename
61+
workbooks[alias_name] = load_workbook(filename) # workbooks = {'workbook_name':Workbook}
62+
if not alias_name in workbooks_list.get('0', 'end'):
63+
workbooks_list.insert('end', alias_name)
64+
for workbook_name in list(workbooks.keys()):
65+
x = 0
66+
for sheet in enumerate(workbooks[workbook_name].sheetnames):
67+
alias_name = f'{sheet[1]} ({workbook_name})'
68+
worksheets[alias_name] = workbooks[workbook_name].worksheets[x] # worksheets = {'worksheet_name-workbook_name':worksheet}
69+
if not alias_name in worksheets_list.get('0', 'end'):
70+
worksheets_list.insert('end', alias_name)
71+
if not base:
72+
base = alias_name
73+
worksheets_list.itemconfig(0, bg='#3498db', selectbackground='#2980b9')
74+
x += 1
75+
76+
def delete_item():
77+
global base
78+
global selection_keys
79+
wb_idx = workbooks_list.curselection()
80+
ws_idx = worksheets_list.curselection()
81+
for wb in wb_idx:
82+
temp = [elem if elem.endswith(workbooks_list.get(wb)+')') else 0 for elem in worksheets_list.get('0', 'end')]
83+
del workbooks[workbooks_list.get(wb)]
84+
workbooks_list.delete(wb)
85+
for elem in temp: # Pour toutes les feuilles associées
86+
if elem != 0:
87+
if elem in selection_keys:
88+
selection_keys.remove(elem)
89+
del worksheets[elem]
90+
worksheets_list.delete(worksheets_list.get('0', 'end').index(elem))
91+
for ws in ws_idx:
92+
item = worksheets_list.get(ws)
93+
if item in selection_keys:
94+
selection_keys.remove(item)
95+
del worksheets[item]
96+
worksheets_list.delete(ws)
97+
lb_ws = worksheets_list.get('0','end')
98+
99+
if not base in lb_ws: # Base supprimée
100+
if len(lb_ws)>0:
101+
base = lb_ws[0]
102+
worksheets_list.itemconfig(0, bg='#3498db', selectbackground='#2980b9')
103+
if base in selection_keys:
104+
selection_keys.remove(base)
105+
else:
106+
base = 0
107+
108+
def add_selection():
109+
global selection_keys
110+
idx = worksheets_list.curselection()
111+
if idx != ():
112+
value = worksheets_list.get(idx)
113+
if value == base:
114+
pass
115+
#print("You can't set base as update of himself.")
116+
elif value in selection_keys:
117+
selection_keys.remove(value)
118+
worksheets_list.itemconfig(idx, bg='SystemButtonFace', selectbackground='#D3D3D3')
119+
else:
120+
selection_keys.append(value)
121+
worksheets_list.itemconfig(idx, bg='#2ecc71', selectbackground='#27ae60')
122+
#print(selection_keys)
123+
else:
124+
pass
125+
#print('Select at least one sheet.')
126+
127+
def set_base():
128+
global base
129+
global selection_keys
130+
idx = worksheets_list.curselection()
131+
if idx == ():
132+
pass
133+
#print('Select at least one sheet.')
134+
else:
135+
idx = idx[0]
136+
if base in worksheets_list.get('0', 'end'):
137+
temp_idx = worksheets_list.get('0', 'end').index(base)
138+
worksheets_list.itemconfig(temp_idx, bg='SystemButtonFace', selectbackground='#D3D3D3')
139+
base = worksheets_list.get(idx)
140+
worksheets_list.itemconfig(idx, bg='#3498db', selectbackground='#2980b9')
141+
if base in selection_keys:
142+
selection_keys.remove(base)
143+
144+
def merge_internal_func(old, upt, new_ws):
145+
alphabet = [chr(k) for k in range(65, 91)]
146+
reorder_rows = lambda sheet: {elem[0]:list(elem[1:]) for elem in list(sheet.values)[1:]} # {A1:[B1, C1...], A2:[B2, C2...]...}
147+
reorder_col_names = lambda sheet: {list(sheet.values)[0][k+1]:k for k in range(len(list(sheet.values)[0])-1)}
148+
sheet_origin = lambda sheet: list(sheet.values)[0][0]
149+
old_rows = reorder_rows(old)
150+
old_titles = reorder_col_names(old)
151+
upt_rows = reorder_rows(upt)
152+
upt_titles = reorder_col_names(upt)
153+
new_titles = list(old_titles.keys()) + list(set(upt_titles.keys()) - set(old_titles.keys()))
154+
new_titles = {new_titles[k]:k for k in range(len(new_titles))} # anciens titres + les nouveaux
155+
for elem in old_rows:
156+
while len(old_rows[elem])<len(new_titles):
157+
old_rows[elem].append(None)
158+
for elem in upt_rows:
159+
if elem in old_rows:
160+
for title in upt_titles:
161+
if isinstance(old_rows[elem][new_titles[title]], int):
162+
if old_rows[elem][new_titles[title]] > upt_rows[elem][upt_titles[title]]:
163+
old_rows[elem][new_titles[title]] = (upt_rows[elem][upt_titles[title]], 0)
164+
elif old_rows[elem][new_titles[title]] < upt_rows[elem][upt_titles[title]]:
165+
old_rows[elem][new_titles[title]] = (upt_rows[elem][upt_titles[title]], 1)
166+
else:
167+
old_rows[elem][new_titles[title]] = upt_rows[elem][upt_titles[title]]
168+
else:
169+
old_rows[elem][new_titles[title]] = upt_rows[elem][upt_titles[title]]
170+
else:
171+
old_rows[elem] = [None for k in range(len(new_titles))]
172+
for title in upt_titles:
173+
old_rows[elem][new_titles[title]] = upt_rows[elem][upt_titles[title]]
174+
# On écrit brut data 0 (Operationnel)
175+
for elem in enumerate([sheet_origin(old)] + list(new_titles.keys())): # Ligne des titres
176+
index = alphabet[elem[0]] + '1'
177+
new_ws[index] = elem[1]
178+
new_ws[index].font = Font(bold=True)
179+
for idx, key in enumerate(old_rows, 2):
180+
index = 'A' + str(idx)
181+
new_ws[index] = key
182+
for i in range(len(old_rows[key])):
183+
index = alphabet[i+1] + str(idx)
184+
if isinstance(old_rows[key][i], tuple):
185+
col = old_rows[key][i][1]
186+
new_ws[index] = old_rows[key][i][0]
187+
new_ws[index].font = Font(color="2ecc71" if col else 'e74c3c')
188+
else:
189+
new_ws[index] = old_rows[key][i]
190+
191+
def merge_action():
192+
if not base or len(selection_keys)==0:
193+
return ''
194+
new_wb = Workbook()
195+
new_ws = new_wb.active
196+
new_ws.title= 'py_merged'
197+
base_ws = worksheets[base]
198+
upt_ws = worksheets[selection_keys[0]]
199+
merge_internal_func(base_ws, upt_ws, new_ws)
200+
for ws in selection_keys[1:]:
201+
upt_ws = worksheets[ws]
202+
merge_internal_func(new_ws, upt_ws, new_ws)
203+
new_fn = asksaveasfilename(filetypes=(("Excel File","*.xlsx"),))
204+
if new_fn != '':
205+
new_fn = new_fn + '.xlsx' if not new_fn.endswith('.xlsx') else new_fn
206+
new_wb.save(new_fn)
207+
import_wb(new_fn)
208+
209+
for k in range(10):
210+
win.rowconfigure(k, weight=1)
211+
win.columnconfigure(k, weight=1)
212+
213+
def stop_hover(event):
214+
title_var.set('Py-Merger panel')
215+
my_title.config(font=('bahnschrift bold', 22), image='')
216+
217+
def hover(data):
218+
title_var.set(data)
219+
my_title.config(font=('bahnschrift light', 22), image=pic_tip)
220+
221+
pic_tip = PhotoImage(file='Images/Py-Merger/Tips.png').subsample(2)
222+
title_var = StringVar(win, 'Py-Merger panel')
223+
my_title = Label(master=win, textvariable=title_var, font=('bahnschrift bold', 22), compound='left')
224+
my_title.grid(columnspan=10, sticky='w', padx=20)
225+
226+
bottom_frame = Frame(master=win)
227+
bottom_frame.grid(row=9, column=0, columnspan=10, sticky='nsew')
228+
229+
for k in range(3):
230+
bottom_frame.rowconfigure(k, weight=1)
231+
bottom_frame.columnconfigure(k, weight=1)
232+
233+
pic1 = PhotoImage(file="Images/Py-Merger/Pic 1.png")
234+
btn_import = Button(master=bottom_frame, image=pic1, relief='flat', borderwidth=0, cursor='hand2', command=import_wb)
235+
btn_import.grid(row=1, column=0, sticky='nse', ipady=10)
236+
btn_import.bind('<Enter>', lambda event: hover(docs['btn_import']))
237+
btn_import.bind('<Leave>', stop_hover)
238+
239+
pic2 = PhotoImage(file="Images/Py-Merger/Pic 2.png")
240+
btn_merge = Button(master=bottom_frame, command=merge_action, image=pic2, relief='flat', borderwidth=0, cursor='hand2')
241+
btn_merge.grid(row=1, column=2, sticky='nsw', ipady=10)
242+
btn_merge.bind('<Enter>', lambda event: hover(docs['btn_merge']))
243+
btn_merge.bind('<Leave>', stop_hover)
244+
245+
interactive_part = Frame(master=win, borderwidth=0, highlightthickness=0)
246+
interactive_part.grid(row=1, column=0, columnspan=10, rowspan=8, pady=15, sticky='nsew')
247+
248+
interactive_part.columnconfigure(0, weight = 5)
249+
interactive_part.columnconfigure(1, weight = 5)
250+
interactive_part.columnconfigure(2, weight = 1)
251+
252+
interactive_part.rowconfigure(0, weight = 1)
253+
interactive_part.rowconfigure(1, weight = 10)
254+
255+
legend_lft = Label(master=interactive_part, text='Imported Workbooks', font=('arial gras', 12))
256+
legend_lft.grid(row=0, column=0)
257+
258+
legend_rgt = Label(master=interactive_part, text='Imported Worksheets', font=('arial gras', 12))
259+
legend_rgt.grid(row=0, column=1)
260+
261+
bin_pic = PhotoImage(file='Images/Py-Merger/recycle_bin.png').subsample(1)
262+
btn_bin = Button(master=interactive_part, command=delete_item ,image=bin_pic, relief='flat', borderwidth=0, cursor='hand2', activebackground='#7f8c8d')
263+
btn_bin.grid(row=0, column=2, ipady=10, ipadx=5, sticky='nsew')
264+
btn_bin.bind('<Enter>', lambda event: [hover(docs['btn_bin']), btn_bin.config(bg="#D3D3D3")])
265+
btn_bin.bind('<Leave>', lambda event: [stop_hover(None), btn_bin.config(bg='SystemButtonFace')])
266+
267+
workbooks_list = Listbox(interactive_part, activestyle='none', selectforeground='black', relief='flat', highlightthickness=0, bg='SystemButtonFace', justify='center', font=('bahnschrift light', 10), selectbackground='#D3D3D3')
268+
workbooks_list.grid(row=1, column=0, sticky='nsew')
269+
270+
worksheets_list = Listbox(interactive_part, activestyle='none', selectforeground='black', relief='flat', highlightthickness=0, bg='SystemButtonFace', justify='center', font=('bahnschrift light', 10), selectbackground='#D3D3D3')
271+
worksheets_list.grid(row=1, column=1, sticky='nsew')
272+
273+
btn_frame = Frame(master=interactive_part)
274+
btn_frame.grid(row=1, column=2, sticky='nsew')
275+
btn_frame.columnconfigure(0, weight=1)
276+
277+
add_pic = PhotoImage(file='Images/Py-Merger/add_elem.png').subsample(1)
278+
btn_add = Button(master=btn_frame, command=add_selection, relief='flat', image=add_pic, bd=0, cursor='hand2', activebackground='#7f8c8d')
279+
btn_add.grid(sticky='nsew', ipady=10, ipadx=5)
280+
btn_add.bind('<Enter>', lambda event: [hover(docs['btn_add']), btn_add.config(bg="#D3D3D3")])
281+
btn_add.bind('<Leave>', lambda event: [stop_hover(None), btn_add.config(bg="SystemButtonFace")])
282+
283+
base_pic = PhotoImage(file='Images/Py-Merger/base_ico.png').subsample(1)
284+
btn_base = Button(master=btn_frame, command=set_base, relief='flat', image=base_pic, bd=0, cursor='hand2', activebackground='#7f8c8d')
285+
btn_base.grid(sticky='nsew', ipady=10, ipadx=5)
286+
btn_base.bind('<Enter>', lambda event: [hover(docs['btn_base']), btn_base.config(bg="#D3D3D3")])
287+
btn_base.bind('<Leave>', lambda event: [stop_hover(None), btn_base.config(bg="SystemButtonFace")])
288+
289+
play_pic = PhotoImage(file='Images/Py-Merger/play_icon.png').subsample(1)
290+
btn_play = Button(master=btn_frame, command=start_wb, relief='flat', image=play_pic, bd=0, cursor='hand2', activebackground='#7f8c8d')
291+
btn_play.grid(sticky='nsew', ipady=10, ipadx=5)
292+
btn_play.bind('<Enter>', lambda event: [hover(docs['btn_play']), btn_play.config(bg="#D3D3D3")])
293+
btn_play.bind('<Leave>', lambda event: [stop_hover(None), btn_play.config(bg="SystemButtonFace")])
294+
295+
folder_pic = PhotoImage(file='Images/Py-Merger/folder_pic.png').subsample(1)
296+
btn_find = Button(master=btn_frame, command=lambda: start_wb(None), relief='flat', image=folder_pic, bd=0, cursor='hand2', activebackground='#7f8c8d')
297+
btn_find.grid(sticky='nsew', ipady=10, ipadx=5)
298+
btn_find.bind('<Enter>', lambda event: [hover(docs['btn_find']), btn_find.config(bg="#D3D3D3")])
299+
btn_find.bind('<Leave>', lambda event: [stop_hover(None), btn_find.config(bg="SystemButtonFace")])
300+
301+
Separator(interactive_part, orient='vertical').grid(column=0, row=0, rowspan=2, sticky='nse')
302+
Separator(interactive_part, orient='vertical').grid(column=2, row=0, rowspan=2, sticky='nsw')
303+
Separator(interactive_part, orient='horizontal').grid(column=0, row=0, columnspan=3, sticky='new')
304+
Separator(interactive_part, orient='horizontal').grid(column=0, row=0, columnspan=3, sticky='sew')
305+
Separator(interactive_part, orient='horizontal').grid(column=0, row=1, columnspan=3, sticky='sew')
306+
307+
win.mainloop()

0 commit comments

Comments
 (0)