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
- 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
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)
- 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