无忧启动论坛

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

[原创] 程序安装管理器

[复制链接]
跳转到指定楼层
1#
发表于 昨天 17:00 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 什么都不说吧 于 2025-8-15 14:11 编辑

这网上的程序安装管理器是真讨厌,配置麻烦,没办法,做个最基础功能的自己用。软件运行时会生成配置文件install_config.ini和安装程序存放文件夹data,需要把安装程序放到data文件夹里,配置文件也很简单,比如:

腾讯会议 = "腾讯会议.exe" /SilentInstall=0
qq = "qq.exe" /S
微信 = "微信.exe" /S
企业微信 = "企业微信.exe" /S

如果遇到一些软件没有自动安装参数的,那得自己想办法把软件先做成自动安装的。
软件的配色使用了论坛里另一位朋友的同类软件的配色,在此表示感谢。

程序安装管理器.part1.rar (4 MB, 下载次数: 84)     程序安装管理器.part2.rar (3.06 MB, 下载次数: 89)











评分

参与人数 1无忧币 +5 收起 理由
奈绪 + 5 很给力!

查看全部评分

2#
发表于 昨天 17:20 | 只看该作者
感谢分享
回复

使用道具 举报

3#
发表于 昨天 17:57 | 只看该作者
感謝大大分享!^^ 辛苦了!
回复

使用道具 举报

4#
发表于 昨天 18:38 | 只看该作者
很是可以的啊
回复

使用道具 举报

5#
发表于 昨天 18:40 | 只看该作者
谢谢分享
回复

使用道具 举报

6#
发表于 昨天 18:46 | 只看该作者
感谢分享!
回复

使用道具 举报

7#
发表于 昨天 19:36 | 只看该作者

谢谢分享
回复

使用道具 举报

8#
发表于 昨天 19:42 | 只看该作者
挺实用 支持了
回复

使用道具 举报

9#
发表于 昨天 21:01 | 只看该作者
感谢分享
回复

使用道具 举报

10#
发表于 昨天 21:09 | 只看该作者
谢谢分享!
回复

使用道具 举报

11#
发表于 昨天 21:11 | 只看该作者
感谢您的分享!
回复

使用道具 举报

12#
发表于 昨天 21:47 | 只看该作者
谢谢分享
回复

使用道具 举报

13#
发表于 昨天 21:59 | 只看该作者

挺实用 支持了
回复

使用道具 举报

14#
发表于 昨天 22:11 | 只看该作者
谢谢分享
回复

使用道具 举报

15#
发表于 昨天 22:40 | 只看该作者
能设置安装路径吗

点评

默认参数,有些程序能指定安装路径的。 不过建议软件还是安装到C盘,理由如下: 1、操作系统备份后,再还原所有的软件就有了。 2、假设操作系统中了病毒,还原操作系统后C盘就干净了。如应用软件安装到其他分区,  详情 回复 发表于 15 小时前
回复

使用道具 举报

16#
发表于 昨天 22:50 | 只看该作者
感谢楼主的分享
回复

使用道具 举报

17#
发表于 17 小时前 | 只看该作者
谢谢楼主的分享!
回复

使用道具 举报

18#
 楼主| 发表于 15 小时前 | 只看该作者
chtqq 发表于 2025-8-14 22:40
能设置安装路径吗

默认参数,有些程序能指定安装路径的。
不过建议软件还是安装到C盘,理由如下:
1、操作系统备份后,再还原所有的软件就有了。
2、假设操作系统中了病毒,还原操作系统后C盘就干净了。如应用软件安装到其他分区,可能一开机就会有程序自动启动,这种情况下病毒可能瞬间又感染了其他程序。

点评

大佬说的没错,备份还原更方便。  详情 回复 发表于 10 小时前
回复

使用道具 举报

19#
发表于 14 小时前 | 只看该作者
谢谢楼主分享
回复

使用道具 举报

20#
发表于 14 小时前 | 只看该作者
谢谢分享
回复

