Python+PyQt5开发一个Windows电脑启动项管理神器

2025-05-06 17:50

本文主要是介绍Python+PyQt5开发一个Windows电脑启动项管理神器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Python+PyQt5开发一个Windows电脑启动项管理神器》:本文主要介绍如何使用PyQt5开发一款颜值与功能并存的Windows启动项管理工具,不仅能查看/删除现有启动项,还能智能添加新...

开篇:为什么我们需要启动项管理工具

在日常使用Windows电脑时,你是否遇到过这些问题?

电脑开机越来越慢,像老牛拉破车

莫名其妙的后台程序占用大量资源

想禁用某些启动项却找不到入口

需要添加自定义启动项但操作复杂

今天,我将带大家用PyQt5开发一款颜值与功能并存的Windows启动项管理工具!不仅能查看/删除现有启动项,还能智能添加新启动项,支持拖拽操作,界面美观大方!

功能全景图

先来看看我们开发的工具具备哪些杀手级功能:

✅ 双模式启动项查看

  • 当前用户启动项(HKCU)
  • 所有用户启动项(HKLM)
  • 启动文件夹项目

✅ 智能分类展示

  • 名称/状态/路径/参数 四维信息
  • 树形结构清晰明了

✅ 三种添加方式

  • 拖拽文件快捷添加(超方便!)
  • 表单手动填写添加
  • 浏览文件选择添加

✅ 多位置支持

  • 当前用户注册表
  • 所有用户注册表(需管理员权限)
  • 启动文件夹

✅ 右键快捷操作

一键删除不需要的启动项

Python+PyQt5开发一个Windows电脑启动项管理神器

Python+PyQt5开发一个Windows电脑启动项管理神器

核心技术解析

1. Windows注册表操作

我们通过python的winreg模块访问Windows注册表:

import winreg

​​​​​​​# 读取当前用户启动项
def load_registry_startup_items(self, parent_item, hive, subkey):
    try:
        with winreg.OpenKey(hive, subkey) as key:
            i = 0
            while True:
                try:
                    name, value, _ = winreg.EnumValue(key, i)
                    # 解析注册表值
                    path, args = self.parse_command(value)
                    # 创建树形项目...
                    i += 1
                except OSError:
                    break

关键点:

  • HKEY_CURRENT_USER - 当前用户配置
  • HKEY_LOCAL_MACHINE - 所有用户配置
  • EnumValue - 枚举注册表值
  • SetValueEx - 写入新值

2. 启动文件夹处理

除了注册表,Windows还会从两个特殊文件夹加载启动项:

# 获取启动文件夹路径
user_startup = os.path.join(os.getenv('APPDATA'), 
                          r'Microsoft\Windows\Start Menu\Programs\Startup')
all_users_startup = os.path.join(os.getenv('ProgramData'),
                               r'Microsoft\Windows\Start Menu\Programs\Startup')

对于.lnk快捷方式,我们使用win32com解析真实路径:

from win32com.client import Dispatch

​​​​​​​shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(item_path)
target_path = shortcut.TargetPath  # 获取实际目标

UI设计美学

我们的工具采用紫色系渐变风格,看起来专业又不失活泼:

1. 全局样式设置

def set_style(self):
    palette = QPalette()
    palette.setColor(QPalette.Window, QColor(250, 245, 255))  # 浅紫色背景
    palette.setColor(QPalette.Highlight, QColor(138, 43, 226))  # 紫罗兰选中色
    QApplication.setPalette(palette)
    
    # 精致按钮样式
    button_style = """
    QPushButton {
        background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                  stop:0 #e6e6fa, stop:1 #d8bfd8);
        border: 1px solid #9370db;
        border-radius: 6px;
        padding: 8px 15px;
        color: #4b0082;
    }
    """
    self.setStyleSheet(button_style)

2. 创新UI元素

拖拽添加区域:

def create_drop_area(self, layout):
    drop_group = QGroupBox("拖拽添加 (将文件拖到此处)")
    drop_group.setAcceptDrops(True)
    drop_group.dragEnterEvent = self.drag_enter_event
    drop_group.dropEvent = self.drop_event
    
    # 添加图标和提示文本...

选项卡设计:

