找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 145|回复: 8

[求助] 剪贴板管理器python代码打包成可执行文件

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
请那位好心大佬给剪贴板管理器,python代码打包成可执行文件
有两个在网上找的本人不会,万分感谢!




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 l




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_())






发表于 2 小时前 | 显示全部楼层
本帖最后由 redapple 于 2026-4-15 09:10 编辑

第一个好像代码不全,第二个代码是全的

通过网盘分享的文件:jqb.7z
链接: https://pan.baidu.com/s/1ldALkdbRljlwCt0V-P4eSg?pwd=yph2 提取码: yph2 复制这段内容后打开百度网盘手机App,操作更方便哦

回复

使用道具 举报

发表于 2 小时前 来自手机 | 显示全部楼层
首先安装python最新版
然后在命令行内执行以下命令
python -m pip install pip --upgrade
pip install wheel
pip install pyperclip pynput pyqt5 pyinstaller auto-py-to-exe
执行auto-py-to-exe打开打包成exe的GUI程序,选择语言,选择源码、打包成单文件还是文件夹,建议文件夹,单文件其实就是自解压。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层
本帖最后由 a66 于 2026-4-15 09:04 编辑

第 1 个:简洁版剪贴板管理器(补全修复)

  1. import sys, json, os
  2. import pyperclip
  3. from PyQt5 import QtWidgets, QtCore, QtGui
  4. from pynput import keyboard

  5. DATA_FILE = "clipboard_data.json"


  6. # =========================
  7. # 数据结构
  8. # =========================
  9. class ClipboardItem:
  10.     def __init__(self, text, count=0, pinned=False, timestamp=None):
  11.         self.text = text
  12.         self.count = count
  13.         self.pinned = pinned
  14.         self.timestamp = timestamp or QtCore.QDateTime.currentDateTime().toString()


  15. # =========================
  16. # 主程序
  17. # =========================
  18. class ClipboardManager(QtWidgets.QWidget):
  19.     def __init__(self):
  20.         super().__init__()

  21.         # ===== 无边框 + 置顶 + 透明 =====
  22.         self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
  23.         self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

  24.         self.resize(420, 320)

  25.         # ===== 状态 =====
  26.         self.dragPos = None
  27.         self.hidden_to_edge = False
  28.         self.edge_side = None

  29.         # =========================
  30.         # UI 主容器
  31.         # =========================
  32.         self.main = QtWidgets.QFrame(self)

  33.         self.layout = QtWidgets.QVBoxLayout(self.main)
  34.         self.layout.setContentsMargins(6, 6, 6, 6)
  35.         self.layout.setSpacing(6)

  36.         # =========================
  37.         # 顶部栏
  38.         # =========================
  39.         top = QtWidgets.QHBoxLayout()

  40.         self.search = QtWidgets.QLineEdit()
  41.         self.search.setPlaceholderText("搜索")

  42.         self.sort_box = QtWidgets.QComboBox()
  43.         self.sort_box.addItems(["次数", "时间"])

  44.         # 皮肤
  45.         self.skin_box = QtWidgets.QComboBox()
  46.         self.skin_box.addItems(["light", "dark", "blue"])
  47.         self.skin_box.currentTextChanged.connect(self.apply_skin)

  48.         close_btn = QtWidgets.QPushButton("✕")
  49.         close_btn.setFixedSize(24, 24)
  50.         close_btn.clicked.connect(self.close)

  51.         top.addWidget(self.search)
  52.         top.addWidget(self.sort_box)
  53.         top.addWidget(self.skin_box)
  54.         top.addWidget(close_btn)

  55.         self.layout.addLayout(top)

  56.         # =========================
  57.         # 表格
  58.         # =========================
  59.         self.table = QtWidgets.QTableWidget(0, 2)
  60.         self.table.setHorizontalHeaderLabels(["内容", "次"])
  61.         self.table.setFrameShape(QtWidgets.QFrame.NoFrame)
  62.         self.table.verticalHeader().setVisible(False)

  63.         self.table.setColumnWidth(0, 300)
  64.         self.table.setColumnWidth(1, 50)

  65.         self.layout.addWidget(self.table)

  66.         # 外层布局
  67.         wrapper = QtWidgets.QVBoxLayout(self)
  68.         wrapper.addWidget(self.main)

  69.         # =========================
  70.         # 数据
  71.         # =========================
  72.         self.data = []
  73.         self.current_text = ""
  74.         self.last_clipboard = ""

  75.         self.load_data()

  76.         # =========================
  77.         # 定时监听剪贴板
  78.         # =========================
  79.         self.timer = QtCore.QTimer()
  80.         self.timer.timeout.connect(self.check_clipboard)
  81.         self.timer.start(600)

  82.         # =========================
  83.         # UI事件
  84.         # =========================
  85.         self.search.textChanged.connect(self.refresh_table)
  86.         self.sort_box.currentIndexChanged.connect(self.refresh_table)
  87.         self.table.cellClicked.connect(self.select_item)

  88.         self.table.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
  89.         self.table.customContextMenuRequested.connect(self.menu)

  90.         # =========================
  91.         # Alt + Q 全局快捷键
  92.         # =========================
  93.         self.hotkey = keyboard.GlobalHotKeys({
  94.             '<alt>+q': self.toggle_window
  95.         })
  96.         self.hotkey.start()

  97.         # =========================
  98.         # 贴边检测
  99.         # =========================
  100.         self.edge_timer = QtCore.QTimer()
  101.         self.edge_timer.timeout.connect(self.edge_check)
  102.         self.edge_timer.start(300)

  103.         # 默认皮肤
  104.         self.apply_skin("light")

  105.     # =========================================================
  106.     # 换肤系统
  107.     # =========================================================
  108.     def apply_skin(self, skin):
  109.         if skin == "light":
  110.             bg = "rgba(255,255,255,190)"
  111.             text = "#222"
  112.         elif skin == "dark":
  113.             bg = "rgba(35,35,35,210)"
  114.             text = "#eee"
  115.         else:
  116.             bg = "rgba(180,210,255,180)"
  117.             text = "#111"

  118.         self.main.setStyleSheet(f"""
  119.             QFrame {{
  120.                 background-color: {bg};
  121.                 border-radius: 10px;
  122.             }}
  123.         """)

  124.         self.table.setStyleSheet(f"""
  125.             QTableWidget {{
  126.                 background: transparent;
  127.                 border: none;
  128.                 color: {text};
  129.                 gridline-color: rgba(0,0,0,40);
  130.             }}
  131.             QHeaderView::section {{
  132.                 background: transparent;
  133.                 border: none;
  134.                 color: {text};
  135.             }}
  136.             QTableWidget::item {{
  137.                 border: none;
  138.                 padding: 4px;
  139.             }}
  140.             QTableWidget::item:hover {{
  141.                 background: rgba(0,0,0,20);
  142.             }}
  143.         """)

  144.     # =========================================================
  145.     # Alt+Q 显示/隐藏
  146.     # =========================================================
  147.     def toggle_window(self):
  148.         if self.isVisible():
  149.             self.hide()
  150.         else:
  151.             self.show()
  152.             self.raise_()

  153.     # =========================================================
  154.     # 贴边隐藏
  155.     # =========================================================
  156.     def edge_check(self):
  157.         if not self.isVisible():
  158.             return

  159.         geo = self.geometry()
  160.         screen = QtWidgets.QApplication.primaryScreen().geometry()

  161.         # 左边
  162.         if geo.x() <= 0:
  163.             self.edge_side = "left"
  164.             self.move(-self.width() + 5, geo.y())
  165.             self.hidden_to_edge = True

  166.         # 右边
  167.         elif geo.right() >= screen.width() - 1:
  168.             self.edge_side = "right"
  169.             self.move(screen.width() - 5, geo.y())
  170.             self.hidden_to_edge = True

  171.         # 鼠标唤出
  172.         pos = QtGui.QCursor.pos()

  173.         if self.hidden_to_edge:
  174.             if self.edge_side == "left" and pos.x() <= 20:
  175.                 self.move(0, self.y())
  176.                 self.hidden_to_edge = False

  177.             if self.edge_side == "right" and pos.x() >= screen.width() - 20:
  178.                 self.move(screen.width() - self.width(), self.y())
  179.                 self.hidden_to_edge = False

  180.     # =========================================================
  181.     # 关键:拖动窗口(已修复)
  182.     # =========================================================
  183.     def mousePressEvent(self, event):
  184.         self.dragPos = event.globalPos()

  185.     def mouseMoveEvent(self, event):
  186.         if self.dragPos:
  187.             self.move(self.pos() + event.globalPos() - self.dragPos)
  188.             self.dragPos = event.globalPos()

  189.     # =========================================================
  190.     # 剪贴板
  191.     # =========================================================
  192.     def check_clipboard(self):
  193.         text = pyperclip.paste()
  194.         if not text:
  195.             return

  196.         # 统计
  197.         if text != self.last_clipboard and text == self.current_text:
  198.             for i in self.data:
  199.                 if i.text == text:
  200.                     i.count += 1
  201.                     break

  202.         self.last_clipboard = text

  203.         # 新增
  204.         if not any(i.text == text for i in self.data):
  205.             self.data.append(ClipboardItem(text))
  206.             self.save_data()

  207.         self.refresh_table()

  208.     # =========================================================
  209.     # 点击复制
  210.     # =========================================================
  211.     def select_item(self, row, col):
  212.         data = self.get_data()
  213.         self.current_text = data[row].text
  214.         pyperclip.copy(self.current_text)

  215.     # =========================================================
  216.     # 排序逻辑(置顶永远优先)
  217.     # =========================================================
  218.     def get_data(self):
  219.         keyword = self.search.text().lower()
  220.         data = [i for i in self.data if keyword in i.text.lower()]

  221.         pinned = [x for x in data if x.pinned]
  222.         normal = [x for x in data if not x.pinned]

  223.         if self.sort_box.currentIndex() == 0:
  224.             normal.sort(key=lambda x: -x.count)
  225.         else:
  226.             normal.sort(key=lambda x: x.timestamp, reverse=True)

  227.         return pinned + normal

  228.     # =========================================================
  229.     # 刷新表格
  230.     # =========================================================
  231.     def refresh_table(self):
  232.         data = self.get_data()
  233.         self.table.setRowCount(len(data))

  234.         for i, item in enumerate(data):
  235.             self.table.setItem(i, 0, QtWidgets.QTableWidgetItem(item.text))
  236.             self.table.setItem(i, 1, QtWidgets.QTableWidgetItem(str(item.count)))

  237.     # =========================================================
  238.     # 右键菜单
  239.     # =========================================================
  240.     def menu(self, pos):
  241.         row = self.table.currentRow()
  242.         if row < 0:
  243.             return

  244.         data = self.get_data()
  245.         item = data[row]

  246.         menu = QtWidgets.QMenu()
  247.         pin = menu.addAction("置顶/取消")
  248.         delete = menu.addAction("删除")

  249.         action = menu.exec_(self.table.viewport().mapToGlobal(pos))

  250.         if action == pin:
  251.             item.pinned = not item.pinned
  252.         elif action == delete:
  253.             self.data.remove(item)

  254.         self.save_data()
  255.         self.refresh_table()

  256.     # =========================================================
  257.     # 数据保存
  258.     # =========================================================
  259.     def save_data(self):
  260.         with open(DATA_FILE, "w", encoding="utf-8") as f:
  261.             json.dump([i.__dict__ for i in self.data], f, ensure_ascii=False)

  262.     def load_data(self):
  263.         if os.path.exists(DATA_FILE):
  264.             try:
  265.                 with open(DATA_FILE, "r", encoding="utf-8") as f:
  266.                     items = json.load(f)
  267.                     for d in items:
  268.                         self.data.append(ClipboardItem(
  269.                             text=d.get("text", ""),
  270.                             count=d.get("count", 0),
  271.                             pinned=d.get("pinned", False),
  272.                             timestamp=d.get("timestamp")
  273.                         ))
  274.             except:
  275.                 self.data = []


  276. if __name__ == "__main__":
  277.     app = QtWidgets.QApplication(sys.argv)
  278.     ui = ClipboardManager()
  279.     ui.show()
  280.     sys.exit(app.exec_())
