Windows程序卸载工具(Python)

import shlex

import subprocess

import tkinter as tk

from tkinter import messagebox, Menu, filedialog

import winreg

from datetime import datetime

import webbrowser

import os

class UninstallerApp:

     # 初始化函数,创建主界面和相关元素

    def __init__(self, root):

        self.root = root

        self.root.title("程序卸载工具")

       

        # 创建并配置主题选择菜单

        # 默认主题为亮色

        self.theme_var = tk.StringVar(value="light")

        self.theme_menu = tk.Menu(self.root)

        self.root.config(menu=self.theme_menu)

        self.settings_menu = tk.Menu(self.theme_menu, tearoff=0)

        self.theme_menu.add_cascade(label="设置", menu=self.settings_menu)

        self.settings_menu.add_radiobutton(label="亮色主题", variable=self.theme_var, value="light", command=self.change_theme)

        self.settings_menu.add_radiobutton(label="暗色主题", variable=self.theme_var, value="dark", command=self.change_theme)

        # 设置程序列表框架

        self.frame = tk.Frame(self.root)

        self.frame.pack(fill=tk.BOTH, expand=True)

        # 设置并配置搜索框,默认显示提示文字

        self.default_search_text = "请输入要查找的程序"

        self.search_var = tk.StringVar(value=self.default_search_text)

        self.search_entry = tk.Entry(self.root, textvariable=self.search_var, fg='grey')

        self.search_entry.pack()

        # 绑定搜索框的焦点事件,以便在用户交互时清除或显示默认文本

        self.search_entry.bind("<FocusIn>", self.on_search_focus_in)

        self.search_entry.bind("<FocusOut>", self.on_search_focus_out)

       

        # 监听搜索变量的变化,实时更新程序列表

        self.search_var.trace("w", lambda name, index, mode, sv=self.search_var: self.update_list())

        # 程序列表和滚动条的配置

        self.listbox = tk.Listbox(self.frame)

        self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        self.scrollbar = tk.Scrollbar(self.frame, orient="vertical")

        self.scrollbar.config(command=self.listbox.yview)

        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        self.listbox.config(yscrollcommand=self.scrollbar.set)

        # 排序选项的设置

        self.sort_option = tk.StringVar()

        self.sort_option.set("name")  # 默认按名称排序

        # 创建排序选项的单选按钮

        tk.Radiobutton(self.root, text="按名称排序", variable=self.sort_option, value="name", command=self.update_list).pack(anchor=tk.W)

        tk.Radiobutton(self.root, text="按安装时间排序", variable=self.sort_option, value="install_date", command=self.update_list).pack(anchor=tk.W)

        # 添加刷新列表按钮

        tk.Button(self.root, text="刷新列表", command=self.update_list).pack()

        # 添加导出按钮

        tk.Button(self.root, text="导出列表", command=self.export_list).pack()

        # 显示当前查询到的程序数量的标签

        self.status_label = tk.Label(self.root, text="")

        self.status_label.pack()

        # 初始化程序列表并立即更新显示

        self.programs = []

        self.update_list()

        # 绑定列表框的选择和右键点击事件

        self.listbox.bind('<<ListboxSelect>>', self.highlight_program)

        self.listbox.bind('<Button-3>', self.show_context_menu)

        self.selected_program_key = None  # 用于存储当前选中程序的注册表路径

    # 更改主题颜色的函数

    def change_theme(self):

        theme = self.theme_var.get()

        if theme == "light":

            self.root.config(bg="white")

            # 更新其他元素的颜色以适应亮色主题

        elif theme == "dark":

            self.root.config(bg="gray")

            # 更新其他元素的颜色以适应暗色主题

    # 搜索框获取焦点时的事件处理

    def on_search_focus_in(self, event):

        if self.search_var.get() == self.default_search_text:

            self.search_var.set('')

            self.search_entry.config(fg='black')

   

     # 搜索框失去焦点时的事件处理

    def on_search_focus_out(self, event):

        if not self.search_var.get():

            self.search_var.set(self.default_search_text)

            self.search_entry.config(fg='grey')

    # 高亮显示选中的程序

    def highlight_program(self, event):

        pass

    # 显示右键菜单的事件处理

    def show_context_menu(self, event):

        self.context_menu = Menu(self.root, tearoff=0)

        # 右键菜单添加选项:卸载、在线搜索、打开注册表条目、打开文件安装目录

        self.context_menu.add_command(label="卸载", command=self.uninstall_program)

        self.context_menu.add_command(label="在Bing中搜索", command=self.search_online)

        self.context_menu.add_command(label="注册表条目", command=self.open_reg_entry)  # 新增注册表条目选项

        self.context_menu.add_command(label="文件安装目录", command=self.open_install_directory)

        self.context_menu.tk_popup(event.x_root, event.y_root)

        # 根据选中的项目更新当前选中程序的安装位置

        selection = self.listbox.curselection()

        if selection:

            _, _, _, _, self.selected_program_install_location = self.programs[selection[0]]

    # 卸载程序的函数

    def uninstall_program(self):

        selection = self.listbox.curselection()

        if selection:

            program_name, uninstall_string, _, _, _ = self.programs[selection[0]]

       

        # 分割命令字符串为命令和参数(适用于包含路径和参数的复杂命令)

            command_parts = shlex.split(uninstall_string)

       

            try:

            # 执行卸载命令,等待命令完成并获取输出

                result = subprocess.run(command_parts, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

           

            # 根据卸载命令的执行结果给用户反馈

                if result.returncode == 0:

                    messagebox.showinfo("卸载成功", f"{program_name} 卸载成功。")

                else:

                # 如果命令执行失败,显示错误信息

                    messagebox.showerror("卸载失败", f"{program_name} 卸载失败:{result.stderr}")

               

            except subprocess.CalledProcessError as e:

            # 捕获执行错误,向用户显示错误信息

                messagebox.showerror("卸载失败", f"尝试卸载 {program_name} 时出错:{e.stderr}")

            except Exception as e:

            # 捕获其他可能的错误,并给予反馈

                messagebox.showerror("卸载失败", f"卸载 {program_name} 时发生未知错误:{str(e)}")

    # 在线搜索选中的程序

    def search_online(self):

        selection = self.listbox.curselection()

        if selection:

            program_name, _, _, _ = self.programs[selection[0]]

            webbrowser.open(f"https://www.bing.com/search?q={program_name}", new=2)

    # 打开选中程序的安装目录

    def open_install_directory(self):

        if self.selected_program_install_location:

            os.startfile(self.selected_program_install_location)

        else:

            messagebox.showinfo("信息", "未找到安装目录或安装目录未指定。")

    # 更新程序列表的函数

    def update_list(self):

        self.listbox.delete(0, tk.END)# 清空当前列表

        self.programs = self.list_installed_programs()# 获取最新的程序列表

        search_term = self.search_var.get().lower()

       

        # 当搜索框显示默认文字时,显示所有程序

        if search_term == self.default_search_text.lower():

            search_term = ''

       

        # 根据搜索条件过滤程序列表

        filtered_programs = [program for program in self.programs if search_term in program[0].lower()]

       

        # 在列表框中显示过滤后的程序列表

        for program in filtered_programs:

            name, uninstall_string, install_date, reg_key, install_location = program

            display_text = f"{name} - 安装时间: {install_date.strftime('%Y-%m-%d') if install_date != datetime.min else '未知'}"

            self.listbox.insert(tk.END, display_text)

       

        # 更新状态标签显示当前查询到的程序数量

        self.status_label.config(text=f"查询到的程序数量: {len(filtered_programs)}")

    # 列出已安装程序的函数

    def list_installed_programs(self):

        programs = []

        # 遍历注册表中的卸载信息

        for key_path in [r"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", r"SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"]:

            base_key = r"HKEY_LOCAL_MACHINE"

            with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path) as key:

                for i in range(0, winreg.QueryInfoKey(key)[0]):

                    skey_name = winreg.EnumKey(key, i)

                    with winreg.OpenKey(key, skey_name) as skey:

                        try:

                            # 尝试获取程序的显示名称

                            name = winreg.QueryValueEx(skey, "DisplayName")[0]

                            # 获取程序的卸载命令字符串

                            uninstall_string = winreg.QueryValueEx(skey, "UninstallString")[0]

                            try:

                                # 尝试获取程序的安装位置

                                install_location = winreg.QueryValueEx(skey, "InstallLocation")[0]

                            except FileNotFoundError:

                                install_location = ""  # 如果未找到安装位置,则设置为空字符串

                            try:

                                # 尝试获取程序的安装日期

                                install_date = winreg.QueryValueEx(skey, "InstallDate")[0]

                                # 转换日期格式

                                install_date = datetime.strptime(install_date, "%Y%m%d")

                            except:

                                # 如果未找到安装日期,则设置为最小日期值

                                install_date = datetime.min

                                 # 构造完整的注册表键路径

                            full_key_path = f"{base_key}\\{key_path}\\{skey_name}"

                            programs.append((name, uninstall_string, install_date, full_key_path, install_location))

                        except OSError:

                            pass  

                         # 如果遇到读取注册表项时的错误,则忽略该项并继续

        # 根据用户选择的排序方式对程序列表进行排序

        if self.sort_option.get() == "name":

            # 如果用户选择按名称排序,则按程序名称的小写形式进行字母升序排序

            programs.sort(key=lambda x: x[0].lower())

        else:

             # 如果用户选择按安装时间排序,则按程序的安装日期进行降序排序

            programs.sort(key=lambda x: x[2], reverse=True)

        return programs

     # 返回排序后的程序列表

    # 导出列表函数:将当前显示的程序列表导出到文本文件

    def export_list(self):

        # 弹出文件保存对话框,让用户选择保存文件的路径和名称,默认扩展名为.txt

        file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files", "*.txt")])

        if file_path:

             # 如果用户选择了文件路径,则打开该路径的文件进行写入操作

            with open(file_path, "w", encoding="utf-8") as file:

                # 遍历程序列表,将每个程序的名称、卸载字符串和安装日期写入文件

                for name, uninstall_string, install_date, _ in self.programs:

                    # 格式化字符串:程序名称、卸载字符串、安装日期(如果未知则显示'N/A')

                    file.write(f"{name}\t{uninstall_string}\t{install_date.strftime('%Y-%m-%d') if install_date != datetime.min else 'N/A'}\n")

            # 数据写入完成后,显示提示信息告知用户程序列表已成功导出

            messagebox.showinfo("导出完成", "程序列表已导出至文本文件。")

    # 打开注册表项函数:用于打开当前选中程序的注册表编辑器位置

    def open_reg_entry(self):

         # 检查当前是否有程序被选中(即selected_program_key是否非空)

        if self.selected_program_key:

            # 指定注册表编辑器的路径

            regedit_path = r"regedit.exe"

             # 使用subprocess模块运行注册表编辑器并打开到指定的注册表项

            subprocess.run([regedit_path, self.selected_program_key])

if __name__ == "__main__":

    root = tk.Tk()

    app = UninstallerApp(root)

    root.mainloop()

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值