self.tabs = QTabWidget()
self.tabs.setTabPosition(QTabWidget.North)
self.tabs.setDocumentMode(True)
self.tabs.setStyleSheet("""
    QTabBar::tab {
        padding: 10px 20px;
        background: qlineargradient(...);
        border: 1px solid #9370db;
    }
""")

完整源码剖析

由于源码较长(约500行),这里重点分析几个核心部分:

1. 主窗口结构

class StartupManager(QMainWindow):
    def __init__(self):
        super().__init__()
        # 窗口基本设置
        self.setWindowTitle("电脑启动项管理工具")
        self.setGeometry(100, 100, 1000, 750)
        
        # 初始化UI
        self.set_style()
        self.init_ui()
        
        # 加载数据
        self.load_startup_items()

2. 启动项加载逻辑

def load_startup_items(self):
    self.tree.clear()
    
    # 1. 加载注册表启动项
    user_item = QTreeWidgetItem(["当前用户启动项"])
    self.load_registry_startup_items(user_item, winreg.HKEY_CURRENT_USER, ...)
    
    # 2. 加载启动文件夹
    folder_item = QTreeWidgetItem(["启动文件夹项目"])
    self.load_startup_folder_items(folder_item)

3. 添加新启动项

def add_manual_startup_item(self):
    # 获取表单数据
    name = self.name_input.text()
    path = self.path_input.text()
    
    # 验证输入
    if not name or not pathGKyQA:
        QMessageBox.warning("名称和路径不能为空")
        return
    
    # 根据选择的位置执行添加
    if self.location_radio_all.isChecked():
        # 写入HKLM注册表(需要管理员权限)
    elif self.location_radio_folder.isChecked():
        # 创建快捷方式到启动文件夹
    else:
        # 写入HKCU注册表

完整源码下载

 import os
import sys
import winreg
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTreeWidget, QTreeWidgetItem, 
                             QvboxLayout, QWidget, QHeaderView, QMenu, QFileDialog,
                             QAbstractItemView, QMessageBox, QLabel, QPushButton, 
                             QHBoxLayout, QTabWidget, QGroupBox, QFormLayout, QLineEdit,
                             QFrame, QScrollArea, QStyle)
from PyQt5.QtCore import Qt, QMimeData, QSize
from PyQt5.QtGui import QIcon, QPalette, QColor, QFont, QPixmap

