吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 11511|回复: 99
收起左侧

[原创工具] 兔子vCard editor v1.8.8(PyInstaller编译)

  [复制链接]
axiuge 发表于 2023-11-4 02:28
本帖最后由 axiuge 于 2024-10-7 22:28 编辑

通讯录里的有几千个电话号码,找手机不好整理,在电脑整理完毕后可以导入手机。作为备份。在论坛搜了一下没找到好用的,自己写了一个。
1、很感谢大家喜欢我的软件,我是一个新手程序员,很多功能不能实现。
2、大家的建议我都看了 不能一一回帖,能实现的我都会劲量实现。
111.png

下载地址:(所有版本)
https://axiu.lanzoue.com/b0cijvnfe 密码:f7mk


1.0版本
  • 新增了导出为csv格式,
  • 新增了删除空号码。

微信图片_20231104022317.png

1.1版本
  • *新增了读取三个手机号码
  • *新增了判断多种格式
  • *新增判断编码
  • *新增删除姓名前面的空格
  • *新增姓名标题左对齐
未标题-2.png

1.3版本
  • 重新布局软件界面
  • 新增重置数据功能
  • 新增删除空号码功能
  • 新增查询功能
  • 新增右键复制功能
  • 新增右键删除功能
未标题-2.png
1.4版本
  • 重新打包降低为27.5mb左右。
  • 优化细节。

1.5版本

  • 优化GUI界面
  • 新增合并一个姓名的多个号码
  • 移动拖拽打开(取消了,tkinterDnD库的问题,调试不报错,编译就报错)

未标题-2.png



1.6版本

  • 新增保存为VCF格式
  • 命名软件名字为兔子vCard editor方便别人搜索和下载。
  • 修改软件图标
  • 以后应该不和考虑增加打开表格的功能,因为这个超出了这个软件的目的。这个软件主要还是以编辑vcf格式为主。

未标题-2(1).png
1.7版本
  • 新增解析华为手机VCF格式


1.8版本
  • 修复右键删除功能



1.8.5版本 6a844bfde5e103bdf06e59aaa54b909.png

1、重新编译,优化文件大小

2、新增打开excel格式
3、新增自动判断编码


2.0全新版本预告
微信图片_20231111175255.png


共享源码:
[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()

分享源码给大家,共同完善,希望大家把修改好的程序能回复到下面的帖子内。
预备功能:使用爬虫获取通讯录里的手机号码爬天眼查之类的公司全称和法人姓名,
然后获取数据后再写入表格,做到不用手动去修改和调整。一
直搞不定这个天眼的爬虫,望论坛的大佬多多指点我。感激不尽。

免费评分

参与人数 15吾爱币 +19 热心值 +14 收起 理由
djj9958 + 1 + 1 我很赞同!
ID5213 + 1 谢谢@Thanks!
shenyun919 + 1 + 1 谢谢@Thanks!
byjimo + 2 + 1 我很赞同!
在氵一方 + 1 + 1 谢谢@Thanks!
Nanfeng + 1 我很赞同!
150164 + 2 + 1 谢谢@Thanks!蹲个2.0
pancnboy + 1 热心回复!
空竹 + 1 + 1 已经处理,感谢您对吾爱破解论坛的支持!
Zatoichi + 1 + 1 谢谢@Thanks!
gbrlkm + 1 + 1 我很赞同!
tanpan + 1 热心回复!
lzy13 + 1 + 1 谢谢@Thanks!
confiant + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
风之暇想 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| axiuge 发表于 2023-11-11 13:38
helh0275 发表于 2023-11-11 01:11
反馈一下问题哈
1.7版使用后,确实能读取华为导出的数据,但不能编辑,双击没反应,右键弹出的是复制某列 ...

已经尝试过了,python 要实现双击编辑表格,还要保持编码不出错,很麻烦。所以放弃了。最好的方法还是导出为csv格式然后编辑好再保存为vcf格式。所以后后面我会增加打开csv格式的支持。

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
helh0275 + 1 + 1 谢谢@Thanks!

查看全部评分

aolmin 发表于 2024-1-10 15:56
我来反馈1.8:
1、能正常打开读取vcf文件,选择保存csv文件也能正常选择保存的位置和文件名,但是点击保存后并没有生成保存的文件。
2、分享的源码是1.6版本,运行后报错UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 21-22: unexpected end of data
wasm2023 发表于 2023-11-4 13:30
好东西,感谢分享,减少包的大小,可以试试重新创建一个虚拟环境,然后只安装这个程序所需的包,然后在新的环境下打包
yxcqr 发表于 2023-11-4 13:44
顶顶更新健康。
 楼主| axiuge 发表于 2023-11-4 14:22
wasm2023 发表于 2023-11-4 13:30
好东西,感谢分享,减少包的大小,可以试试重新创建一个虚拟环境,然后只安装这个程序所需的包,然后在新的 ...

已经重新打包了 最小也是25MB,等下传上来
青花西岳 发表于 2023-11-4 14:24
收藏备用感谢分享
lzy13 发表于 2023-11-4 14:24
好软件,方便整理手机电话号码
netpeng 发表于 2023-11-4 14:59
支持原创,下载试用下。
文鑫孤影130 发表于 2023-11-4 15:06
支持,谢谢开发分享
八月未央 发表于 2023-11-4 15:29
分享一下源码,用e给做一个,软件体积会小很多
 楼主| axiuge 发表于 2023-11-4 15:33

客气。相互帮助
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-23 02:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表