先上图。
自用的换行符转换软件,最常用的功能就是写SQL的时候,把Excel中的回车转成英文逗号加单引号形式,因为可以配置包裹符号和分隔符号,实际用处更多。自己试下就知道了。会自动在程序目录下生成配置文件,用于保存编辑后的各类符号、界面尺寸等(界面第一次启动若与当前系统不配套,可以拉动大小,退出时自动记忆界面尺寸)。这样下次再用就很方便了。代码已经对Linux和Windows做了兼容处理。
还配置了逆转换功能,你可以通过指定分隔符号,将某些有规律的字符串改写成回车样式等,自己琢磨吧,软件写的比较灵活。
Python源码如下:
import tkinter as tk
from tkinter import scrolledtext, ttk, messagebox
import os
import configparser
import platform
import base64
# 获取系统类别
SYSTEM_TYPE = platform.system()
# 获取当前文件的绝对路径
current_file_path = os.path.abspath(__file__)
# 获取当前文件所在的目录
current_dir = os.path.dirname(current_file_path)
# 配置文件路径
CONFIG_FILE = "回车转换器默认配置.ini"
CONFIG_FILE = os.path.join(current_dir,CONFIG_FILE)
# 默认配置
default_config = {
"包裹符号": ["'", '"', "[]", "{}", "《》", "【】"],
"分隔符号": [",", "|", "\\n", "、", "\\", "/", "-"]
}
# Unicode 编码并转换为 Base64
def unicode_encode(symbol_list):
encoded_list = []
for symbol in symbol_list:
# 转字节并进行 Base64 编码
encoded = base64.b64encode(symbol.encode('utf-8')).decode('utf-8')
encoded_list.append(encoded)
return ",".join(encoded_list)
# Base64 解码并恢复原始符号列表
def unicode_decode(symbol_str):
symbols = symbol_str.split(',')
decoded_list = []
for encoded in symbols:
# 解码并转回原字符串
decoded = base64.b64decode(encoded).decode('utf-8')
decoded_list.append(decoded)
return decoded_list
# 加载配置文件
def load_config():
config = configparser.ConfigParser()
if os.path.exists(CONFIG_FILE):
config.read(CONFIG_FILE, encoding='utf-8')
if SYSTEM_TYPE in config:
return config
else:
config[SYSTEM_TYPE] = {
"包裹符号": unicode_encode(default_config["包裹符号"]),
"分隔符号": unicode_encode(default_config["分隔符号"])
}
save_config(config)
return config
else:
config[SYSTEM_TYPE] = {
"包裹符号": unicode_encode(default_config["包裹符号"]),
"分隔符号": unicode_encode(default_config["分隔符号"])
}
save_config(config)
return config
# 保存配置文件
def save_config(config):
with open(CONFIG_FILE, 'w', encoding='utf-8') as configfile:
config.write(configfile)
# 获取当前配置
def get_current_config(config):
try:
wrap_choices = unicode_decode(config[SYSTEM_TYPE]["包裹符号"])
separator_choices = unicode_decode(config[SYSTEM_TYPE]["分隔符号"])
except (SyntaxError, ValueError, KeyError):
wrap_choices = default_config["包裹符号"]
separator_choices = default_config["分隔符号"]
# 过滤空白项
wrap_choices = [item for item in wrap_choices if item.strip()]
separator_choices = [item for item in separator_choices if item.strip()]
return wrap_choices, separator_choices
# 更新配置文件中的符号列表
def update_config(config, wrap, separator):
try:
wrap_list = unicode_decode(config[SYSTEM_TYPE]["包裹符号"])
except (SyntaxError, ValueError):
wrap_list = default_config["包裹符号"]
try:
separator_list = unicode_decode(config[SYSTEM_TYPE]["分隔符号"])
except (SyntaxError, ValueError):
separator_list = default_config["分隔符号"]
if wrap not in wrap_list and wrap.strip():
wrap_list.append(wrap)
if separator not in separator_list and separator.strip():
separator_list.append(separator)
# 过滤空白项
wrap_list = [item for item in wrap_list if item.strip()]
separator_list = [item for item in separator_list if item.strip()]
config[SYSTEM_TYPE]["包裹符号"] = unicode_encode(wrap_list)
config[SYSTEM_TYPE]["分隔符号"] = unicode_encode(separator_list)
save_config(config)
# 文本转换逻辑
def perform_replacement(text, wrap_symbol, separator_symbol):
lines = [line for line in text.splitlines() if line.strip()]
if len(wrap_symbol) == 2:
wrapped_lines = [f"{wrap_symbol[0]}{line}{wrap_symbol[1]}" for line in lines]
else:
wrapped_lines = [f"{wrap_symbol}{line}{wrap_symbol}" for line in lines]
return separator_symbol.join(wrapped_lines)
# 逆转换逻辑
def reverse_replacement(text, wrap_symbol, separator_symbol):
# 确保 separator_symbol 不为空
if not separator_symbol:
messagebox.showerror("错误", "分隔符号不能为空!")
return ""
if len(wrap_symbol) == 2:
unwrapped_lines = [line[len(wrap_symbol[0]):-len(wrap_symbol[1])] for line in text.split(separator_symbol) if
line.startswith(wrap_symbol[0]) and line.endswith(wrap_symbol[1])]
elif len(wrap_symbol) == 1:
unwrapped_lines = [line[len(wrap_symbol):-len(wrap_symbol)] for line in text.split(separator_symbol) if
line.startswith(wrap_symbol) and line.endswith(wrap_symbol)]
else:
unwrapped_lines = text.split(separator_symbol)
# 确保最后一个元素不被遗漏
if unwrapped_lines and unwrapped_lines[-1] == "":
unwrapped_lines.pop()
return "\n".join(unwrapped_lines)
def on_transform():
input_text = input_textbox.get("1.0", tk.END)
wrap_symbol = wrap_var.get()
separator_symbol = separator_var.get().replace("\\n", "\n")
output_text = perform_replacement(input_text, wrap_symbol, separator_symbol)
output_textbox.delete("1.0", tk.END)
output_textbox.insert(tk.END, output_text)
# update_config(config, wrap_symbol, separator_symbol)
def on_reverse_transform():
output_text = output_textbox.get("1.0", tk.END).strip()
wrap_symbol = wrap_var.get()
separator_symbol = separator_var.get().replace("\\n", "\n")
input_text = reverse_replacement(output_text, wrap_symbol, separator_symbol)
input_textbox.delete("1.0", tk.END)
input_textbox.insert(tk.END, input_text)
# update_config(config, wrap_symbol, separator_symbol)
def on_paste_to_input():
input_textbox.focus_set()
input_textbox.delete("1.0", tk.END)
input_textbox.event_generate('<Control-v>')
def on_copy_from_output():
output_textbox.focus_set()
output_textbox.event_generate('<Control-a>')
output_textbox.event_generate('<Control-c>')
def on_save_config():
save_config(config)
def on_reset():
input_textbox.delete("1.0", tk.END)
output_textbox.delete("1.0", tk.END)
def on_exit():
on_save_config() # 保存用户配置
root.quit()
def create_context_menu(text_widget):
context_menu = tk.Menu(root, tearoff=0)
if SYSTEM_TYPE == 'Linux':
# Linux下的设置普通状态鼠标样式
context_menu.configure(cursor="hand2")
# Linux下小键盘的回车与主键盘是有区分的,要额外处理才能保证体验一致。
def on_kp_enter(event):
# 生成主键盘的回车键事件
text_widget.event_generate("<Return>")
return "break" # 防止事件进一步传播
text_widget.bind("<KP_Enter>", on_kp_enter)
# Linux下,粘贴时,若选中有文本,会变成在选中文本后插入,不删除原选中文本,需要手动修改为与Windows一致。
def on_paste(event=None):
# 检查是否有选中文本
if text_widget.tag_ranges(tk.SEL):
# 如果有选中文本,则删除它
text_widget.delete(tk.SEL_FIRST, tk.SEL_LAST)
# 获取当前选中的文本
# selected_text = text_widget.tag_names("sel.first")
# if selected_text:
# # 如果有选中文本,则删除它
# text_widget.delete("sel.first", "sel.last")
# 粘贴剪贴板内容
text_widget.insert(tk.INSERT, text_widget.clipboard_get())
return "break" # 防止事件进一步传播
text_widget.bind("<Control-v>", on_paste)
def select_all(event):
# 使用"end-1c"而不是"end"可以避免选中tk自动添加的最后一个换行符号,此处Windows和Linux都需要调整
event.widget.tag_add("sel", "1.0", "end-1c")
return 'break'
text_widget.bind("<Control-a>", select_all)
context_menu.add_command(label="全选", command=lambda: text_widget.event_generate('<Control-a>'))
context_menu.add_command(label="复制", command=lambda: text_widget.event_generate('<Control-c>'))
context_menu.add_command(label="剪切", command=lambda: text_widget.event_generate('<Control-x>'))
context_menu.add_command(label="粘贴", command=lambda: text_widget.event_generate('<Control-v>'))
# 这个变量用于跟踪菜单的活动状态
menu_active = False
def show_context_menu(event):
nonlocal menu_active
text_widget.focus_set()
context_menu.tk_popup(event.x_root, event.y_root)
menu_active = True # 菜单处于活动状态
def close_context_menu(event):
nonlocal menu_active
if menu_active:
context_menu.unpost() # 关闭菜单
menu_active = False
# 绑定右键点击事件以显示菜单
text_widget.bind("<Button-3>", show_context_menu)
# 绑定左键点击事件以关闭菜单
root.bind("<Button-1>", close_context_menu)
def open_edit_dialog(var_name, current_list):
def save_changes():
new_values = [value.strip() for value in text_edit.get("1.0", tk.END).strip().splitlines() if value.strip()]
if new_values:
# 更新 config 对象中的值
config[SYSTEM_TYPE][var_name] = unicode_encode(new_values)
# 保存到文件
save_config(config)
# 立即更新界面中的下拉菜单
if var_name == "包裹符号":
wrap_combobox['values'] = new_values
wrap_combobox.set(new_values[0])
elif var_name == "分隔符号":
separator_combobox['values'] = new_values
separator_combobox.set(new_values[0])
# 关闭编辑窗口
edit_dialog.destroy()
else:
messagebox.showwarning("警告", "符号列表不能为空!")
# 创建编辑窗口
edit_dialog = tk.Toplevel(root)
edit_dialog.title(f"编辑 {var_name}")
tk.Label(edit_dialog, text=f"编辑 {var_name} (每行一个符号):").pack(padx=10, pady=10)
text_edit = scrolledtext.ScrolledText(edit_dialog, width=40, height=10)
text_edit.pack(padx=10, pady=10)
text_edit.insert(tk.END, "\n".join(current_list))
btn_save = tk.Button(edit_dialog, text="保存", command=save_changes)
btn_save.pack(pady=10)
center_window(edit_dialog) # 使弹出窗口居中
def center_window(window, width=None, height=None):
if width is None or height is None:
window.update_idletasks() # 更新 "requested size"
width = window.winfo_width()
height = window.winfo_height()
else:
# 设置窗口大小
window.geometry(f'{width}x{height}')
# 更新 "requested size" 并计算居中位置
window.update_idletasks()
x = (window.winfo_screenwidth() // 2) - (width // 2)
y = (window.winfo_screenheight() // 2) - (height // 2)
# 设置窗口的初始位置为居中
window.geometry(f'{width}x{height}+{x}+{y}')
# 主程序
# 在主程序启动时调用 center_window
root = tk.Tk()
root.title("换行符转特定格式工具(可设置包裹符号和分隔符号)")
# 在显示主窗口之前调用
center_window(root)
config = load_config()
wrap_choices, separator_choices = get_current_config(config)
# 使用指定的宽度和高度调用 center_window 函数
if SYSTEM_TYPE=='Windows':
center_window(root, 634, 305)
else:
center_window(root, 720, 360)
# Frame 1: Text input and output
frame1 = tk.Frame(root)
frame1.pack(padx=10, pady=5)
input_textbox = scrolledtext.ScrolledText(frame1, width=40, height=10)
input_textbox.grid(row=0, column=0, padx=5, pady=5)
create_context_menu(input_textbox)
output_textbox = scrolledtext.ScrolledText(frame1, width=40, height=10)
output_textbox.grid(row=0, column=1, padx=5, pady=5)
create_context_menu(output_textbox)
# Frame 2: Additional buttons below input_textbox
frame_buttons = tk.Frame(root)
frame_buttons.pack(padx=1, pady=1)
btn_paste = tk.Button(frame_buttons, text="一键粘贴", command=on_paste_to_input)
btn_paste.grid(row=0, column=0, padx=120, pady=5)
btn_copy = tk.Button(frame_buttons, text="一键复制", command=on_copy_from_output)
btn_copy.grid(row=0, column=1, padx=120, pady=5)
# Frame 3: Configurations for wrap and separator
frame_config = tk.Frame(root)
frame_config.pack(padx=10, pady=5)
tk.Label(frame_config, text="包裹符号:").grid(row=0, column=0, padx=5, pady=5)
wrap_var = tk.StringVar(value=wrap_choices[0])
wrap_combobox = ttk.Combobox(frame_config, textvariable=wrap_var, values=wrap_choices)
wrap_combobox.grid(row=0, column=1, padx=5, pady=5)
# 添加编辑按钮
btn_edit_wrap = tk.Button(frame_config, text="编辑", command=lambda: open_edit_dialog("包裹符号", wrap_choices))
btn_edit_wrap.grid(row=0, column=2, padx=5, pady=5)
tk.Label(frame_config, text="分隔符号:").grid(row=0, column=3, padx=5, pady=5)
separator_var = tk.StringVar(value=separator_choices[0])
separator_combobox = ttk.Combobox(frame_config, textvariable=separator_var, values=separator_choices)
separator_combobox.grid(row=0, column=4, padx=5, pady=5)
# 添加编辑按钮
btn_edit_separator = tk.Button(frame_config, text="编辑", command=lambda: open_edit_dialog("分隔符号", separator_choices))
btn_edit_separator.grid(row=0, column=5, padx=5, pady=5)
# Frame 4: Main action buttons
frame_main = tk.Frame(root)
frame_main.pack(padx=10, pady=5)
btn_transform = tk.Button(frame_main, text="转换回车", command=on_transform)
btn_transform.grid(row=0, column=0, padx=5, pady=5)
btn_reverse = tk.Button(frame_main, text="逆转换", command=on_reverse_transform)
btn_reverse.grid(row=0, column=1, padx=5, pady=5)
# btn_save_config = tk.Button(frame_main, text="保存配置", command=on_save_config)
# btn_save_config.grid(row=0, column=2, padx=5, pady=5)
btn_reset = tk.Button(frame_main, text="重置", command=on_reset)
btn_reset.grid(row=0, column=3, padx=5, pady=5)
btn_exit = tk.Button(frame_main, text="退出", command=on_exit)
btn_exit.grid(row=0, column=4, padx=5, pady=5)
root.mainloop()