pyinstaller打包不到⽂件的问题解决
1、将python程序打包成单⽂件(使⽤ -F 参数)后,尝试运⾏外部⽂件却提⽰不到的问题
当你将python程序打包成单⽂件(使⽤ -F 参数)后,运⾏程序,它实际上是先将exe内的资源⽂件解压到临时⽂件夹,然后再运⾏的,所以会导致这种问题
⽐如,当你在程序⾥⾯调⽤⼀个外部exe时,但却提⽰不到该exe⽂件。
例⼦(这⾥我⽤win32api去隐式运⾏外部exe⽂件):
import win32api
win32api.ShellExecute(0, 'open', '', '', '', 0)
⾸先,你需要将这个外部的exe⽂件添加进pyinstaller的打包⾥。
有两种⽅法:
1、直接⽤参数添加:
--add-data ";."
完整命令:pyinstaller -F main.py --add-data ";."
2、在spec⽂件添加:
每次执⾏pyinstaller打包命令后会⽣成spec⽂件,打开它
在⾥⾯到data=[] 列表,添加元素,变成了:datas=[('', '.')]
然后⽤spec打包:pyinstaller main.spec
我解释⼀下这个点“.”是什么意思:
由于使⽤单⽂件打包出来的exe会先解压再运⾏,所以点“.” 其实表⽰你打包的这个exe⽂件运⾏解压的完整路径
如:C:\...\temp(临时⽂件夹)\asdqwezxc(你程序运⾏时⾃动解压到的⽬录)
所以这个 被打包后,会解压到 C:\...\temp\
如果把点“.”改为test,就会解压到 C:\...\temp\asdqwezxc\
以此类推
好,现在已被打包。然后要注意⼀个问题:
打包出来的exe在运⾏时,它的⼯作路径和它解压到的路径,是不⼀样的!
你可以测试⼀下:
import os
wd())
可以发现,打印出来的⼯作路径并不是它运⾏时解压到的路径!
⽽是这个打包出来的exe,它本⾝所存在的路径!
问题来了:
诸如open('')这些操作⽂件的函数,⼀般⾸先都是在⼯作路径查你所指定的⽂件的。
所以,当我们直接这样执⾏已打包的外部⽂件时,程序会报不到⽂件!所以请使⽤它的解压路径。
下⾯提供⼀个函数,可以很⽅便的获取到解压路径:
import os, sys
def base_path(path):
if getattr(sys, 'frozen', None):
basedir = sys._MEIPASS怎样写好作文
else:
basedir = os.path.dirname(__file__)
return os.path.join(basedir, path)
print(base_path(''))
print(base_path(''))
第⼀句打印会显⽰完整的解压路径:
C:\...\temp\asdqwezxc\
第⼆句打印是这样的:
C:\...\temp\asdqwezxc\
所以当我们在调⽤已打包的外部⽂件时,应该先使⽤os.chdir()将⼯作路径改为解压路径:
再进⾏操作,就不会报⽂件不到了
os.chdir(base_path(''))
win32api.ShellExecute(0, 'open', '', '', '', 0)
不过要注意的是,如果你要写出⽂件到程序所在的⽬录(⾮解压⽬录),那么你得把⼯作⽬录改回来,否则⽂件会被写出到解压路径(临时⽂件夹)。
稍微封装⼀下就好了:
import os, sys
def base_path(path):
if getattr(sys, 'frozen', None):
basedir = sys._MEIPASS
else:
basedir = os.path.dirname(__file__)
return os.path.join(basedir, path)
tmd = base_path('') # 这是解压路径
cwd = os.getcwd() # 这是程序的所在路径
# 当需要调⽤打包的外部⽂件时
os.chdir(tmd) # 先把⼯作路径变成解压路径
冲q币do() # 执⾏你要⼲的事情
# 当需要写出⽂件到程序所在⽬录时
os.chdir(cwd) # 把⼯作路径切换回来
do() # 执⾏你要⼲的事情
2、当你使⽤cython将py⽂件编译成pyd⽂件后使⽤pyinstaller打包,提⽰不到模块的问题
直接使⽤pyinstaller打包py⽂件是很容易导致源码被反编译的
所以在打包的时候最好将py⽂件编译成pyd⽂件,这样可以很⼤程度上防⽌反编译。
为什么呢?因为pyd⽂件的来历是这样的:
py⽂件→ c⽂件→ pyd⽂件
所以直接反编译pyd只能得到上⼀步cython⽣成的c⽂件,⽽⽆法得到我们的py源⽂件。
解决⽅法很简单,请看:
⽐如说,我有⼀个⽂件main.py,引⼊了位于同级⽬录下的test.py模块
# main.py:就像这样直接引⼊
import test
现在我将test.py 编译成pyd⽂件,⽣成了:test.cp37-win_amd64.pyd
这个pyd⽂件名除了我们原本的⽂件名test,还会带上编译环境的名称,这个环境后缀名我们可以不⽤管,因为python引⼊模块还是很智能的(会⾃动引⼊.pyd⽂件,因为它的优先级⾼于.py⽂件)。
这么智能,但是为什么我⽤pyinstaller打包时就提⽰不到⽂件?
其实我们需要在打包时--hidden-import这些模块
1、直接添加
在打包时添加--hidden-import test即可
完整命令:pyinstaller -F --hidden-import test
2、使⽤spec⽂件
同样的,运⾏⼀次pyinstaller打包命令后会⽣成spec⽂件,打开它
到hiddenimports=[],添加test模块,变成了:hiddenimports=['test']
很简单对吧?
⽽且除了我们⾃⼰写的⼀些py模块,其它模块在打包时可能也会提⽰不到,都可以⽤这个⽅法解决。
3、打包成单⽂件时(使⽤-F参数),运⾏时要求管理员权限的参数--uac-admin⽆效的问题
请看我的这篇⽂章:
本质上⼤概也是因为不到⽂件。
4、⽆控制台打包(使⽤-w参数),运⾏时弹框提⽰Failed to execute script的问题
请看我的这篇⽂章:
这个问题⼀般是程序内有输⼊导致的,这个输⼊可以是input(),也可以是其它的⼀些stdin操作(如os.popen实际上会造成输⼊请求)
本质上就是:使⽤-w参数(⽆控制台)打包时程序⾥不要请求输⼊
非诚勿扰樊刚当然,实在要⽤输⼊,⼜不想要控制台怎么办?很简单,把控制台隐藏了就⾏!
感恩父母感动流泪句子下列两个⽅法,试试看:
import ctypes
def hideConsole():
"""
Hides the console window in GUI mode. Necessary for frozen application, because
this application support both, command line processing AND GUI mode and theirfor
cannot be run
"""
whnd = ctypes.windll.kernel32.GetConsoleWindow()
康佳液晶电视怎么样if whnd != 0:
ctypes.windll.user32.ShowWindow(whnd, 0)
# if you wanted to close
元宵节吃什么东西#ctypes.windll.kernel32.CloseHandle(whnd)
def showConsole():
"""Unhides console window"""
whnd = ctypes.windll.kernel32.GetConsoleWindow()
if whnd != 0:
ctypes.windll.user32.ShowWindow(whnd, 1)
暂时就这么多呃,都是本⼈在打包过程中实际上遇到过的问题和经验。。基本可⽤
到此这篇关于pyinstaller打包不到⽂件的问题解决的⽂章就介绍到这了,更多相关pyinstaller打包不到⽂件内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!
发布评论