pip install pandas openpyxl customtkinter
pip install ctktable
pip install tksheet pandas openpyxl
pip install customtkinter tksheet pandas openpyxl
pip install customtkinter tksheet pandas openpyxl Pillow
import customtkinter as ctk
import pandas as pd
import string
from tkinter import filedialog, messagebox
from tksheet import Sheet
class ExcelMasterTool(ctk.CTk):
def __init__(self):
super().__init__()
self.title("Excel Pro - Max Even/Odd & Exclusion")
self.geometry("1400x850")
ctk.set_appearance_mode("light")
self.all_sheets = {"Sheet1": pd.DataFrame([["" for _ in range(20)] for _ in range(50)])}
# --- SIDEBAR ---
self.sidebar = ctk.CTkFrame(self, width=280, corner_radius=0)
self.sidebar.pack(side="left", fill="y")
ctk.CTkLabel(self.sidebar, text="BẢNG ĐIỀU KHIỂN", font=("Arial", 18, "bold")).pack(pady=20)
# 1. Cột bắt đầu & Highlight
ctk.CTkLabel(self.sidebar, text="Bắt đầu từ cột:", font=("Arial", 12, "bold")).pack(pady=(5, 0))
self.col_start_entry = ctk.CTkEntry(self.sidebar, width=180)
self.col_start_entry.pack(pady=5)
self.col_start_entry.insert(0, "A")
self.col_start_entry.bind("<KeyRelease>", lambda e: self.update_highlight())
# 2. Ô loại trừ cột
ctk.CTkLabel(self.sidebar, text="Loại trừ cột (vd: AL,AK):", font=("Arial", 12, "bold")).pack(pady=(10, 0))
self.exclude_entry = ctk.CTkEntry(self.sidebar, placeholder_text="Nhập tên cột...", width=180)
self.exclude_entry.pack(pady=5)
# 3. Nút xử lý
self.btn_process = ctk.CTkButton(
self.sidebar, text="🚀 Chèn & Tính Max Chẵn/Lẻ",
fg_color="#E67E22", hover_color="#D35400",
height=45, font=("Arial", 12, "bold"),
command=self.process_advanced_logic
)
self.btn_process.pack(pady=20, padx=20)
ctk.CTkButton(self.sidebar, text="📂 Nạp File Excel", command=self.import_excel).pack(pady=10, padx=20)
ctk.CTkButton(self.sidebar, text="📤 Xuất File Excel", fg_color="#1D6F42", command=self.export_excel).pack(pady=10, padx=20)
# --- BẢNG TÍNH ---
self.work_area = ctk.CTkFrame(self)
self.work_area.pack(side="right", fill="both", expand=True, padx=10, pady=10)
self.sheet = Sheet(self.work_area, show_row_index=True, show_header=True)
self.sheet.enable_bindings("all")
self.sheet.pack(fill="both", expand=True)
self.refresh_display()
self.update_highlight()
# ================= LOGIC HỖ TRỢ =================
def col_to_idx(self, letter):
letter = letter.upper().strip()
idx = 0
for char in letter: idx = idx * 26 + (ord(char) - ord('A') + 1)
return idx - 1
def idx_to_col(self, n):
name = ""
while n >= 0:
name = chr(n % 26 + 65) + name
n = n // 26 - 1
return name
def update_highlight(self):
self.sheet.dehighlight_all()
letter = self.col_start_entry.get().upper().strip()
if letter:
try:
idx = self.col_to_idx(letter)
if idx >= 0: self.sheet.highlight_columns(columns=[idx], background="#C8E6C9", redo=True)
except: pass
# ================= LOGIC XỬ LÝ CHÍNH =================
def process_advanced_logic(self):
start_letter = self.col_start_entry.get().upper().strip()
exclude_str = self.exclude_entry.get().upper().replace(" ", "")
exclude_list = exclude_str.split(",") if exclude_str else []
try:
start_idx = self.col_to_idx(start_letter)
old_data = self.sheet.get_sheet_data()
if not old_data: return
num_cols = len(old_data[0])
new_data = []
# Tính toán danh sách index bị loại trừ
exclude_indices = [self.col_to_idx(c) for c in exclude_list]
for r_idx in range(len(old_data)):
new_row = []
even_lengths = [] # Chứa độ dài các cột chẵn (0, 2, 4...)
odd_lengths = [] # Chứa độ dài các cột lẻ (1, 3, 5...)
# 1. Giữ nguyên các cột trước start_idx
for c_idx in range(start_idx):
new_row.append(old_data[r_idx][c_idx])
# 2. Xử lý xen kẽ từ start_idx
for c_idx in range(start_idx, num_cols):
val = old_data[r_idx][c_idx]
new_row.append(val) # Thêm cột gốc
# Tính độ dài từ dài nhất
txt = str(val).strip() if val is not None and str(val) != "nan" else ""
length = ""
if txt:
words = txt.translate(str.maketrans('', '', string.punctuation)).split()
length = len(max(words, key=len)) if words else ""
new_row.append(length) # Thêm cột phụ (LENG)
# 3. Gom nhóm để tính Max (Kiểm tra loại trừ tại đây)
if length != "" and (c_idx not in exclude_indices):
if c_idx % 2 == 0:
even_lengths.append(length)
else:
odd_lengths.append(length)
# 4. Thêm 2 cột Max cuối cùng
max_even = max(even_lengths) if even_lengths else ""
max_odd = max(odd_lengths) if odd_lengths else ""
new_row.append(max_even)
new_row.append(max_odd)
new_data.append(new_row)
# Cập nhật Header
final_headers = []
for i in range(start_idx): final_headers.append(self.idx_to_col(i))
for i in range(start_idx, num_cols):
label = self.idx_to_col(i)
final_headers.append(label)
final_headers.append(f"LENG {label}")
final_headers.append("MAX CHẴN (0,2..)")
final_headers.append("MAX LẺ (1,3..)")
# Đổ dữ liệu vào bảng
self.sheet.set_sheet_data(new_data)
self.sheet.headers([self.idx_to_col(i) for i in range(len(new_data[0]))]) # Tiêu đề A,B,C
self.sheet.headers(final_headers) # Tiêu đề nội dung
self.sheet.dehighlight_all()
self.sheet.refresh()
messagebox.showinfo("Thành công", "Đã xử lý xen kẽ và tính Max Chẵn/Lẻ!")
except Exception as e:
messagebox.showerror("Lỗi", f"Lỗi hệ thống: {e}")
# ================= HÀM TIỆN ÍCH =================
def refresh_display(self):
df = self.all_sheets["Sheet1"].fillna("")
self.sheet.set_sheet_data(df.values.tolist())
self.sheet.headers([self.idx_to_col(i) for i in range(len(df.columns))])
self.sheet.refresh()
def import_excel(self):
path = filedialog.askopenfilename(filetypes=[("Excel", "*.xlsx *.xls")])
if path:
df = pd.read_excel(path, header=None).fillna("")
self.sheet.set_sheet_data(df.values.tolist())
self.sheet.headers([self.idx_to_col(i) for i in range(df.shape[1])])
self.update_highlight()
self.sheet.refresh()
def export_excel(self):
path = filedialog.asksaveasfilename(defaultextension=".xlsx")
if path:
data = self.sheet.get_sheet_data()
pd.DataFrame(data).to_excel(path, index=False, header=False)
messagebox.showinfo("Xong", "Lưu thành công!")
if __name__ == "__main__":
app = ExcelMasterTool()
app.mainloop()