pyzyd 发表于 2025-7-22 15:13:56

写了一个简单的pyinstaller把py打包成exe的GUI程序


源代码:

static/image/hrline/4.gif

感觉我写的总是有点像屎山{:10_282:}
from tkinter import *
from tkinter import filedialog, messagebox, ttk
import os
import subprocess
import sys
import shutil
import glob



class App(Tk):
    def __init__(self):
      super().__init__()
      self.title("Pytoexe")
      self.top = None
      self.cmd = ""
      self.run()
      
    def initialize(self):
      self.os_type = sys.platform
      self.label1 = Label(self, text="请输入Python文件路径")
      self.label1.grid(row=0, column=0, padx=5, pady=5, sticky='nsew')

      self.entry1 = Entry(self, width=40)
      self.entry1.grid(row=0, column=1, padx=5, pady=5, sticky='nsew')

      self.button1 = Button(self, text="选择文件", command=lambda :self.select_file(self.entry1))
      self.button1.grid(row=1, column=0, padx=5, pady=5, sticky='w')

      self.button2 = Button(self, text="进行打包", command=self.package)
      self.button2.grid(row=1, column=1, padx=5, pady=5, sticky='e')

      yesno = messagebox.askyesno(title="提示", message="是否已配置Python环境?")
      if not yesno:
            messagebox.showinfo(title="提示", message="使用该程序请先配置Python环境!")
            self.destroy()
      else:
            messagebox.showinfo(title="提示", message="请继续使用。")
      
    def package(self):
      if not self.top:
            if os.path.exists(self.entry1.get()):
                self.cmd = f"pyinstaller --clean \"{self.entry1.get()}\""
                self.create_toplevel()
            else:
                messagebox.showwarning(title="警告", message="请输入正确的Python文件路径!!!")
      else:
            messagebox.showwarning(title="警告", message="请先关闭当前窗口!!!")

    def create_toplevel(self):
      self.top = Toplevel(master=self)
      self.tlb1 = Label(self.top, text="请输入exe文件名")
      self.tlb1.grid(row=0, column=0, padx=5, pady=5, sticky='nsew')

      self.tentry1 = Entry(self.top, width=30)
      self.tentry1.grid(row=0, column=1, padx=5, pady=5, sticky='nsew')

      self.tlb2 = Label(self.top, text="请输入exe文件路径")
      self.tlb2.grid(row=1, column=0, padx=5, pady=5, sticky='nsew')

      self.tentry2 = Entry(self.top, width=30)
      self.tentry2.grid(row=1, column=1, padx=5, pady=5, sticky='nsew')

      self.tbutton1 = Button(self.top, text="选择路径", command=self.tbutton1_callback)
      self.tbutton1.grid(row=1, column=2, padx=5, pady=5, sticky='nsew')

      self.tlb3 = Label(self.top, text="请输入依赖文件路径")
      self.tlb3.grid(row=2, column=0, padx=5, pady=5, sticky='nsew')

      self.tentry3 = Entry(self.top, width=30)
      self.tentry3.grid(row=2, column=1, padx=5, pady=5, sticky='nsew')

      self.tbutton2 = Button(self.top, text="选择路径", command=self.tbutton2_callback)
      self.tbutton2.grid(row=2, column=2, padx=5, pady=5, sticky='nsew')

      self.tlb4 = Label(self.top, text="请输入图标路径")
      self.tlb4.grid(row=3, column=0, padx=5, pady=5, sticky='nsew')

      self.tentry4 = Entry(self.top, width=30)
      self.tentry4.grid(row=3, column=1, padx=5, pady=5, sticky='nsew')

      self.tbutton3 = Button(self.top, text="添加图标", command=self.tbutton3_callback)
      self.tbutton3.grid(row=3, column=2, padx=5, pady=5, sticky='nsew')

      self.tlb5 = Label(self.top, text="请选择需要资源的模块名称")
      self.tlb5.grid(row=4, column=0, padx=5, pady=5, sticky='nsew')

      choices = ['tkinter', 'PyQt5', 'PyQt6', 'PySide2', 'PySide6', 'mmatplotlib', 'skimage', 'pandas', 'babel']
      self.tcombox = ttk.Combobox(self.top, values=choices, state="readonly", width=30)
      self.tcombox.set("请选择模块")
      self.tcombox.bind("<<ComboboxSelected>>", self.cb_on_select)
      self.tcombox.grid(row=4, column=1, padx=5, pady=5, sticky='nsew')

      self.tframe = Frame(self.top)
      self.tframe.grid(row=5, column=0, columnspan=2, padx=5, pady=5, sticky='nsew')

      self.tcheck1 = Checkbutton(self.tframe, text="是否单个文件", command=self.check1_callback)
      self.tcheck1.grid(row=0, column=0, padx=5, pady=5, sticky='nsew')

      self.tcheck2 = Checkbutton(self.tframe, text="是否更改文件名", command=self.check2_callback)
      self.tcheck2.grid(row=0, column=1, padx=5, pady=5, sticky='nsew')

      self.var1 = IntVar()
      self.tcheck3 = Checkbutton(self.tframe, text="是否更改文件路径", variable=self.var1, command=self.check3_callback)
      self.tcheck3.grid(row=0, column=2, padx=5, pady=5, sticky='nsew')

      self.tcheck4 = Checkbutton(self.tframe, text="是否为GUI程序", command=self.check4_callback)
      self.tcheck4.grid(row=1, column=0, padx=5, pady=5, sticky='nsew')

      self.var2 = IntVar()
      self.tcheck5 = Checkbutton(self.tframe, text="是否有依赖文件", variable=self.var2, command=self.check5_callback)
      self.tcheck5.grid(row=1, column=1, padx=5, pady=5, sticky='nsew')

      self.var3 = IntVar()
      self.tcheck6 = Checkbutton(self.tframe, text="是否需要模块资源", variable=self.var3, command=self.check6_callback)
      self.tcheck6.grid(row=1, column=2, padx=5, pady=5, sticky='nsew')

      self.tbutton_package = Button(self.top, text="运行", command=self.run_cmd)
      self.tbutton_package.grid(row=5, column=2, padx=5, pady=5, sticky='nsew')

    def tbutton1_callback(self):
      self.select_file(self.tentry2)
      if self.tentry2.get():
            self.var1.set(1)
            self.check3_callback()
   
    def tbutton2_callback(self):
      self.select_file(self.tentry3)
      if self.tentry3.get():
            self.var2.set(1)
            self.check5_callback()

    def tbutton3_callback(self):
      self.select_file(self.tentry4)
      if self.tentry4.get():
            self.ico_callback()
   
    def ico_callback(self):
      if "--icon" in self.cmd:
            self.cmd = self.cmd.replace(f'--icon "{self.tentry4.get()}" ', '')
      else:
            if self.tentry4.get():
                self.cmd = self.cmd.replace("pyinstaller", f"pyinstaller --icon \"{self.tentry4.get()}\"")

    def del_callback(self):
      for path in ['build', '__pycache__']:
            shutil.rmtree(path, ignore_errors=True)
      for f in glob.glob('*.spec'):
            os.remove(f)

    def run_cmd(self):
      """执行程序"""
      if not os.path.exists(self.tentry2.get()):
            messagebox.showwarning(title="警告", message="请输入正确的exe文件路径!!!")
            return
      
      if self.cmd:
            subprocess.run('python -m pip install --upgrade pip', shell=True)
            subprocess.run('python -m pip install pyinstaller -i https://pypi.tuna.tsinghua.edu.cn/simple', shell=True)
            subprocess.run('python -m pip install -U pyinstaller', shell=True)
            subprocess.run(self.cmd, shell=True)

      self.del_callback()
      rt = messagebox.askokcancel("提示", "打包完成!!!\n是否关闭程序")
      if rt:
            exit()
      else:
            self.top.destroy()
            self.top = None

    def check1_callback(self):
      if "--onefile" in self.cmd:
            _ = self.cmd.replace("--onefile ", "")
      else:
            _ = self.cmd.replace("pyinstaller", "pyinstaller --onefile")
      self.cmd = _
      
    def check2_callback(self):
      if "--name" in self.cmd:
            self.cmd = self.cmd.replace(f"--name {self.tentry1.get()} ", "")
      else:
            if self.tentry1.get():
                self.cmd = self.cmd.replace("pyinstaller", f"pyinstaller --name \"{self.tentry1.get()}\"")

    def check3_callback(self):
      if "--distpath" in self.cmd:
            self.cmd = self.cmd.replace(f"pyinstaller --distpath=\"{self.tentry2.get()}\" ", "")
      else:
            if self.tentry2.get():
                self.cmd = self.cmd.replace("pyinstaller", f"pyinstaller --distpath=\"{self.tentry2.get()}\"")
      
    def check4_callback(self):
      if "--windowed" in self.cmd:
            _ = self.cmd.replace("--windowed ", "")
      else:
            _ = self.cmd.replace("pyinstaller", "pyinstaller --windowed")
      self.cmd = _

    def check5_callback(self):
      self.datas = self.getdatas()
      if "--add-data" in self.cmd:
            self.cmd = self.cmd.replace(f'--add-data="{self.datas}" ', '')
      else:
            if self.tentry3.get():
                self.cmd = self.cmd.replace("pyinstaller", f"pyinstaller --add-data=\"{self.datas}\"")
      print(self.cmd)

    def check6_callback(self):
      if "--collect-data" in self.cmd:
            self.cmd = self.cmd.replace(f"--collect-data {self.tcombox.get()}", "")
      else:
            if self.tcombox.get() != "请选择模块":
                self.cmd = self.cmd.replace("pyinstaller", f"pyinstaller --collect-data {self.tcombox.get()}")

    def cb_on_select(self, event):
      if self.tcombox.get() != "请选择模块":
            self.var3.set(1)
            self.check6_callback()

    def getdatas(self):
      datas = ""
      file_path = self.tentry3.get()
      relative_path = file_path.replace(os.path.dirname(self.entry1.get()), "").replace("/", "")
      if self.os_type == "win32":
            datas = f"{file_path}/*;{relative_path}"
      else:
            datas = f"{file_path}/*:{relative_path}"
      return datas

    def select_file(self, entry=None):
      if entry == self.entry1:
            if self.top:
                messagebox.showwarning(title="警告", message="请先关闭当前窗口!!!")
                return
            file_path = filedialog.askopenfilename(filetypes=[("Python Files", "*.py")])
      elif entry == self.tentry4:
            file_path = filedialog.askopenfilename(filetypes=[("icon Files", "*.ico")])
      else:
            file_path = filedialog.askdirectory()
      entry.delete(0, END)
      entry.insert(0, file_path)

    def run(self):
      self.initialize()
      self.mainloop()

