【python、pyqt5】,打包出现的若干问题

every blog every motto: A day is a miniature of eternity.

0. 前言

个人需要,设计了一款小软件,为了方便使用,将它打包,在这其中出现了若干问题,在这里进行总结。
注:

  1. 为了便于说明,在此对相关代码进行了简化。
  2. 经常参考别人博文,但别人有时只给出部分文件,不给出文件名。读者经常陷入不知所云的境地,特此将所需的文件进行罗列,并加以说明。

1. 正文

1. 所需文件

文件1:testui.py(GUI代码)
这是通过designer设计出的一个按钮。具体代码如下。

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(267, 206)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(90, 50, 93, 61))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 267, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "开始"))

文件2:test_mian.py(主程序代码)
这是主程序代码,可以看作时GUI和功能函数的“桥梁”,具体代码如下。
在这里插入图片描述

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys,os
if hasattr(sys, 'frozen'):
    os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH']
from PyQt5.QtWidgets import QApplication,QMainWindow
# from testui import *
from test_function import *
import testui

# class MyWindow(QMainWindow,Ui_MainWindow):
#     def __init__(self,parent=None):
#         super(MyWindow,self).__init__(parent)
#         self.setupUi(self)
#
#
#
# if __name__ == '__main__':
#     app = QApplication(sys.argv)
#     myWin = MyWindow()
#     myWin.show()
#     myWin.pushButton.clicked.connect(print_hello)
#     sys.exit(app.exec())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    MainWindow = QMainWindow()
    ui = testui.Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    ui.pushButton.clicked.connect(printf)
    sys.exit(app.exec_())

文件3: test_function.py (功能函数)
所要实现的功能,也可以写在文件2(test_main.py)中,为了条理清晰,独立出来。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
def printf():
    print('牛逼')

if __name__ == '__main__':
    printf()

2. 问题

2.1 打包程序不可移植问题

问题描述:打包出来的exe文件只能在自己计算机上运行,不能在别人计算机上运行。
解决方法:在主程序(test_main.py),即文件2,开始出添加如下代码

if hasattr(sys, 'frozen'):
    os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH']

说明:这个问题,时之前遇到的,一并记录之。

2.2 打包多个文件问题

问题描述:如果打包多个文件,并且多个文件都为(*.py文件),
网上已有相关教程
简化:如果是多个文件(都为.py),则只打包一个主程序即可。

pyinstaller -F test_main.py

2.3 打包出错

问题描述:ModuleNotFoundError: No module named ‘pkg_resources.py2_warn’
[51988] Failed to execute script pyi_rth_pkgres
原因:setuptools升级太快,pyinstaller跟不上导致。
解决方法:把setuptools降级到44.0.0以下,再重新打包即可 [1]
具体代码如下[2]
1 查看版本

pip isntall setuptools

在这里插入图片描述
2. 卸载已有版本

pip uninstall setuptools
  1. 安装新版本
pip install setuptools==39.1.0

2.4 打包资源文件

说明: 程序中经常会用到额外的资源文件(如:图片、文本、pdf、chromedriver等),需要将这些文件一本打包,在此一并附上。
注:

  1. 为了便于理解,在此用了一个单独的程序,随后附上(与前面程序无关,本程序名a2.py)
  2. 本程序需要的外部资源时一张图片存放在tools中
    完整目录:
    在这里插入图片描述
    原理
  3. pyinstaller打包的可执行文件,运行sys,forzen会被设置成True,因此通过sys.fromzen的值区分,是开发环境还是打包的后的环境
  4. pyinstaller可以将资源文件一起bunild到exe中,当exe运行时,会生成一个临时文件夹,程序通过sys._MEIPASS访问临时文件夹的资源[4-5]

步骤一: 在程序中添加核心代码
核心代码:


def resource_path(relative_path):
    if getattr(sys, 'frozen', False):
        base_path = sys._MEIPASS
    else:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)

步骤二: 修改a2.spec文件
具体:修改datas,引文所需要的文件(图片)在tools中,具体添加如下图所示。
说明: 切换(上面提到)a2.py的目录下,用pyinstaller打包后会生成a2.spec(需要详细了解的参考文献4,6,7,8)
a2.spec文件说明:
analysis:以py文件为输入,它会分析py文件的依赖模块,并生成相应的信息
pyz:是一个.pyz的压缩比搜,包含程序运行需要的所有依赖
exe:根据上面两项生成
collect: 生成其他部分的输出文件夹,collect也可以没有
在这里插入图片描述
步骤三:
重新打包:

pyinstaller -F a2.spec

本例完整代码:
程序输出一条语句,读取图片并展示(图片存放在同级目录下tools)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
from PIL import Image


def resource_path(relative_path):
    if getattr(sys, 'frozen', False):
        base_path = sys._MEIPASS
    else:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)


def main():
    print('我是第二个文件')

    # path = os.path.join('tu.jpg')
    path = resource_path((os.path.join('tools', 'tu.jpg')))

    img = Image.open(path)
    img.show()


if __name__ == '__main__':
    main()

2.5 打包太大问题

问题描述:用pyinstaller打包经常动辄几百M,实在吓人。经过搜索现总结如下方法:
说明: 本部分所用程序与前面无关,可单独写个简单的程序进行验证。方法二、三都是在方法一基础上加以实现,为了更进一步压缩。
方法一: 创建虚拟环境[3]
说明:如果不创建虚拟环境,会将很多无关的库打包进去,创建虚拟环境后,需要什么安装什么。
1.按装pipenv库,用于创建虚拟环境

pip install pipenv
  1. 在控制台(win + r => 输入:cmd)中切换文件夹
    新建一个文件夹,将需要的文件放入其中。如在桌面新建个(wen),在控制台切换进入
cd D:\Data_saved\Desktop\wen
  1. 进入虚拟环境
pipenv shell
  1. 安装程序中需要用到的库(以pandas为例)
pip install pandas
  1. 安装pyinstaller模块,用于打包
pip install pyinstaller
  1. 用pyinstaller 打包
pyinstaller -F main_.py

结果:exe: 220M。
方法二: 打包成目录形式(类型平时我们安装的软件)
去掉参数F 即可。

pyinstaller main_.py

结果:总文件(dist) 615M,exe:5.3M。
方法三: 添加参数 upx[9-10]
–upx-dir=(upx所在路径)
upx下载地址:upx下载

pyinstaller -F main_.py --upx-dir=D:\softeware\upx-3.95-win32

结果:exe: 134M
但是无法运行!!!!!
可以参考文献11,12,进行测试。
在这里插入图片描述

参考文献

[1] https://bbs.csdn.net/topics/395779945
[2] https://blog.csdn.net/petSym/article/details/82840636
[3] https://blog.csdn.net/qq_40529853/article/details/100576791
[4] https://pythonhosted.org/PyInstaller/spec-files.html#spec-file-operation
[5] https://www.cnblogs.com/darcymei/p/9397173.html
[6] https://cloud.tencent.com/developer/news/299957
[7] http://legendtkl.com/2015/11/06/pyinstaller/
[8] https://www.cnblogs.com/chusiyong/p/12052930.html
[9] https://blog.csdn.net/xinyingzai/article/details/80282856
[10] https://blog.csdn.net/qq_27017791/article/details/102748766
[11] https://blog.mioshu.com/archives/570.html
[12] https://stackoverflow.com/questions/47730240/how-do-i-use-upx-with-pyinstaller

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

胡侃有料

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

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

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

打赏作者

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

抵扣说明:

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

余额充值