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

相关文章

Mac电脑如何通过 IntelliJ IDEA 远程连接 MySQL

《Mac电脑如何通过IntelliJIDEA远程连接MySQL》本文详解Mac通过IntelliJIDEA远程连接MySQL的步骤,本文通过图文并茂的形式给大家介绍的非常详细,感兴趣的朋友跟... 目录MAC电脑通过 IntelliJ IDEA 远程连接 mysql 的详细教程一、前缀条件确认二、打开 ID

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键

从基础到进阶详解Python条件判断的实用指南

《从基础到进阶详解Python条件判断的实用指南》本文将通过15个实战案例,带你大家掌握条件判断的核心技巧,并从基础语法到高级应用一网打尽,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录​引言:条件判断为何如此重要一、基础语法:三行代码构建决策系统二、多条件分支:elif的魔法三、

Python WebSockets 库从基础到实战使用举例

《PythonWebSockets库从基础到实战使用举例》WebSocket是一种全双工、持久化的网络通信协议,适用于需要低延迟的应用,如实时聊天、股票行情推送、在线协作、多人游戏等,本文给大家介... 目录1. 引言2. 为什么使用 WebSocket?3. 安装 WebSockets 库4. 使用 We

python中的显式声明类型参数使用方式

《python中的显式声明类型参数使用方式》文章探讨了Python3.10+版本中类型注解的使用,指出FastAPI官方示例强调显式声明参数类型,通过|操作符替代Union/Optional,可提升代... 目录背景python函数显式声明的类型汇总基本类型集合类型Optional and Union(py

基于Java开发一个极简版敏感词检测工具

《基于Java开发一个极简版敏感词检测工具》这篇文章主要为大家详细介绍了如何基于Java开发一个极简版敏感词检测工具,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录你是否还在为敏感词检测头疼一、极简版Java敏感词检测工具的3大核心优势1.1 优势1:DFA算法驱动,效率提升10

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

使用Python实现无损放大图片功能

《使用Python实现无损放大图片功能》本文介绍了如何使用Python的Pillow库进行无损图片放大,区分了JPEG和PNG格式在放大过程中的特点,并给出了示例代码,JPEG格式可能受压缩影响,需先... 目录一、什么是无损放大?二、实现方法步骤1:读取图片步骤2:无损放大图片步骤3:保存图片三、示php

Python文本相似度计算的方法大全

《Python文本相似度计算的方法大全》文本相似度是指两个文本在内容、结构或语义上的相近程度,通常用0到1之间的数值表示,0表示完全不同,1表示完全相同,本文将深入解析多种文本相似度计算方法,帮助您选... 目录前言什么是文本相似度?1. Levenshtein 距离(编辑距离)核心公式实现示例2. Jac

使用Python实现一个简易计算器的新手指南

《使用Python实现一个简易计算器的新手指南》计算器是编程入门的经典项目,它涵盖了变量、输入输出、条件判断等核心编程概念,通过这个小项目,可以快速掌握Python的基础语法,并为后续更复杂的项目打下... 目录准备工作基础概念解析分步实现计算器第一步:获取用户输入第二步:实现基本运算第三步:显示计算结果进