无忧启动论坛

 找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 1184|回复: 35
打印 上一主题 下一主题

[原创] 【2025年6月7日更新】 软件自选安装管理器

[复制链接]
跳转到指定楼层
1#
本帖最后由 piaomusic 于 2025-6-7 22:54 编辑

软件自选安装管理器        下载地址

通过配置文件切换 主题颜色

[全局设置]
窗口标题 = 软件安装管理器
默认全选 = 是
主题 = 深色  #浅色

[软件列表]
腾讯QQ9.7纯净版 = qq.exe /S
微信电脑版 = WeChatWin.exe /S
Winrar压缩优化版 = WinRAR_v7.01_x64_SC.exe /S
2345看图王优化版 = 2345pic.exe /verysilent
2345拼音输入法优化版 = 2345pinyin.exe /quiet
搜狗拼音输入法优化版 = sogou.exe /S
装机人员维护工具箱 = toolkit.exe /install
爱奇艺优化版 = iqiyi.exe /silent
360安全卫士优化版 = 360safe.exe /S
360安全浏览器优化版 = 360browser.exe /verysilent

2025-06-07_22-49-12.png (70.91 KB, 下载次数: 2)

2025-06-07_22-49-12.png

2025-06-07_22-48-54.png (71.41 KB, 下载次数: 4)

2025-06-07_22-48-54.png
来自 6#
 楼主| 发表于 5 天前 | 只看该作者
本帖最后由 piaomusic 于 2025-6-7 23:02 编辑

# -*- coding: utf-8 -*-
import os
import configparser
import tkinter as tk
from tkinter import ttk, messagebox
import subprocess
import sys
import threading
import psutil

