好用————python 库 下载 ,整合在一个小程序 UIUIUI

news2025/5/17 21:40:50


上图~



 



import os
import time
import threading
import requests
import subprocess
import importlib
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urljoin

class PackageInstallerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Design By Tim")
        self.root.geometry("800x600")
        
        # 默认设置
        self.install_dir = r"C:\Users\AAA\Python_Package"
        self.default_packages = ["numpy", "opencv-python", "pyttsx3"]
        self.mirrors = [
            "https://pypi.tuna.tsinghua.edu.cn/simple/",
            "https://mirrors.aliyun.com/pypi/simple/",
            "https://pypi.mirrors.ustc.edu.cn/simple/",
            "https://mirrors.bfsu.edu.cn/pypi/web/simple/",
            "https://mirrors.cloud.tencent.com/pypi/simple/",
            "https://mirrors.nju.edu.cn/pypi/web/simple/",
            "https://mirrors.hit.edu.cn/pypi/web/simple/",
            "https://mirror.sjtu.edu.cn/pypi/web/simple/",
            "https://pypi.doubanio.com/simple/",
            "https://mirrors.zju.edu.cn/pypi/web/simple/",
            "https://mirrors.pku.edu.cn/pypi/simple/",
            "https://mirrors.yun-idc.com/pypi/simple/",
            "https://mirrors.neusoft.edu.cn/pypi/web/simple/",
            "https://mirrors.xjtu.edu.cn/pypi/web/simple/",
            "https://mirrors.huaweicloud.com/repository/pypi/simple/"
        ]
        
        # UI控件引用
        self.start_button = None
        self.cancel_button = None
        
        # 创建UI
        self.create_widgets()
    
    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 包列表输入
        ttk.Label(main_frame, text="要下载的包(逗号分隔):").grid(row=0, column=0, sticky=tk.W)
        self.pkg_entry = ttk.Entry(main_frame, width=50)
        self.pkg_entry.grid(row=0, column=1, sticky=tk.EW)
        self.pkg_entry.insert(0, ",".join(self.default_packages))
        
        # 安装目录
        ttk.Label(main_frame, text="安装目录:").grid(row=1, column=0, sticky=tk.W)
        self.dir_entry = ttk.Entry(main_frame, width=50)
        self.dir_entry.grid(row=1, column=1, sticky=tk.EW)
        self.dir_entry.insert(0, self.install_dir)
        
        # 按钮框架
        btn_frame = ttk.Frame(main_frame)
        btn_frame.grid(row=2, column=0, columnspan=2, pady=10)
        
        # 开始按钮
        self.start_button = ttk.Button(btn_frame, text="开始下载安装", command=self.start_process)
        self.start_button.pack(side=tk.LEFT, padx=5)
        
        # 取消按钮
        self.cancel_button = ttk.Button(btn_frame, text="取消", command=self.root.quit)
        self.cancel_button.pack(side=tk.LEFT, padx=5)
        
        # 进度条
        self.progress = ttk.Progressbar(main_frame, orient=tk.HORIZONTAL, length=500, mode='determinate')
        self.progress.grid(row=3, column=0, columnspan=2, pady=10)
        
        # 状态标签
        self.status_label = ttk.Label(main_frame, text="准备就绪")
        self.status_label.grid(row=4, column=0, columnspan=2)
        
        # 日志输出
        ttk.Label(main_frame, text="进度日志:").grid(row=5, column=0, sticky=tk.W)
        self.log_text = scrolledtext.ScrolledText(main_frame, width=80, height=20, state='normal')
        self.log_text.grid(row=6, column=0, columnspan=2, sticky=tk.NSEW)
        
        # 配置网格权重
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(6, weight=1)
    
    def log_message(self, message):
        self.log_text.config(state='normal')
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.config(state='disabled')
        self.log_text.see(tk.END)
        self.root.update()
    
    def update_progress(self, value):
        self.progress['value'] = value
        self.root.update()
    
    def update_status(self, message):
        self.status_label.config(text=message)
        self.root.update()
    
    def set_ui_state(self, enabled):
        state = tk.NORMAL if enabled else tk.DISABLED
        self.start_button.config(state=state)
        self.cancel_button.config(state=state)
        self.root.update()
    
    def start_process(self):
        # 获取用户输入
        packages = [pkg.strip() for pkg in self.pkg_entry.get().split(",") if pkg.strip()]
        self.install_dir = self.dir_entry.get().strip()
        
        if not packages:
            messagebox.showerror("错误", "请输入至少一个包名")
            return
        
        os.makedirs(self.install_dir, exist_ok=True)
        
        # 禁用UI
        self.set_ui_state(False)
        self.log_text.config(state='normal')
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state='disabled')
        self.update_progress(0)
        
        # 开始下载安装流程
        threading.Thread(target=self.download_and_install, args=(packages,), daemon=True).start()
    
    def download_and_install(self, packages):
        overall_success = True  # 跟踪整体成功状态
        
        try:
            # 1. 测试镜像源速度
            self.update_status("正在测试镜像源速度...")
            self.log_message("="*50)
            self.log_message("开始测试镜像源速度")
            
            fastest_mirrors = self.find_fastest_mirrors()
            
            # 2. 下载包
            self.update_status("开始下载包...")
            self.log_message("="*50)
            self.log_message("开始下载包")
            
            downloaded_files = []
            total_packages = len(packages)
            download_success = True
            
            for i, package in enumerate(packages):
                self.update_progress((i/total_packages)*50)
                mirror = fastest_mirrors[i % len(fastest_mirrors)]
                success, files = self.download_package(mirror, package)
                if not success:
                    download_success = False
                    overall_success = False
                    self.log_message(f"⚠️ 包 {package} 下载失败,将继续尝试其他包")
                else:
                    downloaded_files.extend(files)
            
            if not download_success:
                self.log_message("警告: 部分包下载失败")
            
            # 3. 安装包
            self.update_status("开始安装包...")
            self.log_message("="*50)
            self.log_message("开始安装包")
            
            install_success = True
            
            for i, file in enumerate(downloaded_files):
                self.update_progress(50 + (i/len(downloaded_files))*40)
                if not self.install_package(file):
                    install_success = False
                    overall_success = False
                    self.log_message(f"⚠️ 文件 {file} 安装失败")
            
            if not install_success:
                self.log_message("警告: 部分包安装失败")
            
            # 4. 验证安装
            self.update_status("验证安装...")
            self.log_message("="*50)
            self.log_message("开始验证安装")
            
            verify_success = True
            
            for i, package in enumerate(packages):
                self.update_progress(90 + (i/len(packages))*10)
                if not self.test_installation(package):
                    verify_success = False
                    overall_success = False
                    self.log_message(f"⚠️ 包 {package} 验证失败")
            
            if not verify_success:
                self.log_message("警告: 部分包验证失败")
            
            self.update_progress(100)
            
            # 显示最终结果
            if overall_success:
                self.update_status("所有操作成功完成!")
                self.log_message("="*50)
                self.log_message("✅ 所有包下载安装完成并验证成功!")
                messagebox.showinfo("完成", "所有包下载安装完成并验证成功!")
            else:
                self.update_status("操作完成,但有错误发生")
                self.log_message("="*50)
                self.log_message("⚠️ 操作完成,但部分步骤失败,请检查日志")
                messagebox.showwarning("完成但有错误", 
                    "操作完成,但部分步骤失败,请检查日志了解详情")
            
        except Exception as e:
            self.log_message(f"❌ 发生严重错误: {str(e)}")
            self.update_status("操作因错误中止")
            messagebox.showerror("错误", f"处理过程中发生严重错误: {str(e)}")
            overall_success = False
        finally:
            # 重新启用UI
            self.set_ui_state(True)
    
    def find_fastest_mirrors(self):
        """找出最快的3个镜像源"""
        with ThreadPoolExecutor(max_workers=15) as executor:
            futures = [executor.submit(self.test_mirror_speed, mirror) for mirror in self.mirrors]
            results = []
            for future in as_completed(futures):
                speed, mirror = future.result()
                if speed != float('inf'):
                    results.append((speed, mirror))
                    self.log_message(f"测试镜像源 {mirror} 速度: {speed:.2f}秒")
        
        results.sort()
        fastest_mirrors = [mirror for speed, mirror in results[:3]]
        self.log_message(f"最快的3个镜像源: {', '.join(fastest_mirrors)}")
        return fastest_mirrors
    
    def test_mirror_speed(self, mirror):
        """测试镜像源速度"""
        try:
            start = time.time()
            response = requests.get(urljoin(mirror, "simple/"), timeout=5)
            if response.status_code == 200:
                return time.time() - start, mirror
        except:
            pass
        return float('inf'), mirror
    
    def download_package(self, mirror, package):
        """下载单个包"""
        try:
            self.log_message(f"正在从 {mirror} 下载 {package}...")
            cmd = [
                "python", "-m", "pip", "download", 
                package, 
                "-d", self.install_dir, 
                "-i", mirror, 
                "--trusted-host", mirror.split('//')[1].split('/')[0]
            ]
            
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            stdout, stderr = process.communicate()
            
            if process.returncode == 0:
                # 获取下载的文件列表
                files = [f for f in os.listdir(self.install_dir) if f.startswith(package.replace("-", "_"))]
                self.log_message(f"✅ 成功下载 {package}: {', '.join(files)}")
                return True, files
            else:
                self.log_message(f"❌ 下载 {package} 失败: {stderr.strip()}")
                return False, []
        except Exception as e:
            self.log_message(f"❌ 下载 {package} 时发生错误: {str(e)}")
            return False, []
    
    def install_package(self, filename):
        """安装单个包"""
        try:
            filepath = os.path.join(self.install_dir, filename)
            self.log_message(f"正在安装 {filename}...")
            
            cmd = ["python", "-m", "pip", "install", filepath]
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            stdout, stderr = process.communicate()
            
            if process.returncode == 0:
                self.log_message(f"✅ 成功安装 {filename}")
                return True
            else:
                self.log_message(f"❌ 安装 {filename} 失败: {stderr.strip()}")
                return False
        except Exception as e:
            self.log_message(f"❌ 安装 {filename} 时发生错误: {str(e)}")
            return False
    
    def test_installation(self, package):
        """测试包是否安装成功"""
        try:
            # 转换包名(如opencv-python -> opencv_python)
            import_name = package.replace("-", "_")
            self.log_message(f"正在验证 {package} 是否可以导入...")
            
            module = importlib.import_module(import_name)
            version = getattr(module, "__version__", "未知版本")
            
            self.log_message(f"✅ 验证成功: {package} (版本: {version})")
            return True
        except Exception as e:
            self.log_message(f"❌ 验证失败: 无法导入 {package} - {str(e)}")
            return False

