使用Pyinstaller发布带界面的程序(解决找不到文件问题)

2023-05-16

Pyinstaller

Pyinstaller可以用来打包python代码,生成可执行文件(主流平台都可以),介绍就不说了,可以百度或者去官网看看:https://www.pyinstaller.org/
以Windows为例,简单说一下主要过程:

安装pyinstaller:

pip install pyinstaller

打包,为了讲述方便,贴一张项目的目录结构,方便理解:
在这里插入图片描述

main.py可以理解成是整个项目的接口模块,也是pyinstaller要进行打包的模块。main.py里边的内容很简单,通常就是调用mainInSrc.py里的模块执行。而整个项目的真正逻辑都是从mainInSrc.py里开始的。
打包的指令也很简单,进到项目文件夹,然后执行(请注意这么做完之后,运行通常都会报错):

pyinstaller main.py

指令执行完后,可以看见文件夹里多了些东西:
执行前:
before
执行后:
after
进入dist文件夹,里边会看见一个main文件夹,这个文件夹就是生成的打包文件,里边会有一个main.exe可执行文件。通常情况下,这个main.exe很难执行成功,因为发布的是带界面的程序,里边会用到一些图片或者本地数据。就是即使项目里文件路径写的再好,都可能出现运行程序找不到文件导致程序启动失败。解决这个问题需要了解以下几个问题:

  1. 打包模式是什么?
  2. 运行路径是什么?
  3. 如何为程序添加本地文件或数据?

解决找不到文件无法启动的问题

1. 打包模式是什么?

一般情况,可能用到的打包主要有两种,一种是打包成单独文件夹模式(One Folder),一种是单一文件模式(One File)。默认是One Folder模式。
默认情况下,pyinstaller的打包是One Folder模式:

pyinstaller main.py

如果想打包成One File模式:

pyinstaller -F main.py

2. 运行路径是什么?

可以明确的说,运行打包完的执行文件和从源码运行程序路径肯定是不同的。当捆绑应用程序启动时,引导加载程序设置sys.frozen,并将绑定文件夹的绝对路径存储在sys._MEIPASS中。对于一个文件夹包,这是该文件夹的路径。对于单文件包,这是引导加载程序创建的临时文件夹的路径。
所以,为了避免反复修改文件路径,首先要做的是首先分辨出程序到底是从可执行文件运行还是从源码运行,具体通过如下代码判断:

if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
    print('running in a PyInstaller bundle')
else:
    print('running in a normal Python process')

有了这个判断代码,可以将文件路径分别设置在各自条件下,这样至少可以保证程序在运行时都是在你期望的路径下去寻找文件。

假设你的源文件运行是没有问题的,接下来解决可执行文件文件路径问题:
这里要清楚的是One Folder模式和One File模式在执行时路径是不同的。当然和从源码运行也是不同的。这个可以使用__file__来获取:

  1. 从源码运行:__file__是当前模块路径
  2. One Folder:__file__是打包文件夹的路径
  3. One File: __file__是临时文件夹的路径
    所以,结合sys._MEIPASS,建立从打包文件运行的绝对路径,通过如下代码实现:
# 从打包文件运行时,获取文件的绝对路径
from os import path
import sys

bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))+"\\images\\"
file_path = path.abspath(path.join(bundle_dir, file_name))

这里假设我的图片文件夹就在打包文件下。我从图片文件夹中获取文件。也就是main.exe和images文件夹同级。以此类推,你可以随意设置你想要的路径。
设置为从打包文件获取文件,接下来设置从源代码运行获取文件,你可以直接把你之前源代码中获取文件的路径复制过来,也可以仿照上述代码,构成获取文件路径代码,还是以本例子中的结构为例,代码如下:

# 从源代码文件运行时,获取文件的绝对路径
from os import path
import sys

bundle_dir = getattr(sys, '_MEIPASS', path.abspath(path.dirname(__file__))) + "\\..\\view\\images\\"
        file_path = path.abspath(path.join(bundle_dir, file_name))

这么写的好处是,实际代码中,可以将

getattr(sys, '_MEIPASS', path.abspath(path.dirname(__file__))) 

提取出来包装成一个函数,用来获取程序运行时根目录的绝对路径。可以很方便的供更多函数使用。
最终,获取文件路径的完整代码如下:

def getFilePath(file_name):
    # run in a bundle
    if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
        bundle_dir = getattr(sys, '_MEIPASS', path.abspath(path.dirname(__file__)))+"\\images\\"
        file_path = path.abspath(path.join(bundle_dir, file_name))
    # run from source
    else:
        bundle_dir = getattr(sys, '_MEIPASS', path.abspath(path.dirname(__file__))) + "\\..\\view\\images\\"
        file_path = path.abspath(path.join(bundle_dir, file_name))
    return file_path