使用道具 举报

21#
发表于 14 小时前 | 只看该作者
感谢分享
回复

使用道具 举报

22#
发表于 12 小时前 | 只看该作者
本帖最后由 2010天月来了 于 2025-8-15 09:49 编辑

好东西。确实好,便利部署。
你整个界面上的所有文字都可以配置文件自定义就更好了,例如右下角的立即安装等。整个程序界面的文字都可以自定义才好。
回复

使用道具 举报

23#
发表于 12 小时前 | 只看该作者
谢谢分享!!!
回复

使用道具 举报

24#
发表于 10 小时前 | 只看该作者
什么都不说吧 发表于 2025-8-15 07:16
默认参数,有些程序能指定安装路径的。
不过建议软件还是安装到C盘,理由如下:
1、操作系统备份后,再 ...

大佬说的没错,备份还原更方便。
回复

使用道具 举报

25#
发表于 9 小时前 | 只看该作者
本帖最后由 piaomusic 于 2025-8-15 14:00 编辑
安装命令后添加 | 1或 | 0即可控制默认选中状态未指定时使用 [全局设置]中的 默认全选设置

[软件列表]
# 格式:软件名称 = 安装程序 参数 | 默认选中(1/0)
# 示例:
谷歌浏览器 = chrome_installer.exe /silent | 1
微信办公版 = wechat_setup.exe /S | 0
钉钉 = dingtalk_setup.exe /quiet | 1
Adobe Reader = ar_installer.exe /sAll
7-Zip = 7z_installer.exe /S | 1
================================================

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

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.auto_install = False
        self.countdown_seconds = 10
        
        # 安装相关变量
        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
