开发者

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

目录
  • 开篇:为什么我们需要启动项管理工具
  • 功能全景图
  • 核心技术解析
    • 1. Windows注册表操作
    • 2. 启动文件夹处理
  • UI设计美学
    • 1. 全局样式设置
    • 2. 创新UI元素
  • 完整源码剖析
    • 1. 主窗口结构
    • 2. 启动项加载逻辑
    • 3. 添加新启动项
  • 完整源码下载
    • 扩展思考
      • 总结

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

        在日常使用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; 
                   编程客栈 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编程客栈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电脑启动项管理的资料请关注编程客栈(www.devze.com)其它相关文章!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