Python+PyQt5打造炫酷的桌面快捷方式管理器

 更新时间:2026年03月11日 09:41:59   作者:创客白泽  
这篇文章主要为大家详细介绍了如何基于Python+PyQt5打造炫酷的桌面快捷方式管理器,可以实现多主题切换,动画效果和跨平台支持,感兴趣的小伙伴可以了解下

本文亮点:基于PyQt5+Emoji实现多主题切换、动画效果、跨平台支持的桌面快捷方式管理器,3000字详解开发全流程!

项目概述

在Windows/macOS/Linux系统中,我们经常需要快速访问常用程序和文件夹。传统方式是在桌面创建大量快捷方式,但会导致桌面杂乱无章。本项目使用PyQt5开发一个可视化快捷方式管理器,具有以下特点:

  • 4种精美主题:深色/浅色/紫色/渐变风格
  • 现代化UI:带动画效果的按钮、卡片式布局
  • 跨平台支持:适配Windows/macOS/Linux
  • Emoji集成:让界面更生动有趣
  • 实用功能:添加/打开/删除/创建桌面快捷方式

图1:系统架构图

功能特色

多主题切换系统

class ThemeManager:
    THEMES = {
        "dark": {"name": "🌙 深色主题", "BACKGROUND": "#1E1E1E", ...},
        "light": {"name": "☀️ 浅色主题", "BACKGROUND": "#F5F5F5", ...},
        "purple": {"name": "🟣 紫色主题", "BACKGROUND": "#2A0A3E", ...},
        "gradient": {"name": "🌈 渐变主题", 
                    "BACKGROUND": "qlineargradient(...)", ...}
    }

支持4种预设主题,一键切换不重启

动态交互效果

  • 按钮按压动画(缩放效果)
  • 悬停状态高亮提示
  • 平滑的列表滚动

智能图标系统

def set_icon(self, path):
    if os.path.isdir(path):
        self.icon_label.setText(emoji.emojize("📁"))
    elif ext in ('.exe', '.bat', '.cmd'):
        self.icon_label.setText(emoji.emojize("🖥️"))
    # 其他文件类型处理...

自动识别文件类型显示对应emoji图标

跨平台兼容性

# Windows创建快捷方式
winshell.CreateShortcut(...)
# macOS创建替身
os.symlink(...)
# Linux创建.desktop文件
with open(shortcut_path, "w") as f:
    f.write(desktop_file)

效果展示

浅色主题界面

渐变主题效果

开发步骤详解

环境准备

pip install PyQt5 emoji winshell  # Windows额外安装

项目结构

shortcut_manager/
├── main.py              # 主程序入口
├── themes/              # 主题配置文件
├── icons/               # 图标资源
└── shortcuts.json       # 快捷方式存储文件

核心实现流程

  • 初始化主窗口:设置基本属性和布局
  • 加载主题系统:读取ThemeManager配置
  • 构建UI组件
    • 标题栏(带主题切换)
    • 快捷方式列表(自定义ItemWidget)
    • 操作按钮区域
  • 实现业务逻辑
    • 添加快捷方式
    • 打开/删除条目
    • 创建桌面快捷方式
  • 持久化存储:JSON格式保存配置

核心代码解析

自定义动画按钮

class AnimatedButton(QPushButton):
    def mousePressEvent(self, event):
        self._animation.setEndValue(self.geometry().adjusted(2, 2, -2, -2))
        self._animation.start()

按压时产生缩小效果,释放时恢复

主题切换机制

def change_theme(self, theme_key):
    self.current_theme = theme_key
    # 动态更新所有组件样式
    self.update_ui_style()

跨平台快捷方式创建

def create_desktop_shortcut(self):
    if platform.system() == "Windows":
        # Windows使用winshell
    elif platform.system() == "Darwin":
        # macOS使用符号链接
    else:
        # Linux使用.desktop文件

列表项自定义Widget

class ShortcutItemWidget(QWidget):
    def __init__(self, name, path, theme):
        super().__init__()
        # 构建包含图标、名称、路径的自定义布局

源码下载

import sys
import os
import json
import platform
import emoji
import subprocess
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                            QLabel, QLineEdit, QPushButton, QListWidget, QListWidgetItem,
                            QFrame, QScrollArea, QFileDialog, QMessageBox, QMenu, QAction,
                            QSizePolicy, QSpacerItem, QStatusBar)
from PyQt5.QtCore import Qt, QSize, QPoint, QPropertyAnimation, QEasingCurve, QTimer
from PyQt5.QtGui import (QColor, QLinearGradient, QPainter, QFont, QFontDatabase, QPalette, 
                        QIcon, QPixmap, QMovie)

class EmojiLabel(QLabel):
    """支持emoji的标签"""
    def __init__(self, text="", parent=None):
        super().__init__(parent)
        self.setText(emoji.emojize(text))
        
