-
Notifications
You must be signed in to change notification settings - Fork 6
Open
Description
近期,DailyRoutines的新模块可以支持好友列表显示备注,由于我懒,简单用AI搞了一个导出工具,可以将UsedName的数据导出到DailyRoutines。代码和exe都放在这里
Recently, the new module of DailyRoutines supports displaying nickname for friend lists. Since I'm lazy, I simply used AI to create an export tool that can export UsedName data to DailyRoutines. The code and the exe are placed here.
import json
import os
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import string
import locale
# 语言配置
translations = {
"en": {
"title": "UsedName to DailyRoutines",
"file_paths": "File Paths",
"browse": "Browse...",
"user_list": "User List",
"id": "ID",
"current_name": "Current Name",
"nick_name": "Nickname",
"used_names": "Used Names",
"filter": "Filter:",
"refresh": "Refresh",
"import_selected": "Import Selected",
"import_all": "Import All",
"save": "Save",
"warning": "Warning",
"file_not_exist": "File does not exist: ",
"cannot_create_dir": "Cannot create directory: ",
"notice": "Notice",
"will_create_file": "Will create new file: ",
"error": "Error",
"error_loading": "Error loading data: ",
"select_users": "Please select users to import",
"imported_count": "Successfully imported {0} users",
"no_import": "No user imported",
"imported_all": "Successfully imported all users ({0})",
"save_success": "Data successfully saved to ",
"save_error": "Error saving data: ",
"language": "Language",
"chinese": "中文",
"english": "English"
},
"zh": {
"title": "UsedName to DailyRoutines",
"file_paths": "文件路径",
"browse": "浏览...",
"user_list": "用户列表",
"id": "ID",
"current_name": "当前名称",
"nick_name": "昵称",
"used_names": "历史名称",
"filter": "筛选:",
"refresh": "刷新",
"import_selected": "导入选中",
"import_all": "导入全部",
"save": "保存",
"warning": "警告",
"file_not_exist": "文件不存在: ",
"cannot_create_dir": "无法创建目录: ",
"notice": "提示",
"will_create_file": "将创建新文件: ",
"error": "错误",
"error_loading": "加载数据时出错: ",
"select_users": "请先选择要导入的用户",
"imported_count": "已成功导入 {0} 个用户",
"no_import": "没有导入任何用户",
"imported_all": "已成功导入所有用户 ({0} 个)",
"save_success": "数据已成功保存到 ",
"save_error": "保存数据时出错: ",
"language": "语言",
"chinese": "中文",
"english": "English"
}
}
class UsernameImportTool:
def __init__(self, root):
self.root = root
# 设置语言
self.detect_language()
self.root.title(self.translate("title"))
self.root.geometry("700x600")
# 获取默认路径
self.appdata_path = os.path.expandvars("%APPDATA%")
# 默认路径列表
self.default_store_paths = [
os.path.join(self.appdata_path, "XIVLauncherCN", "pluginConfigs", "UsedName", "storeNames.json"),
os.path.join(self.appdata_path, "XIVLauncher", "pluginConfigs", "UsedName", "storeNames.json"),
"storeNames.json"
]
self.default_friend_paths = [
os.path.join(self.appdata_path, "XIVLauncherCN", "pluginConfigs", "DailyRoutines", "FriendListRemarks.json"),
os.path.join(self.appdata_path, "XIVLauncher", "pluginConfigs", "DailyRoutines", "FriendListRemarks.json"),
"FriendListRemarks.json"
]
# 自动查找存在的文件
self.store_names_path = self.find_existing_file(self.default_store_paths)
self.friend_list_path = self.find_existing_file(self.default_friend_paths)
# 检查DailyRoutines配置文件和文件夹
self.check_dailyroutines_config()
self.store_data = {}
self.friend_data = {}
# 创建UI元素
self.create_ui()
# 更新UI文本
self.update_ui_texts()
self.load_data()
def detect_language(self):
"""检测系统语言并设置语言"""
try:
# 设置locale为系统默认
locale.setlocale(locale.LC_CTYPE, '')
# 获取当前locale设置
current_locale = locale.getlocale(locale.LC_CTYPE)
# 从locale中提取语言信息
if current_locale and current_locale[0]:
# 对于Windows返回的格式如('Chinese (Simplified)_China', '936')
locale_str = current_locale[0].lower()
if "chinese" in locale_str:
self.language = "zh"
else:
self.language = "en"
else:
# 尝试使用备用方法(虽然已弃用但在移除前仍可用)
try:
lang, _ = locale.getdefaultlocale()
if lang and lang.startswith("zh"):
self.language = "zh"
else:
self.language = "en"
except:
self.language = "en" # 默认英文
except Exception as e:
print(f"语言检测错误: {e}")
# 如果出现任何错误,默认使用英文
self.language = "en"
def translate(self, key, *args):
"""根据当前语言返回翻译文本"""
return translations[self.language].get(key, key).format(*args)
def find_existing_file(self, path_list):
"""查找列表中第一个存在的文件路径"""
for path in path_list:
if os.path.exists(path):
return path
# 如果都不存在,返回第一个路径
return path_list[0]
def check_dailyroutines_config(self):
"""检查DailyRoutines配置和文件夹,如需要则创建FriendListRemarks路径"""
# 检查DailyRoutines.json是否存在
dr_config_cn = os.path.join(self.appdata_path, "XIVLauncherCN", "pluginConfigs", "DailyRoutines.json")
dr_config = os.path.join(self.appdata_path, "XIVLauncher", "pluginConfigs", "DailyRoutines.json")
# 检查DailyRoutines文件夹是否存在
dr_folder_cn = os.path.join(self.appdata_path, "XIVLauncherCN", "pluginConfigs", "DailyRoutines")
dr_folder = os.path.join(self.appdata_path, "XIVLauncher", "pluginConfigs", "DailyRoutines")
# 如果配置存在且文件夹也存在,则使用路径
if os.path.exists(dr_config_cn) and os.path.exists(dr_folder_cn):
self.friend_list_path = os.path.join(dr_folder_cn, "FriendListRemarks.json")
elif os.path.exists(dr_config) and os.path.exists(dr_folder):
self.friend_list_path = os.path.join(dr_folder, "FriendListRemarks.json")
def create_ui(self):
"""创建UI元素结构,只在初始化时调用一次"""
# 创建主框架
self.main_frame = ttk.Frame(self.root, padding=10)
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 文件路径框架
self.path_frame = ttk.LabelFrame(self.main_frame, text="", padding=5)
self.path_frame.pack(fill=tk.X, pady=5)
# storeNames.json 路径
self.store_label = ttk.Label(self.path_frame, text="storeNames.json:")
self.store_label.grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
self.store_path_var = tk.StringVar(value=self.store_names_path)
ttk.Entry(self.path_frame, textvariable=self.store_path_var, width=50).grid(row=0, column=1, padx=5, pady=5)
self.browse_store_btn = ttk.Button(self.path_frame, text="", command=lambda: self.browse_file("store"))
self.browse_store_btn.grid(row=0, column=2, padx=5, pady=5)
# FriendListRemarks.json 路径
self.friend_label = ttk.Label(self.path_frame, text="FriendListRemarks.json:")
self.friend_label.grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
self.friend_path_var = tk.StringVar(value=self.friend_list_path)
ttk.Entry(self.path_frame, textvariable=self.friend_path_var, width=50).grid(row=1, column=1, padx=5, pady=5)
self.browse_friend_btn = ttk.Button(self.path_frame, text="", command=lambda: self.browse_file("friend"))
self.browse_friend_btn.grid(row=1, column=2, padx=5, pady=5)
# 语言切换框架
self.lang_frame = ttk.Frame(self.main_frame)
self.lang_frame.pack(fill=tk.X, pady=5)
self.lang_label = ttk.Label(self.lang_frame, text="")
self.lang_label.pack(side=tk.LEFT, padx=5)
self.lang_var = tk.StringVar(value=self.language)
self.lang_menu = ttk.OptionMenu(self.lang_frame, self.lang_var, self.language, "zh", "en", command=self.change_language)
self.lang_menu.pack(side=tk.LEFT, padx=5)
# 用户列表框架
self.list_frame = ttk.LabelFrame(self.main_frame, text="", padding=5)
self.list_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# 创建树状视图
columns = ("id", "current_name", "nick_name", "used_names")
self.tree = ttk.Treeview(self.list_frame, columns=columns, show="headings", selectmode="extended")
# 设置列宽度
self.tree.column("id", width=150)
self.tree.column("current_name", width=150)
self.tree.column("nick_name", width=150)
self.tree.column("used_names", width=200)
# 添加滚动条
scrollbar = ttk.Scrollbar(self.list_frame, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 筛选框架
self.filter_frame = ttk.Frame(self.main_frame)
self.filter_frame.pack(fill=tk.X, pady=5)
self.filter_label = ttk.Label(self.filter_frame, text="")
self.filter_label.pack(side=tk.LEFT, padx=5)
self.filter_var = tk.StringVar()
self.filter_entry = ttk.Entry(self.filter_frame, textvariable=self.filter_var, width=30)
self.filter_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
self.filter_entry.bind("<KeyRelease>", self.filter_treeview)
# 按钮框架
self.button_frame = ttk.Frame(self.main_frame)
self.button_frame.pack(fill=tk.X, pady=10)
self.refresh_btn = ttk.Button(self.button_frame, text="", command=self.load_data)
self.refresh_btn.pack(side=tk.LEFT, padx=5)
self.import_selected_btn = ttk.Button(self.button_frame, text="", command=self.import_selected)
self.import_selected_btn.pack(side=tk.LEFT, padx=5)
self.import_all_btn = ttk.Button(self.button_frame, text="", command=self.import_all)
self.import_all_btn.pack(side=tk.LEFT, padx=5)
self.save_btn = ttk.Button(self.button_frame, text="", command=self.save_data)
self.save_btn.pack(side=tk.RIGHT, padx=5)
def update_ui_texts(self):
"""更新UI元素的文本,在语言切换时调用"""
self.root.title(self.translate("title"))
# 更新框架标题
self.path_frame.configure(text=self.translate("file_paths"))
self.list_frame.configure(text=self.translate("user_list"))
# 更新按钮文本
self.browse_store_btn.configure(text=self.translate("browse"))
self.browse_friend_btn.configure(text=self.translate("browse"))
self.refresh_btn.configure(text=self.translate("refresh"))
self.import_selected_btn.configure(text=self.translate("import_selected"))
self.import_all_btn.configure(text=self.translate("import_all"))
self.save_btn.configure(text=self.translate("save"))
# 更新标签文本
self.lang_label.configure(text=self.translate("language"))
self.filter_label.configure(text=self.translate("filter"))
# 更新树状视图列标题
self.tree.heading("id", text=self.translate("id"))
self.tree.heading("current_name", text=self.translate("current_name"))
self.tree.heading("nick_name", text=self.translate("nick_name"))
self.tree.heading("used_names", text=self.translate("used_names"))
def change_language(self, lang):
"""切换语言"""
self.language = lang
self.update_ui_texts()
# 如果需要刷新数据视图
self.update_treeview()
def browse_file(self, file_type):
filename = filedialog.askopenfilename(
title=self.translate("browse"),
filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
)
if filename:
if file_type == "store":
self.store_path_var.set(filename)
self.store_names_path = filename
else:
self.friend_path_var.set(filename)
self.friend_list_path = filename
self.load_data()
def load_data(self):
try:
# 读取 storeNames.json
if os.path.exists(self.store_names_path):
with open(self.store_names_path, 'r', encoding='utf-8-sig') as f:
self.store_data = json.load(f)
else:
self.store_data = {}
messagebox.showwarning(self.translate("warning"), self.translate("file_not_exist") + self.store_names_path)
# 读取 FriendListRemarks.json
if os.path.exists(self.friend_list_path):
with open(self.friend_list_path, 'r', encoding='utf-8') as f:
self.friend_data = json.load(f)
if "PlayerInfos" not in self.friend_data:
self.friend_data["PlayerInfos"] = {}
else:
self.friend_data = {"PlayerInfos": {}}
# 检查是否需要创建路径
dir_path = os.path.dirname(self.friend_list_path)
if not os.path.exists(dir_path):
try:
os.makedirs(dir_path)
except Exception as e:
messagebox.showwarning(self.translate("warning"), self.translate("cannot_create_dir") + dir_path + "\n" + str(e))
messagebox.showinfo(self.translate("notice"), self.translate("will_create_file") + self.friend_list_path)
# 更新树视图
self.update_treeview()
except Exception as e:
messagebox.showerror(self.translate("error"), self.translate("error_loading") + str(e))
def update_treeview(self):
# 清空当前树视图
for item in self.tree.get_children():
self.tree.delete(item)
# 填充树视图
for player_id, data in self.store_data.items():
current_name = data.get("currentName", "")
nick_name = data.get("nickName", "")
used_names = ", ".join(filter(None, data.get("usedNames", [])))
# 检查是否已经在 FriendListRemarks.json 中
in_friend_list = player_id in self.friend_data.get("PlayerInfos", {})
# 添加到树视图
item_id = self.tree.insert("", tk.END, values=(player_id, current_name, nick_name, used_names))
# 已导入的项目使用不同颜色标记
if in_friend_list:
self.tree.item(item_id, tags=("imported",))
# 设置已导入项的标签颜色
self.tree.tag_configure("imported", background="#e6f7ff")
def filter_treeview(self, event=None):
search_term = self.filter_var.get().lower()
# 清空当前树视图
for item in self.tree.get_children():
self.tree.delete(item)
# 填充树视图,应用筛选
for player_id, data in self.store_data.items():
current_name = data.get("currentName", "")
nick_name = data.get("nickName", "")
used_names = ", ".join(filter(None, data.get("usedNames", [])))
# 检查是否匹配筛选条件
if (search_term in player_id.lower() or
search_term in current_name.lower() or
search_term in nick_name.lower() or
search_term in used_names.lower()):
# 检查是否已经在 FriendListRemarks.json 中
in_friend_list = player_id in self.friend_data.get("PlayerInfos", {})
# 添加到树视图
item_id = self.tree.insert("", tk.END, values=(player_id, current_name, nick_name, used_names))
# 已导入的项目使用不同颜色标记
if in_friend_list:
self.tree.item(item_id, tags=("imported",))
def format_used_names_to_remark(self, used_names):
"""将曾用名格式化为备注内容,按照A, B, C...的格式"""
if not used_names:
return ""
# 过滤掉空字符串
used_names = list(filter(None, used_names))
# 如果没有曾用名,返回空字符串
if not used_names:
return ""
# 合并成一个字符串
return ", ".join(used_names)
def import_selected(self):
selected_items = self.tree.selection()
if not selected_items:
messagebox.showinfo(self.translate("notice"), self.translate("select_users"))
return
imported_count = 0
for item in selected_items:
values = self.tree.item(item, "values")
player_id = values[0]
if player_id in self.store_data:
data = self.store_data[player_id]
# 格式化曾用名到备注
used_names_remark = self.format_used_names_to_remark(data.get("usedNames", []))
# 创建导入数据
import_data = {
"ContentID": int(player_id),
"Name": data.get("currentName", ""),
"Nickname": data.get("nickName", ""),
"Remark": used_names_remark
}
# 更新数据
self.friend_data["PlayerInfos"][player_id] = import_data
imported_count += 1
if imported_count > 0:
messagebox.showinfo(self.translate("notice"), self.translate("imported_count", imported_count))
self.update_treeview()
else:
messagebox.showinfo(self.translate("notice"), self.translate("no_import"))
def import_all(self):
imported_count = 0
for player_id, data in self.store_data.items():
# 格式化曾用名到备注
used_names_remark = self.format_used_names_to_remark(data.get("usedNames", []))
# 创建导入数据
import_data = {
"ContentID": int(player_id),
"Name": data.get("currentName", ""),
"Nickname": data.get("nickName", ""),
"Remark": used_names_remark
}
# 更新数据
self.friend_data["PlayerInfos"][player_id] = import_data
imported_count += 1
if imported_count > 0:
messagebox.showinfo(self.translate("notice"), self.translate("imported_all", imported_count))
self.update_treeview()
else:
messagebox.showinfo(self.translate("notice"), self.translate("no_import"))
def save_data(self):
try:
# 确保目录存在
dir_path = os.path.dirname(self.friend_list_path)
if not os.path.exists(dir_path):
os.makedirs(dir_path)
with open(self.friend_list_path, 'w', encoding='utf-8') as f:
json.dump(self.friend_data, f, ensure_ascii=False, indent=2)
messagebox.showinfo(self.translate("notice"), self.translate("save_success") + self.friend_list_path)
except Exception as e:
messagebox.showerror(self.translate("error"), self.translate("save_error") + str(e))
if __name__ == "__main__":
root = tk.Tk()
app = UsernameImportTool(root)
root.mainloop()Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels