[Python] 纯文本查看 复制代码
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import Menu
import vobject
import pandas as pd
import chardet
data = []
company_to_name = {} # 用于存储公司名称到姓名的映射
def browse_file(file_path=None):
if file_path is None:
file_path = filedialog.askopenfilename(filetypes=[("vCard文件", "*.vcf")])
if file_path:
encoding = detect_encoding(file_path)
if encoding:
parse_vcf(file_path, encoding)
def detect_encoding(file_path):
rawdata = open(file_path, 'rb').read()
result = chardet.detect(rawdata)
encoding = result['encoding']
confidence = result['confidence']
if confidence > 0.8:
return encoding
return 'utf-8'
def parse_vcf(file_path, encoding):
data.clear()
company_to_name.clear()
try:
with open(file_path, 'r', encoding=encoding, errors='ignore') as vcf_file:
cards = vobject.readComponents(vcf_file)
num_entries = 0
for vcard in cards:
name = ""
company = ""
phone_numbers = [""] * 3
if hasattr(vcard, 'fn'):
name = vcard.fn.value.strip()
if hasattr(vcard, 'org'):
company = vcard.org.value[0]
company_to_name[company] = name
if hasattr(vcard, 'tel_list'):
tel_list = [tel.value.replace(" ", "").strip() for tel in vcard.tel_list if hasattr(tel, 'value')]
for i, tel in enumerate(tel_list):
if i < 3:
phone_numbers[i] = tel
if name or company or any(phone_numbers):
data.append([name, company] + phone_numbers)
num_entries += 1
if data:
update_table()
output_text.set(f"总条目数:{num_entries}")
else:
output_text.set("未找到有效的vCard条目")
except Exception as e:
output_text.set(f"错误:{str(e)}")
def reset_data():
data.clear()
update_table()
output_text.set("数据已重置")
def add_to_table(name, phone_numbers, company):
data.append([name, company] + phone_numbers)
update_table()
def update_table():
for i in table.get_children():
table.delete(i)
for i, row in enumerate(data):
formatted_row = [item.strip() if isinstance(item, str) else item for item in row]
table.insert("", "end", values=formatted_row)
def delete_empty_entries():
global data # 引用全局的 data 变量
data = [entry for entry in data if any(entry[2:])]
update_table()
output_text.set(f"总条目数:{len(data)}")
def export_to_csv():
file_path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV文件", "*.csv")])
if file_path:
column_names = ["姓名", "公司", "电话号码1", "电话号码2", "电话号码3"]
df = pd.DataFrame(data, columns=column_names)
df.to_csv(file_path, index=False)
def copy_selected():
selected = table.selection()
if selected:
item = table.selection()[0]
column = table.identify_column(item)
column_index = int(column.split('#')[-1]) - 1 # 获取选定单元格的列索引
row = table.item(item)
data_to_copy = row["values"][column_index]
if data_to_copy is not None:
clipboard = str(data_to_copy)
root.clipboard_clear()
root.clipboard_append(clipboard)
root.update()
def create_table_menu(event):
selected = table.selection()
if selected:
table_menu.delete(0, "end") # 清空菜单项
for i in range(len(table["columns"])):
table_menu.add_command(label=f"复制列 {i + 1}", command=copy_selected)
table_menu.post(event.x_root, event.y_root)
def delete_selected():
selected = table.selection()
if selected:
for item in selected:
table.delete(item)
def merge_duplicate_numbers():
number_to_names = {}
new_data = []
for row in data:
name = row[0]
company = row[1]
numbers = row[2:]
if any(numbers):
numbers_key = tuple(numbers)
if numbers_key in number_to_names:
number_to_names[numbers_key].append(name)
else:
number_to_names[numbers_key] = [name]
new_data.append([" & ".join(number_to_names[numbers_key]), company] + list(numbers))
else:
new_data.append([name, company] + numbers)
data.clear()
data.extend(new_data)
update_table()
def search_data():
query = search_entry.get().strip().lower()
if not query:
update_table()
return
filtered_data = [entry for entry in data if any(query in value.lower() for value in entry)]
update_table_with_filtered_data(filtered_data)
def update_table_with_filtered_data(filtered_data):
for i in table.get_children():
table.delete(i)
for row in filtered_data:
formatted_row = [item.strip if isinstance(item, str) else item for item in row]
table.insert("", "end", values=formatted_row)
def save_as_vcf():
file_path = filedialog.asksaveasfilename(defaultextension=".vcf", filetypes=[("VCF文件", "*.vcf")])
if file_path:
vcf_data = []
for row in data:
name, company, phone1, phone2, phone3 = row
v = vobject.vCard()
if name:
v.add('fn').value = name
if company:
v.add('org').value = [company]
if phone1:
v.add('tel').value = phone1
if phone2:
v.add('tel').value = phone2
if phone3:
v.add('tel').value = phone3
vcf_data.append(v.serialize())
with open(file_path, 'w', encoding='utf-8') as vcf_file:
vcf_file.write("\n".join(vcf_data))
root = tk.Tk()
root.title("兔子vCard Editor v1.6 By 阿修(吾爱破解论坛专版)")
root.resizable(False, False) # 禁用窗口的最大化功能
main_frame = ttk.Frame(root)
main_frame.grid(column=0, row=0, padx=10, pady=10, sticky=(tk.W, tk.E, tk.N, tk.S))
# 左侧功能按钮
left_frame = ttk.Frame(main_frame)
left_frame.grid(column=0, row=0, padx=5, pady=5, sticky=tk.N)
browse_button = ttk.Button(left_frame, text="打开VCF文件", command=browse_file)
browse_button.grid(column=0, row=0, padx=5, pady=5, sticky=tk.W)
reset_button = ttk.Button(left_frame, text="重置数据", command=reset_data)
reset_button.grid(column=0, row=1, padx=5, pady=5, sticky=tk.W)
delete_empty_button = ttk.Button(left_frame, text="删除空号码", command=delete_empty_entries)
delete_empty_button.grid(column=0, row=2, padx=5, pady=5, sticky=tk.W)
merge_button = ttk.Button(left_frame, text="合并号码", command=merge_duplicate_numbers)
merge_button.grid(column=0, row=3, padx=5, pady=5, sticky=tk.W)
output_text = tk.StringVar()
output_label = ttk.Label(left_frame, textvariable=output_text, wraplength=200)
output_label.grid(column=0, row=4, padx=5, pady=5, sticky=tk.W)
# 中间表格
table_frame = ttk.Frame(main_frame)
table_frame.grid(column=1, row=0, padx=5, pady=5, sticky=(tk.W, tk.E))
# 设置表头样式
style = ttk.Style()
style.configure("Treeview.Heading", font=("Arial", 12, "bold"), background="lightgray")
table = ttk.Treeview(table_frame, columns=["姓名", "公司", "电话号码1", "电话号码2", "电话号码3"], show="headings")
table.heading("姓名", text="姓名", anchor="w")
table.heading("公司", text="公司", anchor="w")
table.heading("电话号码1", text="电话号码1", anchor="w")
table.heading("电话号码2", text="电话号码2", anchor="w")
table.heading("电话号码3", text="电话号码3", anchor="w")
# 设置列宽度
table.column("姓名", width=75) # 将姓名的宽度改为75
table.column("公司", width=200) # 将公司名称的宽度改为200
table.column("电话号码1", width=100) # 将电话号码1的宽度改为150
table.column("电话号码2", width=100) # 将电话号码2的宽度改为150
table.column("电话号码3", width=100) # 将电话号码3的宽度改为150
table.pack(fill="both", expand=True)
# 添加右键菜单
table.bind('<Button-3>', create_table_menu)
# 创建右键菜单
table_menu = Menu(root, tearoff=0)
table_menu.add_separator()
table_menu.add_command(label="删除选定行", command=delete_selected)
table_menu.add_command(label="合并号码", command=merge_duplicate_numbers)
# 右侧功能按钮
right_frame = ttk.Frame(main_frame)
right_frame.grid(column=2, row=0, padx=5, pady=5, sticky=tk.N)
export_button = ttk.Button(right_frame, text="导出为CSV", command=export_to_csv)
export_button.grid(column=0, row=0, padx=5, pady=5, sticky=tk.W)
search_label = ttk.Label(right_frame, text="查询数据:")
search_label.grid(column=0, row=1, padx=5, pady=5, sticky=tk.W)
search_entry = ttk.Entry(right_frame)
search_entry.grid(column=0, row=2, padx=5, pady=5, sticky=tk.W)
search_button = ttk.Button(right_frame, text="查询", command=search_data)
search_button.grid(column=0, row=3, padx=5, pady=5, sticky=tk.W)
save_as_vcf_button = ttk.Button(right_frame, text="保存为VCF", command=save_as_vcf)
save_as_vcf_button.grid(column=0, row=4, padx=5, pady=5, sticky=tk.W)
main_frame.columnconfigure(1, weight=1)
main_frame.rowconfigure(0, weight=1)
root.mainloop()