博客 / 詳情

返回

如何將 Python 項目打包成 exe,另帶卸載功能!

前言

雖然我是做 web 的,但是對 Python 的眾多開源項目一直都很關注。像之前的 stable diffusion 的 AI 繪畫,和 so-vits-svc 音色替換等源碼的部署文檔都很完善了。儘管如此,對於剛入門的和本地環境搭建各種踩坑的同學,又急於一試就顯得不太友好了。針對這一現象,網上早有了各種大佬做了一鍵部署的解決方案,不用安裝 Python 環境,不用下載各種依賴包,點一下 exe 就可以用了。       

那他們是怎麼做的呢,到這裏應該已經很多人會説用 pyinstaller。是的這個確實可以解決整體環境打包,但是有時候一些圖標和依賴文件要放在一起才能正常使用,類似於綠色桌面工具。那有沒有方法對這個進一步封裝,就是點擊 exe 後需要安裝,然後安裝後才是項目的文件,還可以卸載,答案就是用 Inno Setup Compiler。那麼,接下來我就用一個 GUI 的小 demo 完成 exe 打包和進一步安裝打包。
圖片

環境

Python 3.8
Tkinter (自帶)
pyinstaller 6.3.0
Inno Setup Compiler 

基本流程

用 conda 單獨創建一虛擬環境,比如寫一個 tkinter 應用,對了為了好看,我們可以設置以下 icon,尺寸 32x32,大圖標 64x64 準備一下即可。然後用 pyinstaller 將應用達成文件包 (含 exe),最後用 Inno Setup Compiler 達成安裝包,裏面就是各種文件關聯,圖標和信息的設置了。
圖片

開始操作

環境創建

# 1. 創建環境
conda create -n tools_env python=3.8
    
# 2. 進入環境
conda activate tools_env
    
# 3. 安裝 pyinstaller
pip install pyinstaller -i https://pypi.tuna.tsinghua.edu.cn/simple

編碼示例

import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from ToolTip import ToolTip
import os
import time


class Application(tk.Tk):

    def __init__(self):
        super().__init__()
        self.title("公眾號:ZERO開發")
        self.iconbitmap('logo.ico')
        self.geometry("600x370")  # 設置固定的窗口大小
        self.resizable(False, False)  # 禁止調整窗口大小
        self.style = ttk.Style(self)
        self.main_color = self.cget("bg")

        self.style.configure("TEntry",
                             padding=6,
                             relief="flat",
                             background="#0078d7",
                             foreground="black",
                             font=("Arial", 12, "bold"))

        self.style.configure("TLabel",
                             font=("Arial", 12, "bold"))

        self.style.configure("TButton",
                             padding=6,
                             font=("Arial", 12))

        self.create_widgets()

    def create_widgets(self):
        self.path_label = ttk.Label(self, text='絕對路徑:')
        self.path_label.grid(row=0, sticky=tk.W, pady=30, padx=20)

        self.path = tk.StringVar()
        self.path_entry = ttk.Entry(self, width=60, textvariable=self.path)
        self.path_entry.grid(row=0, column=1, sticky=tk.E, pady=5)
        ToolTip(self.path_entry, "電腦裏的目錄路徑,如 D:\\3code\\6pytorch\pytorch_ai_demo")

        self.rename_label = ttk.Label(self, text='修改名稱:')
        self.rename_label.grid(row=1, sticky=tk.W, pady=5, padx=20)

        self.rename = tk.StringVar()
        self.rename_entry = ttk.Entry(self, width=60, textvariable=self.rename)
        self.rename_entry.grid(row=1, column=1, sticky=tk.E, pady=5)

        self.msg_text = tk.Text(self, height=2, width=60, wrap='none')  # 添加一個列表框來顯示文件名
        self.msg_text.grid(row=2, column=1, sticky=tk.W, pady=15)
        self.msg_text.configure(bd=0, relief="solid", bg=self.main_color)
        # self.msg_text.insert(tk.END, "這是要顯示的文本。")

        ttk.Button(self, text='確認修改', command=self.start_program).grid(row=3, column=1, sticky=tk.W, pady=20, padx=120)
        ttk.Button(self, text='關於作者', command=self.about).grid(row=4, column=1, sticky=tk.W, padx=120)

    def start_program(self):
        print("絕對路徑:{}, 重命名:{}". format(self.path.get(), self.rename.get()))

        path = self.path.get()
        rename = self.rename.get()

        if path == "" or rename == "":
            messagebox.showwarning("警告", "輸入框不能為空!")
            return

        if os.path.isdir(path) == False:
            messagebox.showwarning("警告", "絕對路徑不正確!")
            return

        i = 0
        # '該文件夾下所有的文件(包括文件夾)'
        FileList = os.listdir(path)

        # '遍歷所有文件'
        for files in FileList:
            oldDirPath = os.path.join(path, files)

            self.msg_text.delete(1.0, tk.END)
            self.msg_text.insert(tk.END, oldDirPath)

            # '如果是文件夾則遞歸調用'
            if os.path.isdir(oldDirPath):
                self.start_program(oldDirPath)

            # '文件名'
            fileName = os.path.splitext(files)[0]
            # '文件擴展名'
            fileType = os.path.splitext(files)[1]
            fileType = fileType.lower()

            newDirPath = os.path.join(path, rename + "_" + str(i) + fileType)

            # '重命名'
            os.rename(oldDirPath, newDirPath)
            i += 1

        messagebox.showinfo("信息", "操作完成!")

    def about(self):
        messagebox.showinfo("關於", "微信公眾號:ZERO開發\r\n\r\n工具:批量修改文件1.0")

    def quit_program(self):
        self.destroy()


if __name__ == "__main__":
    app = Application()
    app.mainloop()

開始打包

pyinstaller -F -w Application.py -–icon=logo.ico
圖片
這裏我們希望打包是自己的圖標就帶上 icon,而且打開時不要出現黑色閃框就帶上 w,當然後面打開 exe 要把 icon 放到與他同一級的目錄中,尺寸上面已經説了,下面是參數説明。

參數説明:    
-–icon=圖標路徑
-F 打包成一個exe文件
-w 使用窗口,無控制枱
-c 使用控制枱,無窗口
-D 創建一個目錄,裏面包含exe以及其他一些依賴性文件

還有一個問題是,如果是修改了代碼,需要反覆多次打包,可以把項目前一次打包生成的生成的 spec 後綴文件刪除即可。打包成功後,控制麪包會顯示 Building EXE from EXE-00.toc completed successfully,然後根目錄的 dist 就是打包後的文件包,裏面需要的文件圖片等都在裏面,後面用 Inno Setup Compiler 也就是對這個文件夾打包。
圖片
圖片

Inno Setup Compiler 操作

新建腳本,填寫信息

圖片

選擇主程序和需要的文件

圖片

設置名稱和 icon

圖片

選擇導出位置 (前面還有協議等設置)

圖片

運行腳本開始生成

圖片

安裝打開,安裝目錄裏有自帶卸載程序

圖片

user avatar coderleo 頭像 ailim 頭像 waweb 頭像 nihaojob 頭像 cipchk 頭像 thehumble 頭像 y_lucky 頭像 lilin_5e390e08b42e4 頭像 dingxi 頭像 tongouba 頭像
10 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.