class StartupManager(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("电脑启动项管理工具")
        self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
        self.setGeometry(100, 100, 1000, 750)
        
        # 设置应用样式
        self.set_style()
        
        self.init_ui()
        self.load_startup_items()
        
    def set_style(self):
        # 设置多彩配色方案
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(250, 245, 255))
        palette.setColor(QPalette.WindowText, QColor(80, 80, 80))
        palette.setColor(QPalette.Base, QColor(255, 255, 255))
        palette.setColor(QPalette.AlternateBase, QColor(245, 245, 255))
        palette.setColor(QPalette.ToolTipBase, QColor(255, 255, 255))
        palette.setColor(QPalette.ToolTipText, QColor(80, 80, 80))
        palette.setColor(QPalette.Text, QColor(80, 80, 80))
        palette.setColor(QPalette.Button, QColor(230, 230, 250))
        palette.setColor(QPalette.ButtonText, QColor(80, 80, 80))
        palette.setColor(QPalette.BrightText, QColor(255, 255, 255))
        palette.setColor(QPalette.Highlight, QColor(138, 43, 226))  # 紫罗兰色
        palette.setColor(QPalette.HighlightedText, QColor(255, 255, 255))
        QApplication.setPalette(palette)
        
        # 设置全局样式表
        style = """
        QMainWindow {
            background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                                      stop:0 #faf5ff, stop:1 #e6e6fa);
        }
        QTreeWidget {
            border: 2px solid #d8bfd8;
            border-radius: 8px;
            background-color: white;
            padding: 5px;
        }
        QTreeWidget::item {
            padding: 5px;
        }
        QTreeWidget::item:hover {
            background: #e6e6fa;
        }
        QGroupBox {
            border: 2px solid #d8bfd8;
            border-radius: 8px;
            margin-top: 15px;
            padding-top: 20px;
            background: rgba(255, 255, 255, 0.8);
        }
        QGroupBox::title {
            subcontrol-origin: margin;
            left: 15px;
            padding: 0 5px;
            color: #9370db;
            font-weight: bold;
        }
        QPushButton {
            background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                      stop:0 #e6e6fa, stop:1 #d8bfd8);
            border: 1px solid #9370db;
            border-radius: 6px;
            padding: 8px 15px;
            min-width: 100px;
            color: #4b0082;
            font-weight: bold;
        }
        QPushButton:hover {
            background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                      stop:0 #d8bfd8, stop:1 #e6e6fa);
        }
        QPushButton:pressed {
            background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                      stop:0 #9370db, stop:1 #d8bfd8);
            color: white;
        }
        QLineEdit {
            border: 1px solid #d8bfd8;
            border-radius: 6px;
            padding: 8px;
            background: white;
        }
        QTabWidget::pane {
            border: 2px solid #d8bfd8;
            border-radius: 8px;
            background: rgba(255, 255, 255, 0.9);
            margin: 5px;
        }
        QTabBar::tab {
            padding: 10px 20px;
            background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                      stop:0 #e6e6fa, stop:1 #d8bfd8);
            border: 1px solid #9370db;
            border-bottom: none;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
            color: #4b0082;
            font-weight: bold;
        }
        QTabBar::tab:selected {
            background: white;
            margin-bottom: -1px;
        }
        QTabBar::tab:hover {
            background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                      stop:0 #d8bfd8, stop:1 #e6e6fa);
        }
        QHeaderView::section {
            background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                      stop:0 #e6e6fa, stop:1 #d8bfd8);
            padding: 5px;
            border: 1px solid #9370db;
            color: #4b0082;
            font-weight: bold;
        }
        QScrollArea {
            border: none;
            background: transparent;
        }
        """
        self.setStyleSheet(style)
        
    def init_ui(self):
        # 主窗口部件
        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)
        
        # 创建选项卡
        self.tabs = QTabWidget()
        self.tabs.setTabPosition(QTabWidget.North)
        self.tabs.setDocumentMode(True)
        main_layout.addwidget(self.tabs)
        
        # 创建"查看启动项"标签页
        self.create_view_tab()
        
        # 创建"添加启动项"标签页
        self.create_add_tab()
        
        # 创建状态栏
        self.statusBar().showMessage("就绪")
        
    def create_title_bar(self, layout):
        # 标题栏布局
        title_layout = QHBoxLayout()
        
        # 图标
        icon_label = QLabel()
        icon_pixmap = self.style().standardIcon(QStyle.SP_ComputerIcon).pixmap(48, 48)
        icon_label.setPixmap(icon_pixmap)
        title_layout.addWidget(icon_label)
        
        # 标题和副标题
        text_layout = QVBoxLayout()
        title_label = QLabel("电脑启动项管理")
        title_label.setStyleSheet("font-size: 24px; font-weight: bold; color: #9370db;")
        subtitle_label = QLabel("轻松管理系统启动项")
        subtitle_label.setStyleSheet("font-size: 14px; color: #9370db;")
        text_layout.addWidget(title_label)
        text_layout.addWidget(subtitle_label)
        title_layout.addLayout(text_layout)
        
        title_layout.addStretch()
        
        # 刷新按钮
        refresh_btn = QPushButton()
        refresh_btn.setIcon(self.style().standardIcon(QStyle.SP_BrowserReload))
        refresh_btn.setIconSize(QSize(24, 24))
        refresh_btn.setToolTip("刷新列表")
        refresh_btn.clicked.connect(self.load_startup_items)
        refresh_btn.setFixedSize(40, 40)
        title_layout.addWidget(refresh_btn)
        
        layout.addLayout(title_layout)
        
        # 添加分隔线
        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setFrameShadow(QFrame.Sunken)
        separator.setStyleSheet("color: #d8bfd8;")
        layout.addWidget(separator)
    
    def create_view_tab(self):
        # 查看启动项标签页
        view_tab = QWidget()
        layout = QVBoxLayout(view_tab)
        layout.setContentsMargins(10, 10, 10, 10)
        layout.setSpacing(10)
        
        # 树形列表
        self.tree = QTreeWidget()
        self.tree.setColumnCount(4)
        self.tree.setHeaderLabels(["名称", "状态", "命令行/路径", "参数"])
        self.tree.setSelectionMode(QAbstractItemView.SingleSelection)
        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(self.show_context_menu)
        
        # 设置列宽
        self.tree.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.tree.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
        self.tree.header().setSectionResizeMode(2, QHeaderView.Stretch)
        self.tree.header().setSectionResizeMode(3, QHeaderView.ResizeToContents)
        
        # 添加到滚动区域
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setWidget(self.tree)
        layout.addWidget(scroll_area)
        
        self.tabs.addTab(view_tab, "查看启动项")
    
    def create_add_tab(self):
        # 添加启动项标签页
        add_tab = QWidget()
        layout = QVBoxLayout(add_tab)
        layout.setContentsMargins(15, 15, 15, 15)
        layout.setSpacing(15)
        
        # 创建拖拽区域
        self.create_drop_area(layout)
        
        # 创建表单区域
        self.create_form_area(layout)
        
        self.tabs.addTab(add_tab, "添加启动项")
    
    def create_drop_area(self, layout):
        # 拖拽区域
        drop_group = QGroupBox("拖拽添加 (将文件拖到此处)")
        drop_group.setAcceptDrops(True)
        drop_group.dragEnterEvent = self.drag_enter_event
        drop_group.dropEvent = lambda e: self.drop_event(e, drop_group)
        
        drop_layout = QVBoxLayout(drop_group)
        drop_layout.setContentsMargins(20, 20, 20, 20)
        drop_layout.setSpacing(20)
        
        # 图标
        drop_icon = QLabel()
        drop_icon.setPixmap(self.style().standardIcon(QStyle.SP_FileIcon).pixmap(64, 64))
        drop_icon.setAlignment(Qt.AlignCenter)
        drop_layout.addWidget(drop_icon)
        
        # 文本
        drop_label = QLabel("拖拽可执行文件或快捷方式到此处")
        drop_label.setAlignment(Qt.AlignCenter)
        drop_label.setStyleSheet("""
            font-size: 16px; 
            font-weight: bold; 
           China编程 color: #9370db;
        """)
        drop_layout.addWidget(drop_label)
        
        # 提示
        drop_hint = QLabel("支持 .exe, .lnk, .BAT, .cmd 文件")
        drop_hint.setAlignment(Qt.AlignCenter)
        drop_hint.setStyleSheet("font-size: 12px; color: #9370db;")
        drop_layout.addWidget(drop_hint)
        
        layout.addWidget(drop_group)
    
    def create_form_area(self, layout):
        # 表单区域
        form_group = QGroupBox("手动添加")
        form_layout = QFormLayout(form_group)
        form_layout.setContentsMargins(15, 15, 15, 15)
        form_layout.setSpacing(15)
        form_layout.setLabelAlignment(Qt.AlignRight)
        
        # 名称输入
        self.name_input = QLineEdit()
        self.name_input.setPlaceholderText("输入启动项名称")
        form_layout.addRow("名称:", self.name_input)
        
        # 路径输入
        path_layout = QHBoxLayout()
        self.path_input = QLineEdit()
        self.path_input.setPlaceholderText("选择或输入程序路径")
        browse_btn = QPushButton("浏览...")
        browse_btn.setIcon(self.style().standardIcon(QStyle.SP_DirOpenIcon))
        browse_btn.clicked.connect(self.browse_file)
        path_layout.addWidget(self.path_input)
        path_layout.addWidget(browse_btn)
        form_layout.addRow("路径:", path_layout)
        
        # 参数输入
        self.args_input = QLineEdit()
        self.args_input.setPlaceholderText("可选参数")
        form_layout.addRow("参数:", self.args_input)
        
        layout.addWidget(form_group)
        
        # 添加位置选择
        self.create_location_selector(layout)
        
        # 添加按钮
        add_btn = QPushButton("添加启动项")
        add_btn.setIcon(self.style().standardIcon(QStyle.SP_DialogOkButton))
        add_btn.clicked.connect(self.add_manual_startup_item)
        add_btn.setStyleSheet("""
            background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                      stop:0 #9370db, stop:1 #8a2be2);
            color: white;
            font-size: 16px;
            padding: 12px;
        """)
        layout.addWidget(add_btn)
        
        layout.addStretch()
    
    def create_location_selector(self, layout):
        # 位置选择器
        location_group = QGroupBox("添加位置")
        location_layout = QHBoxLayout(location_group)
        location_layout.setSpacing(20)
        
        # 当前用户选项
        self.location_radio_user = QPushButton("当前用户")
        self.location_radio_user.setCheckable(True)
        self.location_radio_user.setChecked(True)
        self.location_radio_user.setIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
        self.location_radio_user.setStyleSheet("""
            QPushButton {
                background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                          stop:0 #e6e6fa, stop:1 #d8bfd8);
                border: 2px solid #9370db;
                border-radius: 6px;
                padding: 10px;
            }
            QPushButton:checked {
                background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                          stop:0 #9370db, stop:1 #8a2be2);
                color: white;
            }
        """)
        
        # 所有用户选项
        self.location_radio_all = QPushButton("所有用户")
        self.location_radio_all.setCheckable(True)
        self.location_radio_all.setIcon(self.style().standardIcon(QStyle.SP_DesktopIcon))
        self.location_radio_all.setStyleSheet(self.location_radio_user.styleSheet())
        
        # 启动文件夹选项
        self.location_radio_folder = QPushButton("启动文件夹")
        self.location_radio_folder.setCheckable(True)
        self.location_radio_folder.setIcon(self.style().standardIcon(QStyle.SP_DirIcon))
        self.location_radio_folder.setStyleSheet(self.location_radio_user.styleSheet())
        
        location_layout.addWidget(self.location_radio_user)
        location_layout.addWidget(self.location_radio_all)
        location_layout.addWidget(self.location_radio_folder)
        
        layout.addWidget(location_group)
    
    def load_startup_items(self):
        self.tree.clear()
        
        # 加载当前用户的启动项
        user_item = QTreeWidgetItem(self.tree, ["当前用户启动项", "", "", ""])
        user_item.setExpanded(True)
        self.load_registry_startup_items(user_item, winreg.HKEY_CURRENT_USER,
                                      r"Software\Microsoft\Windows\CurrentVersion\Run")
        
        # 加载所有用户的启动项
        all_users_item = QTreeWidgetItem(self.tree, ["所有用户启动项", "", "", ""])
        all_users_item.setExpanded(True)
        self.load_registry_startup_items(all_users_item, winreg.HKEY_LOCAL_MACHINE,
                                       r"Software\Microsoft\Windows\CurrentVersion\Run")
        
        # 加载启动文件夹中的项目
        startup_folder_item = QTreeWidgetItem(self.tree, ["启动文件夹项目", "", "", ""])
        startup_folder_item.setExpanded(True)
        self.load_startup_folder_items(startup_folder_item)
        
        # 切换到查看标签页
        self.tabs.setCurrentIndex(0)
        self.statusBar().showMessage("启动项列表已刷新", 3000)
    
    def load_registry_startup_items(self, parent_item, hive, subkey):
        try:
            with winreg.OpenKey(hive, subkey) as key:
                i = 0
                while True:
                    try:
                        name, value, _ = winreg.EnumValue(key, i)
                        # 解析值和参数
                        path, args = self.parse_command(value)
                        
                        item = QTreeWidgetItem(parent_item)
                        item.setText(0, name)
                        item.setText(1, "已启用")
                        item.setText(2, path)
          android              item.setText(3, args)
                        
                        # 存储额外信息用于删除
                        item.setData(0, Qt.UserRole, ("registry", hive, subkey, name))
                        
                        i += 1
                    except OSError:
                        break
        except WindowsError:
            pass
    
    def load_startup_folder_items(self, parent_item):
        # 获取当前用户的启动文件夹
        user_startup = os.path.join(os.getenv('APPDATA'), 
                                  r'Microsoft\Windows\Start Menu\Programs\Startup')
        self.load_folder_items(parent_item, user_startup, "user")
        
        # 获取所有用户的启动文件夹
        all_users_startup = os.path.join(os.getenv('ProgramDjavascriptata'),
                                       r'Microsoft\Windows\Start Menu\Programs\Startup')
        self.load_folder_items(parent_item, all_users_startup, "all_users")
    
    def load_folder_items(self, parent_item, folder_path, folder_type):
        if not os.path.exists(folder_path):
            return
            
        for item_name in os.listdir(folder_path):
            item_path = os.path.join(folder_path, item_name)
            
            # 如果是快捷方式,解析目标
            if item_name.lower().endswith('.lnk'):
                try:
                    from win32com.client import Dispatch
                    shell = Dispatch('WScript.Shell')
                    shortcut = shell.CreateShortCut(item_path)
                    target_path = shortcut.TargetPath
                    arguments = shortcut.Arguments
                    working_dir = shortcut.WorkingDirectory
                    
                    # 构建完整路径
                    full_path = target_path
                    if working_dir:
                        full_path = os.path.join(working_dir, target_path)
                    
                    item = QTreeWidgetItem(parent_item)
                    item.setText(0, os.path.splitext(item_name)[0])
                    item.setText(1, "已启用")
                    item.setText(2, full_path)
                    item.setText(3, arguments)
                    
                    # 存储额外信息用于删除
                    item.setData(0, Qt.UserRole, ("folder", folder_path, item_name))
                except:
                    # 如果解析快捷方式失败,直接显示路径
                    item = QTreeWidgetItem(parent_item)
                    item.setText(0, os.path.splitext(item_name)[0])
                    item.setText(1, "已启用")
                    item.setText(2, item_path)
                    item.setText(3, "")
                    item.setData(0, Qt.UserRole, ("folder", folder_path, item_name))
            else:
                # 普通文件
                item = QTreeWidgetItem(parent_item)
                item.setText(0, item_name)
                item.setText(1, "已启用")
                item.setText(2, item_path)
                item.setText(3, "")
                item.setData(0, Qt.UserRole, ("folder", folder_path, item_name))
    
    def parse_command(self, command):
        # 简单的命令解析,分离路径和参数
        if command.startswith('"'):
            # 处理带引号的路径
            end_quote = command.find('"', 1)
            if end_quote != -1:
                path = command[1:end_quote]
                args = command[end_quote+1:].strip()
                return path, args
        else:
            # 不带引号的路径
            space_pos = command.find(' ')
            if space_pos != -1:
                return command[:space_pos], command[space_pos+1:].strip()
        
        # 没有参数的情况
        return command, ""
    
    def show_context_menu(self, position):
        item = self.tree.itemAt(position)
        if not item or not item.parent():
            return
            
        menu = QMenu()
        delete_action = menu.addAction("删除启动项")
        delete_action.setIcon(self.style().standardIcon(QStyle.SP_TrashIcon))
        delete_action.triggered.connect(lambda: self.delete_startup_item(item))
        menu.exec_(self.tree.viewport().mapToGlobal(position))
    
    def delete_startup_item(self, item):
        item_type, *item_data = item.data(0, Qt.UserRole)
        
        reply = QMessageBox.question(self, '确认删除', 
                                    '确定要删除这个启动项吗?', 
                                    QMessageBox.Yes | QMessageBox.No, 
                                    QMessageBox.No)
        
        if reply == QMessageBox.Yes:
            try:
                if item_type == "registry":
                    hive, subkey, name = item_data
                    with winreg.OpenKey(hive, subkey, 0, winreg.KEY_WRITE) as key:
                        winreg.DeleteValue(key, name)
                elif item_type == "folder":
                    folder_path, item_name = item_data
                    os.remove(os.path.join(folder_path, item_name))
                
                # 从树中移除
                item.parent().removeChild(item)
                QMessageBox.information(self, "成功", "启动项已删除")
                self.statusBar().showMessage("启动项已删除", 3000)
            except Exception as e:
                QMessageBox.critical(self, "错误", f"删除失败: {str(e)}")
                self.statusBar().showMessage(f"删除失败: {str(e)}", 3000)
    
    def browse_file(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择可执行文件", "", 
            "可执行文件 (*.exe *.bat *.cmd);;快捷方式 (*.lnk);;所有文件 (*.*)")
        
        if file_path:
            self.path_input.setText(file_path)
            if not self.name_input.text():
                # 自动填充名称
                name = os.path.splitext(os.path.basename(file_path))[0]
                self.name_input.setText(name)
            self.statusBar().showMessage(f"已选择文件: {file_path}", 3000)
    
    def drag_enter_event(self, event):
        if event.mimeData().hasUrls():
            event.acceptProposedAction()
    
    def drop_event(self, event, drop_group):
        urls = event.mimeData().urls()
        if urls:
            file_path = urls[0].toLocalFile()
            if file_path.lower().endswith(('.exe', '.lnk', '.bat', '.cmd')):
                # 更新UI显示
                drop_label = drop_group.findChild(QLabel)
                drop_label.setText(f"已选择: {os.path.basename(file_path)}")
                drop_label.setStyleSheet("""
                    font-size: 16px; 
                    font-weight: bold; 
                    color: #8a2be2;
                """)
                
                # 自动填充表单
                self.path_input.setText(file_path)
                if not self.name_input.text():
                    name = os.path.splitext(os.path.basename(file_path))[0]
                    self.name_input.setText(name)
                
                event.acceptProposedAction()
                self.statusBar().showMessage(f"已拖入文件: {file_path}", 3000)
    
    def add_manual_startup_item(self):
        name = self.name_input.text().strip()
        path = self.path_input.text().strip()
        args = self.args_input.text().strip()
        
        if not name or not path:
            QMessageBox.warning(self, "警告", "名称和路径不能为空")
            self.statusBar().showMessage("错误: 名称和路径不能为空", 3000)
            return
            
        if not os.path.exists(path):
            QMessageBox.warning(self, "警告", "指定的路径不存在")
            self.statusBar().showMessage("错误: 指定的路径不存在", 3000)
            return
        
        # 确定添加位置
        location = "user"
        if self.location_radio_all.isChecked():
            loc编程China编程ation = "all_users"
        elif self.location_radio_folder.isChecked():
            location = "folder"
        
        try:
            if location in ["user", "all_users"]:
                # 添加到注册表
                hive = winreg.HKEY_CURRENT_USER if location == "user" else winreg.HKEY_LOCAL_MACHINE
                subkey = r"Software\Microsoft\Windows\CurrentVersion\Run"
                
                # 构建完整命令
                command = f'"{path}"' if ' ' in path else path
                if args:
                    command += f" {args}"
                
                try:
                    with winreg.OpenKey(hive, subkey, 0, winreg.KEY_WRITE) as key:
                        winreg.SetValueEx(key, name, 0, winreg.REG_SZ, command)
                except PermissionError:
                    QMessageBox.critical(self, "错误", "需要管理员权限才能添加所有用户启动项")
                    self.statusBar().showMessage("错误: 需要管理员权限", 3000)
                    return
            else:
                # 添加到启动文件夹
                startup_folder = os.path.join(os.getenv('APPDATA'), 
                                           r'Microsoft\Windows\Start Menu\Programs\Startup')
                
                # 如果是快捷方式,直接复制
                if path.lower().endswith('.lnk'):
                    import shutil
                    shutil.copy(path, os.path.join(startup_folder, f"{name}.lnk"))
                else:
                    # 创建快捷方式
                    try:
                        from win32com.client import Dispatch
                        shell = Dispatch('WScript.Shell')
                        shortcut = shell.CreateShortCut(
                            os.path.join(startup_folder, f"{name}.lnk"))
                        shortcut.TargetPath = path
                        shortcut.Arguments = args
                        shortcut.WorkingDirectory = os.path.dirname(path)
                        shortcut.save()
                    except:
                        QMessageBox.critical(self, "错误", "无法创建快捷方式")
                        self.statusBar().showMessage("错误: 无法创建快捷方式", 3000)
                        return
            
            QMessageBox.information(self, "成功", "启动项已添加")
            
            # 清空表单
            self.name_input.clear()
            self.path_input.clear()
            self.args_input.clear()
            
            # 重置拖拽区域
            drop_group = self.findChild(QGroupBox, "拖拽添加")
            if drop_group:
                drop_label = drop_group.findChild(QLabel)
                drop_label.setText("拖拽可执行文件或快捷方式到此处")
                drop_label.setStyleSheet("""
                    font-size: 16px; 
                    font-weight: bold; 
                    color: #9370db;
                """)
            
            # 刷新列表
            self.load_startup_items()
            self.statusBar().showMessage("启动项已成功添加", 3000)
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"添加失败: {str(e)}")
            self.statusBar().showMessage(f"添加失败: {str(e)}", 3000)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    
    # 设置应用程序字体
    font = QFont("微软雅黑", 10)
    app.setFont(font)
    
    window = StartupManager()
    window.show()
    sys.exit(app.exec_())