if __name__ == "__main__":
    app = App()
    没有把代码打包成exe文件了,不知道为什么打包成exe之后运行打包py文件总是出问题,用代码运行又没问题,{:9_241:}不知道还有没有什么隐藏的bug,一些简单的py程序应该还是能打包的

static/image/hrline/4.gif


             打包的过程可能会有点久,
      注意这里点击确定没有关闭的话就是打包出错了{:10_304:}
没有鱼币了,发完了{:10_266:}

快速收敛 发表于 2025-7-22 15:34:12

这年头还自己写代码,羡慕{:13_445:}

不二如是 发表于 2025-7-22 16:04:08

不错不错~~

某一个“天” 发表于 2025-7-22 17:30:30

{:10_256:}

某一个“天” 发表于 2025-7-22 17:40:41

真正的石山还得看我,6层If你顶的住吗{:10_256:}

Tyroe 发表于 2025-7-22 18:50:45

{:9_229:}

冲浪的口香糖 发表于 2025-7-22 22:47:10

666

小甲鱼 发表于 2025-7-23 02:59:09

挺实用的哈哈

薰衣草的花语 发表于 2025-7-23 07:14:35

看不懂看不懂

学数学的混子 发表于 2025-7-23 11:03:55

膜拜一下{:10_266:}

王昊扬 发表于 4 天前

膜拜膜拜

密码的 发表于 4 天前

{:10_328:}

921139987 发表于 4 天前

{:10_297:}

qwer12341 发表于 4 天前

{:5_100:}{:5_109:}{:5_107:}
页: [1]
查看完整版本: 写了一个简单的pyinstaller把py打包成exe的GUI程序