|
|
本帖最后由 a66 于 2026-4-15 09:04 编辑
第 1 个:简洁版剪贴板管理器(补全修复)
- import sys, json, os
- import pyperclip
- from PyQt5 import QtWidgets, QtCore, QtGui
- from pynput import keyboard
- DATA_FILE = "clipboard_data.json"
- # =========================
- # 数据结构
- # =========================
- class ClipboardItem:
- def __init__(self, text, count=0, pinned=False, timestamp=None):
- self.text = text
- self.count = count
- self.pinned = pinned
- self.timestamp = timestamp or QtCore.QDateTime.currentDateTime().toString()
- # =========================
- # 主程序
- # =========================
- class ClipboardManager(QtWidgets.QWidget):
- def __init__(self):
- super().__init__()
- # ===== 无边框 + 置顶 + 透明 =====
- self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
- self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
- self.resize(420, 320)
- # ===== 状态 =====
- self.dragPos = None
- self.hidden_to_edge = False
- self.edge_side = None
- # =========================
- # UI 主容器
- # =========================
- self.main = QtWidgets.QFrame(self)
- self.layout = QtWidgets.QVBoxLayout(self.main)
- self.layout.setContentsMargins(6, 6, 6, 6)
- self.layout.setSpacing(6)
- # =========================
- # 顶部栏
- # =========================
- top = QtWidgets.QHBoxLayout()
- self.search = QtWidgets.QLineEdit()
- self.search.setPlaceholderText("搜索")
- self.sort_box = QtWidgets.QComboBox()
- self.sort_box.addItems(["次数", "时间"])
- # 皮肤
- self.skin_box = QtWidgets.QComboBox()
- self.skin_box.addItems(["light", "dark", "blue"])
- self.skin_box.currentTextChanged.connect(self.apply_skin)
- close_btn = QtWidgets.QPushButton("✕")
- close_btn.setFixedSize(24, 24)
- close_btn.clicked.connect(self.close)
- top.addWidget(self.search)
- top.addWidget(self.sort_box)
- top.addWidget(self.skin_box)
- top.addWidget(close_btn)
- self.layout.addLayout(top)
- # =========================
- # 表格
- # =========================
- self.table = QtWidgets.QTableWidget(0, 2)
- self.table.setHorizontalHeaderLabels(["内容", "次"])
- self.table.setFrameShape(QtWidgets.QFrame.NoFrame)
- self.table.verticalHeader().setVisible(False)
- self.table.setColumnWidth(0, 300)
- self.table.setColumnWidth(1, 50)
- self.layout.addWidget(self.table)
- # 外层布局
- wrapper = QtWidgets.QVBoxLayout(self)
- wrapper.addWidget(self.main)
- # =========================
- # 数据
- # =========================
- self.data = []
- self.current_text = ""
- self.last_clipboard = ""
- self.load_data()
- # =========================
- # 定时监听剪贴板
- # =========================
- self.timer = QtCore.QTimer()
- self.timer.timeout.connect(self.check_clipboard)
- self.timer.start(600)
- # =========================
- # UI事件
- # =========================
- self.search.textChanged.connect(self.refresh_table)
- self.sort_box.currentIndexChanged.connect(self.refresh_table)
- self.table.cellClicked.connect(self.select_item)
- self.table.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
- self.table.customContextMenuRequested.connect(self.menu)
- # =========================
- # Alt + Q 全局快捷键
- # =========================
- self.hotkey = keyboard.GlobalHotKeys({
- '<alt>+q': self.toggle_window
- })
- self.hotkey.start()
- # =========================
- # 贴边检测
- # =========================
- self.edge_timer = QtCore.QTimer()
- self.edge_timer.timeout.connect(self.edge_check)
- self.edge_timer.start(300)
- # 默认皮肤
- self.apply_skin("light")
- # =========================================================
- # 换肤系统
- # =========================================================
- def apply_skin(self, skin):
- if skin == "light":
- bg = "rgba(255,255,255,190)"
- text = "#222"
- elif skin == "dark":
- bg = "rgba(35,35,35,210)"
- text = "#eee"
- else:
- bg = "rgba(180,210,255,180)"
- text = "#111"
- self.main.setStyleSheet(f"""
- QFrame {{
- background-color: {bg};
- border-radius: 10px;
- }}
- """)
- self.table.setStyleSheet(f"""
- QTableWidget {{
- background: transparent;
- border: none;
- color: {text};
- gridline-color: rgba(0,0,0,40);
- }}
- QHeaderView::section {{
- background: transparent;
- border: none;
- color: {text};
- }}
- QTableWidget::item {{
- border: none;
- padding: 4px;
- }}
- QTableWidget::item:hover {{
- background: rgba(0,0,0,20);
- }}
- """)
- # =========================================================
- # Alt+Q 显示/隐藏
- # =========================================================
- def toggle_window(self):
- if self.isVisible():
- self.hide()
- else:
- self.show()
- self.raise_()
- # =========================================================
- # 贴边隐藏
- # =========================================================
- def edge_check(self):
- if not self.isVisible():
- return
- geo = self.geometry()
- screen = QtWidgets.QApplication.primaryScreen().geometry()
- # 左边
- if geo.x() <= 0:
- self.edge_side = "left"
- self.move(-self.width() + 5, geo.y())
- self.hidden_to_edge = True
- # 右边
- elif geo.right() >= screen.width() - 1:
- self.edge_side = "right"
- self.move(screen.width() - 5, geo.y())
- self.hidden_to_edge = True
- # 鼠标唤出
- pos = QtGui.QCursor.pos()
- if self.hidden_to_edge:
- if self.edge_side == "left" and pos.x() <= 20:
- self.move(0, self.y())
- self.hidden_to_edge = False
- if self.edge_side == "right" and pos.x() >= screen.width() - 20:
- self.move(screen.width() - self.width(), self.y())
- self.hidden_to_edge = False
- # =========================================================
- # 关键:拖动窗口(已修复)
- # =========================================================
- def mousePressEvent(self, event):
- self.dragPos = event.globalPos()
- def mouseMoveEvent(self, event):
- if self.dragPos:
- self.move(self.pos() + event.globalPos() - self.dragPos)
- self.dragPos = event.globalPos()
- # =========================================================
- # 剪贴板
- # =========================================================
- def check_clipboard(self):
- text = pyperclip.paste()
- if not text:
- return
- # 统计
- if text != self.last_clipboard and text == self.current_text:
- for i in self.data:
- if i.text == text:
- i.count += 1
- break
- self.last_clipboard = text
- # 新增
- if not any(i.text == text for i in self.data):
- self.data.append(ClipboardItem(text))
- self.save_data()
- self.refresh_table()
- # =========================================================
- # 点击复制
- # =========================================================
- def select_item(self, row, col):
- data = self.get_data()
- self.current_text = data[row].text
- pyperclip.copy(self.current_text)
- # =========================================================
- # 排序逻辑(置顶永远优先)
- # =========================================================
- def get_data(self):
- keyword = self.search.text().lower()
- data = [i for i in self.data if keyword in i.text.lower()]
- pinned = [x for x in data if x.pinned]
- normal = [x for x in data if not x.pinned]
- if self.sort_box.currentIndex() == 0:
- normal.sort(key=lambda x: -x.count)
- else:
- normal.sort(key=lambda x: x.timestamp, reverse=True)
- return pinned + normal
- # =========================================================
- # 刷新表格
- # =========================================================
- def refresh_table(self):
- data = self.get_data()
- self.table.setRowCount(len(data))
- for i, item in enumerate(data):
- self.table.setItem(i, 0, QtWidgets.QTableWidgetItem(item.text))
- self.table.setItem(i, 1, QtWidgets.QTableWidgetItem(str(item.count)))
- # =========================================================
- # 右键菜单
- # =========================================================
- def menu(self, pos):
- row = self.table.currentRow()
- if row < 0:
- return
- data = self.get_data()
- item = data[row]
- menu = QtWidgets.QMenu()
- pin = menu.addAction("置顶/取消")
- delete = menu.addAction("删除")
- action = menu.exec_(self.table.viewport().mapToGlobal(pos))
- if action == pin:
- item.pinned = not item.pinned
- elif action == delete:
- self.data.remove(item)
- self.save_data()
- self.refresh_table()
- # =========================================================
- # 数据保存
- # =========================================================
- def save_data(self):
- with open(DATA_FILE, "w", encoding="utf-8") as f:
- json.dump([i.__dict__ for i in self.data], f, ensure_ascii=False)
- def load_data(self):
- if os.path.exists(DATA_FILE):
- try:
- with open(DATA_FILE, "r", encoding="utf-8") as f:
- items = json.load(f)
- for d in items:
- self.data.append(ClipboardItem(
- text=d.get("text", ""),
- count=d.get("count", 0),
- pinned=d.get("pinned", False),
- timestamp=d.get("timestamp")
- ))
- except:
- self.data = []
- if __name__ == "__main__":
- app = QtWidgets.QApplication(sys.argv)
- ui = ClipboardManager()
- ui.show()
- sys.exit(app.exec_())
复制代码
第 2 个:功能完整版剪贴板管理器
- import sys
- import os
- import json
- import keyboard
- import pyperclip
- from datetime import datetime
- from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
- QListWidget, QPushButton, QLabel, QLineEdit, QTextEdit,
- QComboBox, QSpinBox, QCheckBox, QMessageBox, QSystemTrayIcon,
- QMenu, QAction, QStyle, QListWidgetItem)
- from PyQt5.QtCore import Qt, QTimer, QSize
- from PyQt5.QtGui import QIcon, QColor, QPalette, QFont, QBrush
- class ClipboardHistoryApp(QMainWindow):
- def __init__(self):
- super().__init__()
-
- # 初始化设置
- self.history = []
- self.max_history = 100
- self.hotkeys_enabled = True
- self.auto_paste = True
- self.start_with_system = False
- self.history_file = "clipboard_history.json"
-
- # 加载历史记录
- self.load_history()
-
- # 初始化UI
- self.init_ui()
-
- # 设置系统托盘
- self.init_system_tray()
-
- # 设置定时器检查剪贴板变化
- self.clipboard_timer = QTimer(self)
- self.clipboard_timer.timeout.connect(self.check_clipboard)
- self.clipboard_timer.start(500) # 每500毫秒检查一次
-
- # 注册全局快捷键
- self.register_hotkeys()
-
- # 设置窗口样式
- self.set_window_style()
-
- def init_ui(self):
- """初始化用户界面"""
- self.setWindowTitle("剪贴板历史管理器")
- self.setGeometry(100, 100, 800, 600)
-
- # 主窗口部件
- main_widget = QWidget()
- self.setCentralWidget(main_widget)
-
- # 主布局
- main_layout = QHBoxLayout()
- main_widget.setLayout(main_layout)
-
- # 左侧面板 - 历史记录列表
- left_panel = QWidget()
- left_layout = QVBoxLayout()
- left_panel.setLayout(left_layout)
-
- self.history_list = QListWidget()
- self.history_list.setStyleSheet("""
- QListWidget {
- background-color: #f0f0f0;
- border: 1px solid #ccc;
- border-radius: 5px;
- padding: 5px;
- }
- QListWidget::item {
- padding: 8px;
- border-bottom: 1px solid #ddd;
- }
- QListWidget::item:hover {
- background-color: #e0e0e0;
- }
- QListWidget::item:selected {
- background-color: #4CAF50;
- color: white;
- }
- """)
- self.history_list.itemClicked.connect(self.show_selected_item)
- left_layout.addWidget(QLabel("剪贴板历史记录:"))
- left_layout.addWidget(self.history_list)
-
- # 右侧面板 - 详情和设置
- right_panel = QWidget()
- right_layout = QVBoxLayout()
- right_panel.setLayout(right_layout)
-
- # 详情区域
- detail_group = QWidget()
- detail_layout = QVBoxLayout()
- detail_group.setLayout(detail_layout)
-
- self.detail_text = QTextEdit()
- self.detail_text.setReadOnly(True)
- self.detail_text.setStyleSheet("""
- QTextEdit {
- background-color: #f9f9f9;
- border: 1px solid #ccc;
- border-radius: 5px;
- padding: 10px;
- min-height: 150px;
- }
- """)
- detail_layout.addWidget(QLabel("内容详情:"))
- detail_layout.addWidget(self.detail_text)
-
- # 操作按钮
- button_group = QWidget()
- button_layout = QHBoxLayout()
- button_group.setLayout(button_layout)
-
- self.copy_btn = QPushButton("复制选中项")
- self.copy_btn.setStyleSheet("""
- QPushButton {
- background-color: #4CAF50;
- color: white;
- border: none;
- padding: 8px;
- border-radius: 4px;
- }
- QPushButton:hover {
- background-color: #45a049;
- }
- """)
- self.copy_btn.clicked.connect(self.copy_selected)
-
- self.paste_btn = QPushButton("粘贴选中项")
- self.paste_btn.setStyleSheet("""
- QPushButton {
- background-color: #008CBA;
- color: white;
- border: none;
- padding: 8px;
- border-radius: 4px;
- }
- QPushButton:hover {
- background-color: #0077A3;
- }
- """)
- self.paste_btn.clicked.connect(self.paste_selected)
-
- self.delete_btn = QPushButton("删除选中项")
- self.delete_btn.setStyleSheet("""
- QPushButton {
- background-color: #f44336;
- color: white;
- border: none;
- padding: 8px;
- border-radius: 4px;
- }
- QPushButton:hover {
- background-color: #d32f2f;
- }
- """)
- self.delete_btn.clicked.connect(self.delete_selected)
-
- # 新增清空历史按钮
- self.clear_btn = QPushButton("清空历史")
- self.clear_btn.setStyleSheet("""
- QPushButton {
- background-color: #ff9800;
- color: white;
- border: none;
- padding: 8px;
- border-radius: 4px;
- }
- QPushButton:hover {
- background-color: #e68a00;
- }
- """)
- self.clear_btn.clicked.connect(self.clear_history)
-
- button_layout.addWidget(self.copy_btn)
- button_layout.addWidget(self.paste_btn)
- button_layout.addWidget(self.delete_btn)
- button_layout.addWidget(self.clear_btn)
-
- # 搜索区域
- search_group = QWidget()
- search_layout = QHBoxLayout()
- search_group.setLayout(search_layout)
-
- self.search_input = QLineEdit()
- self.search_input.setPlaceholderText("搜索剪贴板历史...")
- self.search_input.textChanged.connect(self.search_history)
- self.search_input.setStyleSheet("""
- QLineEdit {
- padding: 8px;
- border: 1px solid #ccc;
- border-radius: 4px;
- }
- """)
-
- search_btn = QPushButton("搜索")
- search_btn.setStyleSheet("""
- QPushButton {
- background-color: #555;
- color: white;
- border: none;
- padding: 8px 15px;
- border-radius: 4px;
- margin-left: 5px;
- }
- QPushButton:hover {
- background-color: #444;
- }
- """)
- search_btn.clicked.connect(self.search_history)
-
- search_layout.addWidget(self.search_input)
- search_layout.addWidget(search_btn)
-
- # 设置区域
- settings_group = QWidget()
- settings_layout = QVBoxLayout()
- settings_group.setLayout(settings_layout)
-
- settings_layout.addWidget(QLabel("设置:"))
-
- # 历史记录限制
- history_limit_layout = QHBoxLayout()
- history_limit_layout.addWidget(QLabel("最大历史记录数:"))
-
- self.history_limit_spin = QSpinBox()
- self.history_limit_spin.setRange(10, 500)
- self.history_limit_spin.setValue(self.max_history)
- self.history_limit_spin.valueChanged.connect(self.update_history_limit)
- history_limit_layout.addWidget(self.history_limit_spin)
-
- settings_layout.addLayout(history_limit_layout)
-
- # 快捷键设置
- hotkey_layout = QHBoxLayout()
- self.hotkey_checkbox = QCheckBox("启用快捷键 (Ctrl+D+数字)")
- self.hotkey_checkbox.setChecked(self.hotkeys_enabled)
- self.hotkey_checkbox.stateChanged.connect(self.toggle_hotkeys)
- hotkey_layout.addWidget(self.hotkey_checkbox)
-
- settings_layout.addLayout(hotkey_layout)
-
- # 自动粘贴设置
- auto_paste_layout = QHBoxLayout()
- self.auto_paste_checkbox = QCheckBox("使用快捷键时自动粘贴")
- self.auto_paste_checkbox.setChecked(self.auto_paste)
- self.auto_paste_checkbox.stateChanged.connect(self.toggle_auto_paste)
- auto_paste_layout.addWidget(self.auto_paste_checkbox)
-
- settings_layout.addLayout(auto_paste_layout)
-
- # 开机启动设置
- startup_layout = QHBoxLayout()
- self.startup_checkbox = QCheckBox("开机自动启动")
- self.startup_checkbox.setChecked(self.start_with_system)
- self.startup_checkbox.stateChanged.connect(self.toggle_start_with_system)
- startup_layout.addWidget(self.startup_checkbox)
-
- settings_layout.addLayout(startup_layout)
-
- # 主题选择
- theme_layout = QHBoxLayout()
- theme_layout.addWidget(QLabel("主题:"))
-
- self.theme_combo = QComboBox()
- self.theme_combo.addItems(["默认", "深色", "蓝色", "绿色", "粉色"])
- self.theme_combo.currentTextChanged.connect(self.change_theme)
- theme_layout.addWidget(self.theme_combo)
-
- settings_layout.addLayout(theme_layout)
-
- # 添加所有右侧组件
- right_layout.addWidget(detail_group)
- right_layout.addWidget(button_group)
- right_layout.addWidget(search_group)
- right_layout.addWidget(settings_group)
-
- # 添加左右面板到主布局
- main_layout.addWidget(left_panel, 70)
- main_layout.addWidget(right_panel, 30)
-
- # 更新历史记录列表
- self.update_history_list()
-
- def set_window_style(self):
- """设置窗口样式"""
- self.setStyleSheet("""
- QMainWindow {
- background-color: #f5f5f5;
- }
- QLabel {
- font-weight: bold;
- color: #333;
- }
- QGroupBox {
- border: 1px solid #ddd;
- border-radius: 5px;
- margin-top: 10px;
- padding-top: 15px;
- }
- QGroupBox::title {
- subcontrol-origin: margin;
- left: 10px;
- padding: 0 3px;
- }
- """)
-
- def init_system_tray(self):
- """初始化系统托盘"""
- self.tray_icon = QSystemTrayIcon(self)
- self.tray_icon.setIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
-
- tray_menu = QMenu()
-
- show_action = QAction("显示", self)
- show_action.triggered.connect(self.show)
- tray_menu.addAction(show_action)
-
- hide_action = QAction("隐藏", self)
- hide_action.triggered.connect(self.hide)
- tray_menu.addAction(hide_action)
-
- quit_action = QAction("退出", self)
- quit_action.triggered.connect(self.quit_app)
- tray_menu.addAction(quit_action)
-
- self.tray_icon.setContextMenu(tray_menu)
- self.tray_icon.show()
- self.tray_icon.activated.connect(self.tray_icon_activated)
-
- def tray_icon_activated(self, reason):
- """系统托盘图标被激活时的处理"""
- if reason == QSystemTrayIcon.DoubleClick:
- self.show()
-
- def closeEvent(self, event):
- """重写关闭事件,最小化到托盘"""
- event.ignore()
- self.hide()
- self.tray_icon.showMessage(
- "剪贴板历史管理器",
- "程序已最小化到系统托盘",
- QSystemTrayIcon.Information,
- 2000
- )
-
- def register_hotkeys(self):
- """注册全局快捷键"""
- if self.hotkeys_enabled:
- try:
- # 注册Ctrl+D+数字1-9的快捷键
- for i in range(1, 10):
- keyboard.add_hotkey(f'ctrl+d+{i}', lambda i=i: self.paste_from_history(i-1))
- except Exception as e:
- print(f"注册快捷键失败: {e}")
-
- def unregister_hotkeys(self):
- """取消注册全局快捷键"""
- try:
- keyboard.unhook_all_hotkeys()
- except Exception as e:
- print(f"取消注册快捷键失败: {e}")
-
- def toggle_hotkeys(self, state):
- """切换快捷键启用状态"""
- self.hotkeys_enabled = state == Qt.Checked
- if self.hotkeys_enabled:
- self.register_hotkeys()
- else:
- self.unregister_hotkeys()
-
- def toggle_auto_paste(self, state):
- """切换自动粘贴设置"""
- self.auto_paste = state == Qt.Checked
-
- def toggle_start_with_system(self, state):
- """切换开机启动设置"""
- self.start_with_system = state == Qt.Checked
- # 这里需要实现实际的开机启动设置逻辑
- QMessageBox.information(self, "提示", "开机启动功能需要根据操作系统进行额外配置")
-
- def update_history_limit(self, value):
- """更新历史记录最大数量"""
- self.max_history = value
- # 如果当前历史记录超过新限制,截断
- if len(self.history) > self.max_history:
- self.history = self.history[:self.max_history]
- self.update_history_list()
-
- def change_theme(self, theme_name):
- """更改应用程序主题"""
- if theme_name == "默认":
- self.setStyleSheet("")
- elif theme_name == "深色":
- self.set_dark_theme()
- elif theme_name == "蓝色":
- self.set_blue_theme()
- elif theme_name == "绿色":
- self.set_green_theme()
- elif theme_name == "粉色":
- self.set_pink_theme()
-
- def set_dark_theme(self):
- """设置深色主题"""
- dark_style = """
- QMainWindow {
- background-color: #333;
- }
- QListWidget {
- background-color: #444;
- color: #eee;
- border: 1px solid #555;
- }
- QListWidget::item {
- border-bottom: 1px solid #555;
- }
- QListWidget::item:hover {
- background-color: #555;
- }
- QListWidget::item:selected {
- background-color: #4CAF50;
- }
- QTextEdit, QLineEdit {
- background-color: #444;
- color: #eee;
- border: 1px solid #555;
- }
- QPushButton {
- background-color: #555;
- color: white;
- border: none;
- }
- QPushButton:hover {
- background-color: #666;
- }
- QLabel {
- color: #eee;
- }
- QSpinBox, QComboBox {
- background-color: #444;
- color: #eee;
- border: 1px solid #555;
- }
- """
- self.setStyleSheet(dark_style)
-
- def set_blue_theme(self):
- """设置蓝色主题"""
- blue_style = """
- QMainWindow {
- background-color: #e6f2ff;
- }
- QListWidget {
- background-color: #f0f7ff;
- border: 1px solid #b3d1ff;
- }
- QListWidget::item:hover {
- background-color: #d9e6ff;
- }
- QListWidget::item:selected {
- background-color: #4d94ff;
- color: white;
- }
- QPushButton {
- background-color: #4d94ff;
- color: white;
- }
- QPushButton:hover {
- background-color: #3d84ef;
- }
- """
- self.setStyleSheet(blue_style)
-
- def set_green_theme(self):
- """设置绿色主题"""
- green_style = """
- QMainWindow {
- background-color: #e6ffe6;
- }
- QListWidget {
- background-color: #f0fff0;
- border: 1px solid #b3e6b3;
- }
- QListWidget::item:hover {
- background-color: #d9ffd9;
- }
- QListWidget::item:selected {
- background-color: #4CAF50;
- color: white;
- }
- QPushButton {
- background-color: #4CAF50;
- color: white;
- }
- QPushButton:hover {
- background-color: #45a049;
- }
- """
- self.setStyleSheet(green_style)
-
- def set_pink_theme(self):
- """设置粉色主题"""
- pink_style = """
- QMainWindow {
- background-color: #ffe6f2;
- }
- QListWidget {
- background-color: #fff0f7;
- border: 1px solid #ffb3d9;
- }
- QListWidget::item:hover {
- background-color: #ffd9ec;
- }
- QListWidget::item:selected {
- background-color: #ff66b3;
- color: white;
- }
- QPushButton {
- background-color: #ff66b3;
- color: white;
- }
- QPushButton:hover {
- background-color: #ff4da6;
- }
- """
- self.setStyleSheet(pink_style)
-
- def check_clipboard(self):
- """检查剪贴板内容是否变化"""
- current_clipboard = pyperclip.paste()
- if current_clipboard and (not self.history or current_clipboard != self.history[0]['content']):
- # 添加到历史记录
- self.add_to_history(current_clipboard)
-
- def add_to_history(self, content):
- """添加内容到历史记录"""
- if not content.strip():
- return
-
- # 如果内容与上一条相同,不重复添加
- if self.history and content == self.history[0]['content']:
- return
-
- # 创建新的历史记录项
- history_item = {
- 'content': content,
- 'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- 'preview': content[:50] + ("..." if len(content) > 50 else "")
- }
-
- # 添加到历史记录开头
- self.history.insert(0, history_item)
-
- # 如果超过最大限制,移除最旧的记录
- if len(self.history) > self.max_history:
- self.history = self.history[:self.max_history]
-
- # 更新UI
- self.update_history_list()
-
- # 保存历史记录
- self.save_history()
-
- def update_history_list(self):
- """更新历史记录列表显示"""
- self.history_list.clear()
-
- # 定义不同颜色
- colors = [
- QColor("#FF0000"), # 红色
- QColor("#00AA00"), # 绿色
- QColor("#0000FF"), # 蓝色
- QColor("#AA00AA"), # 紫色
- QColor("#00AAAA"), # 青色
- QColor("#AA5500"), # 棕色
- QColor("#FF00FF"), # 粉红
- QColor("#555555"), # 深灰
- QColor("#0055FF") # 天蓝
- ]
-
- for i, item in enumerate(self.history):
- list_item = QListWidgetItem(f"[{i+1}] {item['timestamp']} - {item['preview']}")
-
- # 为前9项设置不同颜色
- if i < 9:
- list_item.setForeground(QBrush(colors[i % len(colors)]))
-
- self.history_list.addItem(list_item)
-
- def show_selected_item(self, item):
- """显示选中项的详细信息"""
- index = self.history_list.row(item)
- if 0 <= index < len(self.history):
- selected_item = self.history[index]
- self.detail_text.setPlainText(selected_item['content'])
-
- def copy_selected(self):
- """复制选中项到剪贴板"""
- selected_items = self.history_list.selectedItems()
- if selected_items:
- index = self.history_list.row(selected_items[0])
- if 0 <= index < len(self.history):
- pyperclip.copy(self.history[index]['content'])
- self.tray_icon.showMessage(
- "剪贴板历史管理器",
- "内容已复制到剪贴板",
- QSystemTrayIcon.Information,
- 1000
- )
-
- def paste_selected(self):
- """粘贴选中项"""
- self.copy_selected()
- # 模拟Ctrl+V粘贴
- if self.auto_paste:
- keyboard.send('ctrl+v')
-
- def paste_from_history(self, index):
- """通过快捷键粘贴指定索引的历史记录"""
- if 0 <= index < len(self.history):
- # 高亮显示对应的项
- if index < self.history_list.count():
- self.history_list.setCurrentRow(index)
- self.history_list.scrollToItem(self.history_list.currentItem())
-
- pyperclip.copy(self.history[index]['content'])
- if self.auto_paste:
- keyboard.send('ctrl+v')
-
- def delete_selected(self):
- """删除选中项"""
- selected_items = self.history_list.selectedItems()
- if selected_items:
- index = self.history_list.row(selected_items[0])
- if 0 <= index < len(self.history):
- # 确认对话框
- reply = QMessageBox.question(
- self, '确认删除',
- '确定要删除这条记录吗?',
- QMessageBox.Yes | QMessageBox.No,
- QMessageBox.No
- )
-
- if reply == QMessageBox.Yes:
- del self.history[index]
- self.update_history_list()
- self.detail_text.clear()
- self.save_history()
-
- def clear_history(self):
- """清空所有历史记录"""
- if not self.history:
- return
-
- # 确认对话框
- reply = QMessageBox.question(
- self, '确认清空',
- '确定要清空所有历史记录吗?此操作不可撤销!',
- QMessageBox.Yes | QMessageBox.No,
- QMessageBox.No
- )
-
- if reply == QMessageBox.Yes:
- self.history = []
- self.update_history_list()
- self.detail_text.clear()
- self.save_history()
-
- def search_history(self):
- """搜索历史记录"""
- search_text = self.search_input.text().lower()
- if not search_text:
- self.update_history_list()
- return
-
- self.history_list.clear()
-
- # 定义不同颜色
- colors = [
- QColor("#FF0000"), # 红色
- QColor("#00AA00"), # 绿色
- QColor("#0000FF"), # 蓝色
- QColor("#AA00AA"), # 紫色
- QColor("#00AAAA"), # 青色
- QColor("#AA5500"), # 棕色
- QColor("#FF00FF"), # 粉红
- QColor("#555555"), # 深灰
- QColor("#0055FF") # 天蓝
- ]
-
- for i, item in enumerate(self.history):
- if search_text in item['content'].lower():
- list_item = QListWidgetItem(f"[{i+1}] {item['timestamp']} - {item['preview']}")
-
- # 保持颜色效果
- if i < 9:
- list_item.setForeground(QBrush(colors[i % len(colors)]))
-
- self.history_list.addItem(list_item)
-
- def save_history(self):
- """保存历史记录到文件"""
- try:
- with open(self.history_file, 'w', encoding='utf-8') as f:
- json.dump(self.history, f, ensure_ascii=False, indent=2)
- except Exception as e:
- print(f"保存历史记录失败: {e}")
-
- def load_history(self):
- """从文件加载历史记录"""
- try:
- if os.path.exists(self.history_file):
- with open(self.history_file, 'r', encoding='utf-8') as f:
- self.history = json.load(f)
- except Exception as e:
- print(f"加载历史记录失败: {e}")
-
- def quit_app(self):
- """退出应用程序"""
- self.save_history()
- self.tray_icon.hide()
- QApplication.quit()
- if __name__ == "__main__":
- app = QApplication(sys.argv)
-
- # 设置应用程序图标
- app_icon = QIcon()
- app_icon.addFile('icon16.png', QSize(16, 16))
- app_icon.addFile('icon32.png', QSize(32, 32))
- app_icon.addFile('icon48.png', QSize(48, 48))
- app_icon.addFile('icon256.png', QSize(256, 256))
- app.setWindowIcon(app_icon)
-
- window = ClipboardHistoryApp()
- window.show()
- sys.exit(app.exec_())
复制代码
打包方法、步骤
1.安装 Python(最新版)
去官网下载安装包:https://www.python.org/downloads/windows/
安装时务必勾选:Add Python to PATH
2.打开「命令提示符」CMD(管理员最好)
依次复制粘贴这 4 行,一行一回车:
python -m pip install pip --upgrade
pip install wheel
pip install pyperclip pynput pyqt5 pyinstaller auto-py-to-exe
auto-py-to-exe
执行最后一行会自动弹出图形化打包界面,点点鼠标就行。
3.在 auto-py-to-exe 里这样设置(必成功)
以你的剪贴板代码为例:
- Script Location选择你保存的:clip1.py 或 clip2.py
- One File 选 One File(单 exe,更干净)
- Console Window 选 Window Based (hide the console)(不弹出黑框,纯 GUI 程序)
- 其他默认不用改直接点底部 Convert .py to .exe
4.打包完成后在哪找?
打包完会出现一个 output 文件夹里面就是:
双击直接用,不需要 Python
|
|