一个简单纯粹的复制粘贴板软件

如果你有很多需要复制的内容,有时候常用的复制内容多,比如有些账号密码不支持输入法呼出常用语,切过来切过去找账户密码,写了这个软件,一些自用常见的内容可以放在这个软件里面,点击就可以复制,非常方便。

功能也比较鸡肋,市面上很多的剪切板都很好用,主要想试下 ChatGPT 写代码,写一个自己用得上的小功能。

只是一个简单的复制粘贴板,没有其他过多功能,也没有读取剪切板的功能。源码和已编译成exe的软件都放在里面了。不放心的可以自己编译,所有方法都写在帮助文档里面了

几乎99%的内容都使用 ChatGPT 写的,全程出问题只提需求,让修改代码,没有过多手动干预,主要是我自己也不太懂,干预不来。

帮助文档也是 ChatGPT 写的。核心功能如下

  1. 复制功能:非编辑模式下,单击任意单元格即可复制其内容到剪贴板。
  2. 编辑模式:进入编辑模式可以编辑单元格内容,完成后点击保存。
  3. 调整网格大小:通过弹出窗口自定义行数和列数。
  4. 调整单元格和字体大小:可以调整每个单元格的尺寸以及文本字体大小。
  5. 开机自动启动:将应用程序设置为随 Windows 启动。
  6. 托盘图标:方便用户通过托盘图标进行快速操作。
  7. 快捷键:使用 Ctrl + Alt + X 快捷键快速呼出窗口,Escape 隐藏窗口。

py代码如下

import tkinter as tk
from tkinter import messagebox, Menu
import pyperclip
import os
import sys
import json
import winreg as reg
import keyboard
from pystray import Icon, MenuItem, Menu as TrayMenu
from PIL import Image, ImageDraw
import threading

# made by ftq


# 文件名
CONTENT_FILE = 'cell_content.json'
CONFIG_FILE = 'window_config.json'

# 用于存储Entry组件,以便控制它们的可编辑状态
entry_widgets = []

# 跟踪编辑模式状态
is_edit_mode = False

def copy_content(entry):
    if not is_edit_mode:  # 仅在非编辑模式下复制内容
        content = entry.get()
        pyperclip.copy(content)
        show_copy_success_message(content)

def show_copy_success_message(content):
    # 创建一个提示框显示“内容 + 复制成功”
    success_message = tk.Toplevel(root)
    success_message.overrideredirect(True)  # 去掉窗口边框
    success_message.attributes("-topmost", True)  # 确保窗口在最上层
    label_text = f"{content} 复制成功"
    label = tk.Label(success_message, text=label_text, bg="black", fg="white", padx=10, pady=5)
    label.pack()

    # 获取主窗口的位置和大小
    root.update_idletasks()
    root_width = root.winfo_width()
    root_height = root.winfo_height()
    root_x = root.winfo_x()
    root_y = root.winfo_y()

    # 设置提示窗口位置为主窗口的右下角对齐
    x = root_x + root_width - success_message.winfo_reqwidth()
    y = root_y + root_height - success_message.winfo_reqheight()
    success_message.geometry(f"+{x}+{y}")

    # 3秒后自动关闭窗口
    success_message.after(3000, success_message.destroy)

def toggle_edit_mode():
    global is_edit_mode
    is_edit_mode = not is_edit_mode
    edit_button.config(text="保存" if is_edit_mode else "编辑")

    if not is_edit_mode:
        save_content_to_file()  # 在保存时保存内容到文件
    
    # 启用或禁用所有单元格的编辑状态
    for row in entry_widgets:
        for entry in row:
            if is_edit_mode:
                entry.config(state='normal')
                entry.unbind("<Button-1>")  # 解除绑定事件在编辑模式下
            else:
                entry.config(state='readonly')
                entry.bind("<Button-1>", lambda e, entry=entry: copy_content(entry))  # 恢复绑定事件在保存模式下

