使用PyQt5开发交互界面

一、前言

最近使用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++
  1. 安装python3的环境
    建议安装python3.6左右的版本,不建议安装python3.7以上的,因为在使用PyInstaller打包时候,会因为版本兼容问题,打包失败,而且一时半会儿不好排查。
    在安装时,会提示类似是否加入到系统环境变量中,如果你要求方便,建议勾选,否则你必须找到安装的Python路径,在那里执行python,以及Scripts文件夹里执行pip。
    安装完成后查看Python版本,以及pip版本
    使用PyQt5开发交互界面_第1张图片
    这样就代表安装成功,最基本的环境搭建完毕。

  2. 依赖的包安装
    当然,因为项目很直接,就没有进行包管理的考虑,直接pip安装了依赖的包:
    使用PyQt5开发交互界面_第2张图片
    使用 pip list 查看已安装包模块的版本信息
    黄色加深的部分为这次要用到的包,其它可以忽略

PyQt5:即直接使用pip安装,它会附带安装PyQt5-sip(PyQt的工具在里面)
PyInstaller:打包工具,https://blog.csdn.net/jirryzhang/article/details/78881512 这里有用法介绍
pywin32可以注意下,如果PyInstaller没有帮您默认安装,最好手动安一个,这个是用来打包的组件,会影响打包成败)

四、开发

先看我的源代码层的目录结构:
(其实不多,有效代码的文件就三个,以下讲解)
使用PyQt5开发交互界面_第3张图片
(以上是本人习惯的命名,我会逐个讲解意思,您按自己的风格命名即可,包括后面的代码命名风格^-^)

  1. __pycache__文件夹: 可以忽略,只是执行main文件时候的临时文件
  2. images文件夹:是我放置图片资源以及生成Images.py资源模块的目录,里面内容如下:
    使用PyQt5开发交互界面_第4张图片
    images.qrc文件可以用Qt工具QtCreator编辑,因为我以前安装过Qt环境,所以我直接就编辑了。
    (给不是很熟悉Qt的朋友讲一下,QtCreator是Qt安装后的一个IDE开发工具,.qrc文件也是Qt用于管理资源文件的一个特定格式文件,其实它本质还是一个xml文件,把.png等资源文件管理起来,在后期编译的时候,通过配置编译到exe里面)
    接着,我写了一个cmd脚本,使用PyQt5工具,调用images.qrc生成Images. py并移动到上一级目录:

*.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

  1. uis文件夹:是我管理.ui的文件夹,将.ui通过pyuic5 生成. py,原理跟资源管理类似。
    .ui需要使用QtDesigner进行设计,即这就是我们的界面文件了,这儿有两个ui界面
    在这里插入图片描述

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
  1. main. py
    LoginGUI. py
    MainWindowGUI. 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)
使用PyQt5开发交互界面_第5张图片
以上,我们就关联好了界面模块,资源模块,与我们要操作的界面类,其继承了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'))

诚然,更多的界面和更复杂的开发,也是在这个基础上的延伸了。

五、PyInstaller打包

这儿是我坑遇到最多的地方,还是老生常谈:环境问题、兼容问题。可谓是不跳不知道,一跳必熬夜。事实上开发时刻轻飘飘,打包发布弄死人。
我写了一个Install.cmd调用 PyInstaller -w main.py,生成了exe
使用PyQt5开发交互界面_第6张图片
生成了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路径呢?特别是打包后

  1. 最开始,我是这么调的
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

你可能感兴趣的:(我的开发故事集)