Python+PyQt5開發(fā)一個Windows電腦啟動項管理神器
開篇:為什么我們需要啟動項管理工具
在日常使用Windows電腦時,你是否遇到過這些問題?
電腦開機越來越慢,像老牛拉破車
莫名其妙的后臺程序占用大量資源
想禁用某些啟動項卻找不到入口
需要添加自定義啟動項但操作復雜
今天,我將帶大家用PyQt5開發(fā)一款顏值與功能并存的Windows啟動項管理工具!不僅能查看/刪除現(xiàn)有啟動項,還能智能添加新啟動項,支持拖拽操作,界面美觀大方!
功能全景圖
先來看看我們開發(fā)的工具具備哪些殺手級功能:
? 雙模式啟動項查看
- 當前用戶啟動項(HKCU)
- 所有用戶啟動項(HKLM)
- 啟動文件夾項目
? 智能分類展示
- 名稱/狀態(tài)/路徑/參數(shù) 四維信息
- 樹形結(jié)構(gòu)清晰明了
? 三種添加方式
- 拖拽文件快捷添加(超方便?。?/li>
- 表單手動填寫添加
- 瀏覽文件選擇添加
? 多位置支持
- 當前用戶注冊表
- 所有用戶注冊表(需管理員權(quán)限)
- 啟動文件夾
? 右鍵快捷操作
一鍵刪除不需要的啟動項
核心技術(shù)解析
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) # 創(chuàng)建樹形項目... i += 1 except OSError: break
關(guān)鍵點:
- 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設計美學
我們的工具采用紫色系漸變風格,看起來專業(yè)又不失活潑:
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. 創(chuàng)新UI元素
拖拽添加區(qū)域:
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. 主窗口結(jié)構(gòu)
class StartupManager(QMainWindow): def __init__(self): super().__init__() # 窗口基本設置 self.setWindowTitle("電腦啟動項管理工具") self.setGeometry(100, 100, 1000, 750) # 初始化UI self.set_style() self.init_ui() # 加載數(shù)據(jù) 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): # 獲取表單數(shù)據(jù) name = self.name_input.text() path = self.path_input.text() # 驗證輸入 if not name or not path: QMessageBox.warning("名稱和路徑不能為空") return # 根據(jù)選擇的位置執(zhí)行添加 if self.location_radio_all.isChecked(): # 寫入HKLM注冊表(需要管理員權(quán)限) elif self.location_radio_folder.isChecked(): # 創(chuàng)建快捷方式到啟動文件夾 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) # 創(chuàng)建標題欄 self.create_title_bar(main_layout) # 創(chuàng)建選項卡 self.tabs = QTabWidget() self.tabs.setTabPosition(QTabWidget.North) self.tabs.setDocumentMode(True) main_layout.addWidget(self.tabs) # 創(chuàng)建"查看啟動項"標簽頁 self.create_view_tab() # 創(chuàng)建"添加啟動項"標簽頁 self.create_add_tab() # 創(chuàng)建狀態(tài)欄 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("輕松管理系統(tǒng)啟動項") 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(["名稱", "狀態(tài)", "命令行/路徑", "參數(shù)"]) 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) # 添加到滾動區(qū)域 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) # 創(chuàng)建拖拽區(qū)域 self.create_drop_area(layout) # 創(chuàng)建表單區(qū)域 self.create_form_area(layout) self.tabs.addTab(add_tab, "添加啟動項") def create_drop_area(self, layout): # 拖拽區(qū)域 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("拖拽可執(zhí)行文件或快捷方式到此處") 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): # 表單區(qū)域 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) # 參數(shù)輸入 self.args_input = QLineEdit() self.args_input.setPlaceholderText("可選參數(shù)") form_layout.addRow("參數(shù):", 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) # 解析值和參數(shù) path, args = self.parse_command(value) item = QTreeWidgetItem(parent_item) item.setText(0, name) item.setText(1, "已啟用") item.setText(2, path) 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('ProgramData'), 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 # 構(gòu)建完整路徑 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): # 簡單的命令解析,分離路徑和參數(shù) 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() # 沒有參數(shù)的情況 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, "選擇可執(zhí)行文件", "", "可執(zhí)行文件 (*.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(): location = "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" # 構(gòu)建完整命令 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, "錯誤", "需要管理員權(quán)限才能添加所有用戶啟動項") self.statusBar().showMessage("錯誤: 需要管理員權(quán)限", 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: # 創(chuàng)建快捷方式 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, "錯誤", "無法創(chuàng)建快捷方式") self.statusBar().showMessage("錯誤: 無法創(chuàng)建快捷方式", 3000) return QMessageBox.information(self, "成功", "啟動項已添加") # 清空表單 self.name_input.clear() self.path_input.clear() self.args_input.clear() # 重置拖拽區(qū)域 drop_group = self.findChild(QGroupBox, "拖拽添加") if drop_group: drop_label = drop_group.findChild(QLabel) drop_label.setText("拖拽可執(zhí)行文件或快捷方式到此處") 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_())
擴展思考
我們的工具還可以進一步優(yōu)化:
1.啟動延遲設置:通過/delay參數(shù)或任務計劃程序?qū)崿F(xiàn)分批啟動
2.啟動影響評估:監(jiān)控各啟動項的資源占用情況
3.云同步功能:將配置保存到云端,多設備同步
4.黑白名單機制:自動識別可疑啟動項
5.管理員權(quán)限自動提權(quán):使用ctypes調(diào)用ShellExecute以管理員身份運行
import ctypes ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)
總結(jié)
通過本項目,我們學到了:
- PyQt5高級UI開發(fā) - 自定義樣式、復雜布局、拖放功能
- Windows注冊表操作 - 安全讀寫系統(tǒng)關(guān)鍵配置
- 系統(tǒng)工具開發(fā)思路 - 從用戶需求出發(fā)設計功能
- 異常處理重要性 - 特別是涉及系統(tǒng)級操作時
完整代碼已在上文提供,建議大家:
- 先理解核心邏輯
- 分段測試各個功能模塊
- 嘗試添加自己的創(chuàng)新功能
以上就是Python+PyQt5開發(fā)一個Windows電腦啟動項管理神器的詳細內(nèi)容,更多關(guān)于Python電腦啟動項管理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
用Python實現(xiàn)一個簡單的多線程TCP服務器的教程
這篇文章主要介紹了用Python實現(xiàn)一個簡單的多線程TCP服務器的教程,示例的運行環(huán)境為Windows操作系統(tǒng),需要的朋友可以參考下2015-05-05關(guān)于python線程池的四種實現(xiàn)方式
這篇文章主要介紹了關(guān)于python線程池的四種實現(xiàn)方式,一個程序運行起來后,一定有一個執(zhí)行代碼的東西,這個東西就是線程,需要的朋友可以參考下2023-04-04