你修改一下,现在只能是二维,如果多维的也要处理,import sys
import win32api
import win32gui
import win32clipboard
import win32con
import time
import threading
import keyboard # 需要安装:pip install keyboard
from datetime import datetime, date
from PyQt5.QtWidgets import (QApplication, QMainWindow, QMessageBox,
QVBoxLayout, QLabel, QPushButton, QLineEdit, QWidget,
QTableWidget, QTableWidgetItem, QHBoxLayout, QGroupBox,
QDialog, QFormLayout, QDialogButtonBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
# 检查日期限制
EXPIRY_DATE = datetime(2026, 6, 1)
if datetime.now() > EXPIRY_DATE:
QMessageBox.critical(None, "过期提示", "此软件已过期,无法使用。")
sys.exit(1)
def show_error_message(message):
"""显示错误信息对话框。"""
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical)
msg.setText(message)
msg.setWindowTitle("错误")
msg.setStandardButtons(QMessageBox.Ok)
msg.exec_()
def show_success_message(message):
"""显示成功信息对话框。"""
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText(message)
msg.setWindowTitle("成功")
msg.setStandardButtons(QMessageBox.Ok)
msg.exec_()
def get_window_handle_at_mouse_cursor():
"""获取当前鼠标光标位置下的窗口句柄。"""
cursor_pos = win32api.GetCursorPos()
hwnd = win32gui.WindowFromPoint(cursor_pos)
return hwnd
def clear_window_content(hwnd):
"""清空窗口内容"""
# 检查窗口是否有效
if not win32gui.IsWindow(hwnd):
return False
# 尝试获取窗口类名
try:
class_name = win32gui.GetClassName(hwnd)
# 对于编辑框控件
if class_name in ["Edit", "RichEdit", "RichEdit20W"]:
win32gui.SendMessage(hwnd, win32con.EM_SETSEL, 0, -1) # 全选
win32gui.SendMessage(hwnd, win32con.WM_CLEAR, 0, 0) # 清除
return True
except:
pass
# 通用方法
try:
win32gui.SendMessage(hwnd, win32con.WM_SETTEXT, 0, "")
return True
except:
return False
def send_text_directly(hwnd, text):
"""直接向窗口发送文本"""
# 检查窗口是否有效
if not win32gui.IsWindow(hwnd):
return False
# 尝试获取窗口类名
try:
class_name = win32gui.GetClassName(hwnd)
# 对于编辑框控件
if class_name in ["Edit", "RichEdit", "RichEdit20W"]:
win32gui.SendMessage(hwnd, win32con.WM_SETTEXT, 0, text)
# 发送回车
win32gui.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
win32gui.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0)
return True
except:
pass
# 通用方法
try:
# 先清空内容
win32gui.SendMessage(hwnd, win32con.WM_SETTEXT, 0, "")
# 发送文本
win32gui.SendMessage(hwnd, win32con.WM_SETTEXT, 0, text)
# 发送回车
win32gui.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
win32gui.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0)
return True
except:
return False
def get_clipboard_data_as_list():
"""获取剪贴板内容并返回一维或二维列表。"""
for _ in range(3): # 重试3次
try:
win32clipboard.OpenClipboard(None)
clipboard_data = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
lines = [line.strip() for line in clipboard_data.split('\n') if line.strip()]
if any('\t' in line for line in lines):
return [line.split('\t') for line in lines]
else:
return lines
except Exception:
try:
win32clipboard.CloseClipboard()
except:
pass
time.sleep(0.1)
return []
class SenderThread(QThread):
"""发送线程,支持停止功能"""
progress = pyqtSignal(int, int) # 当前进度,总数量
finished = pyqtSignal(bool, str) # 是否成功,消息
error = pyqtSignal(str) # 错误消息
def __init__(self, parent, hwnds, data, delay):
super().__init__(parent)
self.hwnds = hwnds # 窗口句柄列表
self.data = data # 要发送的数据
self.delay = delay # 延时
self._stop_flag = False # 停止标志
def stop(self):
"""停止发送"""
self._stop_flag = True
def run(self):
"""执行发送操作"""
try:
total = len(self.data)
for i, item in enumerate(self.data):
# 检查停止标志
if self._stop_flag:
self.finished.emit(False, "用户停止发送")
return
# 检查窗口句柄是否有效
for hwnd in self.hwnds:
if not win32gui.IsWindow(hwnd):
self.error.emit(f"目标窗口已关闭或句柄无效: {hwnd}")
self.finished.emit(False, "窗口已关闭")
return
# 发送数据
if isinstance(item, list): # 二维数据
if len(item) != len(self.hwnds):
self.error.emit(f"数据维度与窗口数量不匹配: {len(item)} != {len(self.hwnds)}")
continue
for j, text in enumerate(item):
if not send_text_directly(self.hwnds[j], text):
self.error.emit(f"向窗口 {self.hwnds[j]} 发送数据失败")
else: # 一维数据
if not send_text_directly(self.hwnds[0], item):
self.error.emit(f"向窗口 {self.hwnds[0]} 发送数据失败")
# 更新进度
self.progress.emit(i + 1, total)
# 延时
time.sleep(self.delay)
self.finished.emit(True, "发送完成")
except Exception as e:
self.finished.emit(False, f"发送过程中发生错误: {str(e)}")
class ClipboardApp(QMainWindow):
def __init__(self):
super().__init__()
self.sender_thread = None
self.stop_requested = False
self.initUI()
self.setWindowTitle(f"剪贴板自动发送工具 (按F8停止)")
# 设置热键处理
self.hotkey_timer = QTimer(self)
self.hotkey_timer.timeout.connect(self.check_hotkey)
self.hotkey_timer.start(100) # 每100毫秒检查一次热键
def initUI(self):
self.setGeometry(300, 300, 800, 600)
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout()
# 添加预览区域
preview_layout = QHBoxLayout()
preview_btn = QPushButton("预览剪贴板", self)
preview_btn.clicked.connect(self.preview_clipboard)
preview_layout.addWidget(preview_btn)
layout.addLayout(preview_layout)
# 添加表格预览
self.table = QTableWidget(self)
self.table.setMinimumHeight(300)
layout.addWidget(self.table)
# 进度标签
self.progress_label = QLabel("准备就绪", self)
layout.addWidget(self.progress_label)
# 延时设置
delay_layout = QHBoxLayout()
delay_label = QLabel("发送延时(秒):", self)
self.delay_input = QLineEdit(self)
self.delay_input.setPlaceholderText("输入延时")
self.delay_input.setText("1.0") # 默认值
delay_layout.addWidget(delay_label)
delay_layout.addWidget(self.delay_input)
layout.addLayout(delay_layout)
# 说明标签
self.label = QLabel(
"使用说明:\n"
"1. 复制要发送的内容\n"
"2. 点击预览按钮查看内容\n"
"3. 设置发送延时\n"
"4. 点击开始后将鼠标移动到目标窗口\n"
"5. 按F8键可随时停止发送"
)
layout.addWidget(self.label)
# 按钮布局
button_layout = QHBoxLayout()
# 开始按钮
self.start_button = QPushButton('开始发送', self)
self.start_button.clicked.connect(self.start)
button_layout.addWidget(self.start_button)
# 停止按钮
self.stop_button = QPushButton('停止发送', self)
self.stop_button.clicked.connect(self.stop_sending)
self.stop_button.setEnabled(False)
button_layout.addWidget(self.stop_button)
layout.addLayout(button_layout)
# 添加有效期提示
expiry_label = QLabel(f"如有问题请联系我", self)
expiry_label.setStyleSheet("color: red; font-weight: bold;")
layout.addWidget(expiry_label)
central_widget.setLayout(layout)
def check_hotkey(self):
"""检查热键是否被按下"""
if keyboard.is_pressed('f8'):
self.stop_sending()
# 等待按键释放,避免连续触发
while keyboard.is_pressed('f8'):
time.sleep(0.1)
def preview_clipboard(self):
"""预览剪贴板内容"""
try:
data = get_clipboard_data_as_list()
if not data:
show_error_message("剪贴板为空")
return
# 判断是一维还是二维数据
if isinstance(data[0], list): # 二维数据
rows = len(data)
cols = max(len(row) for row in data)
self.table.setRowCount(rows)
self.table.setColumnCount(cols)
# 填充数据
for i, row in enumerate(data):
for j, value in enumerate(row):
item = QTableWidgetItem(str(value))
self.table.setItem(i, j, item)
else: # 一维数据
self.table.setRowCount(len(data))
self.table.setColumnCount(1)
# 填充数据
for i, value in enumerate(data):
item = QTableWidgetItem(str(value))
self.table.setItem(i, 0, item)
# 自动调整列宽
self.table.resizeColumnsToContents()
self.progress_label.setText(f"已加载 {len(data)} 行数据")
show_success_message("预览已更新")
except Exception as e:
show_error_message(f"预览失败: {str(e)}")
def start(self):
"""开始处理剪贴板内容"""
try:
self.delay = float(self.delay_input.text())
except ValueError:
show_error_message("请输入有效的延时!")
return
# 获取当前表格中的数据
rows = self.table.rowCount()
cols = self.table.columnCount()
if rows == 0:
show_error_message("没有数据可发送!")
return
self.progress_label.setText("请在5秒内移动鼠标到目标窗口...")
QApplication.processEvents() # 更新UI
time.sleep(5)
hwnd = get_window_handle_at_mouse_cursor()
# 检查窗口是否有效
if not win32gui.IsWindow(hwnd):
show_error_message("目标窗口无效或已关闭!")
return
if cols == 1: # 一维数据
data = [
self.table.item(row, 0).text()
for row in range(rows)
if self.table.item(row, 0)
]
hwnds = [hwnd]
else: # 二维数据
self.progress_label.setText("请在5秒内移动鼠标到第二个目标窗口...")
QApplication.processEvents() # 更新UI
time.sleep(5)
hwnd1 = get_window_handle_at_mouse_cursor()
# 检查窗口是否有效
if not win32gui.IsWindow(hwnd1):
show_error_message("第二个目标窗口无效或已关闭!")
return
# 构建二维数据
data = []
for row in range(rows):
row_data = []
for col in range(cols):
item = self.table.item(row, col)
row_data.append(item.text() if item else "")
data.append(row_data)
hwnds = [hwnd, hwnd1]
# 创建并启动发送线程
self.sender_thread = SenderThread(self, hwnds, data, self.delay)
self.sender_thread.progress.connect(self.update_progress)
self.sender_thread.finished.connect(self.sending_finished)
self.sender_thread.error.connect(self.show_error)
# 更新UI状态
self.start_button.setEnabled(False)
self.stop_button.setEnabled(True)
self.progress_label.setText("开始发送...按F8键停止")
self.sender_thread.start()
def stop_sending(self):
"""停止发送操作"""
if self.sender_thread and self.sender_thread.isRunning():
self.sender_thread.stop()
self.stop_button.setEnabled(False)
self.progress_label.setText("正在停止...")
def update_progress(self, current, total):
"""更新进度显示"""
self.progress_label.setText(f"发送进度: {current}/{total} ({current / total * 100:.1f}%)")
def sending_finished(self, success, message):
"""发送完成处理"""
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
if success:
self.progress_label.setText(message)
show_success_message(message)
else:
self.progress_label.setText(f"发送失败: {message}")
show_error_message(message)
def show_error(self, message):
"""显示错误消息"""
self.progress_label.setText(f"错误: {message}")
show_error_message(message)
def closeEvent(self, event):
"""窗口关闭事件处理"""
if self.sender_thread and self.sender_thread.isRunning():
self.sender_thread.stop()
self.sender_thread.wait(2000) # 等待2秒
# 停止热键检查计时器
self.hotkey_timer.stop()
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
# 再次检查日期限制(双保险)
if datetime.now() > EXPIRY_DATE:
QMessageBox.critical(None, "过期提示", "此软件已过期,无法使用。")
sys.exit(1)
main_window = ClipboardApp()
main_window.show()
sys.exit(app.exec_())