【Python】Python项目打包发布(五)(制作Windows安装包)

Python项目打包发布汇总

【Python】Python项目打包发布(一)(基于Pyinstaller打包多目录项目)
【Python】Python项目打包发布(二)(基于Pyinstaller打包PyWebIO项目)
【Python】Python项目打包发布(三)(基于Aardio打包多目录项目)
【Python】Python项目打包发布(四)(基于Nuitka打包PySide6项目)
【Python】Python项目打包发布(五)(制作Windows安装包)

说明

制作Windows安装包的工具有很多,其中比较知名的有:

  • NSIS
  • WiX
  • InnoSetup
  • InstallShield
  • Advanced Installer

比如Tarui自带的就有WiX和NSIS
在这里插入图片描述

1、NSIS

Python 环境下,已有开源工具包pynsist
github地址:https://github.com/takluyver/pynsist

说明

pynsist并没有对Python文件做任何编译处理,只是生成了Python文件脚本的快捷方式,打包目录下会包括整个Python环境。个人体验欠佳,不推荐。

2、InnoSetup

Inno Setup 是一个功能强大的免费安装程序制作工具。它具有易于使用的脚本语言,可以创建具有自定义界面、安装选项和脚本操作的安装程序。
Inno Setup配合nuitka体验极佳!推荐作为Python环境下的工具链。可以参考我在https://github.com/KmBase/Umi-OCR下写的nuitka_build.py一键打包脚本的配置

本脚本的主要功能有:

nuitka 生成可执行文件、zipfile制作portable文件、 InnoSetup制作安装文件。如不需要生成zip压缩包,或制作安装包。可以自行注释掉create_portable()、create_portable()对应语句

if __name__ == '__main__':
    build()
    create_portable()
    if SYSTEM == 'Windows':
       create_portable()

说明

nuitka是一个可以将Python代码转换为C++代码并编译为可执行文件或扩展模块的工具。可以明显提高python项目的加载运行速度。Inno Setup 是一个免费的 Windows 安装程序制作软件,十分简单实用的打包小工具。

使用步骤

1、安装项目依赖

pip install -r requirements.txt

2、安装nuitka

pip install -U nuitka

3、安装Inno Setup
官网下载地址:https://jrsoftware.org/download.php/is.exe
中文语言包:https://raw.githubusercontent.com/jrsoftware/issrc/main/Files/Languages/Unofficial/ChineseSimplified.isl
请保存语言包到Inno Setup安装目录
在这里插入图片描述

4、执行脚本

python nuitka_build.py

5、安装

  • 生成build目录,包括nuitka编译过程文件目录(main.build)、可执行文件目录(main.release)、Inno Setup安装脚本(.iss)
    在这里插入图片描述

  • 用Inno Setup打开生成的.iss文件,或双击.iss打开。点击Run,生成安装文件
    image

  • release目录包含portable压缩文件以及安装文件
    在这里插入图片描述

  • 双击安装文件,可以采用安装Umi-OCR到指定位置
    在这里插入图片描述

6、卸载
控制面板找到Umi-OCR,卸载即可
在这里插入图片描述

附录

自用build.py

# subprocess.call(['pip', 'install', '-U', 'nuitka'])
# subprocess.call(['pip', 'install', '-r', 'requirements.txt'])
release = False
SYSTEM = platform.system()
ARCH = platform.architecture()[0][:2]
APP_VERSION = global_var.__version__
APP_NAME = global_var.SOFTWARE_NAME
GUID = uuid.uuid4()  # 用于打包的唯一标识
# GUID = ""
EXEC_NAME = global_var.EXEC_NAME
APP_URL = global_var.APP_URL
APP_ICON = global_var.APP_ICON
APP_PUBLISHER = global_var.APP_PUBLISHER
PROJECT_DIR = Path(__file__).absolute().parent
SOURCE_DIR = Path('./')
BUILD_DIR = Path('build')

if not BUILD_DIR.exists():
    BUILD_DIR.mkdir()
OUTPUT_DIR = BUILD_DIR / f'{SYSTEM}-{ARCH}'
DIST_DIR = Path.joinpath(OUTPUT_DIR, f'{global_var.EXEC_NAME}.dist')
ICON_DIR = Path.joinpath(DIST_DIR, APP_ICON)
print(APP_VERSION)
RELEASE_DIR = Path('release') / APP_VERSION
print(RELEASE_DIR)
if not RELEASE_DIR.exists():
    os.makedirs(RELEASE_DIR, exist_ok=True)