3.如何为程序添加本地文件或数据?

设置完路径,需要考虑如何添加本地文件,添加本地文件有两种方法,一种是直接在打包过程中,通过–add-data实现;第二种通过配置.spec文件实现。这里推荐第二种,第一种可以去官网看看:链接
打包执行结束,会在main.py同级别生成一个main.spec文件,这个文件可以编辑并且可以被pyinstaller执行。
使用编辑器打开main.spec文件,在a = Analysis()中找到datas,将所需要添加的文件添加到里边,这里比较推荐的一种做法是先创建一个变量,然后将所要添加的内容一次性写好,然后将变量传给datas:

added_files = [
         ( 'src/view/images', 'images' ),
		 ( 'src/templates/template.html', 'templates' ),
		 ('output', 'output')
         ]
a = Analysis(['emailAGS.py'],
             pathex=['C:\\Users\\90621\\Documents\\我的项目\\emailAutoGeneration'],
             binaries=[],
             datas=added_files,
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)

保存后,运行:

pyinstaller main.spec

这个语句实际上就是一个更新的过程,你不需要重新打包main.py,只需要运行main.spec。即使在你更改了你的源代码后,你依然可以用main.spec进行更新。

至此,因文件找不到而无法运行的问题顺利解决。

需要注意的是,打包成One File的好处是方便并且因为每次运行是在临时文件夹中,所以程序可以同时运行并不影响。缺点是临时文件夹不会自己消失,所以时间长会占用空间。打包成One Folder的好处是不会有临时文件产生,但是不能同时运行多个。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用Pyinstaller发布带界面的程序(解决找不到文件问题) 的相关文章

  • 一个简单的游戏源代码

    一个简单的游戏源代码 作者 xff1a 陈跃峰 出自 xff1a http blog csdn net mailbomb 实现一个简单的翻牌游戏 xff0c 每次最多翻开两个数字 xff0c 如果数字相同则消失 xff0c 否则两个数字不显
  • 数据库事务图解

    一 基本概念 xff08 from baidu xff09 数据库事务 Database Transaction xff0c 是指作为单个逻辑工作单元执行的一系列操作 xff0c 要么完全地执行 xff0c 要么完全地不执行 事务处理可以确
  • Java新手必读

    Java 新手必读 1 学习 Java 学什么 xff1f a Java 语法基础 xff1a 如基本数据类型 流程控制 数组等等 b 面向对象的概念 xff1a 理解类和对象的概念等等 c 开发工具 xff1a Jbuilder Ecli
  • Java编程那些事儿3——你适合学习程序设计吗?

    Java编程那些事儿3 你适合学习程序设计吗 xff1f 作者 xff1a 陈跃峰 出自 xff1a http blog csdn net mailbomb 程序设计是一个技术活 xff0c 所以不是适合所有的人 现在很多人听说程序设计人员
  • J2ME资源:各厂商SDK和模拟器下载地址

    现在支持 Java 的手机厂商很多 xff0c 现将常用的手机厂商 SDK 和下载地址说明一下 xff1a 1 Nokia Nokia 不愧为手机行业的老大 xff0c 对于 j2me 的支持也是一流的 xff0c 有专门的网站提供 SDK
  • 陈跃峰的个人简历

    简 历 基 本 信 息 姓 名 xff1a 陈跃峰性 别 xff1a 男 出生日期 xff1a 1980年01月23日居 住 地 xff1a 郑州市 工作年限 xff1a 五年以上 户 口 xff1a 河南 目前年薪 xff1a 8 10万
  • NokiaS40和S60开发平台1.0已知问题(翻译)

    NokiaS40 和 S60 开发平台 1 0 已知问题 翻译 作者 xff1a 陈跃峰 出自 xff1a http blog csdn net mailbomb 1 Nokia3300 不支 MMA 声音处理 类库 2 Image get
  • 自己动手清除Android系统中的不良程序

    自己动手清除Android系统中的不良程序 陈跃峰 2014 6 29 最近在使用我的Nexus7时 xff0c 总是经常莫名其妙的弹出一些广告弹窗 xff0c 还会自动下载一些应用程序 xff0c 还会在桌面上生成一个叫做 精彩应用 的图
  • 劳动仲裁申请书

    劳动仲裁申请书 申请人 xff1a 陈跃峰 公司职务 xff1a 手机游戏开发课程教师 xff0c 隶属教学部 性别 xff1a 男 身份证号码 xff1a 联系方式 xff1a Email xff1a cqucyf 64 gmail co
  • 赠送Gmail,需要的给我发邮件

    cqucyf 64 gmail com
  • 浅谈当前的手机开发技术

    浅谈当前的手机开发技术 作者 xff1a 陈跃峰 出自 xff1a http blog csdn net mailbomb 随着3G 时代的到来 xff0c 手机软件开发成为了一个热门的软件开发技术 xff0c 但是由于手机种类等一系列的原
  • 玩微博的朋友们请关注我一下,@爪哇米工作室,新浪和腾讯的都可以!

    最近比较忙 xff0c 写博客的时间不多 xff0c 还是用微博交流吧 xff01
  • 常用操作-增删查改

    官方文档 xff1a http www pythondoc com flask sqlalchemy quickstart html 常用的SQLAlchemy查询过滤器 常用的SQLAlchemy查询执行器 eg xff1a 表定义 xf
  • Android开发学什么

    Android开发学什么 爪哇米工作室 陈跃峰 2013 3 1 随着移动互联网的发展 xff0c 现在的手机软件开发成为了一个很热门的技术方向 xff0c 很多的学生以及从事其它开发的程序员都在大量的涌入这个领域 xff0c 但是很多人在
  • IntelliJ IDEA包层级结构显示

    点开项目结构视图右上角那个齿轮 xff0c 如下图所示 看第二行菜单 xff0c Hide Empty Middle Packages 前面有个勾 xff0c 这表示现在包是不分层级显示的 xff0c 把勾去掉 xff0c 包就会分层级显示
  • RN 多行换行布局

    import React Component from 39 react 39 import AppRegistry StyleSheet Text View from 39 react native 39 export default c
  • PCB布局元器件的便捷操作(PCB批量修改线宽,PCB器件放置背面,PCB板反转)

    1 设置同网络内部的线宽 方法一 xff1a 未布线之前可以设置在规则里面提前设置 新加规则 方法二 xff1a 布线以后使用查找相似方法进行修改 找到该网络右击 找到相似属性 点击 gt OK 修改线宽 gt 回车 完成 2 将元器件放置
  • github pages搭建博客的域名解析(简单有效)

    折腾了一个小时终于把域名解析弄好了 xff0c 还顺带了解了不少关于http的知识 xff0c 亲手实践还是最有效的方法 xff0c 哈哈 这里记录一下自己的步骤 注意 xff1a 如果只需要解析主域名 xff0c 那么只做第一步就行 xf
  • 用nginx实现反向代理,实现外网访问内网的服务

    环境背景 xff0c 服务器为Ubuntu xff1a 一台可以连接公网和内网的服务器A xff0c 公网IP地址 xff1a 61 174 xff0c 另一台内网服务器B上安装jenkins服务 xff0c 内网的IP地址192 168
  • g++ is not recognized as an internal or external command解决方法,初用MinGW碰到的坑

    之前一直用Qt或者CodeBlocks上带的MinGW xff0c 这次自己装了一个 xff0c 碰到一堆问题 一 在用g 43 43 编译的时候报错 xff1a stddef h No such file or directory std

