7

I need to deploy a pyside2 application based on Qt 5.12.1 for all major 3 Operative Systems (Windows, Linux and MacOS).

I already checked on How to make a Python script standalone executable to run without ANY dependency? but that is not what I want because I need a Qt-related approach like windeployqt, macdeployqt, linuxdeployqt (separate project).

As pointed by eyllanesc: "python is a scripting language that does not generate a binary". However, the The Qt Company should figure that too and make easier for us to deploy pyside2 applications. At least as easier as deploying C++/QML applications.

So I want a tool like windowsdeployqt, macdeployqt, linuxdeployqt... That works with pyside2 applications.

[UPDATE] eyllanesc recommended fbs (fman build system) as a start point as there is not an official tool to deploy pyside2 applications. That should work as a workaround. New answers are welcome too.
Please answer as soon as possible when The Qt Company releases an official tool.

[NOTE]: I'm using Qt Creator 4.8.1 based on Qt 5.12.1

lateus
  • 371
  • 4
  • 15
  • No. That's not what I mean. I want the best approach for each platform that fits my requirements. I will edit my question make it clear. – lateus Apr 06 '19 at 01:12
  • Another option is that you create a setup.py (you can even publish it in pypi) to distribute your application, and so you can install it in all the OS using pip – eyllanesc Apr 06 '19 at 01:16
  • Well, I have read in Qt Help something about a tool named **windeployqt** but I can'f find a way to apply that to a pyside2 application. There's also **macdeployqt** and in a separate project **linuxdeployqt**. The same problem for all of them. – lateus Apr 06 '19 at 01:17
  • You know, I want (is possible) a Qt way to do this. If The Qt Company added support for python, they should add a way to deploy Qt Python applications, or no? – lateus Apr 06 '19 at 01:19
  • Is that is the C ++ version of Qt, C ++ generates a binary that requires other binaries (dependencies: .dll, .lib, .so, etc) and the xxxdeployqt only look for those binaries in the OS. On the other hand python is a scripting language that does not generate a binary (a binary can be generated with tools such as pyinstaller) – eyllanesc Apr 06 '19 at 01:20
  • So while I agree that using a script is a solution, I disagree about downvoting my question, because the focus of my question is no 100% python-related. – lateus Apr 06 '19 at 01:21
  • I remember that there is a report where "Python for Qt" is thinking of a tool to deploy its applications, unfortunately it does not currently exist. – eyllanesc Apr 06 '19 at 01:21
  • *On the other hand python is a scripting language that does not generate a binary*. Yes, I know, but The Qt Company should figure that too and make easier for us to deploy pyside2 applications. At least as easier as deploying C++/QML applications. – lateus Apr 06 '19 at 01:23
  • PyQt5 for example I implement the tool [pyqtdeploy](https://www.riverbankcomputing.com/software/pyqtdeploy/intro) only as a comment. – eyllanesc Apr 06 '19 at 01:23
  • *unfortunately it does not currently exist*... that's sad. But thanks, that answer my question. I will edit it now. – lateus Apr 06 '19 at 01:25
  • Well "Qt for python" (PySide2) is a recent project (it has a lot of bugs for example) and currently it does not have it unfortunately, in the future it will surely provide. In addition to that project is missing many things (are raised but not developed, I recommend you check the reports of bugs to understand me: https://bugreports.qt.io/projects/PYSIDE/issues) – eyllanesc Apr 06 '19 at 01:25
  • *PyQt5 for example I implement the tool pyqtdeploy* Wow! That's what I want, but applied to pyside2, of course. Thanks. – lateus Apr 06 '19 at 01:26
  • There is an interesting tool that I think will help you: https://github.com/mherrmann/fbs, it supports pyqt5 and pyside2 (but it also has bugs but at least it is a starting point) – eyllanesc Apr 06 '19 at 01:27
  • Well, that's appears to work for me. I will edit my question and then check it out. Thanks. – lateus Apr 06 '19 at 01:28
  • So as a recommendation you should use fbs, the people of Qt are just giving a little love to PySide2 :-), at least that's a hope. – eyllanesc Apr 06 '19 at 01:29
  • Done. I updated my question to primarily focus on the *Qt way*. Better no? – lateus Apr 06 '19 at 01:40
  • I will post an answer ordering the information and with documentation of what I have explained to you – eyllanesc Apr 06 '19 at 01:42

2 Answers2

12

There is currently no Qt way to deploy PySide2 applications (and I do not think there will be any at least in the near future)

On this subject there are the following reports: PYSIDE-901, PYSIDE-913, in it this points out that possibly for Qt for Python 5.13 the documentation will be updated and there will be a section for the deployment. You can see the progress here.

See Deployment

In it 3 options to do deployment:

The options for a project are:

  1. Sending a normal zip-file with the application's content.
  2. Building a proper Python package(wheel): https://packaging.python.org
  3. Freezing the application in a single binary file, or into a directory.

And of the third method they comment on the pro and against of the tools like PyInstaller, cx_Freeze, py2exe and py2app indicating in the end that the best options for them is cx_Freeze or Pyinstaller. There is also another interesting tool that is the fbs project(based on Pyinstaller).

In my personal opinion I would choose fbs because it offers a simple way to package projects based on PyQt5 or PySide2

zeFree
  • 2,129
  • 2
  • 31
  • 39
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • What a great answer! Thanks! I voted up for both Qt-bugs and start watching them. Hope they release a tool before the next (and also last) Qt5 LTS release. – lateus Apr 06 '19 at 03:06
  • 3
    fbs still doesn't support python 3.7 for some reason. – Alex P. Jun 15 '19 at 17:15
1

Pack all necessary dependencies in a folder and distribute that.

Simple idea but the problem is finding the minimal set. The packagers proposed on qt site create complicated file structure and their own EXEs, which is too much for a little-script case (IMHO). Luckily, there are ways to log dependencies: Process Monitor on Windows and strace on Linux. They list all system calls of a monitored program. I wrote a small python script to pick the dependencies from such log.

It's convenient to do this in a virtual environment as shown here, or python embedded distribution with pre-installed essential modules:

    > python -m venv ./MyVEnv
    > cd ./MyVEnv
    > ./.../python -m pip install pyside2
  1. start syscall monitor (even 10sec log is huge so don't leave it running for long) and start your script that uses Qt to load all dependencies (make it do all possible actions to open all necessary dependencies) and close it (to unlock locked ones); may be useful to use cpython's -B flag to avoid having cached files in your final pack; also you could manually filter out needless syscalls in the monitor program to reduce logs

on Windows:

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

on Linux:

    > 2>logFile strace ./bin/python3 -B yourScript.py
  1. after that launch my script to duplicate all logged dependencies into a folder, preserving original file structure:

     > python .\depspicker.py
    

which is:

    #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. now you can replace everything inside the 'basePath' with duplicated file tree from 'destPath' to free space; to distribute your program you could take python embedded, put the new dependencies there, and also further remove all unnecessary files similar way - or just use embedded instead of venv from the start
Community
  • 1
  • 1
alexey
  • 401
  • 4
  • 7
  • Great answer @alexey. Question: the Linux approach is also valid on macOS? – lateus Dec 29 '19 at 09:46
  • @lateo96 Thanks! Though I'm not using mac nowadays, it [looks](http://dtrace.org/blogs/brendan/2011/10/10/top-10-dtrace-scripts-for-mac-os-x/) like they have **DTrace**. The _opensnoop_ example there is what I'd try first. Then incorporate its output format into my script (find file paths in it). sth like `src = row.split()[-1]` – alexey Dec 29 '19 at 18:32
  • @lateo96 would be great if you provided us with your results on the matter to help improve the method ! – alexey Dec 29 '19 at 19:25