扩展思考

我们的工具还可以进一步优化

1.启动延迟设置:通过/delay参数或任务计划程序实现分批启动

2.启动影响评估:监控各启动项的资源占用情况

3.云同步功能:将配置保存到云端,多设备同步

4.黑白名单机制:自动识别可疑启动项

5.管理员权限自动提权:使用ctypes调用ShellExecute以管理员身份运行

import ctypes
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)

总结

通过本项目,我们学到了:

  • PyQt5高级UI开发 - 自定义样式、复杂布局、拖放功能
  • Windows注册表操作 - 安全读写系统关键配置
  • 系统工具开发思路 - 从用户需求出发设计功能
  • 异常处理重要性 - 特别是涉及系统级操作时

完整代码已在上文提供,建议大家:

  • 先理解核心逻辑
  • 分段测试各个功能模块
  • 尝试添加自己的创新功能

以上就是Python+PyQt5开发一个Windows电脑启动项管理神器的详细内容,更多关于Python电脑启动项管理的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于Python+PyQt5开发一个Windows电脑启动项管理神器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154497

相关文章

Python多进程、多线程、协程典型示例解析(最新推荐)

《Python多进程、多线程、协程典型示例解析(最新推荐)》:本文主要介绍Python多进程、多线程、协程典型示例解析(最新推荐),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 目录一、multiprocessing(多进程)1. 模块简介2. 案例详解:并行计算平方和3. 实现逻