def open_adjust_grid_window():
    # 创建弹出窗口
    popup = tk.Toplevel(root)
    popup.title("调整网格大小")
    popup.geometry("200x100")
    
    # 弹出窗口中的内容
    row_label = tk.Label(popup, text="行数:")
    row_label.grid(row=0, column=0, padx=5, pady=5)
    
    row_entry = tk.Entry(popup, width=5)
    row_entry.grid(row=0, column=1, padx=5, pady=5)
    row_entry.insert(0, str(len(entry_widgets)))
    
    mul_label = tk.Label(popup, text="*")
    mul_label.grid(row=0, column=2, padx=5, pady=5)
    
    col_label = tk.Label(popup, text="列数:")
    col_label.grid(row=0, column=3, padx=5, pady=5)
    
    col_entry = tk.Entry(popup, width=5)
    col_entry.grid(row=0, column=4, padx=5, pady=5)
    col_entry.insert(0, str(len(entry_widgets[0]) if entry_widgets else 3))
    
    def confirm_grid_size():
        try:
            rows = int(row_entry.get())
            cols = int(col_entry.get())
            adjust_grid(rows, cols)
            popup.destroy()
        except ValueError:
            tk.messagebox.showerror("输入错误", "请输入有效的整数")
    
    confirm_button = tk.Button(popup, text="确认", command=confirm_grid_size)
    confirm_button.grid(row=1, column=0, columnspan=5, pady=10)

def open_adjust_cell_size_window():
    # 创建弹出窗口
    popup = tk.Toplevel(root)
    popup.title("调整单元格和字体大小")
    popup.geometry("250x150")
    
    # 弹出窗口中的内容
    font_label = tk.Label(popup, text="字体大小:")
    font_label.grid(row=0, column=0, padx=5, pady=5)
    
    font_entry = tk.Entry(popup, width=5)
    font_entry.grid(row=0, column=1, padx=5, pady=5)
    font_entry.insert(0, str(current_font_size))
    
    cell_width_label = tk.Label(popup, text="单元格宽度:")
    cell_width_label.grid(row=1, column=0, padx=5, pady=5)
    
    cell_width_entry = tk.Entry(popup, width=5)
    cell_width_entry.grid(row=1, column=1, padx=5, pady=5)
    cell_width_entry.insert(0, str(current_cell_width))
    
    cell_height_label = tk.Label(popup, text="单元格高度:")
    cell_height_label.grid(row=2, column=0, padx=5, pady=5)
    
    cell_height_entry = tk.Entry(popup, width=5)
    cell_height_entry.grid(row=2, column=1, padx=5, pady=5)
    cell_height_entry.insert(0, str(current_cell_height))
    
    def confirm_cell_size():
        try:
            global current_font_size, current_cell_width, current_cell_height
            current_font_size = int(font_entry.get())
            current_cell_width = int(cell_width_entry.get())
            current_cell_height = int(cell_height_entry.get())
            adjust_grid(len(entry_widgets), len(entry_widgets[0]) if entry_widgets else 3)
            save_config_to_file(
                grid_width, grid_height, len(entry_widgets), len(entry_widgets[0]), 
                current_font_size, current_cell_width, current_cell_height
            )
            popup.destroy()
        except ValueError:
            tk.messagebox.showerror("输入错误", "请输入有效的整数")
    
    confirm_button = tk.Button(popup, text="确认", command=confirm_cell_size)
    confirm_button.grid(row=3, column=0, columnspan=2, pady=10)

def adjust_grid(rows, cols):
    # 清除旧的单元格
    for widget in entry_widgets:
        for entry in widget:
            entry.destroy()
    entry_widgets.clear()

    # 加载已有内容或初始化空内容
    contents = load_content_from_file(rows, cols)

    # 创建新的单元格布局
    for i in range(rows):
        row_entries = []
        for j in range(cols):
            entry = tk.Entry(
                grid_frame, 
                width=current_cell_width, 
                justify='left', 
                font=('Arial', current_font_size),
                bg="#f7f7f7", # 单元格背景颜色
                relief=tk.RIDGE, # 单元格边框样式
            )
            entry.grid(
                row=i, 
                column=j, 
                padx=5, 
                pady=5, 
                ipady=(current_cell_height - current_font_size) // 2
            )
            entry.insert(0, contents[i][j])
            entry.config(state='readonly')  # 设置为只读状态
            entry.bind("<Button-1>", lambda e, entry=entry: copy_content(entry))  # 单击复制
            row_entries.append(entry)
        entry_widgets.append(row_entries)

    # 调整主窗口大小以适应新的网格
    extra_width = 5  # 额外宽度以确保右边不会太窄
    extra_height = 200  # 额外高度以确保底部空间

    global grid_width, grid_height
    # 每个单元格宽度乘以列数,加上额外的间隙
    grid_width = cols * (current_cell_width * 8 + 10) + extra_width
    # 每个单元格高度乘以行数,加上额外的间隙
    grid_height = rows * (current_cell_height + 10) + extra_height
    
    # 更新窗口的尺寸并确保足够的空间展示所有组件
    root.geometry(f"{int(grid_width)}x{grid_height}")
    root.update_idletasks()  # 确保更新所有组件布局