class SoftwareInstaller:
    def __init__(self, root):
        self.root = root
        self.root.title("软件安装管理器")
        self.root.geometry("750x500")
        self.root.resizable(False, False)

        # 初始化变量
        self.software_list = []
        self.install_commands = {}
        self.data_dir = os.path.join(os.path.dirname(sys.argv[0]), "data")
        self.window_title = "软件安装管理器"
        self.default_select_all = False
        self.theme = "浅色"  # 默认主题

        # 安装相关变量
        self.installation_processes = []
        self.failed_installations = []
        self.installation_complete = False
        self.progress_window = None

        # 检查并创建配置文件
        self.init_config_file()

        # 验证配置文件完整性
        if not self.verify_author_info():
            messagebox.showerror("启动失败", "文件被篡改,程序无法启动!")
            sys.exit()

        # 加载配置
        self.load_config()

        # 设置窗口居中
        self.center_window()

        # 设置样式(根据配置)
        self.setup_styles()

        # 创建界面
        self.create_widgets()

        # 加载软件列表
        self.load_software_list()
        self.populate_treeview()

    def verify_author_info(self):
        """验证程序信息部分是否被修改"""
        REQUIRED_AUTHOR_INFO = {
            '作者': '缘起性空',
            '版权': '版权所有 © 2025',
            '联系': '32897251@qq.com'
        }

        if not os.path.exists('config.ini'):
            return False

        config = configparser.ConfigParser()
        config.read('config.ini', encoding='utf-8')

        if not config.has_section('程序信息'):
            return False

        for key, value in REQUIRED_AUTHOR_INFO.items():
            if not config.has_option('程序信息', key):
                return False
            if config.get('程序信息', key) != value:
                return False

        return True

    def init_config_file(self):
        """初始化配置文件"""
        if not os.path.exists('config.ini'):
            try:
                with open('config.ini', 'w', encoding='utf-8') as f:
                    f.write("""[程序信息]
作者 = 缘起性空
版权 = 版权所有 © 2025
联系 = 32897251@qq.com

[全局设置]
窗口标题 = 软件安装管理器
默认全选 = 否
主题 = 浅色  # 可选值:浅色/深色

[软件列表]
# 格式:软件名称 = 安装程序 参数
# 示例:
# 谷歌浏览器 = chrome_installer.exe /silent
# 微信办公版 = wechat_setup.exe /S
""")
                messagebox.showinfo("提示", "已创建默认配置文件 config.ini")
            except Exception as e:
                messagebox.showerror("错误", f"创建配置文件失败: {e}")

    def center_window(self):
        """使窗口居中显示"""
        self.root.update_idletasks()
        width = self.root.winfo_width()
        height = self.root.winfo_height()
        x = (self.root.winfo_screenwidth() // 2) - (width // 2)
        y = (self.root.winfo_screenheight() // 2) - (height // 2)
        self.root.geometry(f'+{x}+{y}')

    def load_config(self):
        """加载配置文件设置"""
        config = configparser.ConfigParser()
        try:
            if os.path.exists('config.ini'):
                config.read('config.ini', encoding='utf-8')

                # 读取窗口标题
                title = self.get_config_value(config, '全局设置', '窗口标题')
                if title:
                    self.window_title = title
                    self.root.title(self.window_title)

                # 读取默认全选设置
                select_all = self.get_config_value(config, '全局设置', '默认全选')
                if select_all:
                    select_all = select_all.strip().lower()
                    self.default_select_all = select_all in ('是', 'true', '1', 'yes', '开启', 'on')

                # 读取主题设置
                theme = self.get_config_value(config, '全局设置', '主题')
                if theme:
                    self.theme = theme.strip()

        except Exception as e:
            messagebox.showerror("配置错误", f"读取配置文件失败: {e}")

    def get_config_value(self, config, section, key):
        """安全获取配置值"""
        try:
            if section in config and key in config[section]:
                return config[section][key]
        except:
            pass
        return None

    def setup_styles(self):
        """根据主题配置设置样式"""
        self.style = ttk.Style()
        self.style.theme_use('clam')

        # 根据主题选择配色方案
        if self.theme == "深色":
            # 深色主题配色
            bg_color = "#2c3e50"      # 背景色
            text_color = "#ecf0f1"    # 文字颜色
            tree_bg = "#34495e"       # 表格背景
            button_bg = "#34495e"     # 按钮背景
            primary_color = "#3498db" # 主色
            secondary_color = "#2ecc71" # 辅助色
        else:
            # 浅色主题配色(默认)
            bg_color = "#f5f5f5"      # 背景色
            text_color = "#333333"    # 文字颜色
            tree_bg = "#ffffff"       # 表格背景
            button_bg = "#e0e0e0"     # 按钮背景
            primary_color = "#4a90e2" # 主色
            secondary_color = "#50c878" # 辅助色

        # 设置窗口背景
        self.root.configure(bg=bg_color)

        # 标题样式
        self.style.configure("Title.TLabel",
                           font=('Microsoft YaHei', 18, 'bold'),
                           background=bg_color,
                           foreground=text_color)

        # 表格样式
        self.style.configure("Treeview",
                           font=('Microsoft YaHei', 11),
                           rowheight=30,
                           background=tree_bg,
                           fieldbackground=tree_bg,
                           foreground=text_color,
                           bordercolor="#dddddd")

        # 表头样式(保持默认)
        self.style.configure("Treeview.Heading",
                           font=('Microsoft YaHei', 11, 'bold'),
                           relief="flat")

        # 选中行样式
        self.style.map("Treeview",
                      background=[('selected', primary_color)],
                      foreground=[('selected', 'white')])

        # 按钮样式
        self.style.configure("Exit.TButton",
                           background=button_bg,
                           foreground=text_color,
                           font=('Microsoft YaHei', 11),
                           padding=8)

        self.style.configure("Install.TButton",
                           background=secondary_color,
                           foreground="white",
                           font=('Microsoft YaHei', 11),
                           padding=8)

        # 按钮悬停效果
        hover_bg = "#3d566e" if self.theme == "深色" else "#e8e8e8"
        self.style.map("Exit.TButton",
                     background=[('active', hover_bg), ('pressed', '!disabled', button_bg)])

        self.style.map("Install.TButton",
                     background=[('active', '#60c080'), ('pressed', '!disabled', secondary_color)])

        # 进度条样式
        self.style.configure("Custom.Horizontal.TProgressbar",
                           background=primary_color,
                           troughcolor=button_bg,
                           thickness=20)

    def create_widgets(self):
        """创建界面组件"""
        # 获取当前主题的背景色
        bg_color = "#2c3e50" if self.theme == "深色" else "#f5f5f5"
        text_color = "#ecf0f1" if self.theme == "深色" else "#333333"

        # 标题区域
        title_frame = tk.Frame(self.root, bg=bg_color)
        title_frame.pack(pady=(20, 10))
        ttk.Label(title_frame, text=self.window_title, style="Title.TLabel").pack()

        # 表格区域
        tree_frame = tk.Frame(self.root, bg=bg_color)
        tree_frame.pack(fill=tk.BOTH, expand=True, padx=30, pady=(0, 15))

        # 创建Treeview
        self.tree = ttk.Treeview(
            tree_frame,
            columns=('选择', '序号', '软件名称', '软件大小'),
            show='headings',
            selectmode='none'
        )

        # 设置列
        columns = {
            '选择': {'width': 60, 'anchor': 'center'},
            '序号': {'width': 60, 'anchor': 'center'},
            '软件名称': {'width': 380, 'anchor': 'center'},
            '软件大小': {'width': 120, 'anchor': 'center'}
        }

        for col, opts in columns.items():
            self.tree.column(col, **opts)
            self.tree.heading(col, text=col)

        # 滚动条
        scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview)
        self.tree.configure(yscrollcommand=scrollbar.set)
        self.tree.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # 绑定复选框点击事件
        self.tree.bind('<Button-1>', self.on_tree_click)

        # 底部按钮区域
        bottom_frame = tk.Frame(self.root, bg=bg_color)
        bottom_frame.pack(fill=tk.X, padx=30, pady=(0, 20))

        # 左侧按钮区域(全选/反选)
        left_button_frame = tk.Frame(bottom_frame, bg=bg_color)
        left_button_frame.pack(side="left")

        # 全选/反选复选框
        self.select_all_var = tk.BooleanVar(value=self.default_select_all)
        select_all_cb = tk.Checkbutton(
            left_button_frame,
            text="全选/反选",
            variable=self.select_all_var,
            font=("Microsoft YaHei", 10),
            bg=bg_color,
            fg=text_color,
            activebackground=bg_color,
            activeforeground=text_color,
            selectcolor=bg_color,
            command=self.toggle_select_all
        )
        select_all_cb.pack(side="left", padx=(0, 10))

        # 右侧按钮区域
        right_button_frame = tk.Frame(bottom_frame, bg=bg_color)
        right_button_frame.pack(side="right")

        # 退出按钮
        exit_btn = ttk.Button(
            right_button_frame,
            text="退出安装",
            command=self.root.quit,
            style="Exit.TButton"
        )
        exit_btn.pack(side="left", padx=5)

        # 开始安装按钮
        install_btn = ttk.Button(
            right_button_frame,
            text="开始安装",
            command=self.start_installation,
            style="Install.TButton"
        )
        install_btn.pack(side="left", padx=5)

    def on_tree_click(self, event):
        """处理复选框点击事件"""
        region = self.tree.identify("region", event.x, event.y)
        if region == "cell":
            column = self.tree.identify_column(event.x)
            if column == "#1":  # 复选框列
                item = self.tree.identify_row(event.y)
                current_values = self.tree.item(item, 'values')
                new_state = "☑" if current_values[0] == "☐" else "☐"
                self.tree.item(item, values=(new_state, *current_values[1:]))
                self.update_select_all_state()

    def update_select_all_state(self):
        """更新全选复选框状态"""
        all_selected = all(
            self.tree.item(item, 'values')[0] == "☑"
            for item in self.tree.get_children()
        )
        self.select_all_var.set(all_selected)

    def load_software_list(self):
        """加载软件列表"""
        config = configparser.ConfigParser()
        try:
            if os.path.exists('config.ini'):
                config.read('config.ini', encoding='utf-8')

                if not os.path.exists(self.data_dir):
                    os.makedirs(self.data_dir)

                software_section = None
                if '软件列表' in config:
                    software_section = config['软件列表']
                elif 'Software' in config:
                    software_section = config['Software']

                if software_section:
                    for idx, (name, cmd) in enumerate(software_section.items(), 1):
                        if name.startswith('#'):
                            continue

                        exe_name = cmd.split()[0]
                        exe_path = os.path.join(self.data_dir, exe_name)

                        software = {
                            'name': name,
                            'exe': exe_name,
                            'path': exe_path,
                            'cmd': cmd.replace(exe_name, exe_path),
                            'size': '未知'
                        }

                        if os.path.exists(exe_path):
                            try:
                                software['size'] = self.format_size(os.path.getsize(exe_path))
                            except Exception as e:
                                print(f"获取 {name} 大小失败: {e}")
                        else:
                            print(f"文件不存在: {exe_path}")

                        self.software_list.append(software)
                        self.install_commands[name] = software['cmd']
                else:
                    messagebox.showerror("错误", "配置文件中缺少[软件列表]或[Software]段")

        except Exception as e:
            messagebox.showerror("错误", f"读取配置文件失败: {e}")

    def format_size(self, size):
        """格式化文件大小"""
        for unit in ['B', 'KB', 'MB', 'GB']:
            if size < 1024.0:
                return f"{size:.1f} {unit}"
            size /= 1024.0
        return f"{size:.1f} TB"

    def populate_treeview(self):
        """填充表格数据"""
        for idx, software in enumerate(self.software_list, 1):
            initial_state = "☑" if self.default_select_all else "☐"
            self.tree.insert('', 'end', values=(
                initial_state,
                idx,
                software['name'],
                software['size']
            ))
        self.select_all_var.set(self.default_select_all)

    def toggle_select_all(self):
        """全选/反选功能"""
        new_state = "☑" if self.select_all_var.get() else "☐"
        for item in self.tree.get_children():
            current_values = self.tree.item(item, 'values')
            self.tree.item(item, values=(new_state, *current_values[1:]))

    def start_installation(self):
        """开始安装选中的软件"""
        selected_software = []
        for item in self.tree.get_children():
            values = self.tree.item(item, 'values')
            if values[0] == "☑":
                for software in self.software_list:
                    if software['name'] == values[2]:
                        selected_software.append(software)
                        break

        if not selected_software:
            messagebox.showwarning("警告", "请至少选择一个软件进行安装")
            return

        # 禁用按钮
        for widget in self.root.winfo_children():
            if isinstance(widget, tk.Button):
                widget.config(state="disabled")

        # 创建安装进度窗口
        self.progress_window = tk.Toplevel(self.root)
        self.progress_window.title("安装进度")
        self.progress_window.geometry("400x200")
        self.progress_window.resizable(False, False)
        bg_color = "#2c3e50" if self.theme == "深色" else "#f5f5f5"
        text_color = "#ecf0f1" if self.theme == "深色" else "#333333"
        self.progress_window.configure(bg=bg_color)
        self.progress_window.attributes('-topmost', True)

        # 居中窗口
        self.progress_window.update_idletasks()
        width = self.progress_window.winfo_width()
        height = self.progress_window.winfo_height()
        x = (self.progress_window.winfo_screenwidth() // 2) - (width // 2)
        y = (self.progress_window.winfo_screenheight() // 2) - (height // 2)
        self.progress_window.geometry(f"{width}x{height}+{x}+{y}")

        # 进度窗口内容
        tk.Label(
            self.progress_window,
            text="正在安装软件,请稍候...",
            font=("Microsoft YaHei", 12),
            bg=bg_color,
            fg=text_color,
            pady=20
        ).pack()

        self.progress_var = tk.DoubleVar()
        progress_bar = ttk.Progressbar(
            self.progress_window,
            variable=self.progress_var,
            maximum=100,
            mode="determinate",
            style="Custom.Horizontal.TProgressbar"
        )
        progress_bar.pack(fill="x", padx=20, pady=10)

        self.status_label = tk.Label(
            self.progress_window,
            text="准备安装...",
            font=("Microsoft YaHei", 10),
            bg=bg_color,
            fg=text_color
        )
        self.status_label.pack()

        # 重置安装状态
        self.installation_processes = []
        self.failed_installations = []
        self.installation_complete = False

        # 在新线程中执行安装
        threading.Thread(
            target=self.install_software,
            args=(selected_software,),
            daemon=True
        ).start()

        # 检查安装状态
        self.check_installation_status()

    def install_software(self, software_list):
        """安装软件"""
        total = len(software_list)
        success_count = 0

        for i, software in enumerate(software_list):
            exe_path = os.path.join(self.data_dir, software["exe"])

            if not os.path.exists(exe_path):
                self.failed_installations.append(f"{software['name']} (文件缺失)")
                continue

            try:
                self.root.after(0, lambda s=software: self.status_label.config(text=f"正在安装 {s['name']}..."))
                cmd = self.install_commands.get(software["name"], "")
                if not cmd:
                    self.failed_installations.append(f"{software['name']} (未找到安装命令)")
                    continue

                process = subprocess.Popen(cmd, shell=True)
                self.installation_processes.append((software["name"], process))
                self.root.after(0, lambda p=(i + 1) / total * 100: self.progress_var.set(p))
                success_count += 1

            except Exception as e:
                self.failed_installations.append(f"{software['name']} (错误: {str(e)})")

        self.installation_complete = True
        if success_count > 0:
            self.root.after(0, lambda: self.status_label.config(text=f"已完成 {success_count}/{total} 个软件安装"))

    def check_installation_status(self):
        """检查安装状态"""
        if self.installation_complete and all(not self.is_process_running(p[1].pid) for p in self.installation_processes):
            if self.progress_window:
                self.progress_window.destroy()

            if self.failed_installations:
                failed_list = "\n".join(self.failed_installations)
                messagebox.showwarning(
                    "安装完成",
                    f"安装完成,但有 {len(self.failed_installations)} 个软件安装失败:\n{failed_list}"
                )
            else:
                messagebox.showinfo("完成", "所有软件安装已完成")

            # 重新启用按钮
            for widget in self.root.winfo_children():
                if isinstance(widget, tk.Button):
                    widget.config(state="normal")
        else:
            self.root.after(1000, self.check_installation_status)

    def is_process_running(self, pid):
        """检查进程是否在运行"""
        try:
            return psutil.pid_exists(pid)
        except:
            return False

def main():
    root = tk.Tk()
    app = SoftwareInstaller(root)
    root.mainloop()

if __name__ == "__main__":
    main()
回复

使用道具 举报

2#
发表于 5 天前 | 只看该作者
感谢楼主分享,先存到我的云盘里备用。
回复

使用道具 举报

3#
发表于 5 天前 | 只看该作者
谢谢
回复

使用道具 举报

4#
发表于 5 天前 | 只看该作者
这些软件很难到吗?

点评

没听懂,什么意思?  详情 回复 发表于 5 天前
回复

使用道具 举报

5#
 楼主| 发表于 5 天前 | 只看该作者
绿光科技 发表于 2025-6-6 19:44
这些软件很难到吗?

没听懂,什么意思?
回复

使用道具 举报

7#
发表于 5 天前 | 只看该作者
多谢分享!!!
回复

使用道具 举报

8#
发表于 5 天前 | 只看该作者
感谢分享!!!
回复

使用道具 举报

9#
发表于 5 天前 | 只看该作者
可以试试 感谢分享 !
回复

使用道具 举报

10#
发表于 5 天前 | 只看该作者
集成安装软件好
回复

使用道具 举报

11#
发表于 5 天前 | 只看该作者
驚為天人的神作,看了讓人熱血沸騰,感謝分享。
回复

使用道具 举报

12#
发表于 5 天前 | 只看该作者
感謝分享
回复

使用道具 举报

13#
发表于 5 天前 | 只看该作者
谢谢楼主无私分享!
回复

使用道具 举报

14#
 楼主| 发表于 5 天前 | 只看该作者
晚上更新了,下午的版本配置文件太麻烦。

Icons 文件夹存放图标,Data文件夹存放需要安装的EXE文件,程序会自动读取软件的版本信息和大小。
回复

使用道具 举报

15#
发表于 4 天前 | 只看该作者
感谢分享
回复

使用道具 举报

16#
发表于 4 天前 | 只看该作者
谢谢楼主分享
回复

使用道具 举报

17#
发表于 4 天前 | 只看该作者
感谢分享
回复

使用道具 举报

18#
发表于 4 天前 来自手机 | 只看该作者
谢谢分享,赞
回复

使用道具 举报

19#
发表于 4 天前 | 只看该作者
感谢分享
回复

使用道具 举报

20#
发表于 4 天前 | 只看该作者
感谢分享软件自选安装管理器 ,辛苦了
回复

使用道具 举报

21#
发表于 4 天前 | 只看该作者
谢谢分享
回复

使用道具 举报

22#
发表于 4 天前 | 只看该作者

感谢分享!!
回复

使用道具 举报

23#
发表于 4 天前 | 只看该作者
感谢分享
回复

使用道具 举报

24#
发表于 4 天前 | 只看该作者
感谢楼主分享!
回复

使用道具 举报

25#
发表于 4 天前 来自手机 | 只看该作者
谢谢楼主分享
回复

使用道具 举报

26#
发表于 4 天前 | 只看该作者
谢谢分享,辛苦了,就是我想要的。

点评

我还在修改。  详情 回复 发表于 4 天前
回复

使用道具 举报

27#
 楼主| 发表于 4 天前 | 只看该作者
奈绪 发表于 2025-6-7 19:39
谢谢分享,辛苦了,就是我想要的。

我还在修改。
回复

使用道具 举报

28#
发表于 4 天前 | 只看该作者
谢谢分享
回复

使用道具 举报

29#
 楼主| 发表于 4 天前 | 只看该作者
更新了   

点评

你那个7系统的,还是不行 [attachimg]559968[/attachimg]  详情 回复 发表于 3 天前
回复

使用道具 举报

30#
发表于 3 天前 | 只看该作者

你那个7系统的,还是不行


点评

你的系统缺少 Visual C++ Redistributable 2025.4 微软运行库合集 v91 Visual C++ Redistributable 2025.4 微软运行库合集 v91 – 小兵下载站  详情 回复 发表于 3 天前
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1 )

闽公网安备 35020302032614号

GMT+8, 2025-6-11 05:53

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表