Python对PDF书签进行添加,修改提取和删除操作

《Python对PDF书签进行添加,修改提取和删除操作》PDF书签是PDF文件中的导航工具,通常包含一个标题和一个跳转位置,本教程将详细介绍如何使用Python对PDF文件中的书签进行操作... 目录简介使用工具python 向 PDF 添加书签添加书签添加嵌套书签Python 修改 PDF 书签Pytho

PyQt5 QDate类的具体使用

《PyQt5QDate类的具体使用》QDate是PyQt5中处理日期的核心类,本文主要介绍了PyQt5QDate类的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录核心功能常用方法及代码示例​1. 创建日期对象​2. 获取日期信息​3. 日期计算与比较​4. 日

利用Python实现可回滚方案的示例代码

《利用Python实现可回滚方案的示例代码》很多项目翻车不是因为不会做,而是走错了方向却没法回头,技术选型失败的风险我们都清楚,但真正能提前规划“回滚方案”的人不多,本文从实际项目出发,教你如何用Py... 目录描述题解答案(核心思路)题解代码分析第一步:抽象缓存接口第二步:实现两个版本第三步:根据 Fea

Python中CSV文件处理全攻略

《Python中CSV文件处理全攻略》在数据处理和存储领域,CSV格式凭借其简单高效的特性,成为了电子表格和数据库中常用的文件格式,Python的csv模块为操作CSV文件提供了强大的支持,本文将深入... 目录一、CSV 格式简介二、csv模块核心内容(一)模块函数(二)模块类(三)模块常量(四)模块异常