class ThemeManager:
    """管理不同的UI主题"""
    THEMES = {
        "dark": {
            "name": "🌙 深色主题",
            "BACKGROUND": "#1E1E1E",
            "FOREGROUND": "#FFFFFF",
            "PRIMARY": "#6A5ACD",  # 紫罗兰色
            "SECONDARY": "#303030",
            "TERTIARY": "#404040",
            "HIGHLIGHT": "#9370DB",  # 中等紫罗兰色
            "ERROR": "#FF5252",
            "SUCCESS": "#4CAF50",
            "WARNING": "#FFA500",
            "TITLE_FONT": ("Segoe UI", 14, QFont.Bold),
            "HEADING_FONT": ("Segoe UI", 12, QFont.Bold),
            "NORMAL_FONT": ("Segoe UI", 10, QFont.Normal),
            "SMALL_FONT": ("Segoe UI", 9, QFont.Normal),
            "BUTTON_RADIUS": "12px",
            "CARD_RADIUS": "15px",
            "SHADOW": "0 4px 8px rgba(0, 0, 0, 0.3)"
        },
        "light": {
            "name": "☀️ 浅色主题",
            "BACKGROUND": "#F5F5F5",
            "FOREGROUND": "#000000",
            "PRIMARY": "#4169E1",  # 皇家蓝
            "SECONDARY": "#E0E0E0",
            "TERTIARY": "#BDBDBD",
            "HIGHLIGHT": "#6495ED",  # 矢车菊蓝
            "ERROR": "#D32F2F",
            "SUCCESS": "#388E3C",
            "WARNING": "#F57C00",
            "TITLE_FONT": ("Segoe UI", 14, QFont.Bold),
            "HEADING_FONT": ("Segoe UI", 12, QFont.Bold),
            "NORMAL_FONT": ("Segoe UI", 10, QFont.Normal),
            "SMALL_FONT": ("Segoe UI", 9, QFont.Normal),
            "BUTTON_RADIUS": "12px",
            "CARD_RADIUS": "15px",
            "SHADOW": "0 4px 8px rgba(0, 0, 0, 0.1)"
        },
        "purple": {
            "name": "🟣 紫色主题",
            "BACKGROUND": "#2A0A3E",
            "FOREGROUND": "#FFFFFF",
            "PRIMARY": "#9C27B0",
            "SECONDARY": "#4A148C",
            "TERTIARY": "#7B1FA2",
            "HIGHLIGHT": "#BA68C8",
            "ERROR": "#FF5252",
            "SUCCESS": "#4CAF50",
            "WARNING": "#FFA500",
            "TITLE_FONT": ("Segoe UI", 14, QFont.Bold),
            "HEADING_FONT": ("Segoe UI", 12, QFont.Bold),
            "NORMAL_FONT": ("Segoe UI", 10, QFont.Normal),
            "SMALL_FONT": ("Segoe UI", 9, QFont.Normal),
            "BUTTON_RADIUS": "12px",
            "CARD_RADIUS": "15px",
            "SHADOW": "0 4px 8px rgba(0, 0, 0, 0.3)"
        },
        "gradient": {
            "name": "🌈 渐变主题",
            "BACKGROUND": "qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #1E1E1E, stop:1 #2A0A3E)",
            "FOREGROUND": "#FFFFFF",
            "PRIMARY": "qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #6A5ACD, stop:1 #9C27B0)",
            "SECONDARY": "rgba(48, 48, 48, 0.7)",
            "TERTIARY": "rgba(64, 64, 64, 0.7)",
            "HIGHLIGHT": "qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #9370DB, stop:1 #BA68C8)",
            "ERROR": "#FF5252",
            "SUCCESS": "#4CAF50",
            "WARNING": "#FFA500",
            "TITLE_FONT": ("Segoe UI", 14, QFont.Bold),
            "HEADING_FONT": ("Segoe UI", 12, QFont.Bold),
            "NORMAL_FONT": ("Segoe UI", 10, QFont.Normal),
            "SMALL_FONT": ("Segoe UI", 9, QFont.Normal),
            "BUTTON_RADIUS": "12px",
            "CARD_RADIUS": "15px",
            "SHADOW": "0 4px 8px rgba(0, 0, 0, 0.3)"
        }
    }

    @staticmethod
    def get_theme(theme_name):
        """获取指定主题的配置"""
        return ThemeManager.THEMES.get(theme_name, ThemeManager.THEMES["dark"])

    @staticmethod
    def get_theme_names():
        """获取所有主题的名称"""
        return [ThemeManager.THEMES[key]["name"] for key in ThemeManager.THEMES]

    @staticmethod
    def get_theme_keys():
        """获取所有主题的键"""
        return list(ThemeManager.THEMES.keys())

class AnimatedButton(QPushButton):
    """带有动画效果的按钮"""
    def __init__(self, text="", parent=None):
        super().__init__(text, parent)
        self.setCursor(Qt.PointingHandCursor)
        self._animation = QPropertyAnimation(self, b"geometry")
        self._animation.setDuration(200)
        self._animation.setEasingCurve(QEasingCurve.OutQuad)
        
    def mousePressEvent(self, event):
        self._animation.stop()
        self._animation.setStartValue(self.geometry())
        self._animation.setEndValue(self.geometry().adjusted(2, 2, -2, -2))
        self._animation.start()
        super().mousePressEvent(event)
        
    def mouseReleaseEvent(self, event):
        self._animation.stop()
        self._animation.setStartValue(self.geometry())
        self._animation.setEndValue(self.geometry().adjusted(-2, -2, 2, 2))
        self._animation.start()
        super().mouseReleaseEvent(event)