随机推荐

  • Ubuntu 20.04: harfbuzz version too old解决方法

    参考链接 xff1a unix stackexchange com 现象 在ubuntu20 04上安装了Ao xff08 可以看做Microsoft todo的linux版 xff09 xff0c 从终端启动时报错 xff1a Faile
  • 图像处理:理想低通滤波器、butterworth滤波器(巴特沃斯)、高斯滤波器实现(python)

    对图像进行频域滤波的几种常用低通滤波器 xff0c python opencv实现 xff1a 低通滤波器 xff08 Low pass filter xff09 容许低频信号通过 xff0c 但减弱频率高于截止频率的信号的通过 对于不同滤
  • 图像处理:迭代阈值分割算法实现(python)

    使用迭代的方法对图像全局的阈值进行估计 xff0c 效果优于传统的双峰阈值分割 算法流程 xff1a 给定初始值 T 0 T 0 T 0 xff0c 最好是全局灰度平均值根据阈值
  • filter- 条件合并

    发现一个很好用的用法 可以根据前端传来的数据判断 xff0c 数据筛选的条件 可以先新建一个列表 xff1a filter 61 根据条件 xff0c 给这个列表加条件 比如 xff1a if status filter append Us
  • HC-05蓝牙模块主从连接配置(无坑)

    做课程设计要用到两个HC 05连接 xff0c 之前用过HC 06 xff0c 拿原来那个方法配还是有些坑 xff0c 搞了一下午最后还是成功了 尝试了两个方案 xff0c 一是用usb转TTL配置 xff1b 二是用Arduino xff
  • PyQt 自定义气泡弹窗

    效果图 xff1a 用来做弹窗提醒 xff0c 气泡弹窗会自动关闭 xff0c 持续时间1600ms xff0c 在750ms时开始逐渐透明 xff0c 1600ms时消失 窗口继承的是QDialog 设置背景透明 无标题栏 无边框 xff
  • pyinstaller打包PyQt程序 + 制作安装包

    打包发布PyQt5程序 xff0c 制作安装文件 以自己的密码管理器为例 用到的工具 xff1a 平台 xff1a Windows10python 3 7 6 xff08 安装好所需的包 xff09 pyinstallerupx xff08
  • 树莓派开机自动发送邮件脚本

    开机联网后自动获取本机内网IP xff0c 并通过邮件或者server酱发送自己的内网IP span class token keyword import span smtplib span class token keyword from
  • C++ STL Map按照value排序

    xff08 记录一下 xff09 STL的map底层实现一般是红黑树 xff0c 会自动按照key排序 xff0c 按照value排序好像也没有更好的方法了 xff0c 只能将map转成vector lt pair gt 再进行排序了 sp
  • typescript

    TypeScript TypeScript 是一种给 JavaScript 添加特性的语言扩展 支持es6 xff0c 是微软提出的一种编程语言 TypeScript 设计目标是开发大型应用 xff0c 它可以编译成纯 JavaScript
  • 串口打印中途无log出来,显示console:$字样,一段时间后才再显示

    调试时 xff0c 我们在外接串口打印log时 xff0c 发现lk打印完成后跳转到kernel阶段 xff0c 显示console 字样 xff0c 过一段时间后才重新开始打印log xff0c 这样导致我们抓取的log不全 xff0c
  • 【Hexo】域名绑定篇

    关于Hexo的一切 我的Hexo专栏 零 前言 继上篇 xff0c 本篇主要讨论购买域名以及如何绑定并进行解析 一 购买 我这里只推荐两个平台 xff1a 阿里云官网和 腾讯云官网 xff0c 选择你中意的即可 我买的是一个很辣鸡的域名ww
  • 配置一个好看的PowerShell

    工作生活中用到 PowerShell 的时刻其实有很多 xff0c 但是那深蓝色的背景实在让人想吐槽几句 今天我们就来美化一下它 xff0c 几十种花里胡哨的主题任你选择 用到的是oh my posh xff0c 跟oh my zsh类似
  • scikit-learn介绍-非常流行的python机器学习库

    scikit learn是一个建立在Scipy基础上的用于机器学习的Python模块 在不同的应用领域中 xff0c 已经大展出为数众多的基于Scipy的工具包 xff0c 他们统称为Scikits 而在所有的分支版本中 xff0c sci
  • redis

    redis Redis 是一个Key Value 数据库 xff0c 主要用于存储缓存 redis支持的数据类型 xff1a String字符串 xff1a 设置key值 xff1a set key value string类型是二进制安全
  • 多生产者——多消费者问题

    问题背景 假设有四个人 xff1a 父亲 母亲 女儿 儿子 xff0c 和一个空盘子 xff0c 里面最多放一个水果 父亲每次向盘子中放一个苹果 xff0c 女儿只会吃苹果 母亲每次向盘子中放一个橘子 xff0c 儿子只会吃橘子 这个问题可
  • Android-MVVM-Databinding的原理、用法与封装

    前言 说起 DataBinding ViewBinding 的历史 xff0c 可谓是一波三折 xff0c 甚至是比 Dagger Hilt 还要传奇 说起依赖注入框架 Dagger2 Hilt xff0c 也是比较传奇 xff0c 刚出来
  • day03 Python基础

    day03 Python基础 版权声明 xff1a 本博客转载自路飞学城Python全栈开发培训课件 xff0c 仅用于学习之用 xff0c 严禁用于商业用途 xff0c 未经授权 xff0c 严禁转载 欢迎访问路飞学城官网 xff1a h
  • 最新Spire.pdf Spire.Doc Spire.Xls等无水印使用

    Aspose与Spire功能都很强大 xff0c 为什么要选择Spire xff0c Spire支持WPF组件 xff0c Aspose默认没有 新建 net6控制台程序 xff0c 用NuGet包添加Spire PDF引用 添加代码 us
  • 使用Pyinstaller发布带界面的程序(解决找不到文件问题)

    Pyinstaller Pyinstaller可以用来打包python代码 xff0c 生成可执行文件 xff08 主流平台都可以 xff09 xff0c 介绍就不说了 xff0c 可以百度或者去官网看看 xff1a https www p