win10小程序(十八)剪切板循环粘贴

'''

一个统信linux中运行的一个名叫"剪切板循环粘贴"的python程序,程序会自动保存设置,用于读取剪切板的内容并输出,如果是文本则以硬回车为分组且不忽略空段落,如果是表格则以单元格为分组且不忽略空单元。通过tktinter输入参数,界面尺寸300*250,底色淡黄色,一键处理的按键是糖果绿色,字体判断:字号12号。如果是linux字体fangsong ti,否则字体仿宋。有删除选项,默认选中。有开始延时输入框默认0.3s。有间隔延时输入框默认0.2s。有间隔操作输入框默认回车键。有浅绿开始键。

程序开始运行时,等待Ctrl+B为触发功能,执行以下操作命令,执行完毕后自动进入下一次等待触发一直到循环次数结束。具体是打开资源管理器触发功能后,自动将选中的文件通过该F2进入改名状态

功能1:输入每组的间隔,默认值0.2s,输入默认循环次数,默认循环次数为剪切板中的表格内容数量(包括空单元格)。

功能2:通过读取剪切板中的段落或表格内容,按单元格逐个执行粘贴判断及间隔时间及回车及间隔时间,实现各个组的连续输入。默认是:首先执行执行删除,然后按照间隔延时时间(默认0.2秒),然后执行粘贴判断,然后按照间隔延时时间(默认0.2秒),然后执行"回车键",然后按照间隔延时(默认0.2秒),然后执行下一次循环。

粘贴判断:如果段落或单元格有内容则正常执行粘贴操作,是空段落或空单元格则判断:如果勾选删除选项则执行删除,否则无操作,直接执行间隔延时时间(默认0.2秒)。

功能3:可以点击浅绿开始键,则也能开始连点,首先执行开始延时之后执行操作命令,点击浅红结束键或者再按一次Ctrl+B,则自动结束。

'''

#!/usr/bin/env python3

-*- coding: utf-8 -*-

"""

剪切板循环粘贴工具

功能:读取剪切板内容(文本按段落分组,表格按单元格分组),

按组循环执行粘贴、延时、回车操作。

触发方式:Ctrl+B 或点击开始按钮

"""

import tkinter as tk

from tkinter import messagebox

import json

import os

import threading

import time

import sys

import csv

import io

import re

尝试导入键盘监听库

try:

from pynput import keyboard

PYNPUT_AVAILABLE = True

except ImportError:

PYNPUT_AVAILABLE = False

print("提示: 未安装pynput库,全局热键Ctrl+B可能无法使用。")

print("请运行: pip install pynput")

尝试导入剪切板库

try:

import pyperclip

PYPERCLIP_AVAILABLE = True

except ImportError:

PYPERCLIP_AVAILABLE = False

print("提示: 未安装pyperclip库,剪切板功能无法使用。")

print("请运行: pip install pyperclip")

CONFIG_FILE = "clipboard_looper_config.json"

默认配置

DEFAULT_CONFIG = {

"window_size": "320x300",

"window_x": None,

"window_y": None,

"delete_empty": True,

"start_delay": 0.3,

"interval_delay": 0.2,

"action_key": "enter",

"loop_count": 0,

}

def load_config():

"""加载配置文件"""

if os.path.exists(CONFIG_FILE):

try:

with open(CONFIG_FILE, "r", encoding="utf-8") as f:

cfg = json.load(f)

for key, value in DEFAULT_CONFIG.items():

if key not in cfg:

cfg[key] = value

return cfg

except Exception as e:

print(f"加载配置失败: {e}")

return DEFAULT_CONFIG.copy()

return DEFAULT_CONFIG.copy()

def save_config(cfg):

"""保存配置文件"""

try:

with open(CONFIG_FILE, "w", encoding="utf-8") as f:

json.dump(cfg, f, ensure_ascii=False, indent=2)

except Exception as e:

print(f"保存配置失败: {e}")

字体设置

if sys.platform.startswith("linux"):

DEFAULT_FONT = ("Fangsong Ti", 12)

else:

DEFAULT_FONT = ("仿宋", 12)

颜色

COLOR_BG = "#FAF0D7"