class ShortcutItemWidget(QWidget):
    """自定义快捷方式列表项"""
    def __init__(self, name, path, theme, parent=None):
        super().__init__(parent)
        self.theme = theme
        self.setup_ui(name, path)
        
    def setup_ui(self, name, path):
        layout = QHBoxLayout(self)
        layout.setContentsMargins(15, 10, 15, 10)
        layout.setSpacing(15)
        
        # 图标
        self.icon_label = QLabel()
        self.icon_label.setFixedSize(64, 64)
        self.set_icon(path)
        layout.addWidget(self.icon_label)
        
        # 文本信息
        text_layout = QVBoxLayout()
        text_layout.setSpacing(30)
        text_layout.setContentsMargins(5,0, 0, 0)# 调整文本边距
        
        self.name_label = QLabel(name)
        self.name_label.setFont(QFont(self.theme["NORMAL_FONT"][0], self.theme["NORMAL_FONT"][1], self.theme["NORMAL_FONT"][2]))
        self.name_label.setStyleSheet(f"color: {self.theme['FOREGROUND']};")
        self.name_label.setWordWrap(True)  # 允许文本换行
        
        self.path_label = QLabel(path)
        self.path_label.setFont(QFont(self.theme["SMALL_FONT"][0], self.theme["SMALL_FONT"][1], self.theme["SMALL_FONT"][2]))
        self.path_label.setStyleSheet(f"color: {self.theme['TERTIARY']};")
        self.path_label.setWordWrap(True)  # 允许文本换行
        self.path_label.setMaximumWidth(400)  # 限制路径显示宽度
        
        text_layout.addWidget(self.name_label)
        text_layout.addWidget(self.path_label)
        layout.addLayout(text_layout, stretch=1)  # 添加拉伸因子
        
        # 添加弹性空间
        layout.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum))
        
        # 悬停效果
        self.setAutoFillBackground(True)
        palette = self.palette()
        palette.setColor(QPalette.Background, QColor(self.theme["SECONDARY"]))
        self.setPalette(palette)
        
    def set_icon(self, path):
        """根据路径类型设置不同图标"""
        if os.path.isdir(path):
            # 文件夹图标
            icon = QIcon.fromTheme("folder")
            if icon.isNull():
                self.icon_label.setText(emoji.emojize("📁"))
                self.icon_label.setStyleSheet(f"""
                    font-size: 20px;
                    color: {self.theme['PRIMARY']};
                """)
            else:
                pixmap = icon.pixmap(32, 32)
                self.icon_label.setPixmap(pixmap)
        else:
            # 文件图标
            icon = QIcon.fromTheme("application-x-executable")
            if icon.isNull():
                # 根据文件扩展名显示不同emoji
                ext = os.path.splitext(path)[1].lower()
                if ext in ('.exe', '.bat', '.cmd'):
                    self.icon_label.setText(emoji.emojize("🖥️"))
                elif ext in ('.py', '.sh'):
                    self.icon_label.setText(emoji.emojize("📜"))
                else:
                    self.icon_label.setText(emoji.emojize("📄"))
                self.icon_label.setStyleSheet(f"""
                    font-size: 20px;
                    color: {self.theme['PRIMARY']};
                """)
            else:
                # 使用系统文件图标
                icon = QIcon.fromTheme(QIcon.fromTheme("application-x-executable"))
                if os.path.exists(path):
                    icon = QIcon(path)
                pixmap = icon.pixmap(32, 32)
                self.icon_label.setPixmap(pixmap)
        
        self.icon_label.setAlignment(Qt.AlignCenter)

