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开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符

Python版本与package版本兼容性检查方法总结

《Python版本与package版本兼容性检查方法总结》:本文主要介绍Python版本与package版本兼容性检查方法的相关资料,文中提供四种检查方法,分别是pip查询、conda管理、PyP... 目录引言为什么会出现兼容性问题方法一:用 pip 官方命令查询可用版本方法二:conda 管理包环境方法

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注

pycharm跑python项目易出错的问题总结

《pycharm跑python项目易出错的问题总结》:本文主要介绍pycharm跑python项目易出错问题的相关资料,当你在PyCharm中运行Python程序时遇到报错,可以按照以下步骤进行排... 1. 一定不要在pycharm终端里面创建环境安装别人的项目子模块等,有可能出现的问题就是你不报错都安装

Python打包成exe常用的四种方法小结

《Python打包成exe常用的四种方法小结》本文主要介绍了Python打包成exe常用的四种方法,包括PyInstaller、cx_Freeze、Py2exe、Nuitka,文中通过示例代码介绍的非... 目录一.PyInstaller11.安装:2. PyInstaller常用参数下面是pyinstal