最近使用python3 + PyQt5 做了一个带界面的小软件,并使用PyInstaller进行了软件打包发布,遇到了一些坑,不过经过一通查阅,基本解决,接下来就汇个总,简单讲解使用python与Qt如何开发一个交互界面。
实现一个简单的用户登录后跳转到另一个UI这样的流程,重在解析如何使用python将Qt融合进去,比如登录时访问数据库就暂时忽略。
登录 --> 访问数据库(本文略) --> 跳转到主界面 --> 使用PyInstaller打包为exe
几点经验
- 当你想用PyInstaller或者cx_Freeze打包python程序时,一定要考虑版本和兼容性问题,否则会出现很多偶然性问题,不易排查,所以个人建议,包模块安装版本不要太高。
- import包模块,小心谨慎,不要引入不必要的包,否则后面打包会很庞大,或者牵扯很麻烦的库,不好打包,比如pandas这样底层为C开发的库,需要考虑其他环境。
- 如果不熟悉Qt+C++开发的环境,其实也不用担心在python下使用PyQt,因为它更简单,但有Qt经验,会很能理解使用PyQt5附带的工具开发的使用机制。
- 跟C++调用Qt库的接口区别不大,简单说就是用python的调用方式去调用Qt的接口,熟悉Qt接口就很容易上手。
- 使用一款简洁高效的IDE开发。因为功能不是很复杂,我也没有选择大型的IDE如PyCharm,我选择了VS Code这样一个半文本编辑器的开发工具进行开发调试,并且写了简单的cmd脚本,调用PyQt5工具,加入图片资源,以及用.ui文件生成.py文件来管理整个小工程。
- 事实上,一切Qt资源,如.ui文件、.qrc文件,都应该用PyQt5附带的工具生成.py文件,作为一个模块去使用和管理。
- 如果网络环境不是很好,使用豆瓣的
pip install -i https://pypi.doubanio.com/simple/ 包名
pip install -i xxx -U 包名==版本号 (这个是指定该包模块的版本号安装,如果已有,会先卸载再安)- 人生苦短,我用Python,虽然我确实喜欢C++
安装python3的环境
建议安装python3.6左右的版本,不建议安装python3.7以上的,因为在使用PyInstaller打包时候,会因为版本兼容问题,打包失败,而且一时半会儿不好排查。
在安装时,会提示类似是否加入到系统环境变量中,如果你要求方便,建议勾选,否则你必须找到安装的Python路径,在那里执行python,以及Scripts文件夹里执行pip。
安装完成后查看Python版本,以及pip版本
这样就代表安装成功,最基本的环境搭建完毕。
依赖的包安装
当然,因为项目很直接,就没有进行包管理的考虑,直接pip安装了依赖的包:
使用 pip list 查看已安装包模块的版本信息
黄色加深的部分为这次要用到的包,其它可以忽略
PyQt5:即直接使用pip安装,它会附带安装PyQt5-sip(PyQt的工具在里面)
PyInstaller:打包工具,https://blog.csdn.net/jirryzhang/article/details/78881512 这里有用法介绍
(pywin32可以注意下,如果PyInstaller没有帮您默认安装,最好手动安一个,这个是用来打包的组件,会影响打包成败)
先看我的源代码层的目录结构:
(其实不多,有效代码的文件就三个,以下讲解)
(以上是本人习惯的命名,我会逐个讲解意思,您按自己的风格命名即可,包括后面的代码命名风格^-^)
*.png 通过images.qrc管理;
当要与python发生联系时, 使用pyrcc5生成Images.py模块;
为了工程管理,将Images.py移动到上一级
以下是start.cmd脚本,实现以上流程:
@echo off
cd /d %~dp0
set DIR=%~dp0
echo %DIR%
pyrcc5 -o .\Images.py .\images.qrc
move /Y %DIR%*.py %DIR%..\
所以,每当我的images.qrc有更新时,我便执行start.cmd,以更新Images. py
ui2py.bat:调用pyuic5,将一个.ui生成对应的 . py
start.cmd:批处理*.ui生成对应.py并将*.py移动到上一级,实际是start.cmd调用了ui2py.bat多次
(这儿我的命名习惯是
Login.ui --> Login. py
MainWindow.ui --> MainWindow. py)
所以当我的.ui有更新时,我就执行下start.cmd以更新对应的所有由ui生成的py模块。
ui2py.bat脚本如下,关于批处理,可以任意发挥了:
@echo off
@cd /d "%~dp0"
pyuic5 %1 > %~n1.py
main. py顾名思义,这是我们程序的入口,既然要结合python,我们的入口这样写:
实现简单的登录成功、跳转逻辑,联动了两个界面。
from sys import argv
from PyQt5.QtWidgets import QApplication, QDialog
from LoginGUI import CLoginGUI #界面Login
from MainWindowGUI import CMainWindowGUI #界面MainWindow
def main():
app = QApplication(argv)
loginGui = CLoginGUI()
suc = loginGui.exec_()
if suc == QDialog.Accepted:
mainwindow = CMainWindowGUI()
mainwindow.show()
app.exec_()
if __name__ == '__main__':
main()
# print(__name__)
接下来仔细讲解下LoginGUI. py如何关联ui:
from PyQt5.QtWidgets import QDialog
from PyQt5.Qt import QIcon
import Images
import Login
class CLoginGUI(QDialog, Login.Ui_Form):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.setupUi(self)
# 加入Qt资源
self.setWindowTitle("IIS用户登录")
self.setWindowIcon(QIcon('://Login.png'))
# Qt信号与槽机制
self.pushButton_Login.clicked.connect(self.OnLoginBtnClicked)
def OnLoginBtnClicked(self):
self.accept()
self.close()
首先是import PyQt5相关的模块
其次是引入Images资源模块
引入Login界面模块,关联Login.ui
(其实类似于,继承了QWidget或QDialog等的.cpp文件里的类,关联.ui文件)
(当双击main. py或者在vs code下执行main.py时,会首先弹出如下登录界面,代表环境OK,Python脚本起该界面的所到之处OK)
以上,我们就关联好了界面模块,资源模块,与我们要操作的界面类,其继承了QDialog,并且使用了信号与槽机制,实现了简单的交互。
点击登录按钮,触发槽函数,登录成功后,跳转到MainWindow界面,以下是MainWindowGUI. py(多余的操作去掉了,为了方面理解,简洁),相关的关联逻辑与方式同LoginGUI .py:
from PyQt5.QtWidgets import QWidget, QDialog
from PyQt5.Qt import QIcon
import MainWindow
class CMainWindowGUI(QWidget, MainWindow.Ui_Form):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setupUi(self)
self.setWindowTitle("涉密载体管理一体化综合信息系统");
self.setWindowIcon(QIcon('://Main.png'))
诚然,更多的界面和更复杂的开发,也是在这个基础上的延伸了。
这儿是我坑遇到最多的地方,还是老生常谈:环境问题、兼容问题。可谓是不跳不知道,一跳必熬夜。事实上开发时刻轻飘飘,打包发布弄死人。
我写了一个Install.cmd调用 PyInstaller -w main.py,生成了exe
生成了build和dist文件夹,注意,有一个main.spec文件,这个就是剖析PyInstaller打包原理的入口了。build只是PyInstaller在打包时的中间文件,生成的exe或者exe+依赖库在dist下面。
找到exe双击启动即可,如果不行,那就开始分析之路吧。
如果您根据我上面的提示一步步安下来,打包还是会很顺利的,但如果您引入了许多包,失败的话,要一个个排查了,或者看PyInstaller生成的日志,在build下面warn-main.txt,定位问题。
这儿给一份定位问题的索引:
是否包模块没有找到,试试 -p 包模块路径 手动加入呢?
是否python脚本本身bug,先调试启动试试呢?
可以在打包时,试试-c,将控制台打开,或者将生成的exe拖入控制台执行,看看具体报的什么错;
能否定位到某个模块的问题,降个版本试试呢,或者先屏蔽掉吧?
都不行,看看.spec文件能否获得一些线索。
平台合理吗,64bit python下打包的放在win32的系统下可不行哦。
这儿有一个要注意的地方,怎样获取自己的一些配置文件路径呢,很简单,知道exe路径,不就可以知道配置文件相对路径了吗?是的,那么怎么获取exe路径呢?特别是打包后。
sys.path[0]
在调试启动,或双击main.py启动时,没问题,它会获取main.py的路径,仔细了解了sys模块的path的相关介绍,确实就是[0],但在打包为exe后,它的路径就找到Windows下AppData的…之类路径里去了,像是python的默认文件指定目录,导致我一直初始化失败。
import sys
import os
print(sys.path[0])
print(sys.argv[0])
print(os.path.dirname(os.path.realpath(sys.executable)))
print(os.path.dirname(os.path.realpath(sys.argv[0])))
后来试了其它方法,便解决了,os.path.dirname(os.path.realpath(sys.argv[0])) 这个最靠谱,其他的大家有兴趣可以打包为exe后,打印出路径看效果。
以上是简单的Python + Qt开发的环境搭建,更多深入的探索还需再今后的实践中持续学习,希望大家一起交流。
如果要测试环境,可以拷我的这个demo,就是以上讲解的这个小例子。
百度网盘: https://pan.baidu.com/s/182H03HOB5JVUNGz1P_Lkrw