class DesktopShortcutManager(QMainWindow):
    def __init__(self):
        super().__init__()
        
        # 窗口设置
        self.setWindowTitle("🚀 桌面快捷方式管理器")
        self.setGeometry(100, 100, 1000, 700)  # 增大窗口尺寸
        
        # 初始化数据
        self.current_theme = "dark"
        self.shortcuts = {}
        self.config_file = "shortcut_manager_config.json"
        self.shortcuts_file = "desktop_shortcuts.json"
        
        # 加载配置
        self.load_config()
        
        # 创建UI
        self.init_ui()
        
        # 加载快捷方式
        self.load_shortcuts()
        
    def get_current_theme(self):
        """获取当前主题配置"""
        return ThemeManager.get_theme(self.current_theme)
    
    def load_config(self):
        """加载配置文件"""
        if os.path.exists(self.config_file):
            try:
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    if "theme" in config and config["theme"] in ThemeManager.get_theme_keys():
                        self.current_theme = config["theme"]
            except Exception as e:
                print(f"加载配置失败: {str(e)}")
    
    def save_config(self):
        """保存配置文件"""
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump({"theme": self.current_theme}, f)
        except Exception as e:
            print(f"保存配置失败: {str(e)}")
    
    def init_ui(self):
        """初始化用户界面"""
        theme = self.get_current_theme()
        
        # 设置主窗口背景
        if theme["BACKGROUND"].startswith("qlineargradient"):
            self.setStyleSheet(f"""
                QMainWindow {{
                    background: {theme['BACKGROUND']};
                }}
            """)
        else:
            palette = self.palette()
            palette.setColor(QPalette.Background, QColor(theme["BACKGROUND"]))
            self.setPalette(palette)
        
        # 创建中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        main_layout = QVBoxLayout(central_widget)
        main_layout.setContentsMargins(15, 15, 15, 15)  # 调整边距
        main_layout.setSpacing(15)
        
        # 标题栏
        self.create_title_bar(main_layout)
        
        # 内容区域
        content_widget = QWidget()
        content_layout = QHBoxLayout(content_widget)
        content_layout.setContentsMargins(0, 0, 0, 0)
        content_layout.setSpacing(15)
        
        # 快捷方式列表
        self.create_shortcut_list(content_layout)
        
        # 操作按钮区域
        self.create_action_buttons(content_layout)
        
        main_layout.addWidget(content_widget)
        
        # 状态栏
        self.create_status_bar()
        
        # 创建菜单(只创建一次)
        if not hasattr(self, 'menu_created'):
            self.create_menu()
            self.menu_created = True
    
    def create_title_bar(self, parent_layout):
        """创建标题栏"""
        theme = self.get_current_theme()
        
        title_bar = QWidget()
        title_bar_layout = QHBoxLayout(title_bar)
        title_bar_layout.setContentsMargins(0, 0, 0, 0)
        
        # 标题
        self.title_label = EmojiLabel("🚀 桌面快捷方式管理器")
        self.title_label.setFont(QFont(theme["TITLE_FONT"][0], theme["TITLE_FONT"][1], theme["TITLE_FONT"][2]))
        self.title_label.setStyleSheet(f"color: {theme['PRIMARY']};")
        
        # 主题选择器
        self.theme_selector = QPushButton(emoji.emojize(theme["name"] + " ▼"))
        self.theme_selector.setFont(QFont(theme["NORMAL_FONT"][0], theme["NORMAL_FONT"][1], theme["NORMAL_FONT"][2]))
        self.theme_selector.setStyleSheet(f"""
            QPushButton {{
                background-color: {theme['SECONDARY']};
                color: {theme['FOREGROUND']};
                border-radius: {theme['BUTTON_RADIUS']};
                padding: 8px 12px;
                border: none;
            }}
            QPushButton:hover {{
                background-color: {theme['TERTIARY']};
            }}
        """)
        
        # 创建主题菜单
        self.theme_menu = QMenu(self)
        for theme_key in ThemeManager.get_theme_keys():
            theme_data = ThemeManager.get_theme(theme_key)
            action = QAction(emoji.emojize(theme_data["name"]), self)
            action.triggered.connect(lambda _, key=theme_key: self.change_theme(key))
            self.theme_menu.addAction(action)
        
        self.theme_selector.setMenu(self.theme_menu)
        
        # 添加弹性空间
        spacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
        
        title_bar_layout.addWidget(self.title_label)
        title_bar_layout.addSpacerItem(spacer)
        title_bar_layout.addWidget(self.theme_selector)
        
        parent_layout.addWidget(title_bar)
    
    def create_shortcut_list(self, parent_layout):
        """创建快捷方式列表"""
        theme = self.get_current_theme()
        
        # 创建卡片容器
        self.list_card = QWidget()
        self.list_card.setStyleSheet(f"""
            background-color: {theme['SECONDARY']};
            border-radius: {theme['CARD_RADIUS']};
            padding: 15px;
        """)
        
        card_layout = QVBoxLayout(self.list_card)
        card_layout.setContentsMargins(0, 0, 0, 0)
        card_layout.setSpacing(15)
        
        # 标题
        self.list_title = QLabel("📋 快捷方式列表")
        self.list_title.setFont(QFont(theme["HEADING_FONT"][0], theme["HEADING_FONT"][1], theme["HEADING_FONT"][2]))
        self.list_title.setStyleSheet(f"color: {theme['FOREGROUND']};")
        card_layout.addWidget(self.list_title)
        
        # 列表部件
        self.shortcut_list = QListWidget()
        self.shortcut_list.setStyleSheet(f"""
            QListWidget {{
                background-color: transparent;
                border: none;
                outline: none;
            }}
            QListWidget::item {{
                border-bottom: 1px solid {theme['TERTIARY']};
            }}
            QListWidget::item:selected {{
                background-color: {theme['PRIMARY']};
                border-radius: 5px;
            }}
        """)
        self.shortcut_list.setVerticalScrollMode(QListWidget.ScrollPerPixel)
        self.shortcut_list.setHorizontalScrollMode(QListWidget.ScrollPerPixel)
        
        # 添加到卡片
        card_layout.addWidget(self.shortcut_list)
        
        # 添加到主布局
        parent_layout.addWidget(self.list_card, stretch=3)
    
    def create_action_buttons(self, parent_layout):
        """创建操作按钮区域"""
        theme = self.get_current_theme()
        
        # 创建卡片容器
        self.action_card = QWidget()
        self.action_card.setStyleSheet(f"""
            background-color: {theme['SECONDARY']};
            border-radius: {theme['CARD_RADIUS']};
            padding: 15px;
        """)
        
        card_layout = QVBoxLayout(self.action_card)
        card_layout.setContentsMargins(0, 0, 0, 0)
        card_layout.setSpacing(15)
        
        # 标题
        self.actions_title = QLabel("⚡ 快捷操作")
        self.actions_title.setFont(QFont(theme["HEADING_FONT"][0], theme["HEADING_FONT"][1], theme["HEADING_FONT"][2]))
        self.actions_title.setStyleSheet(f"color: {theme['FOREGROUND']};")
        card_layout.addWidget(self.actions_title)
        
        # 添加按钮
        self.add_btn = AnimatedButton(emoji.emojize("➕ 添加快捷方式"))
        self.add_btn.setStyleSheet(self.get_button_style())
        self.add_btn.clicked.connect(self.add_shortcut)
        
        # 打开按钮
        self.open_btn = AnimatedButton(emoji.emojize("📂 打开快捷方式"))
        self.open_btn.setStyleSheet(self.get_button_style())
        self.open_btn.clicked.connect(self.open_selected_shortcut)
        
        # 删除按钮
        self.delete_btn = AnimatedButton(emoji.emojize("🗑️ 删除快捷方式"))
        self.delete_btn.setStyleSheet(self.get_button_style(True))
        self.delete_btn.clicked.connect(self.delete_shortcut)
        
        # 创建到桌面按钮
        self.create_btn = AnimatedButton(emoji.emojize("🖥️ 创建到桌面"))
        self.create_btn.setStyleSheet(self.get_button_style())
        self.create_btn.clicked.connect(self.create_desktop_shortcut)
        
        # 添加到布局
        card_layout.addWidget(self.add_btn)
        card_layout.addWidget(self.open_btn)
        card_layout.addWidget(self.delete_btn)
        card_layout.addWidget(self.create_btn)
        
        # 添加弹性空间
        card_layout.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
        
        # 添加到主布局
        parent_layout.addWidget(self.action_card, stretch=1)
    
    def get_button_style(self, is_destructive=False):
        """获取按钮样式"""
        theme = self.get_current_theme()
        color = theme["ERROR"] if is_destructive else theme["PRIMARY"]
        
        return f"""
            QPushButton {{
                background-color: {color};
                color: {theme['FOREGROUND']};
                border-radius: {theme['BUTTON_RADIUS']};
                padding: 12px;
                font-weight: bold;
                border: none;
            }}
            QPushButton:hover {{
                background-color: {theme['HIGHLIGHT']};
            }}
            QPushButton:pressed {{
                background-color: {theme['PRIMARY']};
            }}
        """
    
    def create_status_bar(self):
        """创建状态栏"""
        theme = self.get_current_theme()
        
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        
        # 状态消息
        self.status_message = QLabel("🟢 就绪")
        self.status_message.setFont(QFont(theme["SMALL_FONT"][0], theme["SMALL_FONT"][1], theme["SMALL_FONT"][2]))
        self.status_message.setStyleSheet(f"color: {theme['FOREGROUND']};")
        
        self.status_bar.addWidget(self.status_message, stretch=1)
    
    def create_menu(self):
        """创建菜单栏(只创建一次)"""
        menubar = self.menuBar()
        theme = self.get_current_theme()
        
        # 设置菜单栏样式
        menubar.setStyleSheet(f"""
            QMenuBar {{
                background-color: {theme['SECONDARY']};
                color: {theme['FOREGROUND']};
                padding: 5px;
                border-bottom: 1px solid {theme['TERTIARY']};
            }}
            QMenuBar::item {{
                padding: 5px 10px;
                background-color: transparent;
            }}
            QMenuBar::item:selected {{
                background-color: {theme['PRIMARY']};
                border-radius: 5px;
            }}
            QMenu {{
                background-color: {theme['SECONDARY']};
                color: {theme['FOREGROUND']};
                border: 1px solid {theme['TERTIARY']};
            }}
            QMenu::item:selected {{
                background-color: {theme['PRIMARY']};
            }}
        """)
        
        # 文件菜单
        file_menu = menubar.addMenu("📁 文件")
        
        save_action = QAction("💾 保存所有快捷方式", self)
        save_action.triggered.connect(self.save_shortcuts)
        file_menu.addAction(save_action)
        
        file_menu.addSeparator()
        
        exit_action = QAction("🚪 退出", self)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)
        
        # 帮助菜单
        help_menu = menubar.addMenu("❓ 帮助")
        
        about_action = QAction("ℹ️ 关于", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)
    
    def change_theme(self, theme_key):
        """更改UI主题"""
        if theme_key not in ThemeManager.get_theme_keys():
            return
        
        self.current_theme = theme_key
        self.save_config()
        
        # 更新UI样式而不重新创建整个UI
        theme = self.get_current_theme()
        
        # 更新主窗口背景
        if theme["BACKGROUND"].startswith("qlineargradient"):
            self.setStyleSheet(f"""
                QMainWindow {{
                    background: {theme['BACKGROUND']};
                }}
            """)
        else:
            palette = self.palette()
            palette.setColor(QPalette.Background, QColor(theme["BACKGROUND"]))
            self.setPalette(palette)
        
        # 更新标题栏
        self.title_label.setFont(QFont(theme["TITLE_FONT"][0], theme["TITLE_FONT"][1], theme["TITLE_FONT"][2]))
        self.title_label.setStyleSheet(f"color: {theme['PRIMARY']};")
        
        # 更新主题选择器
        self.theme_selector.setText(emoji.emojize(theme["name"] + " ▼"))
        self.theme_selector.setFont(QFont(theme["NORMAL_FONT"][0], theme["NORMAL_FONT"][1], theme["NORMAL_FONT"][2]))
        self.theme_selector.setStyleSheet(f"""
            QPushButton {{
                background-color: {theme['SECONDARY']};
                color: {theme['FOREGROUND']};
                border-radius: {theme['BUTTON_RADIUS']};
                padding: 8px 12px;
                border: none;
            }}
            QPushButton:hover {{
                background-color: {theme['TERTIARY']};
            }}
        """)
        
        # 更新快捷方式列表卡片
        self.list_card.setStyleSheet(f"""
            background-color: {theme['SECONDARY']};
            border-radius: {theme['CARD_RADIUS']};
            padding: 15px;
        """)
        self.list_title.setFont(QFont(theme["HEADING_FONT"][0], theme["HEADING_FONT"][1], theme["HEADING_FONT"][2]))
        self.list_title.setStyleSheet(f"color: {theme['FOREGROUND']};")
        self.shortcut_list.setStyleSheet(f"""
            QListWidget {{
                background-color: transparent;
                border: none;
                outline: none;
            }}
            QListWidget::item {{
                border-bottom: 1px solid {theme['TERTIARY']};
            }}
            QListWidget::item:selected {{
                background-color: {theme['PRIMARY']};
                border-radius: 5px;
            }}
        """)
        
        # 更新操作按钮卡片
        self.action_card.setStyleSheet(f"""
            background-color: {theme['SECONDARY']};
            border-radius: {theme['CARD_RADIUS']};
            padding: 15px;
        """)
        self.actions_title.setFont(QFont(theme["HEADING_FONT"][0], theme["HEADING_FONT"][1], theme["HEADING_FONT"][2]))
        self.actions_title.setStyleSheet(f"color: {theme['FOREGROUND']};")
        
        # 更新按钮样式
        self.add_btn.setStyleSheet(self.get_button_style())
        self.open_btn.setStyleSheet(self.get_button_style())
        self.delete_btn.setStyleSheet(self.get_button_style(True))
        self.create_btn.setStyleSheet(self.get_button_style())
        
        # 更新状态栏
        self.status_message.setFont(QFont(theme["SMALL_FONT"][0], theme["SMALL_FONT"][1], theme["SMALL_FONT"][2]))
        self.status_message.setStyleSheet(f"color: {theme['FOREGROUND']};")
        
        # 更新菜单栏样式
        menubar = self.menuBar()
        menubar.setStyleSheet(f"""
            QMenuBar {{
                background-color: {theme['SECONDARY']};
                color: {theme['FOREGROUND']};
                padding: 5px;
                border-bottom: 1px solid {theme['TERTIARY']};
            }}
            QMenuBar::item {{
                padding: 5px 10px;
                background-color: transparent;
            }}
            QMenuBar::item:selected {{
                background-color: {theme['PRIMARY']};
                border-radius: 5px;
            }}
            QMenu {{
                background-color: {theme['SECONDARY']};
                color: {theme['FOREGROUND']};
                border: 1px solid {theme['TERTIARY']};
            }}
            QMenu::item:selected {{
                background-color: {theme['PRIMARY']};
            }}
        """)
        
        # 刷新快捷方式列表项样式
        self.refresh_shortcut_list()
        
        # 显示主题更改成功消息
        theme_name = ThemeManager.get_theme(theme_key)["name"]
        self.show_status_message(f"已切换到 {theme_name}", "success")
    
    def show_status_message(self, message, type="info"):
        """显示状态栏消息"""
        theme = self.get_current_theme()
        
        if type == "info":
            icon = "🔵"
            color = theme["PRIMARY"]
        elif type == "success":
            icon = "🟢"
            color = theme["SUCCESS"]
        elif type == "error":
            icon = "🔴"
            color = theme["ERROR"]
        elif type == "warning":
            icon = "🟡"
            color = theme["WARNING"]
        else:
            icon = "🔵"
            color = theme["PRIMARY"]
        
        self.status_message.setText(f"{icon} {message}")
        self.status_message.setStyleSheet(f"color: {color};")
        
        # 3秒后自动清除消息
        QTimer.singleShot(3000, lambda: self.status_message.setText("🟢 就绪"))
    
    def load_shortcuts(self):
        """从文件加载已保存的快捷方式"""
        if os.path.exists(self.shortcuts_file):
            try:
                with open(self.shortcuts_file, 'r', encoding='utf-8') as f:
                    self.shortcuts = json.load(f)
                self.refresh_shortcut_list()
                self.show_status_message(f"已加载 {len(self.shortcuts)} 个快捷方式", "success")
            except Exception as e:
                self.show_status_message(f"加载快捷方式失败: {str(e)}", "error")
                self.shortcuts = {}
        else:
            self.shortcuts = {}
            self.show_status_message("没有找到快捷方式文件,已创建新列表", "info")
    
    def save_shortcuts(self):
        """保存快捷方式到文件"""
        try:
            with open(self.shortcuts_file, 'w', encoding='utf-8') as f:
                json.dump(self.shortcuts, f, ensure_ascii=False, indent=4)
            self.show_status_message("快捷方式保存成功", "success")
            return True
        except Exception as e:
            self.show_status_message(f"保存快捷方式失败: {str(e)}", "error")
            return False
    
    def refresh_shortcut_list(self):
        """刷新快捷方式列表"""
        self.shortcut_list.clear()
        
        for name, path in self.shortcuts.items():
            item = QListWidgetItem()
            item.setSizeHint(QSize(0, 80))  # 增加项高度以适应多行文本
            
            widget = ShortcutItemWidget(name, path, self.get_current_theme())
            
            self.shortcut_list.addItem(item)
            self.shortcut_list.setItemWidget(item, widget)
    
    def add_shortcut(self):
        """添加新的快捷方式(支持文件和文件夹)"""
        path, _ = QFileDialog.getOpenFileName(
            self, 
            "选择文件或文件夹", 
            "", 
            "所有文件 (*);;可执行文件 (*.exe *.bat *.cmd);;脚本文件 (*.py *.sh);;文件夹"
        )
        
        if not path:
            return
        
        # 获取名称
        name = os.path.basename(path)
        
        # 如果已存在同名快捷方式,添加数字后缀
        base_name = os.path.splitext(name)[0]
        ext = os.path.splitext(name)[1]
        counter = 1
        while name in self.shortcuts:
            name = f"{base_name} ({counter}){ext}"
            counter += 1
        
        # 添加到快捷方式字典
        self.shortcuts[name] = path
        
        # 保存并刷新列表
        if self.save_shortcuts():
            self.refresh_shortcut_list()
            self.show_status_message(f"已添加快捷方式: {name}", "success")
    
    def get_selected_shortcut(self):
        """获取选中的快捷方式"""
        selected_items = self.shortcut_list.selectedItems()
        if not selected_items:
            return None, None
        
        item = selected_items[0]
        widget = self.shortcut_list.itemWidget(item)
        return widget.name_label.text(), widget.path_label.text()
    
    def open_selected_shortcut(self):
        """打开选中的快捷方式"""
        name, path = self.get_selected_shortcut()
        if not name:
            self.show_status_message("请先选择一个快捷方式", "warning")
            return
        
        if not os.path.exists(path):
            self.show_status_message(f"路径不存在: {path}", "error")
            return
        
        try:
            if platform.system() == "Windows":
                if os.path.isdir(path):
                    os.startfile(path)
                else:
                    os.startfile(path)
            elif platform.system() == "Darwin":
                if os.path.isdir(path):
                    subprocess.run(["open", path])
                else:
                    subprocess.run(["open", path])
            else:
                if os.path.isdir(path):
                    subprocess.run(["xdg-open", path])
                else:
                    subprocess.run(["xdg-open", path])
            
            self.show_status_message(f"已打开: {name}", "success")
        except Exception as e:
            self.show_status_message(f"打开失败: {str(e)}", "error")
    
    def delete_shortcut(self):
        """删除选中的快捷方式"""
        name, path = self.get_selected_shortcut()
        if not name:
            self.show_status_message("请先选择一个快捷方式", "warning")
            return
        
        # 确认对话框
        reply = QMessageBox.question(
            self, "确认删除",
            f"确定要删除快捷方式 '{name}' 吗?",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            del self.shortcuts[name]
            if self.save_shortcuts():
                self.refresh_shortcut_list()
                self.show_status_message(f"已删除快捷方式: {name}", "success")
    
    def create_desktop_shortcut(self):
        """创建选中的快捷方式到桌面"""
        name, path = self.get_selected_shortcut()
        if not name:
            self.show_status_message("请先选择一个快捷方式", "warning")
            return
        
        if not os.path.exists(path):
            self.show_status_message(f"路径不存在: {path}", "error")
            return
        
        desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
        
        try:
            if platform.system() == "Windows":
                try:
                    import winshell
                    shortcut_path = os.path.join(desktop_path, f"{name}.lnk")
                    winshell.CreateShortcut(
                        Path=shortcut_path,
                        Target=path,
                        Description=f"快捷方式到 {name}",
                        Icon=(path, 0)
                    )
                except ImportError:
                    self.show_status_message("需要安装winshell库: pip install winshell", "error")
                    return
            elif platform.system() == "Darwin":
                # macOS 创建替身
                shortcut_path = os.path.join(desktop_path, name)
                os.symlink(path, shortcut_path)
            else:
                # Linux 创建.desktop文件
                shortcut_path = os.path.join(desktop_path, f"{name}.desktop")
                icon_path = path if os.path.isfile(path) else "system-file-manager"
                desktop_file = f"""[Desktop Entry]
Version=1.0
Type=Application
Name={name}
Exec={path}
Icon={icon_path}
Terminal=false
"""
                with open(shortcut_path, "w") as f:
                    f.write(desktop_file)
                os.chmod(shortcut_path, 0o755)
            
            self.show_status_message(f"已在桌面创建快捷方式: {name}", "success")
        except Exception as e:
            self.show_status_message(f"创建快捷方式失败: {str(e)}", "error")
    
    def show_about(self):
        """显示关于对话框"""
        theme = self.get_current_theme()
        
        about_box = QMessageBox(self)
        about_box.setWindowTitle("ℹ️ 关于")
        about_box.setText("""
            <h2>🚀 桌面快捷方式管理器</h2>
            <p>版本 2.0</p>
            <p>一个美观实用的快捷方式管理工具</p>
            <p>使用 PyQt5 和 emoji 构建</p>
            <p>© 2025 版权所有 创客白泽</p>
        """)
        
        # 设置样式
        about_box.setStyleSheet(f"""
            QMessageBox {{
                background-color: {theme['SECONDARY']};
                color: {theme['FOREGROUND']};
            }}
            QLabel {{
                color: {theme['FOREGROUND']};
            }}
            QPushButton {{
                background-color: {theme['PRIMARY']};
                color: {theme['FOREGROUND']};
                border-radius: {theme['BUTTON_RADIUS']};
                padding: 8px;
                min-width: 80px;
            }}
            QPushButton:hover {{
                background-color: {theme['HIGHLIGHT']};
            }}
        """)
        
        about_box.exec_()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    
    # 设置应用程序图标
    if platform.system() == "Windows":
        import ctypes
        myappid = "desktop.shortcut.manager.2.0"
        ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
    
    # 创建并显示主窗口
    window = DesktopShortcutManager()
    window.show()
    
    sys.exit(app.exec_())

总结

本项目通过PyQt5实现了:

  • 现代化UI设计:卡片布局+动画+多主题
  • 完整功能闭环:增删改查快捷方式
  • 跨平台兼容:适配三大操作系统
  • 用户体验优化:状态提示、emoji图标

扩展建议

  • 增加云同步功能
  • 支持快捷键操作
  • 添加分类标签系统

到此这篇关于Python+PyQt5打造炫酷的桌面快捷方式管理器的文章就介绍到这了,更多相关Python快捷方式管理器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 利用Python实现高效数据收集与挖掘的实战指南

    利用Python实现高效数据收集与挖掘的实战指南

    在当今数据驱动的时代,如何高效获取互联网上的海量数据成为许多企业和研究者的核心需求,Python凭借其丰富的爬虫库和简洁的语法,成为了数据采集领域的首选工具,本文将带你全面了解如何利用Python爬虫技术实现数据收集,需要的朋友可以参考下
    2025-07-07
  • python神经网络学习使用Keras进行回归运算

    python神经网络学习使用Keras进行回归运算

    这篇文章主要为大家介绍了python神经网络学习使用Keras进行回归运算,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Python实现的KMeans聚类算法实例分析

    Python实现的KMeans聚类算法实例分析

    这篇文章主要介绍了Python实现的KMeans聚类算法,结合实例形式较为详细的分析了KMeans聚类算法概念、原理、定义及使用相关操作技巧,需要的朋友可以参考下
    2018-12-12
  • Python实现类似比特币的加密货币区块链的创建与交易实例

    Python实现类似比特币的加密货币区块链的创建与交易实例

    本文讲解了Python实现类似比特币的加密货币区块链的创建与交易实例方法
    2018-03-03
  • Python字典中的值为列表或字典的构造实例

    Python字典中的值为列表或字典的构造实例

    今天小编就为大家分享一篇Python字典中的值为列表或字典的构造实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • Python中使用hashlib模块处理算法的教程

    Python中使用hashlib模块处理算法的教程

    这篇文章主要介绍了Python中使用hashlib模块处理算法的教程,代码基于Python2.x版本,需要的朋友可以参考下
    2015-04-04
  • 使用Python paramiko模块利用多线程实现ssh并发执行操作

    使用Python paramiko模块利用多线程实现ssh并发执行操作

    ssh是一个协议,OpenSSH是其中一个开源实现,paramiko是Python的一个库,实现了SSHv2协议(底层使用cryptography)。这篇文章主要介绍了使用Python paramiko模块利用多线程实现ssh并发执行操作,需要的朋友可以参考下
    2019-12-12
  • Python3.4学习笔记之类型判断,异常处理,终止程序操作小结

    Python3.4学习笔记之类型判断,异常处理,终止程序操作小结

    这篇文章主要介绍了Python3.4学习笔记之类型判断,异常处理,终止程序操作,结合具体实例形式分析了Python3.4模块导入、异常处理、退出程序等相关操作技巧与注意事项,需要的朋友可以参考下
    2019-03-03
  • Python的jsonpath库使用方法实例

    Python的jsonpath库使用方法实例

    这篇文章主要介绍了Python的jsonpath库使用方法实例,接口返回的jsonn数据,需要取值后断言,一般我们是使用jsonpath来提取接口返回的数据 ,JsonPath是一种信息抽取类库,是从JSON文档中抽取指定信息的工具,,需要的朋友可以参考下
    2023-08-08
  • Python的函数使用示例详解

    Python的函数使用示例详解

    在Python的函数中,我们将其分为内置函数、自定义函数、main函数三个模块,当然,使用的过程中会涉及到变量以及参数,这些都会举例进行说明,对Python函数使用相关知识感兴趣的朋友跟随小编一起看看吧
    2021-12-12

最新评论