if __name__ == "__main__":
    root = tk.Tk()
    app = PackageInstallerApp(root)
    root.mainloop()



 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2341995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenVINO教程(五):实现YOLOv11+OpenVINO实时视频目标检测

目录 实现讲解效果展示完整代码 本文作为上篇博客的延续,在之前实现了图片推理的基础上,进一步介绍如何进行视频推理。 实现讲解 首先,我们需要对之前的 predict_and_show_image 函数进行拆分,将图像显示与推理器(pre…

CentOS的安装以及网络配置

CentOS的下载 在学习docker之前,我们需要知道的就是docker是运行在Linux内核之上的,所以我们需要Linux环境的操作系统,当然了你也可以选择安装ubuntu等操作系统,如果你不想在本机安装的话还可以考虑买阿里或者华为的云服务器&…

「Java EE开发指南」用MyEclipse开发EJB 3无状态会话Bean(二)

本教程介绍在MyEclipse中开发EJB 3无状态会话bean,由于JPA实体和EJB 3实体非常相似,因此本教程不涉及EJB 3实体Bean的开发。在本教程中,您将学习如何: 创建EJB 3项目创建无状态会话bean部署并测试bean 在上文中(点击…

深度学习在DOM解析中的应用:自动识别页面关键内容区块

摘要 本文介绍了如何在爬取东方财富吧(https://www.eastmoney.com)财经新闻时,利用深度学习模型对 DOM 树中的内容区块进行自动识别和过滤,并将新闻标题、时间、正文等关键信息分类存储。文章聚焦爬虫整体性能瓶颈,通…

PyQt6实例_pyqtgraph多曲线显示工具_代码分享

目录 概述 效果 代码 返回结果对象 字符型横坐标 通用折线图工具 工具主界面 使用举例 概述 1 分析数据遇到需要一个股票多个指标对比或一个指标多个股票对比,涉及到同轴多条曲线的显示,所以开发了本工具。 2 多曲线显示部分可以当通用工具使…

Linux网络编程 多线程Web服务器:HTTP协议与TCP并发实战

问题解答 TCP是如何防止SYN洪流攻击的? 方式有很多种,我仅举例部分: 1、调整内核参数 我们知道SYN洪流攻击的原理就是发送一系列无法完成三次握手的特殊信号,导致正常的能够完成三次握手的信号因为 连接队列空间不足&#xff…

Qt 下载的地址集合

Qt 下载离线安装包 download.qt.io/archive/qt/5.14/5.14.2/ Qt 6 安装下载在线安装包 Index of /qt/official_releases/online_installers/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror

ubuntu下gcc/g++安装及不同版本切换

1. 查看当前gcc版本 $ gcc --version# 查看当前系统中已安装版本 $ ls /usr/bin/gcc*2. 安装新版本gcc $ sudo apt-get update# 这里以版本12为依据(也可以通过源码方式安装,请自行Google!) $ sudo apt-get install -y gcc-12 g…

FPGA入门学习Day1——设计一个DDS信号发生器

目录 一、DDS简介 (一)基本原理 (二)主要优势 (三)与传统技术的对比 二、FPGA存储器 (一)ROM波形存储器 (二)RAM随机存取存储器 (三&…

微信小程序拖拽排序有效果图

效果图 .wxml <view class"container" style"--w:{{w}}px;" wx:if"{{location.length}}"><view class"container-item" wx:for"{{list}}" wx:key"index" data-index"{{index}}"style"--…

WT2000T专业录音芯片:破解普通录音设备信息留存、合规安全与远程协作三大难题

在快节奏的现代商业环境中&#xff0c;会议是企业决策、创意碰撞和战略部署的核心场景。然而&#xff0c;传统会议记录方式常面临效率低、信息遗漏、回溯困难等痛点。如何确保会议内容被精准记录并高效利用&#xff1f;会议室专用录音芯片应运而生&#xff0c;以智能化、高保真…

【Python 学习笔记】 pip指令使用

系列文章目录 pip指令使用 文章目录 系列文章目录前言安装配置使用pip 管理Python包修改pip下载源 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 当前文章记录的是我在学习过程的一些笔记和思考&#xff0c;可能存在有误解的地方&#xff0c;仅供大家…

C# 文件读取

文件读取是指使用 C# 程序从计算机文件系统中获取文件内容的过程。将存储在磁盘上的文件内容加载到内存中&#xff0c;供程序处理。主要类型有&#xff1a;文本文件读取&#xff08;如 .txt, .csv, .json, .xml&#xff09;&#xff1b;二进制文件读取&#xff08;如 .jpg, .pn…

leetcode125.验证回文串

class Solution {public boolean isPalindrome(String s) {s s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();for(int i0,js.length()-1;i<j;i,j--){if(s.charAt(i)!s.charAt(j))return false;}return true;} }

【Android面试八股文】Android系统架构【一】

Android系统架构图 1.1 安卓系统启动 1.设备加电后执行第一段代码&#xff1a;Bootloader 系统引导分三种模式&#xff1a;fastboot&#xff0c;recovery&#xff0c;normal&#xff1a; fastboot模式&#xff1a;用于工厂模式的刷机。在关机状态下&#xff0c;按返回开机 键进…

【数据可视化-21】水质安全数据可视化:探索化学物质与水质安全的关联

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

【prometheus+Grafana篇】从零开始:Linux 7.6 上二进制安装 Prometheus、Grafana 和 Node Exporter

&#x1f4ab;《博主主页》&#xff1a;奈斯DB-CSDN博客 &#x1f525;《擅长领域》&#xff1a;擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对SQLserver、NoSQL(MongoDB)有了解 &#x1f496;如果觉得文章对你有所帮…

STM32(M4)入门:GPIO与位带操作(价值 3w + 的嵌入式开发指南)

一&#xff1a;GPIO 1.1 了解时钟树&#xff08;必懂的硬件基础&#xff09; 在 STM32 开发中&#xff0c;时钟系统是一切外设工作的 “心脏”。理解时钟树的工作原理&#xff0c;是正确配置 GPIO、UART 等外设的核心前提。 1.1.1 为什么必须开启外设时钟&#xff1f; 1. 计…

Linux419 三次握手四次挥手抓包 wireshark

还是Notfound 没连接 可能我在/home 准备配置静态IP vim ctrlr 撤销 u撤销 配置成功 准备关闭防火墙 准备配置 YUM源 df -h 未看到sr0文件 准备排查 准备挂载 还是没连接 计划重启 有了 不重启了 挂载准备 修改配置文件准备 准备清理缓存 ok 重新修改配…

CSS-跟随图片变化的背景色

CSS-跟随图片变化的背景色 获取图片的主要颜色并用于背景渐变需要安装依赖 colorthief获取图片的主要颜色. 并丢给背景注意 getPalette并不是个异步方法 import styles from ./styles.less; import React, { useState } from react; import Colortheif from colorthief;cons…