COLOR_BUTTON_START = "#98FB98"

COLOR_BUTTON_STOP = "#FF9999"

class ClipboardLooper:

def init(self, master):

self.master = master

self.config = load_config()

master.title("剪切板循环粘贴工具")

master.configure(bg=COLOR_BG)

master.geometry(self.config.get("window_size", "320x300"))

if self.config.get("window_x") and self.config.get("window_y"):

master.geometry(f"+{self.config['window_x']}+{self.config['window_y']}")

self.running = False

self.loop_thread = None

self.create_widgets()

master.protocol("WM_DELETE_WINDOW", self.on_closing)

self.register_hotkey()

def create_widgets(self):

"""创建界面组件"""

main_frame = tk.Frame(self.master, bg=COLOR_BG)

main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

删除空项选项

self.delete_var = tk.BooleanVar(value=self.config.get("delete_empty", True))

delete_cb = tk.Checkbutton(

main_frame, text="删除空段落/空单元格",

variable=self.delete_var, bg=COLOR_BG, font=DEFAULT_FONT

)

delete_cb.pack(anchor=tk.W, pady=2)

开始延时

delay_frame = tk.Frame(main_frame, bg=COLOR_BG)

delay_frame.pack(fill=tk.X, pady=5)

tk.Label(delay_frame, text="开始延时(秒):", bg=COLOR_BG, font=DEFAULT_FONT).pack(side=tk.LEFT)

self.start_delay_var = tk.StringVar(value=str(self.config.get("start_delay", 0.3)))

tk.Entry(delay_frame, textvariable=self.start_delay_var, width=8, font=DEFAULT_FONT).pack(side=tk.LEFT, padx=5)

间隔延时

interval_frame = tk.Frame(main_frame, bg=COLOR_BG)

interval_frame.pack(fill=tk.X, pady=5)

tk.Label(interval_frame, text="间隔延时(秒):", bg=COLOR_BG, font=DEFAULT_FONT).pack(side=tk.LEFT)

self.interval_delay_var = tk.StringVar(value=str(self.config.get("interval_delay", 0.2)))

tk.Entry(interval_frame, textvariable=self.interval_delay_var, width=8, font=DEFAULT_FONT).pack(side=tk.LEFT, padx=5)

间隔操作

action_frame = tk.Frame(main_frame, bg=COLOR_BG)

action_frame.pack(fill=tk.X, pady=5)

tk.Label(action_frame, text="间隔操作:", bg=COLOR_BG, font=DEFAULT_FONT).pack(side=tk.LEFT)

self.action_var = tk.StringVar(value=self.config.get("action_key", "enter"))

action_combo = tk.OptionMenu(action_frame, self.action_var, "enter", "tab", "space", "none")

action_combo.config(font=DEFAULT_FONT, bg=COLOR_BG)

action_combo.pack(side=tk.LEFT, padx=5)

循环次数

loop_frame = tk.Frame(main_frame, bg=COLOR_BG)

loop_frame.pack(fill=tk.X, pady=5)

tk.Label(loop_frame, text="循环次数(0=自动):", bg=COLOR_BG, font=DEFAULT_FONT).pack(side=tk.LEFT)

self.loop_count_var = tk.StringVar(value=str(self.config.get("loop_count", 0)))

tk.Entry(loop_frame, textvariable=self.loop_count_var, width=8, font=DEFAULT_FONT).pack(side=tk.LEFT, padx=5)

预览区域

preview_frame = tk.LabelFrame(main_frame, text="剪切板预览", bg=COLOR_BG, font=DEFAULT_FONT)

preview_frame.pack(fill=tk.BOTH, expand=True, pady=5)

self.preview_text = tk.Text(preview_frame, height=6, width=35, font=DEFAULT_FONT, wrap=tk.WORD)

self.preview_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

刷新预览按钮

refresh_btn = tk.Button(

preview_frame, text="刷新预览", bg=COLOR_BUTTON_START,

font=DEFAULT_FONT, command=self.refresh_preview

)

refresh_btn.pack(pady=2)

提示信息

