ronaldoussoren / py2app

py2app is a Python setuptools command which will allow you to make standalone Mac OS X application bundles and plugins from Python scripts.
Other
343 stars 36 forks source link

bundled PyQt5 apps fail to start since py2app v0.26 #380

Open MAKOMO opened 2 years ago

MAKOMO commented 2 years ago

Using python 3.9.7 installed from python.org on macOS 11.6, the trivial PyQt5 GUI app

from PyQt5.QtWidgets import QApplication, QWidget

app = QApplication([])
window = QWidget()
window.show()
app.exec()

fails to run after bundling with

from setuptools import setup

OPTIONS = {
    'strip': False,
    'argv_emulation': False
}

setup(
    app = ['main.py'],
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
    script_args = ['py2app']
)

## build with:
# sudo python3 setup.py build

on py2app v0.26 and v0.26.1 installed via pip as well as using the current py2app trunk, but runs fine if build with v0.25 or older.

[Max:/Users/luther/Desktop/tmp/Test2] dist/main.app/Contents/MacOS/main 
qt.qpa.plugin: Could not load the Qt platform plugin "cocoa" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: cocoa, minimal, offscreen, webgl.

Abort trap: 6

Reason seems to be the extra Qt plugins folder on top of the Content folder that is created since py2app v0.26. After removing this one starts fine.

# sudo rm -rf dist/main.app/Contents/plugins
# dist/main.app/Contents/MacOS/main 

Is there a reason to duplicate those Qt plugins on the top level while they are already available under Resources?

Resources/lib/python3.9/PyQt5/Qt5/plugins
ronaldoussoren commented 2 years ago

Sigh. I'll have to debug. The changes I made in 0.26 were necessary to get working apps on my machine.

How was PyQt installed on your machine, through PyPI or through a system like homebrew?

MAKOMO commented 2 years ago

Sorry for the troubles. Installation was standard python.org Python installation and packages all installed via PyPI directly without any virtual environment. To me the issue seems to be those extra plugins that get installed on the toplevel of the app which seem to be preferred by Qt (default plugin location) over those installed deeper in the PyQt5 directory in a subdirectory of Resources. py2app versions before this seem not to have created that toplevel plugins directory at all.

MAKOMO commented 2 years ago

Oh, should have noted that I am using the current PyQt5 package which is 5.15.4 at the moment.

ronaldoussoren commented 2 years ago

The sigh was not directed at you.

I'll have to look at the source code to be sure, but wouldn't be surprised if this issue is caused by code that I added to deal with PyQt from homebrew where additional files have to be copied into the app because Qt isn't in the PyQt directory.

mrclary commented 2 years ago

I see the same issue, but I can narrow it down to commit c9fcb5e41da33a5ce8a60c71b438506d33ce9451. The previous commit does not produce the error, and commit d206e57f14b3a31cbd43dcdcaa745b92d5286b29 does not resolve the issue (though I don't think it was intended to resolve it).

I have PyQt5 installed via PyPi in a pyenv virtual environment (via Homebrew).

mrclary commented 2 years ago

FWIW, on my installation, PyQt5 = 5.12.3 os.path.dirname(PyQt5.__file__) = '/Users/rclary/.pyenv/versions/3.9.5/envs/spy-build-py2app/lib/python3.9/site-packages/PyQt5' QLibraryInfo.location(QLibraryInfo.LibrariesPath) = '/Users/rclary/.pyenv/versions/3.9.5/envs/spy-build-py2app/lib/python3.9/site-packages/PyQt5/Qt/lib'.

Under what condition do you expect os.path.dirname(PyQt5.__file__) and QLibraryInfo.location(QLibraryInfo.LibrariesPath) to be the same? Or different?

mrclary commented 2 years ago

Would it work to use

if os.path.dirname(PyQt5.__file__) not in qtdir:

instead of

if qtdir != os.path.dirname(PyQt5.__file__):

?

mrclary commented 2 years ago

It seems to me that if PyQt is added to the 'packages' directive, everything in the package should be included in the build because it will be outside the zip folder. Then, the only question is whether the library is located somewhere inside the package directory or somewhere else. Only if it is located somewhere outside the package directory should the plugin directory be copied to Resources.

Perhaps an even better solution would be

if not qtdir.startswith(os.path.dirname(PyQt5.__file__)):
mrclary commented 2 years ago

@ronaldoussoren, has there been any progress on this?

mharbison72 commented 1 year ago

This appears to be fixed in 0.28.5 with py3.9.9 from python.org, and either PyQt 5.15.6 or 6.4.0 from PyPI.