""")

            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, 80))  # 增加右边距

        # 右侧按钮区域
        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, value) in enumerate(software_section.items(), 1):
                        if name.startswith('#'):
                            continue
                        
                        # 解析安装命令和默认选中状态
                        if '|' in value:
                            cmd, default = value.split('|', 1)
                            cmd = cmd.strip()
                            default = default.strip() == '1'
                        else:
                            cmd = value.strip()
                            default = self.default_select_all
                        
                        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': '未知',
                            'default': default,
                            'index': idx  # 自动生成序号
                        }
                        
                        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']

        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 software in self.software_list:
            initial_state = "☑☑" if software['default'] else "☐"
            self.tree.insert('', 'end', values=(
                initial_state,
                software['index'],  # 使用自动生成的序号
                software['name'],
                software['size']
            ))
        
        # 根据实际选中情况更新全选复选框状态
        self.update_select_all_state()
   
    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()

111.png (33.88 KB, 下载次数: 0)

111.png

000.png (33.69 KB, 下载次数: 0)

000.png

编译好的.part1.rar

3 MB, 下载次数: 2, 下载积分: 无忧币 -2

编译好的.part2.rar

3 MB, 下载次数: 2, 下载积分: 无忧币 -2

编译好的.part3.rar

2.04 MB, 下载次数: 1, 下载积分: 无忧币 -2

点评

附件无法读取的,可以弄个网盘吗? 大神  详情 回复 发表于 7 小时前
我试了好些颜色,都觉得没有你这种 颜色舒服。要么太亮,要么太暗。最终还是用了你的代码里的颜色。  详情 回复 发表于 8 小时前
好吧,贴出源码,看下吧,真不一样。 import configparser import subprocess import threading import queue import time import tkinter as tk from tkinter import messagebox, Checkbutton, Button, Labe  详情 回复 发表于 8 小时前
这是我使用配色的原程序的代码,我不是抄袭你的哈,只是借用了你的配色。我的发贴中已对您表示了感谢。呵。。用deepseek写的程序,你是开源的是不?我第一个程序灵感就来自于您这里,在此再次表示感谢!  详情 回复 发表于 8 小时前
呵,这么快就反翻译出来了。。不过做程序最关键就是思路的问题,我不发源代码也没有其他意思,只是自用的程序,特别是不想涉及到版权的问题,这是原则问题。  详情 回复 发表于 8 小时前
回复

使用道具 举报

26#
 楼主| 发表于 8 小时前 | 只看该作者
piaomusic 发表于 2025-8-15 12:42
# -*- coding: utf-8 -*-
import os
import configparser

呵,这么快就反翻译出来了。。不过做程序最关键就是思路的问题,我不发源代码也没有其他意思,只是自用的程序,特别是不想涉及到版权的问题,这是原则问题。

点评

我没有反翻译你的啊。 只是看的你发了。修改完善了一下原来的代码、。  详情 回复 发表于 8 小时前
回复

使用道具 举报

27#
 楼主| 发表于 8 小时前 | 只看该作者
piaomusic 发表于 2025-8-15 12:42
# -*- coding: utf-8 -*-
import os
import configparser

这是我使用配色的原程序的代码,我不是抄袭你的哈,只是借用了你的配色。我的发贴中已对您表示了感谢。呵。。用deepseek写的程序,你是开源的是不?我第一个程序灵感就来自于您这里,在此再次表示感谢!

点评

不存在抄袭的说法。本来就是开源的。 我既然公开了源代码。任何人都可以随便修改。  详情 回复 发表于 8 小时前
回复

使用道具 举报

28#
 楼主| 发表于 8 小时前 | 只看该作者
piaomusic 发表于 2025-8-15 12:42
# -*- coding: utf-8 -*-
import os
import configparser

好吧,贴出源码,看下吧,真不一样。
import configparser
import subprocess
import threading
import queue
import time
import tkinter as tk
from tkinter import messagebox, Checkbutton, Button, Label, Scrollbar, Frame, ttk
import os
import sys
import ctypes
import shlex

# 获取程序所在目录
def get_app_directory():
    try:
        if getattr(sys, 'frozen', False):
            return os.path.dirname(sys.executable)
        else:
            return os.path.dirname(os.path.abspath(__file__))
    except Exception as e:
        print(f"Error getting app directory: {e}")
        return os.getcwd()  # 默认返回当前工作目录

# 创建配置文件路径
def get_config_path():
    try:
        app_dir = get_app_directory()
        return os.path.join(app_dir, "install_config.ini")
    except Exception as e:
        print(f"Error getting config path: {e}")
        return "install_config.ini"

# 获取data目录路径
def get_data_directory():
    try:
        app_dir = get_app_directory()
        return os.path.join(app_dir, "data")
    except Exception as e:
        print(f"Error getting data directory: {e}")
        return "data"

class InstallManager:
    def set_dark_theme(self, window):
        """设置改进的深色主题"""
        try:
            # 使用深色主题配色方案
            bg_color = "#2c3e50"      # 背景色
            text_color = "#ecf0f1"    # 文字颜色
            tree_bg = "#34495e"       # 表格背景
            primary_color = "#3498db" # 主色
            secondary_color = "#2ecc71" # 辅助色
            
            window.configure(bg=bg_color)
            
            # 设置默认颜色
            window.option_add("*Background", bg_color)
            window.option_add("*Foreground", text_color)
            window.option_add("*Checkbutton.Background", tree_bg)
            window.option_add("*Checkbutton.Foreground", text_color)
            window.option_add("*Label.Background", bg_color)
            window.option_add("*Label.Foreground", text_color)
            window.option_add("*Frame.Background", bg_color)
            
            # 设置滚动条颜色
            window.option_add("*Scrollbar.Background", bg_color)
            window.option_add("*Scrollbar.TroughColor", tree_bg)
            window.option_add("*Scrollbar.Slider", primary_color)
            
            # 设置进度条样式
            style = ttk.Style()
            style.theme_use('default')
            style.configure("Custom.Horizontal.TProgressbar",
                            background=primary_color,
                            troughcolor=tree_bg,
                            thickness=12,
                            lightcolor=primary_color,
                            darkcolor=bg_color,
                            bordercolor=bg_color)
        except Exception as e:
            print(f"设置深色主题时出错: {e}")

    def __init__(self, master):
        try:
            # 设置 DPI 感知以兼容高 DPI 显示器
            try:
                if hasattr(ctypes.windll, 'shcore'):
                    ctypes.windll.shcore.SetProcessDpiAwareness(1)
            except:
                pass
            
            self.master = master
            master.title("程序安装管理器")
            
            # 设置窗口大小并居中显示
            width = 600
            height = 600
            screen_width = master.winfo_screenwidth()
            screen_height = master.winfo_screenheight()
            x = (screen_width - width) // 2
            y = (screen_height - height) // 2
            master.geometry(f"{width}x{height}+{x}+{y}")
            
            # 设置深色主题
            self.set_dark_theme(master)
            
            # 安装队列和状态变量
            self.install_queue = queue.Queue()
            self.currently_installing = []
            self.max_concurrent = 1
            self.selected_count = 0
            self.installations_completed = 0
            self.total_to_install = 0
            self.current_program = ""
            self.success_count = 0  # 成功安装计数
            
            # 创建主容器框架
            self.main_frame = tk.Frame(master, bg="#2c3e50")
            self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
            
            # 创建带滚动条的容器
            self.container_frame = Frame(self.main_frame, bg="#34495e")
            self.container_frame.pack(fill=tk.BOTH, expand=True)
            
            # 创建滚动条
            self.scrollbar = Scrollbar(self.container_frame, orient=tk.VERTICAL)
            self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
            
            # 创建Canvas用于滚动
            self.canvas = tk.Canvas(
                self.container_frame,
                bg="#34495e",
                yscrollcommand=self.scrollbar.set,
                highlightthickness=0
            )
            self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
            
            # 配置滚动条
            self.scrollbar.config(command=self.canvas.yview)
            
            # 创建内部框架
            self.inner_frame = Frame(self.canvas, bg="#34495e")
            self.canvas_window = self.canvas.create_window(
                (0, 0),
                window=self.inner_frame,
                anchor="nw"
            )
            
            # 绑定事件
            self.inner_frame.bind("<Configure>", self._on_frame_configure)
            self.canvas.bind("<Configure>", self._on_canvas_configure)
            
            # 绑定鼠标滚轮事件
            self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
            self.inner_frame.bind("<MouseWheel>", self._on_mousewheel)
            self.scrollbar.bind("<MouseWheel>", self._on_mousewheel)
            
            # 加载配置
            self.programs = []
            self.check_vars = []
            self.check_buttons = []
            self.load_config()
            
            # 创建底部控制面板
            self.bottom_frame = tk.Frame(master, bg="#2c3e50")
            self.bottom_frame.pack(fill=tk.X, padx=20, pady=(0, 10), side=tk.BOTTOM)
            
            # 状态标签
            self.status_frame = tk.Frame(self.bottom_frame, bg="#2c3e50")
            self.status_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
            
            # 使用更兼容的字体
            try:
                # 尝试使用系统默认字体
                self.status_label = Label(
                    self.status_frame,
                    text="就绪",
                    fg="#2ecc71",  # 使用辅助色
                    bg="#2c3e50",
                    font=("TkDefaultFont", 11),
                    anchor="w"
                )
            except:
                # 回退到基本设置
                self.status_label = Label(
                    self.status_frame,
                    text="就绪",
                    fg="#2ecc71",  # 使用辅助色
                    bg="#2c3e50",
                    anchor="w"
                )
            self.status_label.pack(side=tk.LEFT, padx=(15, 0))
            
            # 已选安装标签
            try:
                self.selected_label = Label(
                    self.bottom_frame,
                    text="已选安装: 0",
                    fg="#3498db",  # 使用主色
                    bg="#2c3e50",
                    font=("TkDefaultFont", 12, "bold")
                )
            except:
                self.selected_label = Label(
                    self.bottom_frame,
                    text="已选安装: 0",
                    fg="#3498db",  # 使用主色
                    bg="#2c3e50"
                )
            self.selected_label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
            
            # 创建安装按钮
            try:
                self.install_btn = Button(
                    self.bottom_frame,
                    text="立即安装",
                    command=self.start_installation,
                    height=1,
                    width=10,
                    bg="#34495e",
                    fg="white",
                    activebackground="#1A2530",
                    activeforeground="white",
                    font=("TkDefaultFont", 12, "bold"),
                    relief=tk.GROOVE,
                    bd=2
                )
            except:
                self.install_btn = Button(
                    self.bottom_frame,
                    text="立即安装",
                    command=self.start_installation,
                    bg="#34495e",
                    fg="white"
                )
            self.install_btn.pack(side=tk.RIGHT, padx=(0, 15))
            
            # 创建进度显示区域
            self.progress_frame = tk.Frame(master, bg="#2c3e50", height=80)
            # 初始隐藏进度区域
            self.progress_frame.pack_forget()
            
            # 正在安装标签
            self.installing_label = Label(
                self.progress_frame,
                text="",
                fg="#3498db",  # 使用主色
                bg="#2c3e50",
                font=("TkDefaultFont", 10),
                anchor="w"
            )
            self.installing_label.pack(fill=tk.X, padx=20, pady=(5, 0))
            
            # 当前安装标签
            self.current_label = Label(
                self.progress_frame,
                text="",
                fg="#3498db",  # 使用主色
                bg="#2c3e50",
                font=("TkDefaultFont", 10),
                anchor="w"
            )
            self.current_label.pack(fill=tk.X, padx=20, pady=(0, 2))
            
            # 进度标签
            self.progress_text = Label(
                self.progress_frame,
                text="",
                fg="#2ecc71",  # 使用辅助色
                bg="#2c3e50",
                font=("TkDefaultFont", 10),
                anchor="w"
            )
            self.progress_text.pack(fill=tk.X, padx=20, pady=(0, 2))
            
            # 进度条
            self.progress_bar = ttk.Progressbar(
                self.progress_frame,
                orient="horizontal",
                length=500,
                mode="determinate",
                style="Custom.Horizontal.TProgressbar"
            )
            self.progress_bar.pack(fill=tk.X, padx=20, pady=(0, 5))
            
            # 启动安装监控线程
            self.monitor_thread = threading.Thread(target=self.monitor_installations, daemon=True)
            self.monitor_thread.start()
        
        except Exception as e:
            # 在初始化失败时显示错误信息
            error_msg = f"初始化失败: {str(e)}\n\n请检查配置文件是否存在且格式正确。"
            messagebox.showerror("致命错误", error_msg)
            self.master.destroy()
            sys.exit(1)
   
    def _on_frame_configure(self, event=None):
        """当内部框架大小改变时更新滚动区域"""
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))
   
    def _on_canvas_configure(self, event):
        """当Canvas大小改变时调整内部框架宽度"""
        canvas_width = event.width
        self.canvas.itemconfig(self.canvas_window, width=canvas_width)
   
    def _on_mousewheel(self, event):
        """处理鼠标滚轮事件"""
        # Windows使用delta值
        if event.delta > 0:
            self.canvas.yview_scroll(-1, "units")
        elif event.delta < 0:
            self.canvas.yview_scroll(1, "units")
   
    def update_status(self, message, color="#2ecc71"):  # 使用辅助色
        """更新状态标签"""
        self.status_label.config(text=message, fg=color)
   
    def update_selected_count(self):
        """更新已选安装计数"""
        self.selected_label.config(text=f"已选安装: {self.selected_count}")
   
    def load_config(self):
        """加载配置"""
        try:
            config_file = get_config_path()
            
            # 检查配置文件是否存在
            if not os.path.exists(config_file):
                # 尝试创建默认配置文件
                try:
                    self.create_default_config(config_file)
                    # 创建后重新加载
                    self.programs = []
                    self.check_vars = []
                    self.check_buttons = []
                except Exception as e:
                    error_msg = (
                        f"找不到配置文件且无法创建新文件:\n{config_file}\n"
                        f"错误: {str(e)}\n\n"
                        "请手动创建配置文件并重新启动程序。"
                    )
                    messagebox.showerror("配置错误", error_msg)
                    return
            
            config = configparser.ConfigParser()
            
            # 尝试不同编码读取配置文件
            encodings = ['utf-8', 'gbk', 'utf-8-sig', 'latin-1']
            read_ok = False
            
            for encoding in encodings:
                try:
                    config.read(config_file, encoding=encoding)
                    if 'programs' in config:
                        read_ok = True
                        break
                except Exception as e:
                    print(f"尝试 {encoding} 编码失败: {e}")
                    continue
            
            if not read_ok:
                messagebox.showerror("错误", "无法读取配置文件,请检查文件格式和编码")
                return
               
            if 'programs' not in config:
                messagebox.showerror("配置错误", f"配置文件中缺少[programs]部分\n配置文件路径: {config_file}")
                return
            
            # 创建复选框
            row = 0
            for program_name in config['programs']:
                try:
                    full_config = config['programs'][program_name].strip()
                    
                    if not full_config:
                        continue
                        
                    parts = shlex.split(full_config)
                    if not parts:
                        continue
                        
                    path = parts[0]
                    
                    args = parts[1:] if len(parts) > 1 else []
                    
                    if not os.path.isabs(path):
                        data_dir = get_data_directory()
                        path = os.path.join(data_dir, path)
                    
                    # 检查路径是否存在 - 如果不存在则跳过
                    if not os.path.exists(path):
                        print(f"警告: 找不到 {program_name} 的安装路径: {path}")
                        continue
                    
                    var = tk.BooleanVar()
                    
                    # 创建复选框
                    try:
                        cb = Checkbutton(
                            self.inner_frame,
                            text=program_name,
                            variable=var,
                            anchor="w",
                            padx=15,
                            pady=8,
                            bg="#34495e",
                            fg="white",
                            activebackground="#2c3e50",
                            activeforeground="white",
                            selectcolor="#3498db",  # 使用主色
                            font=("TkDefaultFont", 11),
                            command=lambda v=var: self.on_checkbox_change(v)
                        )
                    except:
                        # 字体回退
                        cb = Checkbutton(
                            self.inner_frame,
                            text=program_name,
                            variable=var,
                            anchor="w",
                            padx=15,
                            pady=8,
                            bg="#34495e",
                            fg="white",
                            command=lambda v=var: self.on_checkbox_change(v)
                        )
                    
                    cb.grid(row=row, column=0, sticky="ew", padx=10)
                    
                    self.programs.append({
                        'name': program_name,
                        'path': path,
                        'args': args
                    })
                    self.check_vars.append(var)
                    self.check_buttons.append(cb)
                    row += 1
                    
                except Exception as e:
                    print(f"加载程序 {program_name} 时出错: {e}")
                    continue
               
            # 如果没有加载到任何程序
            if not self.programs:
                try:
                    label = tk.Label(
                        self.inner_frame,
                        text="未找到可安装的程序,请检查配置文件",
                        bg="#34495e",
                        fg="white",
                        font=("TkDefaultFont", 12)
                    )
                    label.grid(row=0, column=0, sticky="w", padx=20, pady=20)
                except:
                    # 简单回退
                    label = tk.Label(
                        self.inner_frame,
                        text="未找到可安装的程序,请检查配置文件",
                        bg="#34495e",
                        fg="white"
                    )
                    label.grid(row=0, column=0, sticky="w", padx=20, pady=20)
               
        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            error_msg = (
                f"加载配置时出错: {str(e)}\n"
                f"错误位置: 第 {exc_tb.tb_lineno} 行\n\n"
                "请检查配置文件格式是否正确。"
            )
            messagebox.showerror("配置错误", error_msg)

    def on_checkbox_change(self, var):
        """复选框状态改变时调用"""
        # 更新已选择的数量
        self.selected_count = sum(1 for v in self.check_vars if v.get())
        self.update_selected_count()
   
    def create_default_config(self, config_path):
        """创建默认配置文件"""
        default_config = """[programs]