tip_label = tk.Label(

main_frame,

text="提示: 按 Ctrl+B 开始/停止循环粘贴\n\n"

"使用说明:\n"

"1. 复制文本或表格内容到剪切板\n"

"2. 选中文件后按F2进入改名状态\n"

"3. 按Ctrl+B开始自动粘贴\n"

"4. 再次按Ctrl+B或点击停止按钮结束",

bg=COLOR_BG, font=DEFAULT_FONT, justify=tk.LEFT

)

tip_label.pack(pady=5)

按钮框架

button_frame = tk.Frame(main_frame, bg=COLOR_BG)

button_frame.pack(fill=tk.X, pady=10)

self.start_btn = tk.Button(

button_frame, text="开始", bg=COLOR_BUTTON_START,

font=DEFAULT_FONT, command=self.start_loop, width=8

)

self.start_btn.pack(side=tk.LEFT, padx=10)

self.stop_btn = tk.Button(

button_frame, text="停止", bg=COLOR_BUTTON_STOP,

font=DEFAULT_FONT, command=self.stop_loop, width=8

)

self.stop_btn.pack(side=tk.LEFT, padx=10)

状态显示

self.status_var = tk.StringVar(value="就绪")

status_label = tk.Label(

main_frame, textvariable=self.status_var,

bg=COLOR_BG, font=DEFAULT_FONT, fg="blue"

)

status_label.pack(pady=5)

初始预览

self.refresh_preview()

def refresh_preview(self):

"""刷新剪切板预览"""

if not PYPERCLIP_AVAILABLE:

self.preview_text.delete(1.0, tk.END)

self.preview_text.insert(1.0, "pyperclip未安装")

return

content = pyperclip.paste()

if content:

显示前500字符

preview = content[:500]

if len(content) > 500:

preview += "..."

self.preview_text.delete(1.0, tk.END)

self.preview_text.insert(1.0, preview)

else:

self.preview_text.delete(1.0, tk.END)

self.preview_text.insert(1.0, "剪切板为空")

def register_hotkey(self):

"""注册全局热键Ctrl+B"""

if not PYNPUT_AVAILABLE:

self.status_var.set("提示: 未安装pynput,热键不可用")

return

try:

def on_activate():

self.master.after(0, self.toggle_loop)

self.hotkey = keyboard.GlobalHotKeys({

'<ctrl>+b': on_activate

})

self.hotkey.start()

except Exception as e:

print(f"注册热键失败: {e}")

self.status_var.set("热键注册失败")

def toggle_loop(self):

"""切换运行状态"""

if self.running:

self.stop_loop()

else:

self.start_loop()

def remove_surrounding_quotes(self, text):

"""删除开头和结尾的双引号(保留中间的双引号)"""

if not text or text == "":

return ""

result = text

循环删除开头的双引号

while result and result[0] == '"':

result = result[1:]

循环删除结尾的双引号

while result and result[-1] == '"':

result = result[:-1]

return result

def clean_cell_for_paste(self, cell):

"""清理单元格内容,准备粘贴(删除双引号,保留换行符)"""

if cell is None:

return ""

cell_str = str(cell)

第一步:删除开头和结尾的双引号(处理CSV包裹标记)

cell_str = self.remove_surrounding_quotes(cell_str)

第二步:处理内部的双引号转义(CSV中两个双引号代表一个)

cell_str = cell_str.replace('""', '"')

第三步:再次删除可能残留的开头结尾双引号

cell_str = self.remove_surrounding_quotes(cell_str)

第四步:如果单元格内容以引号开头但内容很短,可能是空单元格标记

if cell_str == '"' or cell_str == '""':

cell_str = ""

return cell_str

def parse_table_with_quotes(self, content):

"""正确解析包含引号和换行符的表格内容"""

cells = []

current_cell = ""

in_quotes = False

i = 0

length = len(content)

while i < length:

char = content[i]

if char == '"':

检查是否是转义引号(两个连续引号)

if i + 1 < length and content[i + 1] == '"':

两个连续引号是转义,代表一个引号字符

current_cell += '"'

i += 2

continue

else:

切换引号状态(开始或结束引号区域)

in_quotes = not in_quotes

i += 1

continue

