统信小程序(十)nutika打包elf格式程序

'''

制作一个名为elf打包的linux环境python程序,通过tktinter输入参数,界面尺寸1200*850,底色淡黄色,按键是糖果绿色,字体:fangsong ti,字号12号。注意避免版本冲突。提前配置nuitka 参数如下。

--standalone:生成独立的可执行文件,不依赖外部库1。

--follow-imports:自动包含所有导入的模块1。

--recurse-all:递归包含所有资源文件1。

Anaconda环境中缺少静态Python库。需要在命令中添加 --static-libpython=no 选项

并使用以下优化选项

性能优化 :--follow-imports、--recurse-all、--remove-output(生成后删除临时目录)

'''

复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import subprocess
import json
import threading
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, scrolledtext

class ElfPacker:
    def __init__(self, root):
        self.root = root
        self.root.title("ELF打包工具")
        self.root.geometry("1200x850")
        self.root.configure(bg='#FFFACD')  # 淡黄色背景

        # 设置字体
        self.font = ('fangsong ti', 12)

        # 当前工作目录
        self.current_dir = os.getcwd()

        # 配置文件路径
        self.config_file = os.path.expanduser("~/.elf_packer_config.json")

        # 窗口置顶选项(放在create_ui之前)
        self.topmost_var = tk.BooleanVar(value=False)

        # 加载配置
        self.load_config()

        # 创建UI
        self.create_ui()

        # 绑定快捷键
        self.root.bind('<Control-q>', self.quit_app)
        self.root.bind('<Control-s>', self.save_config)

        # 设置窗口图标
        try:
            self.root.iconbitmap()
        except:
            pass

    def create_ui(self):
        """创建主界面"""
        # 主框架
        main_frame = tk.Frame(self.root, bg='#FFFACD')
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 创建左右两栏
        left_frame = tk.Frame(main_frame, bg='#FFFACD')
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)

        right_frame = tk.Frame(main_frame, bg='#FFFACD')
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5)

        # ==================== 左侧:基本配置 ====================
        # 标题
        title_label = tk.Label(
            left_frame,
            text="基本配置",
            font=(self.font[0], 14, 'bold'),
            bg='#FFFACD'
        )
        title_label.pack(anchor=tk.W, pady=(0, 10))

        # Python脚本路径
        script_frame = tk.Frame(left_frame, bg='#FFFACD')
        script_frame.pack(fill=tk.X, pady=5)

        tk.Label(script_frame, text="Python脚本:", font=self.font, bg='#FFFACD', width=12, anchor=tk.W).pack(side=tk.LEFT)
        self.script_path = tk.Entry(script_frame, font=self.font, bg='white')
        self.script_path.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)

        browse_btn = tk.Button(
            script_frame,
            text="浏览",
            font=self.font,
            bg='#98FB98',
            command=self.browse_script
        )
        browse_btn.pack(side=tk.RIGHT, padx=2)

        # 输出目录
        output_frame = tk.Frame(left_frame, bg='#FFFACD')
        output_frame.pack(fill=tk.X, pady=5)

        tk.Label(output_frame, text="输出目录:", font=self.font, bg='#FFFACD', width=12, anchor=tk.W).pack(side=tk.LEFT)
        self.output_dir = tk.Entry(output_frame, font=self.font, bg='white')
        self.output_dir.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.output_dir.insert(0, "/home/huanghe/test/xuanzhuan")

        browse_output_btn = tk.Button(
            output_frame,
            text="浏览",
            font=self.font,
            bg='#98FB98',
            command=self.browse_output
        )
        browse_output_btn.pack(side=tk.RIGHT, padx=2)

        # 输出文件名
        name_frame = tk.Frame(left_frame, bg='#FFFACD')
        name_frame.pack(fill=tk.X, pady=5)

        tk.Label(name_frame, text="输出名称:", font=self.font, bg='#FFFACD', width=12, anchor=tk.W).pack(side=tk.LEFT)
        self.output_name = tk.Entry(name_frame, font=self.font, bg='white')
        self.output_name.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.output_name.insert(0, "快捷地址")

        # 分隔线
        separator = ttk.Separator(left_frame, orient='horizontal')
        separator.pack(fill=tk.X, pady=10)

        # ==================== 左侧:Nuitka选项 ====================
        options_label = tk.Label(
            left_frame,
            text="Nuitka选项",
            font=(self.font[0], 14, 'bold'),
            bg='#FFFACD'
        )
        options_label.pack(anchor=tk.W, pady=(0, 10))

        # 复选框选项
        self.options = {
            'standalone': tk.BooleanVar(value=True),
            'follow_imports': tk.BooleanVar(value=True),
            'remove_output': tk.BooleanVar(value=True),
            'onefile': tk.BooleanVar(value=True),
            'enable_plugin': tk.BooleanVar(value=True),
            'show_progress': tk.BooleanVar(value=True),
            'static_libpython': tk.BooleanVar(value=False),  # 新增:禁用静态Python库
            'lto': tk.BooleanVar(value=False)
        }

        checkbox_frame = tk.Frame(left_frame, bg='#FFFACD')
        checkbox_frame.pack(fill=tk.X, pady=5)

        row1 = tk.Frame(checkbox_frame, bg='#FFFACD')
        row1.pack(fill=tk.X, pady=2)
        tk.Checkbutton(row1, text="--standalone (独立可执行文件)", variable=self.options['standalone'],
                       font=self.font, bg='#FFFACD').pack(side=tk.LEFT, padx=5)
        tk.Checkbutton(row1, text="--follow-imports (包含导入模块)", variable=self.options['follow_imports'],
                       font=self.font, bg='#FFFACD').pack(side=tk.LEFT, padx=5)

        row2 = tk.Frame(checkbox_frame, bg='#FFFACD')
        row2.pack(fill=tk.X, pady=2)
        tk.Checkbutton(row2, text="--remove-output (删除临时目录)", variable=self.options['remove_output'],
                       font=self.font, bg='#FFFACD').pack(side=tk.LEFT, padx=5)
        tk.Checkbutton(row2, text="--onefile (单文件模式)", variable=self.options['onefile'],
                       font=self.font, bg='#FFFACD').pack(side=tk.LEFT, padx=5)

        row3 = tk.Frame(checkbox_frame, bg='#FFFACD')
        row3.pack(fill=tk.X, pady=2)
        tk.Checkbutton(row3, text="--enable-plugin=tk-inter (启用tkinter插件)", variable=self.options['enable_plugin'],
                       font=self.font, bg='#FFFACD').pack(side=tk.LEFT, padx=5)
        tk.Checkbutton(row3, text="--show-progress (显示进度)", variable=self.options['show_progress'],
                       font=self.font, bg='#FFFACD').pack(side=tk.LEFT, padx=5)

        row4 = tk.Frame(checkbox_frame, bg='#FFFACD')
        row4.pack(fill=tk.X, pady=2)
        tk.Checkbutton(row4, text="--static-libpython=no (禁用静态Python库)", variable=self.options['static_libpython'],
                       font=self.font, bg='#FFFACD').pack(side=tk.LEFT, padx=5)
        tk.Checkbutton(row4, text="--lto=no (禁用链接时优化)", variable=self.options['lto'],
                       font=self.font, bg='#FFFACD').pack(side=tk.LEFT, padx=5)

        # 其他参数
        other_frame = tk.Frame(left_frame, bg='#FFFACD')
        other_frame.pack(fill=tk.X, pady=10)

        tk.Label(other_frame, text="其他参数:", font=self.font, bg='#FFFACD').pack(anchor=tk.W)
        self.other_args = tk.Entry(other_frame, font=self.font, bg='white')
        self.other_args.pack(fill=tk.X, pady=2)
        self.other_args.insert(0, "--jobs=4")

        # ==================== 右侧:执行控制 ====================
        # 打包按钮
        pack_btn = tk.Button(
            right_frame,
            text="开始打包",
            font=(self.font[0], 14, 'bold'),
            bg='#98FB98',
            height=2,
            command=self.start_packing
        )
        pack_btn.pack(fill=tk.X, pady=10)

        # 保存配置按钮
        save_btn = tk.Button(
            right_frame,
            text="保存配置",
            font=self.font,
            bg='#FFB6C1',
            command=self.save_config
        )
        save_btn.pack(fill=tk.X, pady=5)

        # 加载配置按钮
        load_btn = tk.Button(
            right_frame,
            text="加载配置",
            font=self.font,
            bg='#FFB6C1',
            command=self.load_config_ui
        )
        load_btn.pack(fill=tk.X, pady=5)

        # 清空日志按钮
        clear_btn = tk.Button(
            right_frame,
            text="清空日志",
            font=self.font,
            bg='#FFB6C1',
            command=self.clear_log
        )
        clear_btn.pack(fill=tk.X, pady=5)

        # 窗口置顶选项
        topmost_check = tk.Checkbutton(
            right_frame,
            text="窗口置顶",
            variable=self.topmost_var,
            font=self.font,
            bg='#FFFACD',
            command=self.toggle_topmost
        )
        topmost_check.pack(pady=5)

        # 日志区域
        log_label = tk.Label(
            right_frame,
            text="执行日志",
            font=(self.font[0], 14, 'bold'),
            bg='#FFFACD'
        )
        log_label.pack(anchor=tk.W, pady=(10, 5))

        self.log_text = scrolledtext.ScrolledText(
            right_frame,
            font=self.font,
            bg='white',
            height=25,
            wrap=tk.WORD
        )
        self.log_text.pack(fill=tk.BOTH, expand=True)

        # 进度条
        self.progress = ttk.Progressbar(right_frame, mode='indeterminate')
        self.progress.pack(fill=tk.X, pady=5)

        # 状态栏
        self.status_label = tk.Label(
            self.root,
            text="就绪",
            font=self.font,
            bg='#FFFACD',
            relief=tk.SUNKEN,
            anchor=tk.W
        )
        self.status_label.pack(side=tk.BOTTOM, fill=tk.X)

        # 加载之前的配置
        if hasattr(self, 'saved_config'):
            self.load_saved_config()

    def browse_script(self):
        """浏览Python脚本"""
        filename = filedialog.askopenfilename(
            title="选择Python脚本",
            filetypes=[("Python文件", "*.py"), ("所有文件", "*.*")]
        )
        if filename:
            self.script_path.delete(0, tk.END)
            self.script_path.insert(0, filename)
            # 自动设置输出名称
            base_name = os.path.splitext(os.path.basename(filename))[0]
            self.output_name.delete(0, tk.END)
            self.output_name.insert(0, base_name)

    def browse_output(self):
        """浏览输出目录"""
        directory = filedialog.askdirectory(title="选择输出目录")
        if directory:
            self.output_dir.delete(0, tk.END)
            self.output_dir.insert(0, directory)

    def toggle_topmost(self):
        """切换窗口置顶"""
        self.root.attributes('-topmost', self.topmost_var.get())

    def start_packing(self):
        """开始打包"""
        # 验证输入
        script = self.script_path.get().strip()
        if not script:
            messagebox.showerror("错误", "请选择Python脚本")
            return

        if not os.path.exists(script):
            messagebox.showerror("错误", f"脚本文件不存在: {script}")
            return

        output_dir = self.output_dir.get().strip()
        if not output_dir:
            messagebox.showerror("错误", "请选择输出目录")
            return

        # 在独立线程中执行打包
        threading.Thread(target=self.pack_worker, daemon=True).start()

    def pack_worker(self):
        """打包工作线程"""
        try:
            # 禁用按钮
            self.root.after(0, lambda: self.progress.start())
            self.root.after(0, lambda: self.status_label.config(text="正在打包..."))
            self.log("=" * 60)
            self.log("开始打包...")

            # 构建命令
            cmd = ["nuitka"]

            # 添加选项
            if self.options['standalone'].get():
                cmd.append("--standalone")
                self.log("启用 --standalone")

            if self.options['follow_imports'].get():
                cmd.append("--follow-imports")
                self.log("启用 --follow-imports")

            if self.options['remove_output'].get():
                cmd.append("--remove-output")
                self.log("启用 --remove-output")

            if self.options['onefile'].get():
                cmd.append("--onefile")
                self.log("启用 --onefile")

            if self.options['enable_plugin'].get():
                cmd.append("--enable-plugin=tk-inter")
                self.log("启用 tkinter 插件")

            if self.options['show_progress'].get():
                cmd.append("--show-progress")
                self.log("启用进度显示")

            # 重要:禁用静态Python库(解决Anaconda环境问题)
            if self.options['static_libpython'].get():
                cmd.append("--static-libpython=no")
                self.log("禁用静态Python库")

            if self.options['lto'].get():
                cmd.append("--lto=no")
                self.log("禁用链接时优化")

            # 添加其他参数
            other_args = self.other_args.get().strip()
            if other_args:
                cmd.extend(other_args.split())
                self.log(f"其他参数: {other_args}")

            # 输出目录
            output_dir = self.output_dir.get().strip()
            cmd.append(f"--output-dir={output_dir}")

            # 输出文件名
            output_name = self.output_name.get().strip()
            if output_name:
                cmd.append(f"--output-filename={output_name}")

            # 脚本路径
            cmd.append(self.script_path.get().strip())

            self.log(f"\n执行命令: {' '.join(cmd)}\n")

            # 执行命令
            process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
                env=os.environ.copy()
            )

            # 实时输出
            for line in process.stdout:
                self.log(line.rstrip())

            process.wait()

            # 检查结果
            if process.returncode == 0:
                self.log("\n✓ 打包成功完成!")
                output_file = os.path.join(output_dir, output_name)
                if self.options['onefile'].get():
                    if not output_file.endswith('.bin'):
                        output_file += '.bin'
                self.log(f"生成文件: {output_file}")
                self.root.after(0, lambda: self.status_label.config(text="打包完成"))
                self.root.after(0, lambda: messagebox.showinfo("成功", f"打包完成!\n输出文件: {output_file}"))
            else:
                self.log(f"\n✗ 打包失败,返回码: {process.returncode}")
                self.root.after(0, lambda: self.status_label.config(text="打包失败"))
                self.root.after(0, lambda: messagebox.showerror("错误", "打包失败,请查看日志"))

        except Exception as e:
            self.log(f"\n错误: {str(e)}")
            self.root.after(0, lambda: self.status_label.config(text="发生错误"))
            self.root.after(0, lambda: messagebox.showerror("错误", f"打包过程出错: {e}"))
        finally:
            self.root.after(0, lambda: self.progress.stop())

    def log(self, message):
        """添加日志"""
        def _log():
            self.log_text.insert(tk.END, message + "\n")
            self.log_text.see(tk.END)
            self.root.update_idletasks()

        self.root.after(0, _log)

    def clear_log(self):
        """清空日志"""
        self.log_text.delete(1.0, tk.END)

    def save_config(self, event=None):
        """保存配置"""
        try:
            config = {
                'script_path': self.script_path.get(),
                'output_dir': self.output_dir.get(),
                'output_name': self.output_name.get(),
                'other_args': self.other_args.get(),
                'options': {k: v.get() for k, v in self.options.items()}
            }
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)
            self.status_label.config(text="配置已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存配置失败: {e}")

    def load_config(self):
        """加载配置"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    self.saved_config = json.load(f)
            else:
                self.saved_config = None
        except:
            self.saved_config = None

    def load_config_ui(self):
        """从UI加载配置"""
        if hasattr(self, 'saved_config') and self.saved_config:
            self.load_saved_config()
            messagebox.showinfo("成功", "配置已加载")
        else:
            messagebox.showwarning("警告", "没有找到保存的配置")

    def load_saved_config(self):
        """加载保存的配置到UI"""
        config = self.saved_config
        if config:
            self.script_path.delete(0, tk.END)
            self.script_path.insert(0, config.get('script_path', ''))

            self.output_dir.delete(0, tk.END)
            self.output_dir.insert(0, config.get('output_dir', '/home/huanghe/test/xuanzhuan'))

            self.output_name.delete(0, tk.END)
            self.output_name.insert(0, config.get('output_name', '快捷地址'))

            self.other_args.delete(0, tk.END)
            self.other_args.insert(0, config.get('other_args', '--jobs=4'))

            options = config.get('options', {})
            for k, v in options.items():
                if k in self.options:
                    self.options[k].set(v)

    def quit_app(self, event=None):
        """退出程序"""
        self.root.quit()
        self.root.destroy()

def main():
    # 检查nuitka是否安装
    try:
        result = subprocess.run(["nuitka", "--version"], capture_output=True, text=True)
        if result.returncode != 0:
            raise Exception("Nuitka not found")
    except:
        result = messagebox.askyesno("提示", "未检测到Nuitka,是否现在安装?\n\n安装命令: pip install nuitka")
        if result:
            os.system("pip install nuitka")
            messagebox.showinfo("提示", "安装完成后请重新运行程序")
            return

    root = tk.Tk()
    app = ElfPacker(root)
    root.mainloop()

if __name__ == "__main__":
    main()
相关推荐
CHU7290352 小时前
家门口的邻里集市:社区团购小程序的功能探索
小程序
hnxaoli2 小时前
统信小程序(十一)快捷地址栏
linux·python·小程序
职豚求职小程序5 小时前
中国人保财险笔试如何通过?附刷题库小程序
小程序
chushiyunen5 小时前
python轻量级框架flask、做桌面小程序
python·小程序·flask
蓝色心灵-海7 小时前
小律书 技术架构详解:前后端分离的自律管理系统设计
java·http·小程序·架构·uni-app
00后程序员张9 小时前
iPhone 无需越狱文件管理 使用Keymob查看导出文件
android·ios·小程序·https·uni-app·iphone·webview
毕设源码-朱学姐9 小时前
【开题答辩全过程】以 基于微信小程序的运动减肥管理系统设计与实现为例,包含答辩的问题和答案
微信小程序·小程序
2501_916008899 小时前
Unity3D iOS 应用防篡改实战 资源校验、 IPA 二进制保护
android·ios·小程序·https·uni-app·iphone·webview
2501_915909069 小时前
MachObfuscator全面解析:Apple平台Mach-O应用程序混淆技术指南
macos·ios·小程序·uni-app·objective-c·cocoa·iphone