# 格式:程序名称 = "安装程序路径" [静默安装参数]
# 示例:
# Google Chrome = "ChromeInstaller.exe" /silent
# Visual Studio Code = "VSCodeSetup.exe" /verysilent /suppressmsgboxes
# WinRAR = "wrar.exe" /S

"""
        with open(config_path, 'w', encoding='utf-8') as f:
            f.write(default_config)
            
        # 创建data目录(如果不存在)
        data_dir = get_data_directory()
        if not os.path.exists(data_dir):
            os.makedirs(data_dir)
   
    def start_installation(self):
        """开始安装选中的程序"""
        self.selected_indices = []  # 存储选中程序的索引
        for i, var in enumerate(self.check_vars):
            if var.get():
                self.selected_indices.append(i)  # 记录选中程序的索引
        
        # 使用索引获取选中的程序
        selected = [self.programs for i in self.selected_indices]
        
        if not selected:
            messagebox.showinfo("提示", "请选择至少一个程序进行安装")
            return
        
        # 隐藏"已选安装"标签
        self.selected_label.place_forget()
        
        # 禁用所有复选框
        for cb in self.check_buttons:
            cb.config(state=tk.DISABLED)
        
        # 隐藏安装按钮
        self.install_btn.pack_forget()
        
        # 清空队列并添加新任务
        while not self.install_queue.empty():
            self.install_queue.get()
        
        for program in selected:
            self.install_queue.put(program)
        
        # 重置安装计数器
        self.installations_completed = 0
        self.total_to_install = len(selected)
        self.success_count = 0  # 重置成功计数
        
        # 显示进度区域 - 放在窗口底部
        self.progress_frame.pack(fill=tk.X, padx=20, pady=(0, 0), side=tk.BOTTOM, anchor='s')
        self.update_progress(0, "准备开始安装...")
        
        # 更新正在安装标签
        self.installing_label.config(text=f"正在安装 {self.total_to_install} 个程序")
        
        # 清空状态标签(什么也不显示)
        self.status_label.config(text="")
   
    def update_progress(self, percent, message):
        """更新进度显示"""
        self.progress_bar["value"] = percent
        self.progress_text.config(text=message)
   
    def start_installation_process(self, program):
        """启动单个安装进程"""
        try:
            # 设置当前安装程序
            self.current_program = program['name']
            
            # 根据是否有静默参数显示不同的安装类型
            install_type = "静默安装" if program['args'] else "安装"
            
            # 更新当前安装标签
            self.current_label.config(text=f"当前{install_type}: {program['name']}")
            
            # 更新进度显示
            progress_percent = int((self.installations_completed / self.total_to_install) * 100)
            self.update_progress(progress_percent, f"正在{install_type} {program['name']}...")
            
            # 构建命令
            command = f'"{program["path"]}"'
            
            # 添加静默安装参数(如果有)
            if program['args']:
                command += " " + " ".join(program['args'])
            
            # 执行安装命令
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = 0  # 隐藏窗口
            
            process = subprocess.Popen(
                command,
                shell=True,
                startupinfo=startupinfo,
                creationflags=subprocess.CREATE_NO_WINDOW
            )
            
            # 添加到当前安装列表
            self.currently_installing.append(process)
            
            # 启动线程等待安装完成
            threading.Thread(target=self.wait_for_installation, args=(program, process), daemon=True).start()
            
        except Exception as e:
            self.installations_completed += 1
            # 更新进度显示为错误信息
            self.update_progress(100, f"安装失败: {program['name']}")
   
    def monitor_installations(self):
        """监控安装队列并启动安装任务"""
        while True:
            # 检查活跃安装数量
            self.currently_installing = [p for p in self.currently_installing if p.poll() is None]
            
            # 如果有空闲槽位且队列中有任务
            if len(self.currently_installing) < self.max_concurrent and not self.install_queue.empty():
                program = self.install_queue.get()
                self.start_installation_process(program)
            
            # 检查是否所有安装都已完成
            if self.install_queue.empty() and not self.currently_installing and self.total_to_install > 0:
                if self.installations_completed >= self.total_to_install:
                    self.master.after(100, self.finalize_installation)
            
            # 更新进度条
            if self.total_to_install > 0:
                progress_percent = int((self.installations_completed / self.total_to_install) * 100)
                self.master.after(100, lambda: self.update_progress(progress_percent,
                    f"已完成 {self.installations_completed}/{self.total_to_install} 个程序"))
            
            time.sleep(0.5)  # 更频繁地检查(500毫秒)
   
    def wait_for_installation(self, program, process):
        """等待安装完成并处理结果"""
        try:
            # 等待进程结束
            return_code = process.wait()
            
            # 更新安装计数
            self.installations_completed += 1
            
            # 从活跃列表中移除
            if process in self.currently_installing:
                self.currently_installing.remove(process)
            
            # 记录成功安装
            if return_code == 0:
                self.success_count += 1
            
        except Exception as e:
            self.installations_completed += 1
            if process in self.currently_installing:
                self.currently_installing.remove(process)
   
    def finalize_installation(self):
        """所有安装完成后调用"""
        # 更新进度显示
        success_message = f"成功安装 {self.success_count} 个软件"
        self.update_progress(100, success_message)
        
        # 2秒后重置界面
        self.master.after(2000, self.reset_interface)
   
    def reset_interface(self):
        """重置界面状态"""
        # 取消选中所有已安装程序的复选框
        for index in self.selected_indices:
            self.check_vars[index].set(False)
        
        # 更新已选安装计数
        self.selected_count = 0
        self.update_selected_count()
        
        # 重新显示"已选安装"标签
        self.selected_label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
        
        # 启用所有复选框
        for cb in self.check_buttons:
            cb.config(state=tk.NORMAL)
        
        # 重新显示安装按钮
        self.install_btn.pack(side=tk.RIGHT, padx=(0, 15))
        
        # 重置进度显示
        self.progress_frame.pack_forget()
        self.progress_bar["value"] = 0
        self.progress_text.config(text="")
        self.current_label.config(text="")
        self.installing_label.config(text="")  # 清空正在安装标签
        self.current_program = ""
        
        # 重置计数器
        self.total_to_install = 0
        self.installations_completed = 0
        self.success_count = 0
        
        # 恢复状态标签为"就绪"
        self.update_status("就绪", "#2ecc71")  # 使用辅助色

if __name__ == "__main__":
    try:
        root = tk.Tk()
        app = InstallManager(root)
        root.mainloop()
    except Exception as e:
        # 捕获并显示未处理的异常
        error_msg = f"程序崩溃: {str(e)}\n\n请检查配置文件是否存在且格式正确。"
        messagebox.showerror("致命错误", error_msg)
        sys.exit(1)
回复

使用道具 举报

29#
发表于 8 小时前 来自手机 | 只看该作者
感谢楼主分享
回复

使用道具 举报

30#
发表于 8 小时前 | 只看该作者
什么都不说吧 发表于 2025-8-15 13:48
呵,这么快就反翻译出来了。。不过做程序最关键就是思路的问题,我不发源代码也没有其他意思,只是自用的 ...

我没有反翻译你的啊。 只是看的你发了。修改完善了一下原来的代码、。

点评

好的,为了论坛和广大的网友,大家都多多奉献。  详情 回复 发表于 8 小时前
回复

使用道具 举报

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

本版积分规则

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

闽公网安备 35020302032614号

GMT+8, 2025-8-15 22:28

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

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