换行符转特定格式工具(可设置包裹符号和分隔符号),支持uos国产系统,带界面

先上图。

自用的换行符转换软件,最常用的功能就是写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()

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值