def init_adjust_grid(rows, cols):
    # 清除旧的单元格
    for widget in entry_widgets:
        for entry in widget:
            entry.destroy()
    entry_widgets.clear()

    # 加载已有内容或初始化空内容
    contents = load_content_from_file(rows, cols)

    # 创建新的单元格布局
    for i in range(rows):
        row_entries = []
        for j in range(cols):
            entry = tk.Entry(
                grid_frame, 
                width=current_cell_width, 
                justify='left', 
                font=('Arial', current_font_size),
                bg="#f7f7f7", # 单元格背景颜色
                relief=tk.RIDGE, # 单元格边框样式
            )
            entry.grid(
                row=i, 
                column=j, 
                padx=5, 
                pady=5, 
                ipady=(current_cell_height - current_font_size) // 2
            )
            entry.insert(0, contents[i][j])
            entry.config(state='readonly')  # 设置为只读状态
            entry.bind("<Button-1>", lambda e, entry=entry: copy_content(entry))  # 单击复制
            row_entries.append(entry)
        entry_widgets.append(row_entries)

    root.geometry(f"{int(window_width)}x{window_height}")
    root.update_idletasks()  # 确保更新所有组件布局


def load_content_from_file(rows, cols):
    """从文件加载单元格内容"""
    try:
        with open(CONTENT_FILE, 'r', encoding='utf-8') as file:
            data = json.load(file)
            contents = data.get('contents', [])
            loaded_contents = []
            for i in range(rows):
                row_contents = []
                for j in range(cols):
                    if i < len(contents) and j < len(contents[i]):
                        row_contents.append(contents[i][j])
                    else:
                        row_contents.append("")  # 默认设置为空字符串
                loaded_contents.append(row_contents)
            return loaded_contents
    except (FileNotFoundError, json.JSONDecodeError):
        pass

    # 如果文件不存在或格式不匹配,初始化为空内容
    return [["" for _ in range(cols)] for _ in range(rows)]

def save_content_to_file():
    """保存单元格内容到文件"""
    contents = [[entry.get() for entry in row] for row in entry_widgets]
    with open(CONTENT_FILE, 'w', encoding='utf-8') as file:
        json.dump({'contents': contents}, file, ensure_ascii=False, indent=4)

def load_config_from_file():
    """从文件加载窗口配置"""
    try:
        with open(CONFIG_FILE, 'r', encoding='utf-8') as file:
            data = json.load(file)
            return (
                data.get('width', 700), 
                data.get('height', 400), 
                data.get('rows', 3), 
                data.get('cols', 3),
                data.get('font_size', 10),
                data.get('cell_width', 20),
                data.get('cell_height', 10)
            )
    except (FileNotFoundError, json.JSONDecodeError):
        return 700, 400, 3, 3, 10, 20, 10  # 默认配置

def save_config_to_file(width, height, rows, cols, font_size, cell_width, cell_height):
    """保存窗口配置到文件"""
    with open(CONFIG_FILE, 'w', encoding='utf-8') as file:
        json.dump({
            'width': width, 
            'height': height, 
            'rows': rows, 
            'cols': cols,
            'font_size': font_size,
            'cell_width': cell_width,
            'cell_height': cell_height
        }, file, ensure_ascii=False, indent=4)

def set_startup():
    """设置开机自动启动"""
    # 获取当前程序的路径
    script_path = sys.executable if getattr(sys, 'frozen', False) else os.path.abspath(__file__)
    startup_path = r"Software\Microsoft\Windows\CurrentVersion\Run"
    app_name = "MyClipboardApp"

    try:
        # 打开注册表
        with reg.OpenKey(reg.HKEY_CURRENT_USER, startup_path, 0, reg.KEY_SET_VALUE) as key:
            reg.SetValueEx(key, app_name, 0, reg.REG_SZ, script_path)
        messagebox.showinfo("设置成功", "应用已设置为开机自动启动")
    except Exception as e:
        messagebox.showerror("设置失败", f"无法设置开机启动:{e}")

def on_closing():
    """窗口关闭事件的自定义功能,隐藏窗口而不是退出"""
    root.withdraw()  # 隐藏窗口

def toggle_window():
    """切换窗口的显示和隐藏状态"""
    if root.winfo_viewable():
        print("隐藏窗口")
        root.withdraw()
    else:
        print("显示窗口")
        root.deiconify()
        root.lift()
        root.attributes('-topmost', True)
        root.attributes('-topmost', False)