if char == '\t' and not in_quotes:

制表符分隔单元格(在引号外)

cleaned = self.clean_cell_for_paste(current_cell)

cells.append(cleaned)

current_cell = ""

i += 1

continue

if char in ('\n', '\r', '\x0a', '\x0d') and not in_quotes:

换行符在引号外:表格模式下,换行符表示行结束

将当前单元格添加到列表中

cleaned = self.clean_cell_for_paste(current_cell)

cells.append(cleaned)

current_cell = ""

处理Windows换行符(\r\n)

if char == '\r' and i + 1 < length and content[i + 1] == '\n':

i += 2

else:

i += 1

continue

普通字符(包括引号内的换行符),添加到当前单元格

current_cell += char

i += 1

添加最后一个单元格

cleaned = self.clean_cell_for_paste(current_cell)

cells.append(cleaned)

移除末尾可能产生的空单元格(如果原内容末尾有换行符)

while len(cells) > 1 and cells[-1] == "" and content.rstrip('\n\r').endswith(('\n', '\r')):

保留由连续制表符产生的空单元格,只移除末尾多余的

pass

return cells

def parse_clipboard_content(self):

"""解析剪切板内容,返回分组列表"""

if not PYPERCLIP_AVAILABLE:

messagebox.showerror("错误", "未安装pyperclip库,无法读取剪切板")

return []

content = pyperclip.paste()

if not content:

return []

lines = content.splitlines()

has_tabs = any('\t' in line for line in lines if line.strip())

has_quotes = any('"' in line for line in lines)

if has_tabs or has_quotes:

表格模式(制表符分隔或CSV格式)

使用自定义解析器正确处理引号内的换行符

cells = self.parse_table_with_quotes(content)

return cells

else:

文本模式:按段落分组(保留空行)

paragraphs = []

for line in content.splitlines():

paragraphs.append(line)

return paragraphs

def send_key(self, key_name):

"""发送按键"""

try:

import pyautogui

if key_name == "enter":

pyautogui.press('enter')

elif key_name == "tab":

pyautogui.press('tab')

elif key_name == "space":

pyautogui.press('space')

return True

except ImportError:

try:

import keyboard

if key_name == "enter":

keyboard.press_and_release('enter')

elif key_name == "tab":

keyboard.press_and_release('tab')

elif key_name == "space":

keyboard.press_and_release('space')

return True

except ImportError:

self.status_var.set("错误: 请安装pyautogui或keyboard库")

return False

def paste_text(self, text):

"""粘贴文本"""

if not PYPERCLIP_AVAILABLE:

return False

清理文本:删除开头和结尾的双引号

if text and text.startswith('"'):

text = text[1:]

if text and text.endswith('"'):

text = text[:-1]

保存原剪切板内容

old_content = pyperclip.paste()

设置新内容并粘贴

pyperclip.copy(text)

try:

import pyautogui

pyautogui.hotkey('ctrl', 'v')

except ImportError:

try:

import keyboard

keyboard.press_and_release('ctrl+v')

except ImportError:

self.status_var.set("错误: 请安装pyautogui或keyboard库")

return False

恢复原剪切板内容

pyperclip.copy(old_content)

return True

def delete_current(self):

"""删除当前选中的内容(按Delete键)"""

try:

import pyautogui

pyautogui.press('delete')

except ImportError:

try:

import keyboard

keyboard.press_and_release('delete')

except ImportError:

return False

return True

def run_loop(self):

"""运行循环粘贴主逻辑"""

解析剪切板内容

items = self.parse_clipboard_content()

if not items:

self.master.after(0, lambda: self.status_var.set("错误: 剪切板为空或无法解析"))

self.master.after(0, self.stop_loop)

return

显示解析结果

item_count = len(items)

non_empty_count = len([i for i in items if i and i.strip()])

self.master.after(0, lambda: self.status_var.set(f"解析完成: 共{item_count}项, 非空{non_empty_count}项"))

确定循环次数

try:

loop_count = int(self.loop_count_var.get())

except ValueError:

loop_count = 0

if loop_count <= 0:

loop_count = len(items)

获取参数

try:

interval_delay = float(self.interval_delay_var.get())

except ValueError:

interval_delay = 0.2

action_key = self.action_var.get()

delete_empty = self.delete_var.get()

self.master.after(0, lambda: self.status_var.set(f"开始循环,共{loop_count}项"))

for i in range(loop_count):

if not self.running:

break

获取当前项

current_item = items[i % len(items)]

显示当前进度

preview = current_item[:25] + "..." if len(current_item) > 25 else current_item

preview = preview.replace('\n', '↵').replace('\r', '↵')

self.master.after(0, lambda idx=i+1, total=loop_count, p=preview:

self.status_var.set(f"处理 {idx}/{total}: [{p}]"))

步骤1:首先执行删除

self.delete_current()

步骤2:按照间隔延时时间

if not self.running:

break

time.sleep(interval_delay)

步骤3:执行粘贴判断

is_empty = (current_item == "" or current_item is None)

if not is_empty:

有内容则粘贴

self.paste_text(current_item)

如果是空内容,不再执行额外删除(已经删除过了)

if not self.running:

break

time.sleep(interval_delay)

if action_key != "none" and self.running:

self.send_key(action_key)

if not self.running:

break

time.sleep(interval_delay)

if self.running:

self.master.after(0, lambda: self.status_var.set("循环完成"))

self.master.after(0, self.stop_loop)

def start_loop(self):

"""开始循环粘贴"""

if self.running:

return

try:

start_delay = float(self.start_delay_var.get())

except ValueError:

start_delay = 0.3

self.running = True

self.start_btn.config(state=tk.DISABLED)

self.status_var.set(f"开始延时 {start_delay} 秒...")

def delayed_start():

if self.running:

self.loop_thread = threading.Thread(target=self.run_loop, daemon=True)

self.loop_thread.start()

self.master.after(int(start_delay * 1000), delayed_start)

def stop_loop(self):

"""停止循环粘贴"""

self.running = False

self.start_btn.config(state=tk.NORMAL)

self.status_var.set("已停止")

def on_closing(self):

"""窗口关闭时保存设置"""

self.config["window_size"] = self.master.geometry().split('+')[0]

self.config["window_x"] = self.master.winfo_x()

self.config["window_y"] = self.master.winfo_y()

self.config["delete_empty"] = self.delete_var.get()

self.config["start_delay"] = float(self.start_delay_var.get() or 0.3)

self.config["interval_delay"] = float(self.interval_delay_var.get() or 0.2)

self.config["action_key"] = self.action_var.get()

self.config["loop_count"] = int(self.loop_count_var.get() or 0)

save_config(self.config)

self.running = False

if hasattr(self, 'hotkey') and self.hotkey:

try:

self.hotkey.stop()

except:

pass

self.master.destroy()

def main():

root = tk.Tk()

app = ClipboardLooper(root)

root.mainloop()

if name == "main":

main()

相关推荐
APIshop2 小时前
Java获取淘宝商品价格、图片与视频:淘宝开放平台API实战指南
开发语言·python
唐叔在学习2 小时前
Python移动端应用消息提醒开发实践
开发语言·python
好家伙VCC2 小时前
**发散创新:基于Python与OpenCV的视频流帧级分析实战**在当前人工智能与计算机视觉飞速发展的背景下
java·人工智能·python·计算机视觉
xiaotao1312 小时前
阶段零:IDE选择 与 Jupyter Notebook / Lab 使用
ide·人工智能·python·jupyter
Pocker_Spades_A2 小时前
Python快速入门专业版(五十七)——POST请求与模拟登录:从表单分析到实战(以测试网站为例)
开发语言·python
Highcharts.js2 小时前
企业级数据可视化|BI 仪表板数据中台工业监控平台的选择分析
人工智能·python·信息可视化·数据挖掘·数据分析·highcharts
橙露2 小时前
Seaborn 高级可视化:统计图表制作与报告导出
python
2601_954434552 小时前
2026年专业深度测评:入门电钢琴品牌排名前五权威发布
大数据·人工智能·python
道清茗3 小时前
【RH294知识点汇总】第 3 章 《 管理变量和事实 》1
开发语言·python