Python报错ModuleNotFoundError的10种解决方案

《Python报错ModuleNotFoundError的10种解决方案》在Python开发中,ModuleNotFoundError是最常见的运行时错误之一,通常由模块路径配置错误、依赖缺失或命名冲... 目录一、常见错误场景与原因分析二、10种解决方案与代码示例1. 检查并安装缺失模块2. 动态添加模块

python利用backoff实现异常自动重试详解

《python利用backoff实现异常自动重试详解》backoff是一个用于实现重试机制的Python库,通过指数退避或其他策略自动重试失败的操作,下面小编就来和大家详细讲讲如何利用backoff实... 目录1. backoff 库简介2. on_exception 装饰器的原理2.1 核心逻辑2.2

python如何下载网络文件到本地指定文件夹

《python如何下载网络文件到本地指定文件夹》这篇文章主要为大家详细介绍了python如何实现下载网络文件到本地指定文件夹,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下...  在python中下载文件到本地指定文件夹可以通过以下步骤实现,使用requests库处理HTTP请求,并结合o

Python实现获取带合并单元格的表格数据

《Python实现获取带合并单元格的表格数据》由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,所以本文我们就来聊聊如何使用Python实现获取带合并单元格的表格数据吧... 由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,现将将封装成类,并通过调用list_exc

Python logging模块使用示例详解

《Pythonlogging模块使用示例详解》Python的logging模块是一个灵活且强大的日志记录工具,广泛应用于应用程序的调试、运行监控和问题排查,下面给大家介绍Pythonlogging模... 目录一、为什么使用 logging 模块?二、核心组件三、日志级别四、基本使用步骤五、快速配置(bas