
wechat_2025-07-02_164417_846.png (40.69 KB, 下载次数: 0)
下载附件
2025-7-2 16:49 上传
python编译文件太大了就不上传成品了,以下是源码可以自行打包
[Python] 纯文本查看 复制代码import sys
import os
import winreg
import win32api
import win32con
import re
import ctypes
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QTableWidget, QTableWidgetItem,
QHeaderView, QFileDialog, QMessageBox, QVBoxLayout, QWidget,
QAbstractItemView, QHBoxLayout, QFileIconProvider)
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtCore import Qt, QSize, QFileInfo
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
class StartupManager(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("启动项管理器")
self.setFixedSize(900, 500)
# 初始化样式
style = ttk.Style(theme='cosmo')
# 配置边框线为纯黑色的样式
style.configure('BlackBorder.TLabelframe', bordercolor='black', relief='solid', borderwidth=1)
# 主布局
main_widget = QWidget()
main_layout = QVBoxLayout()
main_widget.setLayout(main_layout)
self.setCentralWidget(main_widget)
# 第一行:加载按钮
self.load_button = QPushButton("加载启动项")
self.load_button.setStyleSheet("bootstyle='primary'; height: 30px;")
self.load_button.clicked.connect(self.load_startup_items)
main_layout.addWidget(self.load_button)
# 第二行:表格
self.table = QTableWidget()
self.table.setStyleSheet("border: 1px solid black;")
self.table.setColumnCount(5)
self.table.setHorizontalHeaderLabels(["序号", "图标", "名称", "路径", "操作"])
self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents)
self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
self.table.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Fixed)
self.table.setColumnWidth(4, 180)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.table.verticalHeader().setDefaultSectionSize(40)
self.table.setStyleSheet("border: 1px solid black;")
main_layout.addWidget(self.table)
# 创建图标提供者
self.icon_provider = QFileIconProvider()
def load_startup_items(self):
"""加载Windows启动项到表格中"""
self.table.setRowCount(0) # 清空表格
# 获取当前用户启动目录
startup_path = os.path.join(os.getenv('APPDATA'),
r'Microsoft\Windows\Start Menu\Programs\Startup')
# 获取注册表启动项 (HKEY_CURRENT_USER)
registry_startup = self.get_registry_startup()
# 添加文件启动项
row = 0
if os.path.exists(startup_path):
for filename in os.listdir(startup_path):
filepath = os.path.join(startup_path, filename)
if os.path.isfile(filepath):
self.add_item_to_table(row, filename, filepath, "File")
row += 1
# 添加注册表启动项
for name, value in registry_startup.items():
# 尝试从注册表值中提取路径
path = self.extract_path_from_registry_value(value)
if path:
self.add_item_to_table(row, name, path, "Registry")
row += 1
def get_registry_startup(self):
"""从注册表获取启动项"""
startup_items = {}
try:
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Run")
index = 0
while True:
try:
name, value, _ = winreg.EnumValue(key, index)
startup_items[name] = value
index += 1
except OSError:
break
except WindowsError:
pass
return startup_items
def extract_path_from_registry_value(self, value):
"""精确提取注册表值中的文件路径"""
# 尝试匹配双引号内的路径
quoted_path_match = re.search(r'"([^"]+)"', value)
if quoted_path_match:
path = quoted_path_match.group(1)
# 检查是否是有效路径
if os.path.exists(path):
return path
# 检查是否是带参数的可执行文件
exe_match = re.search(r'^(.+?\.(?:exe|bat|cmd|vbs))', path, re.IGNORECASE)
if exe_match and os.path.exists(exe_match.group(1)):
return exe_match.group(1)
# 如果没有双引号包裹的路径,尝试匹配可能的文件路径
# 匹配常见的可执行文件扩展名
exe_match = re.search(r'[a-zA-Z]:[\\/](?:[^\\/:*?"|\r\n]+[\\/])*[^\\/:*?"|\r\n]+\.(?:exe|bat|cmd|vbs)',
value, re.IGNORECASE)
if exe_match:
path = exe_match.group(0)
if os.path.exists(path):
return path
# 尝试直接使用整个值(如果它是有效路径)
if os.path.exists(value):
return value
# 尝试提取路径 (处理带参数的情况)
parts = value.split()
if parts:
# 检查第一个部分是否是路径
if os.path.exists(parts[0]):
return parts[0]
# 检查是否有以常见扩展名结尾的部分
for part in parts:
if re.search(r'\.(exe|bat|cmd|vbs)$', part, re.IGNORECASE) and os.path.exists(part):
return part
# 如果以上方法都失败,返回原始值
return value
def get_file_icon(self, path):
"""获取文件的系统图标"""
try:
# 使用Qt内置的图标提供者
file_info = QFileInfo(path)
# 如果路径是文件且存在
if file_info.exists() and file_info.isFile():
return self.icon_provider.icon(file_info)
# 如果文件不存在,尝试获取文件类型的图标
if '.' in path:
ext = os.path.splitext(path)[1]
if ext:
# 创建临时文件信息对象
temp_file = QFileInfo("dummy" + ext)
return self.icon_provider.icon(temp_file)
# 返回默认文件图标
return self.icon_provider.icon(self.icon_provider.File)
except:
# 如果获取图标失败,返回默认图标
return self.icon_provider.icon(self.icon_provider.File)
def add_item_to_table(self, row, name, path, item_type):
"""添加启动项到表格"""
self.table.insertRow(row)
# 序号列
self.table.setItem(row, 0, QTableWidgetItem(str(row + 1)))
# 图标列
icon_item = QTableWidgetItem()
try:
# 获取文件图标
icon = self.get_file_icon(path)
icon_item.setIcon(icon)
except:
# 如果无法获取图标,使用默认图标
icon_item.setIcon(self.icon_provider.icon(self.icon_provider.File))
icon_item.setTextAlignment(Qt.AlignCenter)
self.table.setItem(row, 1, icon_item)
# 名称列
self.table.setItem(row, 2, QTableWidgetItem(name))
# 路径列
path_item = QTableWidgetItem(path)
path_item.setToolTip(path) # 添加提示
self.table.setItem(row, 3, path_item)
# 操作列 (添加两个水平排列的按钮)
widget = QWidget()
layout = QHBoxLayout(widget)
layout.setContentsMargins(5, 5, 5, 5)
layout.setSpacing(5)
# 打开按钮
open_btn = QPushButton("打开位置")
open_btn.setFixedSize(80, 25)
open_btn.clicked.connect(lambda _, p=path: self.open_path(p))
# 删除按钮
delete_btn = QPushButton("删除")
delete_btn.setFixedSize(80, 25)
delete_btn.setStyleSheet("background-color: #ff4d4d; color: white;")
delete_btn.clicked.connect(lambda _, r=row, t=item_type, n=name, p=path:
self.delete_startup_item(r, t, n, p))
layout.addWidget(open_btn)
layout.addWidget(delete_btn)
self.table.setCellWidget(row, 4, widget)
def open_path(self, path):
"""打开文件所在位置"""
# 如果路径包含双引号,尝试去除
clean_path = path.strip('"')
if os.path.isfile(clean_path):
# 打开文件所在文件夹并选中文件
os.startfile(os.path.dirname(clean_path))
elif os.path.isdir(clean_path):
# 打开文件夹
os.startfile(clean_path)
else:
# 尝试打开包含该文件的文件夹
dir_path = os.path.dirname(clean_path)
if os.path.exists(dir_path):
os.startfile(dir_path)
else:
QMessageBox.warning(self, "错误", "无法找到路径: " + path)
def delete_startup_item(self, row, item_type, name, path):
"""删除启动项"""
reply = QMessageBox.question(
self, "确认删除",
f"确定要删除启动项 '{name}' 吗?",
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
try:
# 清理路径(去除可能的双引号)
clean_path = path.strip('"')
if item_type == "File":
# 删除文件启动项
if os.path.exists(clean_path):
os.remove(clean_path)
else:
# 如果文件不存在,尝试从注册表删除
self.delete_registry_startup(name)
elif item_type == "Registry":
# 删除注册表启动项
self.delete_registry_startup(name)
# 从表格中移除
self.table.removeRow(row)
QMessageBox.information(self, "成功", "启动项已删除")
# 更新序号
for i in range(row, self.table.rowCount()):
self.table.item(i, 0).setText(str(i + 1))
except Exception as e:
QMessageBox.critical(self, "错误", f"删除失败: {str(e)}")
def delete_registry_startup(self, name):
"""从注册表删除启动项"""
try:
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Run",
0, winreg.KEY_WRITE)
winreg.DeleteValue(key, name)
winreg.CloseKey(key)
except WindowsError:
# 尝试删除文件启动项作为后备
startup_path = os.path.join(os.getenv('APPDATA'),
r'Microsoft\Windows\Start Menu\Programs\Startup')
file_path = os.path.join(startup_path, name)
if os.path.exists(file_path):
os.remove(file_path)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = StartupManager()
window.show()
sys.exit(app.exec_())