复制代码

第 2 个:功能完整版剪贴板管理器

  1. import sys
  2. import os
  3. import json
  4. import keyboard
  5. import pyperclip
  6. from datetime import datetime
  7. from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
  8.                             QListWidget, QPushButton, QLabel, QLineEdit, QTextEdit,
  9.                             QComboBox, QSpinBox, QCheckBox, QMessageBox, QSystemTrayIcon,
  10.                             QMenu, QAction, QStyle, QListWidgetItem)
  11. from PyQt5.QtCore import Qt, QTimer, QSize
  12. from PyQt5.QtGui import QIcon, QColor, QPalette, QFont, QBrush


  13. class ClipboardHistoryApp(QMainWindow):
  14.     def __init__(self):
  15.         super().__init__()
  16.         
  17.         # 初始化设置
  18.         self.history = []
  19.         self.max_history = 100
  20.         self.hotkeys_enabled = True
  21.         self.auto_paste = True
  22.         self.start_with_system = False
  23.         self.history_file = "clipboard_history.json"
  24.         
  25.         # 加载历史记录
  26.         self.load_history()
  27.         
  28.         # 初始化UI
  29.         self.init_ui()
  30.         
  31.         # 设置系统托盘
  32.         self.init_system_tray()
  33.         
  34.         # 设置定时器检查剪贴板变化
  35.         self.clipboard_timer = QTimer(self)
  36.         self.clipboard_timer.timeout.connect(self.check_clipboard)
  37.         self.clipboard_timer.start(500)  # 每500毫秒检查一次
  38.         
  39.         # 注册全局快捷键
  40.         self.register_hotkeys()
  41.         
  42.         # 设置窗口样式
  43.         self.set_window_style()
  44.    
  45.     def init_ui(self):
  46.         """初始化用户界面"""
  47.         self.setWindowTitle("剪贴板历史管理器")
  48.         self.setGeometry(100, 100, 800, 600)
  49.         
  50.         # 主窗口部件
  51.         main_widget = QWidget()
  52.         self.setCentralWidget(main_widget)
  53.         
  54.         # 主布局
  55.         main_layout = QHBoxLayout()
  56.         main_widget.setLayout(main_layout)
  57.         
  58.         # 左侧面板 - 历史记录列表
  59.         left_panel = QWidget()
  60.         left_layout = QVBoxLayout()
  61.         left_panel.setLayout(left_layout)
  62.         
  63.         self.history_list = QListWidget()
  64.         self.history_list.setStyleSheet("""
  65.             QListWidget {
  66.                 background-color: #f0f0f0;
  67.                 border: 1px solid #ccc;
  68.                 border-radius: 5px;
  69.                 padding: 5px;
  70.             }
  71.             QListWidget::item {
  72.                 padding: 8px;
  73.                 border-bottom: 1px solid #ddd;
  74.             }
  75.             QListWidget::item:hover {
  76.                 background-color: #e0e0e0;
  77.             }
  78.             QListWidget::item:selected {
  79.                 background-color: #4CAF50;
  80.                 color: white;
  81.             }
  82.         """)
  83.         self.history_list.itemClicked.connect(self.show_selected_item)
  84.         left_layout.addWidget(QLabel("剪贴板历史记录:"))
  85.         left_layout.addWidget(self.history_list)
  86.         
  87.         # 右侧面板 - 详情和设置
  88.         right_panel = QWidget()
  89.         right_layout = QVBoxLayout()
  90.         right_panel.setLayout(right_layout)
  91.         
  92.         # 详情区域
  93.         detail_group = QWidget()
  94.         detail_layout = QVBoxLayout()
  95.         detail_group.setLayout(detail_layout)
  96.         
  97.         self.detail_text = QTextEdit()
  98.         self.detail_text.setReadOnly(True)
  99.         self.detail_text.setStyleSheet("""
  100.             QTextEdit {
  101.                 background-color: #f9f9f9;
  102.                 border: 1px solid #ccc;
  103.                 border-radius: 5px;
  104.                 padding: 10px;
  105.                 min-height: 150px;
  106.             }
  107.         """)
  108.         detail_layout.addWidget(QLabel("内容详情:"))
  109.         detail_layout.addWidget(self.detail_text)
  110.         
  111.         # 操作按钮
  112.         button_group = QWidget()
  113.         button_layout = QHBoxLayout()
  114.         button_group.setLayout(button_layout)
  115.         
  116.         self.copy_btn = QPushButton("复制选中项")
  117.         self.copy_btn.setStyleSheet("""
  118.             QPushButton {
  119.                 background-color: #4CAF50;
  120.                 color: white;
  121.                 border: none;
  122.                 padding: 8px;
  123.                 border-radius: 4px;
  124.             }
  125.             QPushButton:hover {
  126.                 background-color: #45a049;
  127.             }
  128.         """)
  129.         self.copy_btn.clicked.connect(self.copy_selected)
  130.         
  131.         self.paste_btn = QPushButton("粘贴选中项")
  132.         self.paste_btn.setStyleSheet("""
  133.             QPushButton {
  134.                 background-color: #008CBA;
  135.                 color: white;
  136.                 border: none;
  137.                 padding: 8px;
  138.                 border-radius: 4px;
  139.             }
  140.             QPushButton:hover {
  141.                 background-color: #0077A3;
  142.             }
  143.         """)
  144.         self.paste_btn.clicked.connect(self.paste_selected)
  145.         
  146.         self.delete_btn = QPushButton("删除选中项")
  147.         self.delete_btn.setStyleSheet("""
  148.             QPushButton {
  149.                 background-color: #f44336;
  150.                 color: white;
  151.                 border: none;
  152.                 padding: 8px;
  153.                 border-radius: 4px;
  154.             }
  155.             QPushButton:hover {
  156.                 background-color: #d32f2f;
  157.             }
  158.         """)
  159.         self.delete_btn.clicked.connect(self.delete_selected)
  160.         
  161.         # 新增清空历史按钮
  162.         self.clear_btn = QPushButton("清空历史")
  163.         self.clear_btn.setStyleSheet("""
  164.             QPushButton {
  165.                 background-color: #ff9800;
  166.                 color: white;
  167.                 border: none;
  168.                 padding: 8px;
  169.                 border-radius: 4px;
  170.             }
  171.             QPushButton:hover {
  172.                 background-color: #e68a00;
  173.             }
  174.         """)
  175.         self.clear_btn.clicked.connect(self.clear_history)
  176.         
  177.         button_layout.addWidget(self.copy_btn)
  178.         button_layout.addWidget(self.paste_btn)
  179.         button_layout.addWidget(self.delete_btn)
  180.         button_layout.addWidget(self.clear_btn)
  181.         
  182.         # 搜索区域
  183.         search_group = QWidget()
  184.         search_layout = QHBoxLayout()
  185.         search_group.setLayout(search_layout)
  186.         
  187.         self.search_input = QLineEdit()
  188.         self.search_input.setPlaceholderText("搜索剪贴板历史...")
  189.         self.search_input.textChanged.connect(self.search_history)
  190.         self.search_input.setStyleSheet("""
  191.             QLineEdit {
  192.                 padding: 8px;
  193.                 border: 1px solid #ccc;
  194.                 border-radius: 4px;
  195.             }
  196.         """)
  197.         
  198.         search_btn = QPushButton("搜索")
  199.         search_btn.setStyleSheet("""
  200.             QPushButton {
  201.                 background-color: #555;
  202.                 color: white;
  203.                 border: none;
  204.                 padding: 8px 15px;
  205.                 border-radius: 4px;
  206.                 margin-left: 5px;
  207.             }
  208.             QPushButton:hover {
  209.                 background-color: #444;
  210.             }
  211.         """)
  212.         search_btn.clicked.connect(self.search_history)
  213.         
  214.         search_layout.addWidget(self.search_input)
  215.         search_layout.addWidget(search_btn)
  216.         
  217.         # 设置区域
  218.         settings_group = QWidget()
  219.         settings_layout = QVBoxLayout()
  220.         settings_group.setLayout(settings_layout)
  221.         
  222.         settings_layout.addWidget(QLabel("设置:"))
  223.         
  224.         # 历史记录限制
  225.         history_limit_layout = QHBoxLayout()
  226.         history_limit_layout.addWidget(QLabel("最大历史记录数:"))
  227.         
  228.         self.history_limit_spin = QSpinBox()
  229.         self.history_limit_spin.setRange(10, 500)
  230.         self.history_limit_spin.setValue(self.max_history)
  231.         self.history_limit_spin.valueChanged.connect(self.update_history_limit)
  232.         history_limit_layout.addWidget(self.history_limit_spin)
  233.         
  234.         settings_layout.addLayout(history_limit_layout)
  235.         
  236.         # 快捷键设置
  237.         hotkey_layout = QHBoxLayout()
  238.         self.hotkey_checkbox = QCheckBox("启用快捷键 (Ctrl+D+数字)")
  239.         self.hotkey_checkbox.setChecked(self.hotkeys_enabled)
  240.         self.hotkey_checkbox.stateChanged.connect(self.toggle_hotkeys)
  241.         hotkey_layout.addWidget(self.hotkey_checkbox)
  242.         
  243.         settings_layout.addLayout(hotkey_layout)
  244.         
  245.         # 自动粘贴设置
  246.         auto_paste_layout = QHBoxLayout()
  247.         self.auto_paste_checkbox = QCheckBox("使用快捷键时自动粘贴")
  248.         self.auto_paste_checkbox.setChecked(self.auto_paste)
  249.         self.auto_paste_checkbox.stateChanged.connect(self.toggle_auto_paste)
  250.         auto_paste_layout.addWidget(self.auto_paste_checkbox)
  251.         
  252.         settings_layout.addLayout(auto_paste_layout)
  253.         
  254.         # 开机启动设置
  255.         startup_layout = QHBoxLayout()
  256.         self.startup_checkbox = QCheckBox("开机自动启动")
  257.         self.startup_checkbox.setChecked(self.start_with_system)
  258.         self.startup_checkbox.stateChanged.connect(self.toggle_start_with_system)
  259.         startup_layout.addWidget(self.startup_checkbox)
  260.         
  261.         settings_layout.addLayout(startup_layout)
  262.         
  263.         # 主题选择
  264.         theme_layout = QHBoxLayout()
  265.         theme_layout.addWidget(QLabel("主题:"))
  266.         
  267.         self.theme_combo = QComboBox()
  268.         self.theme_combo.addItems(["默认", "深色", "蓝色", "绿色", "粉色"])
  269.         self.theme_combo.currentTextChanged.connect(self.change_theme)
  270.         theme_layout.addWidget(self.theme_combo)
  271.         
  272.         settings_layout.addLayout(theme_layout)
  273.         
  274.         # 添加所有右侧组件
  275.         right_layout.addWidget(detail_group)
  276.         right_layout.addWidget(button_group)
  277.         right_layout.addWidget(search_group)
  278.         right_layout.addWidget(settings_group)
  279.         
  280.         # 添加左右面板到主布局
  281.         main_layout.addWidget(left_panel, 70)
  282.         main_layout.addWidget(right_panel, 30)
  283.         
  284.         # 更新历史记录列表
  285.         self.update_history_list()
  286.    
  287.     def set_window_style(self):
  288.         """设置窗口样式"""
  289.         self.setStyleSheet("""
  290.             QMainWindow {
  291.                 background-color: #f5f5f5;
  292.             }
  293.             QLabel {
  294.                 font-weight: bold;
  295.                 color: #333;
  296.             }
  297.             QGroupBox {
  298.                 border: 1px solid #ddd;
  299.                 border-radius: 5px;
  300.                 margin-top: 10px;
  301.                 padding-top: 15px;
  302.             }
  303.             QGroupBox::title {
  304.                 subcontrol-origin: margin;
  305.                 left: 10px;
  306.                 padding: 0 3px;
  307.             }
  308.         """)
  309.    
  310.     def init_system_tray(self):
  311.         """初始化系统托盘"""
  312.         self.tray_icon = QSystemTrayIcon(self)
  313.         self.tray_icon.setIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
  314.         
  315.         tray_menu = QMenu()
  316.         
  317.         show_action = QAction("显示", self)
  318.         show_action.triggered.connect(self.show)
  319.         tray_menu.addAction(show_action)
  320.         
  321.         hide_action = QAction("隐藏", self)
  322.         hide_action.triggered.connect(self.hide)
  323.         tray_menu.addAction(hide_action)
  324.         
  325.         quit_action = QAction("退出", self)
  326.         quit_action.triggered.connect(self.quit_app)
  327.         tray_menu.addAction(quit_action)
  328.         
  329.         self.tray_icon.setContextMenu(tray_menu)
  330.         self.tray_icon.show()
  331.         self.tray_icon.activated.connect(self.tray_icon_activated)
  332.    
  333.     def tray_icon_activated(self, reason):
  334.         """系统托盘图标被激活时的处理"""
  335.         if reason == QSystemTrayIcon.DoubleClick:
  336.             self.show()
  337.    
  338.     def closeEvent(self, event):
  339.         """重写关闭事件,最小化到托盘"""
  340.         event.ignore()
  341.         self.hide()
  342.         self.tray_icon.showMessage(
  343.             "剪贴板历史管理器",
  344.             "程序已最小化到系统托盘",
  345.             QSystemTrayIcon.Information,
  346.             2000
  347.         )
  348.    
  349.     def register_hotkeys(self):
  350.         """注册全局快捷键"""
  351.         if self.hotkeys_enabled:
  352.             try:
  353.                 # 注册Ctrl+D+数字1-9的快捷键
  354.                 for i in range(1, 10):
  355.                     keyboard.add_hotkey(f'ctrl+d+{i}', lambda i=i: self.paste_from_history(i-1))
  356.             except Exception as e:
  357.                 print(f"注册快捷键失败: {e}")
  358.    
  359.     def unregister_hotkeys(self):
  360.         """取消注册全局快捷键"""
  361.         try:
  362.             keyboard.unhook_all_hotkeys()
  363.         except Exception as e:
  364.             print(f"取消注册快捷键失败: {e}")
  365.    
  366.     def toggle_hotkeys(self, state):
  367.         """切换快捷键启用状态"""
  368.         self.hotkeys_enabled = state == Qt.Checked
  369.         if self.hotkeys_enabled:
  370.             self.register_hotkeys()
  371.         else:
  372.             self.unregister_hotkeys()
  373.    
  374.     def toggle_auto_paste(self, state):
  375.         """切换自动粘贴设置"""
  376.         self.auto_paste = state == Qt.Checked
  377.    
  378.     def toggle_start_with_system(self, state):
  379.         """切换开机启动设置"""
  380.         self.start_with_system = state == Qt.Checked
  381.         # 这里需要实现实际的开机启动设置逻辑
  382.         QMessageBox.information(self, "提示", "开机启动功能需要根据操作系统进行额外配置")
  383.    
  384.     def update_history_limit(self, value):
  385.         """更新历史记录最大数量"""
  386.         self.max_history = value
  387.         # 如果当前历史记录超过新限制,截断
  388.         if len(self.history) > self.max_history:
  389.             self.history = self.history[:self.max_history]
  390.             self.update_history_list()
  391.    
  392.     def change_theme(self, theme_name):
  393.         """更改应用程序主题"""
  394.         if theme_name == "默认":
  395.             self.setStyleSheet("")
  396.         elif theme_name == "深色":
  397.             self.set_dark_theme()
  398.         elif theme_name == "蓝色":
  399.             self.set_blue_theme()
  400.         elif theme_name == "绿色":
  401.             self.set_green_theme()
  402.         elif theme_name == "粉色":
  403.             self.set_pink_theme()
  404.    
  405.     def set_dark_theme(self):
  406.         """设置深色主题"""
  407.         dark_style = """
  408.             QMainWindow {
  409.                 background-color: #333;
  410.             }
  411.             QListWidget {
  412.                 background-color: #444;
  413.                 color: #eee;
  414.                 border: 1px solid #555;
  415.             }
  416.             QListWidget::item {
  417.                 border-bottom: 1px solid #555;
  418.             }
  419.             QListWidget::item:hover {
  420.                 background-color: #555;
  421.             }
  422.             QListWidget::item:selected {
  423.                 background-color: #4CAF50;
  424.             }
  425.             QTextEdit, QLineEdit {
  426.                 background-color: #444;
  427.                 color: #eee;
  428.                 border: 1px solid #555;
  429.             }
  430.             QPushButton {
  431.                 background-color: #555;
  432.                 color: white;
  433.                 border: none;
  434.             }
  435.             QPushButton:hover {
  436.                 background-color: #666;
  437.             }
  438.             QLabel {
  439.                 color: #eee;
  440.             }
  441.             QSpinBox, QComboBox {
  442.                 background-color: #444;
  443.                 color: #eee;
  444.                 border: 1px solid #555;
  445.             }
  446.         """
  447.         self.setStyleSheet(dark_style)
  448.    
  449.     def set_blue_theme(self):
  450.         """设置蓝色主题"""
  451.         blue_style = """
  452.             QMainWindow {
  453.                 background-color: #e6f2ff;
  454.             }
  455.             QListWidget {
  456.                 background-color: #f0f7ff;
  457.                 border: 1px solid #b3d1ff;
  458.             }
  459.             QListWidget::item:hover {
  460.                 background-color: #d9e6ff;
  461.             }
  462.             QListWidget::item:selected {
  463.                 background-color: #4d94ff;
  464.                 color: white;
  465.             }
  466.             QPushButton {
  467.                 background-color: #4d94ff;
  468.                 color: white;
  469.             }
  470.             QPushButton:hover {
  471.                 background-color: #3d84ef;
  472.             }
  473.         """
  474.         self.setStyleSheet(blue_style)
  475.    
  476.     def set_green_theme(self):
  477.         """设置绿色主题"""
  478.         green_style = """
  479.             QMainWindow {
  480.                 background-color: #e6ffe6;
  481.             }
  482.             QListWidget {
  483.                 background-color: #f0fff0;
  484.                 border: 1px solid #b3e6b3;
  485.             }
  486.             QListWidget::item:hover {
  487.                 background-color: #d9ffd9;
  488.             }
  489.             QListWidget::item:selected {
  490.                 background-color: #4CAF50;
  491.                 color: white;
  492.             }
  493.             QPushButton {
  494.                 background-color: #4CAF50;
  495.                 color: white;
  496.             }
  497.             QPushButton:hover {
  498.                 background-color: #45a049;
  499.             }
  500.         """
  501.         self.setStyleSheet(green_style)
  502.    
  503.     def set_pink_theme(self):
  504.         """设置粉色主题"""
  505.         pink_style = """
  506.             QMainWindow {
  507.                 background-color: #ffe6f2;
  508.             }
  509.             QListWidget {
  510.                 background-color: #fff0f7;
  511.                 border: 1px solid #ffb3d9;
  512.             }
  513.             QListWidget::item:hover {
  514.                 background-color: #ffd9ec;
  515.             }
  516.             QListWidget::item:selected {
  517.                 background-color: #ff66b3;
  518.                 color: white;
  519.             }
  520.             QPushButton {
  521.                 background-color: #ff66b3;
  522.                 color: white;
  523.             }
  524.             QPushButton:hover {
  525.                 background-color: #ff4da6;
  526.             }
  527.         """
  528.         self.setStyleSheet(pink_style)
  529.    
  530.     def check_clipboard(self):
  531.         """检查剪贴板内容是否变化"""
  532.         current_clipboard = pyperclip.paste()
  533.         if current_clipboard and (not self.history or current_clipboard != self.history[0]['content']):
  534.             # 添加到历史记录
  535.             self.add_to_history(current_clipboard)
  536.    
  537.     def add_to_history(self, content):
  538.         """添加内容到历史记录"""
  539.         if not content.strip():
  540.             return
  541.             
  542.         # 如果内容与上一条相同,不重复添加
  543.         if self.history and content == self.history[0]['content']:
  544.             return
  545.             
  546.         # 创建新的历史记录项
  547.         history_item = {
  548.             'content': content,
  549.             'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  550.             'preview': content[:50] + ("..." if len(content) > 50 else "")
  551.         }
  552.         
  553.         # 添加到历史记录开头
  554.         self.history.insert(0, history_item)
  555.         
  556.         # 如果超过最大限制,移除最旧的记录
  557.         if len(self.history) > self.max_history:
  558.             self.history = self.history[:self.max_history]
  559.         
  560.         # 更新UI
  561.         self.update_history_list()
  562.         
  563.         # 保存历史记录
  564.         self.save_history()
  565.    
  566.     def update_history_list(self):
  567.         """更新历史记录列表显示"""
  568.         self.history_list.clear()
  569.         
  570.         # 定义不同颜色
  571.         colors = [
  572.             QColor("#FF0000"),  # 红色
  573.             QColor("#00AA00"),  # 绿色
  574.             QColor("#0000FF"),  # 蓝色
  575.             QColor("#AA00AA"),  # 紫色
  576.             QColor("#00AAAA"),  # 青色
  577.             QColor("#AA5500"),  # 棕色
  578.             QColor("#FF00FF"),  # 粉红
  579.             QColor("#555555"),  # 深灰
  580.             QColor("#0055FF")   # 天蓝
  581.         ]
  582.         
  583.         for i, item in enumerate(self.history):
  584.             list_item = QListWidgetItem(f"[{i+1}] {item['timestamp']} - {item['preview']}")
  585.             
  586.             # 为前9项设置不同颜色
  587.             if i < 9:
  588.                 list_item.setForeground(QBrush(colors[i % len(colors)]))
  589.             
  590.             self.history_list.addItem(list_item)
  591.    
  592.     def show_selected_item(self, item):
  593.         """显示选中项的详细信息"""
  594.         index = self.history_list.row(item)
  595.         if 0 <= index < len(self.history):
  596.             selected_item = self.history[index]
  597.             self.detail_text.setPlainText(selected_item['content'])
  598.    
  599.     def copy_selected(self):
  600.         """复制选中项到剪贴板"""
  601.         selected_items = self.history_list.selectedItems()
  602.         if selected_items:
  603.             index = self.history_list.row(selected_items[0])
  604.             if 0 <= index < len(self.history):
  605.                 pyperclip.copy(self.history[index]['content'])
  606.                 self.tray_icon.showMessage(
  607.                     "剪贴板历史管理器",
  608.                     "内容已复制到剪贴板",
  609.                     QSystemTrayIcon.Information,
  610.                     1000
  611.                 )
  612.    
  613.     def paste_selected(self):
  614.         """粘贴选中项"""
  615.         self.copy_selected()
  616.         # 模拟Ctrl+V粘贴
  617.         if self.auto_paste:
  618.             keyboard.send('ctrl+v')
  619.    
  620.     def paste_from_history(self, index):
  621.         """通过快捷键粘贴指定索引的历史记录"""
  622.         if 0 <= index < len(self.history):
  623.             # 高亮显示对应的项
  624.             if index < self.history_list.count():
  625.                 self.history_list.setCurrentRow(index)
  626.                 self.history_list.scrollToItem(self.history_list.currentItem())
  627.             
  628.             pyperclip.copy(self.history[index]['content'])
  629.             if self.auto_paste:
  630.                 keyboard.send('ctrl+v')
  631.    
  632.     def delete_selected(self):
  633.         """删除选中项"""
  634.         selected_items = self.history_list.selectedItems()
  635.         if selected_items:
  636.             index = self.history_list.row(selected_items[0])
  637.             if 0 <= index < len(self.history):
  638.                 # 确认对话框
  639.                 reply = QMessageBox.question(
  640.                     self, '确认删除',
  641.                     '确定要删除这条记录吗?',
  642.                     QMessageBox.Yes | QMessageBox.No,
  643.                     QMessageBox.No
  644.                 )
  645.                
  646.                 if reply == QMessageBox.Yes:
  647.                     del self.history[index]
  648.                     self.update_history_list()
  649.                     self.detail_text.clear()
  650.                     self.save_history()
  651.    
  652.     def clear_history(self):
  653.         """清空所有历史记录"""
  654.         if not self.history:
  655.             return
  656.             
  657.         # 确认对话框
  658.         reply = QMessageBox.question(
  659.             self, '确认清空',
  660.             '确定要清空所有历史记录吗?此操作不可撤销!',
  661.             QMessageBox.Yes | QMessageBox.No,
  662.             QMessageBox.No
  663.         )
  664.         
  665.         if reply == QMessageBox.Yes:
  666.             self.history = []
  667.             self.update_history_list()
  668.             self.detail_text.clear()
  669.             self.save_history()
  670.    
  671.     def search_history(self):
  672.         """搜索历史记录"""
  673.         search_text = self.search_input.text().lower()
  674.         if not search_text:
  675.             self.update_history_list()
  676.             return
  677.             
  678.         self.history_list.clear()
  679.         
  680.         # 定义不同颜色
  681.         colors = [
  682.             QColor("#FF0000"),  # 红色
  683.             QColor("#00AA00"),  # 绿色
  684.             QColor("#0000FF"),  # 蓝色
  685.             QColor("#AA00AA"),  # 紫色
  686.             QColor("#00AAAA"),  # 青色
  687.             QColor("#AA5500"),  # 棕色
  688.             QColor("#FF00FF"),  # 粉红
  689.             QColor("#555555"),  # 深灰
  690.             QColor("#0055FF")   # 天蓝
  691.         ]
  692.         
  693.         for i, item in enumerate(self.history):
  694.             if search_text in item['content'].lower():
  695.                 list_item = QListWidgetItem(f"[{i+1}] {item['timestamp']} - {item['preview']}")
  696.                
  697.                 # 保持颜色效果
  698.                 if i < 9:
  699.                     list_item.setForeground(QBrush(colors[i % len(colors)]))
  700.                
  701.                 self.history_list.addItem(list_item)
  702.    
  703.     def save_history(self):
  704.         """保存历史记录到文件"""
  705.         try:
  706.             with open(self.history_file, 'w', encoding='utf-8') as f:
  707.                 json.dump(self.history, f, ensure_ascii=False, indent=2)
  708.         except Exception as e:
  709.             print(f"保存历史记录失败: {e}")
  710.    
  711.     def load_history(self):
  712.         """从文件加载历史记录"""
  713.         try:
  714.             if os.path.exists(self.history_file):
  715.                 with open(self.history_file, 'r', encoding='utf-8') as f:
  716.                     self.history = json.load(f)
  717.         except Exception as e:
  718.             print(f"加载历史记录失败: {e}")
  719.    
  720.     def quit_app(self):
  721.         """退出应用程序"""
  722.         self.save_history()
  723.         self.tray_icon.hide()
  724.         QApplication.quit()


  725. if __name__ == "__main__":
  726.     app = QApplication(sys.argv)
  727.    
  728.     # 设置应用程序图标
  729.     app_icon = QIcon()
  730.     app_icon.addFile('icon16.png', QSize(16, 16))
  731.     app_icon.addFile('icon32.png', QSize(32, 32))
  732.     app_icon.addFile('icon48.png', QSize(48, 48))
  733.     app_icon.addFile('icon256.png', QSize(256, 256))
  734.     app.setWindowIcon(app_icon)
  735.    
  736.     window = ClipboardHistoryApp()
  737.     window.show()
  738.     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 FileOne File(单 exe,更干净)
  • Console WindowWindow Based (hide the console)(不弹出黑框,纯 GUI 程序)
  • 其他默认不用改直接点底部 Convert .py to .exe



4.打包完成后在哪找?

打包完会出现一个 output 文件夹里面就是:
  • clip1.exe
  • clip2.exe
双击直接用,不需要 Python














回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层
丢给豆包不行吗?
回复

使用道具 举报

发表于 1 小时前 来自手机 | 显示全部楼层
电脑上如果装有Python环境,直接命令行打包就行,很方便
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层
你就扔给谷哥 AI
叫他教你转成 EXE
它会告诉你下载 XX包
调用那个打包工具
工具可能还缺少东西,让你教你来

不过生成的文件 一般会大上20M-30M
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层
py程序体积太大
回复

使用道具 举报

发表于 半小时前 | 显示全部楼层
谢谢4楼a66大佬的代码,打包成单文件了。
https://wwaos.lanzouu.com/iB1Vf3nadtpe
密码:51iq
以前用python了,现在用C/C++了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1|闽公网安备35020302032614号 )

GMT+8, 2026-4-15 11:29

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表