【问题标题】:How to deploy pyside2 applications? - The Qt way如何部署pyside2应用程序? - Qt 方式
【发布时间】:2019-08-27 22:08:44
【问题描述】:

我需要为所有主要 3 操作系统(Windows、Linux 和 MacOS)部署一个基于 Qt 5.12.1pyside2 应用程序。

我已经检查了How to make a Python script standalone executable to run without ANY dependency?,但这不是我想要的,因为我需要一个与 Qt 相关的方法,例如 windeployqtmacdeployqtlinuxdeployqt(单独的项目)。

正如 eyllanesc 所指出的:“python 是一种不会生成二进制文件的脚本语言”。但是,Qt 公司也应该考虑到这一点,让我们更容易部署 pyside2 应用程序。至少与部署 C++/QML 应用程序一样容易。

所以我想要一个像 windowsdeployqtmacdeployqtlinuxdeployqt 之类的工具...它适用于 pyside2 应用程序.

[更新] eyllanesc 推荐 fbs (fman build system) 作为起点,因为没有官方工具来部署 pyside2 应用程序。这应该作为一种解决方法。也欢迎新的答案。
请在 Qt 公司发布官方工具后尽快回答。

[注意]:我正在使用基于Qt 5.12.1

Qt Creator 4.8.1

【问题讨论】:

  • 没有。这不是我的意思。我想要适合我要求的每个平台的最佳方法。我会编辑我的问题,说明清楚。
  • 另一种选择是您创建一个 setup.py(您甚至可以在 pypi 中发布它)来分发您的应用程序,因此您可以使用 pip 在所有操作系统中安装它
  • 好吧,我在 Qt 帮助中读到了一些关于名为 windeployqt 的工具的信息,但我找不到将其应用于 pyside2 应用程序的方法。还有 macdeployqt 和一个单独的项目 linuxdeployqt。所有人都面临同样的问题。
  • 你知道,我想要(可能)一种 Qt 方式来做到这一点。如果 Qt 公司添加了对 python 的支持,他们应该添加一种部署 Qt Python 应用程序的方法,还是不添加?
  • 这是 Qt 的 C++ 版本,C++ 生成一个需要其他二进制文件(依赖项:.dll、.lib、.so 等)的二进制文件,而 xxxdeployqt 只查找那些操作系统中的二进制文件。另一方面python是一种脚本语言,不会生成二进制文件(可以用pyinstaller等工具生成二进制文件)

标签: python qt qt5 pyside2


【解决方案1】:

目前没有部署 PySide2 应用程序的 Qt 方法(我认为至少在不久的将来不会有)

关于这个主题有以下报道:PYSIDE-901PYSIDE-913,其中指出可能对于 Qt for Python 5.13 文档将进行更新,并且将有一个部署部分。你可以看到进度here

Deployment

其中有 3 个部署选项:

项目的选项是:

  1. 发送包含应用程序内容的普通 zip 文件。
  2. 构建合适的 Python 包(wheel):https://packaging.python.org
  3. 将应用程序冻结在单个二进制文件或目录中。

在第三种方法中,他们对PyInstallercx_Freezepy2exepy2app 等工具的优劣发表了评论,最后表明最适合他们的选择是 cx_Freeze 或 Pyinstaller。还有另一个有趣的工具是fbs 项目(基于 Pyinstaller)。

在我个人看来,我会选择 fbs,因为它提供了一种基于 PyQt5 或 PySide2 打包项目的简单方法

【讨论】:

  • 多么棒的答案!谢谢!我对这两个 Qt-bugs 都投了赞成票并开始关注它们。希望他们在下一个(也是最后一个)Qt5 LTS 版本之前发布一个工具。
  • fbs 由于某种原因仍然不支持 python 3.7。
【解决方案2】:

将所有必要的依赖项打包到一个文件夹中并分发。

简单的想法,但问题是找到最小集合。 qt 站点上提出的打包程序创建了复杂的文件结构和他们自己的 EXE,这对于小脚本案例(恕我直言)来说太多了。 幸运的是,有一些方法可以记录依赖关系:Windows 上的 Process Monitor 和 Linux 上的 strace。它们列出了受监控程序的所有系统调用。我写了一个小 Python 脚本来从此类日志中挑选依赖项。

在此处显示的虚拟环境中执行此操作很方便,或者带有预安装基本模块的 python 嵌入式分发:

    > python -m venv ./MyVEnv
    > cd ./MyVEnv
    > ./.../python -m pip install pyside2
  1. 启动系统调用监视器(即使 10 秒的日志也很大,所以不要让它运行太久)并启动使用 Qt 加载所有依赖项的脚本(使其执行所有可能的操作以打开所有必要的依赖项)和 关闭它(解锁锁定的);使用 cpython 的 -B 标志可能有助于避免在最终包中缓存文件;您也可以在监控程序中手动过滤掉不必要的系统调用以减少日志

在 Windows 上:

    > Procmon /AcceptEula /NoFilter /BackingFile log1
    > .\MyVEnv\...\python -B yourScript.py
    > Procmon.exe /OpenLog log1.PML /SaveAs logFile.csv

在 Linux 上:

    > 2>logFile strace ./bin/python3 -B yourScript.py
  1. 之后启动我的脚本,将所有记录的依赖项复制到一个文件夹中,保留原始文件结构:

     > python .\depspicker.py
    

这是:

    #depspicker.py
    #changes from Windows to Linux version commented
    
    logF = r".\logFile.CSV" #Linux: ./logFile
    basePath = r".\...\site-packages" #base of the file-tree to be copied (where the needed dependencies originally reside)
    destPath = r".\site-packages" #destination of copy
    
    
    import csv, shutil
    from pathlib import Path
    
    logF = Path(logFile)
    basePath = Path(basePath).resolve()
    destPath = Path(destPath).resolve()
    
    with open(logF, newline='', encoding="utf-8") as log:
        checked = set()
        reader = csv.DictReader(log) # Linux: -
         for row in reader: #Linux: for row in log:
            try:
                src = Path(row["Path"]) # Linux: src = Path(row.split('"')[1])
                src = src.resolve()
                if src in checked or not (src.is_file() and\
                    basePath.parts == src.parts[:len(basePath.parts)]):
                    continue
            except (OSError, IndexError): #not a file
                continue
            finally:
                checked.add(src)
            dst = destPath / src.relative_to(basePath)
            dst.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(src, dst.parent)
  1. 现在您可以将“basePath”中的所有内容替换为从“destPath”到可用空间的重复文件树;要分发您的程序,您可以嵌入 python,将新的依赖项放在那里,并以类似的方式进一步删除所有不必要的文件 - 或者从一开始就使用嵌入式而不是 venv

【讨论】:

  • 很好的答案@alexey。问题:Linux 方法在 macOS 上也有效?
  • @lateo96 谢谢!虽然我现在不使用 mac,但 looks 就像他们有 DTraceopensnoop 示例是我首先要尝试的。然后将其输出格式合并到我的脚本中(在其中查找文件路径)。某事喜欢src = row.split()[-1]
  • @lateo96 如果您向我们提供有关此事的结果以帮助改进方法,那就太好了!
猜你喜欢
  • 1970-01-01
  • 2021-10-03
  • 1970-01-01
  • 1970-01-01
  • 2011-09-07
  • 1970-01-01
  • 2010-10-11
  • 2013-07-22
相关资源
最近更新 更多