def build():
    nuitka_cmd = [
        'python',
        '-m',
        'nuitka',
        '--mingw64',
        '--show-progress',
        '--show-memory',
        '--standalone',
        '--include-data-dir=data=data',
        '--include-data-dir=view/res=view/res',
        "--include-data-dir=pj=pj",
        '--enable-plugin=pyside6',
        '--nofollow-import-to=tkinter',
        f'--output-dir={OUTPUT_DIR}'
    ]
    if platform.system() == 'Windows':
        nuitka_cmd.extend([f'--windows-icon-from-ico={APP_ICON}'])
    elif platform.system() == 'Linux':
        nuitka_cmd.extend([f'--linux-icon={APP_ICON}'])
    else:
        pass
    if release:
        nuitka_cmd.extend(['--disable-console'])
    else:
        nuitka_cmd.extend(['--enable-console'])
    nuitka_cmd.append(f'{SOURCE_DIR}/{EXEC_NAME}.py')
    process = subprocess.run(nuitka_cmd, shell=True)
    if process.returncode != 0:
        raise ChildProcessError('Nuitka building failed.')
    print('Building done.')


def create_portable():
    file_list = glob.glob(f'{OUTPUT_DIR / APP_NAME}.dist/**', recursive=True)
    file_list.sort()
    portable_file = RELEASE_DIR / f'{APP_NAME}-{APP_VERSION}-Portable-{SYSTEM}-{ARCH}.zip'

    print('Creating portable package...')
    with ZipFile(portable_file, 'w', compression=ZIP_DEFLATED) as zf:
        for file in file_list:
            file = Path(file)
            name_in_zip = f'{APP_NAME}-{ARCH}/{"/".join(file.parts[3:])}'
            print(name_in_zip)
            if file.is_file():
                zf.write(file, name_in_zip)
    print('Creating portable package done.')


def update_iss():
    settings = {
        'APP_NAME': APP_NAME,
        'APP_URL': APP_URL,
        'GUID': GUID,
        'APP_PUBLISHER': APP_PUBLISHER,
        'APP_VERSION': APP_VERSION,
        'EXEC_NAME': EXEC_NAME,
        'PROJECT_DIR': str(PROJECT_DIR),
        'OUTPUT_DIR': str(OUTPUT_DIR),
        'DIST_DIR': str(DIST_DIR),
        'RELEASE_DIR': str(RELEASE_DIR),
        'ICON_DIR': str(ICON_DIR),
        'ARCH': ARCH,
        'ARCH_MODE': 'ArchitecturesInstallIn64BitMode=x64' if ARCH == '64' else ''
    }

    iss_template = f'nuitka-setup-template.iss'
    iss_work = Path(BUILD_DIR) / f'{APP_NAME}-{ARCH}.iss'

    with open(iss_template) as template:
        iss_script = template.read()

    for key in settings:
        iss_script = iss_script.replace(f'%%{key}%%', settings.get(key))

    with open(iss_work, 'w') as iss:
        iss.write(iss_script)

    return iss_work


def check_iss():
    if ARCH == '64':
        program_files = os.environ.get('ProgramFiles(x86)')
    else:
        program_files = os.environ.get('ProgramFiles')
    iss_compiler = Path(program_files) / 'Inno Setup 6' / 'Compil32.exe'

    if iss_compiler.exists():
        return iss_compiler
    return None


def create_setup():
    iss_work = update_iss()
    iss_compiler = check_iss()
    if iss_compiler:
        print('Creating Windows Installer...', end='')
        compiler_cmd = [str(iss_compiler), '/cc', str(iss_work)]
        process = subprocess.run(compiler_cmd)
        if process.returncode != 0:
            raise ChildProcessError('Creating Windows installer failed.')
        print('done')

自用nuitka-setup-template.iss

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!



[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={#GUID}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
LicenseFile={#MyProjectDir}\LICENSE
;InfoBeforeFile={#MyProjectDir}\README.md
; Uncomment the following line to run in non administrative install mode (install for current user only.)
;PrivilegesRequired=lowest
OutputDir={#MyProjectDir}\%%RELEASE_DIR%%
OutputBaseFilename={#MyAppName}-{#MyAppVersion}-Setup-Windows-%%ARCH%%
SetupIconFile={#MyProjectDir}\{#MyIcon}
Compression=lzma
SolidCompression=yes
WizardStyle=modern
%%ARCH_MODE%%

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "chinesesimplified"; MessagesFile: "compiler:Languages\ChineseSimplified.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "{#MyProjectDir}\{#MyDist}\{#MyAppExeName}.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyProjectDir}\{#MyDist}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

参考文献:

https://www.cnblogs.com/chrisfang/p/17027553.html
inno setup打包程序安装出现CreateProcess failed;code 740

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KmBase

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值