def hide_window():
    print("窗口隐藏")  # 调试信息,检查隐藏函数是否触发
    root.withdraw()  # 隐藏窗口

def show_help():
    """显示帮助信息"""
    help_message = (
        "调整字体和单元格大小:点击“调整字体和单元格大小”按钮,在弹出窗口中输入新的设置并确认。\n"
        "调整网格大小:点击“调整网格大小”按钮,弹出窗口后输入行数和列数,点击“确认”以调整网格。\n"
        "编辑内容:点击“编辑”按钮进入编辑模式,完成后点击“保存”按钮保存内容。\n"
        "设置开机启动:点击“设置开机自动启动”按钮以将应用程序设置为开机启动。\n"
        "Ctrl + Alt + X:呼出窗口。\n"
        "Escape:隐藏窗口。"
    )
    messagebox.showinfo("帮助", help_message)

def on_exit(icon, item):
    """退出程序"""
    icon.stop()
    root.quit()
    os._exit(0)

# 创建托盘图标
def create_image():
    #使用自己图片当图标
    return Image.open('icon.ico')

def setup_tray_icon():
    # 创建托盘菜单
    menu = TrayMenu(
        MenuItem('打开', toggle_window,default=True),
        MenuItem('退出', on_exit)
    )
    image = create_image()
    icon = Icon("test", image, "EasyCopy", menu)

    icon.run()

# 创建主窗口
root = tk.Tk()
root.title("EasyCopy")

# 加载配置
(window_width, window_height, 
 default_rows, default_cols, 
 current_font_size, current_cell_width, 
 current_cell_height) = load_config_from_file()

root.iconbitmap('icon.ico')

root.geometry(f"{window_width}x{window_height}")
root.attributes('-topmost', False)  # 确保窗口不总是置顶

# 设置窗口关闭事件的自定义处理函数
root.protocol("WM_DELETE_WINDOW", on_closing)

# 设置最小窗口大小防止窗口过小
root.minsize(300, 200)

# 设置窗口背景颜色
root.config(bg="#ececec")

# 创建用于存放按钮的框架
button_frame = tk.Frame(root, bg="#ececec")
button_frame.pack(anchor='nw', pady=5, padx=5)

# 创建编辑按钮
edit_button = tk.Button(button_frame, text="编辑", command=toggle_edit_mode, bg="#007bff", fg="white", relief=tk.FLAT, activebackground="#0056b3")
edit_button.pack(side='left', padx=5)

# 创建设置菜单按钮
settings_button = tk.Menubutton(button_frame, text="设置", relief=tk.RAISED, bg="#007bff", fg="white", activebackground="#0056b3")
settings_button.pack(side='left', padx=5)

# 创建菜单
settings_menu = Menu(settings_button, tearoff=0, bg="white", fg="#333333")
settings_button.config(menu=settings_menu)

# 添加菜单项
settings_menu.add_command(label="调整字体和单元格大小", command=open_adjust_cell_size_window)
settings_menu.add_command(label="调整网格大小", command=open_adjust_grid_window)
settings_menu.add_command(label="设置开机自动启动", command=set_startup)
settings_menu.add_command(label="帮助", command=show_help)

# 创建用于存放网格的框架
grid_frame = tk.Frame(root, bg="#ececec")
grid_frame.pack(expand=True, fill='both', pady=20, padx=10)

# 使用 keyboard 库设置全局快捷键以切换窗口显示状态
keyboard.add_hotkey("ctrl+alt+x", toggle_window)

# 绑定快捷键以隐藏窗口
root.bind("<Escape>", lambda event: hide_window())  # 绑定Escape键隐藏窗口

# 绑定配置变化事件以检测窗口大小变化
def on_window_resize(event):
    """保存窗口大小配置"""
    save_config_to_file(
        root.winfo_width(), root.winfo_height(), len(entry_widgets), len(entry_widgets[0]) if entry_widgets else 3, 
        current_font_size, current_cell_width, current_cell_height
    )

root.bind("<Configure>", on_window_resize)

# 启动时隐藏窗口
hide_window()

# 初始化网格布局
init_adjust_grid(default_rows, default_cols)


# 在单独的线程中运行托盘图标以避免阻塞主线程
tray_thread = threading.Thread(target=setup_tray_icon, daemon=True)
tray_thread.start()

# 启动应用程序
root.mainloop()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值