情况1:当所使用的代码都在一个.py文件中,且该代码中没有调用其他文件和import自己写的函数时
例如:
打包下面这个检查输入的ip是否符合规范的程序ipcheck.py,程序中只Import了自带的库re
from re import compile
ipAddr = input("请输入一个ip: ")
compile_ip = compile(
'^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$')
if compile_ip.match(ipAddr):
print(True)
else:
print(False)
下载pyinstaller
pip install pyinstaller
使用Pyinstaller打包。先进入该程序所在的文件夹,在文件资源管理器的文件路径处输入cmd,打开一个console。在界面中输入下面的语句:
pyinstaller -F ipcheck.py
待该指令执行完毕,可在.py文件所在文件夹内,发现两个新建的文件夹build/和dist/,以及一个新建的文件ipcheck.spec。在dist文件夹下可以找到一个.exe文件,直接双击该文件即可执行程序。
在这里附上pyinstaller的使用手册:pyinstaller user manual
情况2:一般来说,当我们做一个比较大的项目的时候,难免会遇到调用不同.py文件中的自建函数,
比如,如果我在代码中加入这一行:
from file_loader import xml_loader
文件file_loader.py与ipcheck.py的文件夹层级关系如下,xml_loader是我在file_loader.py中自定义的一个函数。
|--dir
|--ipcheck.py
|--file_loader.py
此时如果还是按照情况1去操作,在运行exe时,会出现下列问题:
也就是说在打包exe时,我的自定义函数文件并没有被一起打包进去,导致找不到file_loader.py这个文件。那么此时我的解决方式为修改ipcheck.spec中的内容:
#pathex=[], 将这一行改为下面的一行
pathex=['file_loader.py文件的绝对路径'],
可以通过这个参数给需要打包的文件添加扩展的自定义py文件。
再执行下面指令
pyinstaller ipcheck.spec
情况3:如果在.py文件中加载进了一些其他文件资源。例如,使用pyside编写GUI时,必然需要调用.ui文件,那么这些文件怎么添加进去呢?这个地方的坑我真的踩了很久出不来,几乎花了一天去解决这个问题,写出来给兄弟姐妹们参考一下。
下面先贴一下我的当时参考的链接:
参考1
还有一些忘记收藏了,在这里谢谢 这些帖子给了我解决的思路,但是这些帖子里的方法也没有完全解决我的问题。
从参考1中可以看到,作者使用了pyinstaller --add-binary这个参数(对应spec文件中的binaries),我从pyinstaller的官方文档中找到了这个参数的含义,我的理解是,添加进一些在script中使用到的二进制文件。可我的文件不是二进制的,咋办呢?我看到官方手册中,还有一个类似参数–add-data(spec文件中的datas),这里也可以添加一些程序所需要的其他文件。
"–add-data"使用方式:‘本机文件绝对路径’,‘打包时希望文件存放的虚拟路径(此路径可随意自定义,如‘.’即可)’
于是,我开开心心使用这个参数,使用方法如下,在spec文件中添加added_list:
added_list=[('文件在本机绝对路径\\config.yml','.'),
('文件在本机绝对路径\\icon\\login.jpg','./icon'),
('文件在本机绝对路径\\icon\\tool.jpeg', './icon'),
('文件在本机绝对路径\\ui\\ToolWindow_TabWindow.ui', './ui'),
('文件在本机绝对路径\\ui\\ToolWindow_TabWindow.ui', './ui'),
('文件在本机绝对路径\\ui\\UETNameWindow.ui','./ui'),
('文件在本机绝对路径\\ui\\help_window.ui', './ui'),
('文件在本机绝对路径\\ui\\myTab.ui','./ui'),
('文件在本机绝对路径\\ui\\searchwindow.ui', './ui'),
('文件在本机绝对路径\\ui\\FileConversionWindow.ui', './ui')
]
a = Analysis(
['ipcheck.py'],
pathex=['file_loader.py或其他python文件的绝对路径'],
#pathex=[],
binaries=[],
datas=added_list,
......
再使用
pyinstaller ipcheck.spec
啪,弹出弹窗提示我:
长叹一口气,继续试试吧。
然后,我发现我在ipcheck.py中调用这些文件时,采用的是相对路径,然后我将这些路径全部改为绝对路径,再结合上面的spec,然后再生成一次。发现,竟然可以弹出我做的gui了,可以正常使用了,快乐了!!!
PS:我后来尝试了一下,在调用这些文件时,使用绝对路径,然后spec文件中不添加datas参数,好像也能用。具体啥情况啥原因,我还没来得及研究。
情况4:研究了一下,当.py文件采用绝对路径,打包好的exe可以在我的本机上使用,在我把对应的.exe复制到另外的电脑上时,就不能运行了!!!!!所以采用绝对路径时,其实我的spec中datas这个参数并没有起作用。
后面我找到了一篇博客,里面写的很详细:一篇实用的博客地址
我的问题在于,对于datas这个参数的使用有误。这里只列举datas参数如何使用,对应的.py文件中如何调用文件,以及这些文件与.py文件的位置关系。
在我的项目中,文件路径如下图所示:
|--ipcheck.py
|--config.yml
|--ui
|--login.ui
|--....
|--icon
|--login.jpg
UiLoader().loadUi('ui/login.ui', self)
icon.addPixmap(QPixmap("icon/login.jpg"), QIcon.Normal, QIcon.Off)
与之相对应,我的datas中的参数应该为:
added_list=[('config.yml','.'),
('icon\\login.jpg','icon'),
('ui\\login.ui', 'ui')
]
a = Analysis(
['MyMainWindow.py'],
pathex=['file_loader.py或其他python文件的绝对路径'],
#pathex=[],
binaries=[],
datas=added_list,
...
)
根据我参考的实用博客,我在exe执行时,会在一个临时解压路径中去寻找这些相关文件,也就是说,我在直接执行ipcheck.py以及执行ipcheck.exe时,这些文件的相对路径应该一致,否则就有可能导致.py或者.exe无法执行。
如果你的文件结构,在.py中的调用方式,以及在spec中的datas设置方式和我的一致,还报错时,就要考虑下面这个问题。
为了使exe在执行时,调用这些文件时去临时解压的路径下去寻找这些路径,在程序中调用文件时,需要切换路径。具体的方式和原因可以在上面提到的实用博客中寻找。这里只写出了使用方式。
class Path4EXE():
def __init__(self):
self.tmd = self.base_path('') # 这是解压路径
self.cwd = os.getcwd() # 这是程序的所在路径
def base_path(self,path):
if getattr(sys, 'frozen', None):
basedir = sys._MEIPASS
else:
basedir = os.path.dirname(__file__)
# print(basedir)
return os.path.join(basedir, path)
def CompressPath2WorkPath(self):
os.chdir(self.cwd) # 把工作路径切换回来
def WorkPath2CompressPath(self):
# 当需要调用打包的外部文件时
os.chdir(self.tmd) # 先把工作路径变成解压路径
# print(self.tmd)
chPath = Parh4EXE()
# 进入解压目录
chPath.WorkPath2CompressPath()
print(os.path.abspath(os.curdir))
UiLoader().loadUi('ui/login.ui', self)
# 转回工作目录
chPath.CompressPath2WorkPath()
到此为止,我的exe可以正常在其他计算机上运行了。
详情请参加此篇文章:
如何给exe添加图标
以上。