How to build an mac os app from a python script having a PySide2 GUI?
Asked Answered
E

6

18

Context:

I am developping a simple Python application using a PySide2 GUI. It currently works fine in Windows, Linux and Mac. On Windows, I could use PyInstaller and InnoSetup to build a simple installer. Then I tried to do the same thing on Mac. It soon broke, because the system refused to start the command or the app generated by PyInstaller because it was not correctly signed. And as I am not an apple developper, I cannot sign anything...

After some research, I tried py2app. I can go one step further here. With

python setup.py py2app -A

I can create a runnable app. Which obviously cannot be ported to a different system because it uses my development folders. And if I use python setup.py py2app the generated program cannot start because py2app did not copy all the required Qt stuff. I tried to add one by one the missing libraries, but on the end the system could not find the plugins and I gave up...

Question:

Can someone help me with a recipe to convert a python script or package using a Qt GUI into a portable app on Mac? Ideally, the recipe should say how to use a custom application icon, but this is not required.

References:

  • Python 3.8.5
  • macOS 10.15.7 Catalina
  • PySide2 5.15.1
  • PyInstaller 4.0
  • py2app 0.22

As my real package is too large for a SO question I trimmed it down to a minimal reproducible example:

from PySide3.QtWidgets import *
import sys


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        hello = QLabel('Hello', self)
        hello.move(50, 50)

def run(args):
    app = QApplication(args)
    main = MainWindow()
    main.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    run(sys.argv)

And here is the setup.py file used for py2app:

from setuptools import setup

APP = ['app.py']
DATA_FILES = []
OPTIONS = {}

setup(
    app=APP,
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)
Esmaria answered 6/10, 2020 at 14:24 Comment(6)
there is a typo in setup.py that prevents it from runningShaina
you may look into this medium.com/python-pandemonium/…Nonmetallic
also take a look into learnpyqt.com/pyside2-bookNonmetallic
@anon01: My bad. I generally copy and paste code to avoid typos but here I could not.Esmaria
learnpyqt.com/courses/packaging-and-distribution/… this is going to helpMaegan
@Divyessh: I already successfully use PyInstaller on Windows. My problem is only on macos.Esmaria
B
13

Requirements

  • works with Python 3.8.5
  • macOS 10.15.7 Catalina
  • uses PySide2 and py2app

Problems

  • PySide2 must be added under OPTIONS to the packages list
  • when running the app then still an error occurs: Library not loaded: @rpath/libshiboken2.abi3.5.15.dylib, Reason: image not found

Solution

The slightly modified setup.py could look like this:

from setuptools import setup

APP = ['app.py']
DATA_FILES = []
OPTIONS = {
    'packages': ['PySide2'],
    'iconfile': 'some_icon.icns',
    'plist': {
        'CFBundleDevelopmentRegion': 'English',
        'CFBundleIdentifier': "com.ballesta.xxx",
        'CFBundleVersion': "1.0.0",
        'NSHumanReadableCopyright': u"Copyright © 2020, Serge Ballesta, All Rights Reserved"
    }
}

setup(
    app=APP,
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)

Additionally, an icon definition and a few plist entries for some basic information have been added.

The whole build is best triggered with a script that could look like this:

#!/bin/sh
python3 -m venv venv
. venv/bin/activate
pip install PySide2
pip install py2app
python setup.py py2app      
cp ./venv/lib/python3.8/site-packages/shiboken2/libshiboken2.abi3.5.15.dylib ./dist/app.app/Contents/Resources/lib/python3.8/lib-dynload/shiboken2

Test

Here the screenshot of a test run:

screenshot

Betulaceous answered 15/10, 2020 at 22:58 Comment(3)
A detailed answer with a full working example, and even a demo. Thank you!Esmaria
Thanks, this was the first set of instructions that actually worked for building an app bundle for a pyside2 application! Do you know if there is any reason why this 'libshiboken2' thing is required but not added to the bundle automatically?Becket
@RalfEbert I don't have any further information, but would assume it's probably just a bug.Betulaceous
H
2

I think what you're missing is the inclusion of the Python3 Framework in your application bundle. I've developed a simple macOS app myself recently, however I wanted to have a little more insight on how to do so, so I did a bit of digging into the actual structure of an application. Basically, you are going to put everything into a normal folder with the name of your application. Call this folder MyApp. Inside this folder we'll have another called Contents. From my understanding, py2app just takes all of the things that make up your app, and structures them inside of this folder as well as creates an Info.plist file, which also goes inside of Contents. So far, here is what you have:

MyApp

-> Contents

-> -> Info.plist

In addition to the Info.plist file with all of the necessary properties, in your Contents folder you will have a MacOS folder and a Resources folder at minimum. Your issue is that you also need a Frameworks folder, where you would add the required version of Python.

Now, your app hierarchy should look like:

MyApp

-> Contents

-> -> Info.plist

-> -> MacOS

-> -> Resources

-> -> Frameworks

In the Frameworks folder, you can put the full Python 3 framework you're working with to build the app, as well as any site-packages that you require to run the application, and then you can reflect all of those changes in the executable so that you are pointing to the correct installations.

To my understanding, all that's necessary to make the application functional on MacOS is to ensure that your main executable is placed in the MacOS folder and points to the Python located in your Frameworks folder, your icon .icns file is placed in the Resources folder, and your Info.plist file is built.

In order for MacOS to recognize it as a full application, I believe you possibly need to use productbuild and include a Developer license certificate, but it's really only necessary if you want the application to be distributed. Otherwise, I just added the extension .app to MyApp, which converts it into an application.

Without the above-mentioned license/certificate whatever, it probably won't recognize that it should find your icon file and add it, so if you open it in Preview, select-all, and copy it, you should be able to right-click on the application, press 'Get Info', and paste the icon on top of the current icon in the window to make it display correctly.

EDIT: My resources for learning about making macOS apps:

Bundle structure

Including frameworks

Signing your application

productbuild manpage

Hyperemia answered 15/10, 2020 at 5:26 Comment(2)
Thank you for your answer. It helps to understand what macos applications are under the hood. The point on framework inclusion is probably a possible way to build the app by hand. I also gives a hint that a native/C application linked with python to call my own PySide2 code should work. I'll give it a try if I cannot have py2app to do the job (what it does if I do not use PySide2...)Esmaria
@SergeBallesta PySide2 is just the GUI package. Even though I built my application with Tkinter, the same principles still stand. What you could do to make life a little easier than locating the package on your computer is to transfer a version of Python into the Frameworks folder (a copy of whichever version you used to make your program outside of the application), and then call that Python's pip installer to download PySide and Qt. I believe this will make sure that they are installed inside of your application. Just use path/to/app/python3 -m pip install PySide2 PyQt5Hyperemia
C
2

If you want to package for OSX, you should either

Create a Brew Tap

This probably makes the most sense for an open source developer

General Instructions https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap

  • host your code in git (does not need to be GitHub)
  • create a Formula (Formula Cookbook)
  • fork homebrew-core on GitHub
  • add your Formula and create a pull request to get it into the main repo
  • support your pull request such that it is completed

Join the Apple Developer Program

This probably makes the most sense for a closed source developer

Overview: https://developer.apple.com/programs/how-it-works/

This program costs 99USD annually, but will allow you to sign your package/final binary and distribute it yourself or on their App Store

After creating your account, here's a guide for packaging and signing for OSX https://developer.apple.com/forums/thread/128166

  • structure your code to support signing (add a build step to copy your work into a clean path to avoid frustrating rework)
  • % codesign -s <Developer ID Application signing identity> /path/to/code
  • pick a storage format (.zip, .dmg, .pkg) and bundle your application as it
Chaing answered 15/10, 2020 at 16:32 Comment(2)
Thank you for the generic information on how to build apps for macOS. The brew part could probably help. But my current problem is focused on Qt for Python (PySide2) applications, what your answer does not really address.Esmaria
@SergeBallesta 'hopefully helpful to a later user then! It seems your Qt part works great through the installer, but you're fighting against signing it yourself or skipping an installer and moving the dependency management upstream.Chaing
B
1

After fiddling around a lot with the different options to build a macOS bundle for a PySide2 application I found the following steps to work pretty much out of the box.

This recipe using pyinstaller to create a macOS app bundle was tested with Python 3.9.1, PySide2 5.15.2, pyinstaller 4.2 on macOS Catalina 10.15.7.

  • Install pyenv and latest Python with Framework support (see Pyenv, How to manage multiple Python versions and virtual environments for a general introduction into how pyenv works):

    brew install pyenv
    PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.9.1
    
  • Create a folder for the example, create an example pyside application:

    mkdir hello && cd hello
    nano hello.py
    

    hello.py:

    import sys
    from PySide2.QtCore import *
    from PySide2.QtGui import *
    from PySide2.QtWidgets import *
    
    class MainWindow(QMainWindow):
    
        def __init__(self):
            QMainWindow.__init__(self)
            self.setWindowTitle("Hello World")
    
            self.button = QPushButton("Click me")
            self.button.clicked.connect(self.say_hello)
    
            self.setCentralWidget(self.button)
    
        @Slot()
        def say_hello(self, url):
            self.button.setText("Hello!")
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
    
        window = MainWindow()
        window.show()
    
        sys.exit(app.exec_())
    
  • Create a venv for the project, install python packages:

    pyenv local 3.9.1
    python3 -m venv venv
    source venv/bin/activate
    pip install pip pyside2 pyinstaller --upgrade
    
  • Try running the app using the python interpreter:

    python3 hello.py
    
  • Create a macos app bundle:

    pyinstaller --windowed hello.py
    
  • Run the app using the app bundle:

    open dist/hello.app
    
  • Check out the generated hello.spec for the build configuration, re-build the app with pyinstaller hello.spec.

Becket answered 23/1, 2021 at 17:55 Comment(0)
Y
0

I think the solution to your problem can be found here. First, create a virtual environment and install all modules to the same virtual environment.

mkvirtualenv --python="PATH/TO/PYTHON3.6.5/python.exe" venv

Install Qt framework

# Install qt via homebrew
brew install qt
# Switch to version 5.11.1
brew switch qt 5.11.1
# Do this to link qmake with qt
brew link qt5 --force

Then, install the PySide2

git clone --recursive https://code.qt.io/pyside/pyside-setup

After that,

 cd pyside-setup && git checkout 5.11

Build, and install PySide2 and make sure to set the path of QMAKE that comes with the Qt installation

#To get the path
 which qmake
# Make sure that your virtual environment is activated
# Build PySide2 from source
python setup.py install --qmake=<PATH/TO/QMAKE> --build-tests --ignore-git --jobs=8
# Install PySide2
python setup.py build --qmake=/path/to/qmake --build-tests --ignore-git --jobs=8

Update

First, make sure that you run your code in the same virtual environment, and to convert it to a standard mac OS app you can use py2app or pyinstaller . Also, try to downgrade your py2app if it is not working with your current version after you follow the same process.

sudo easy_install py2app
#or
pip install -U git+https://github.com/metachris/py2app.git@master

Create setup.py

py2applet --make-setup app.py
Wrote setup.py

and you have to create a config file or see this example and include any file that you have

from setuptools import setup

APP = ['app.py']
DATA_FILES = []
OPTIONS = {
    'argv_emulation': True,
    'iconfile': 'app.icns'
}

setup(
    app=APP,
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)

To build the application use

python setup.py py2app -A

To run the app you have to use this way

./dist/app.app/Contents/MacOS/app

If it is work with python setup.py py2app -A that means that everything is going ok and you need to use

rm -rf build dist
python setup.py py2app

If any things go wrongs please refer to these references1, 2, and 3. Also, there are alternatives ways to convert your app to os.

After I read your comment, I tried to see what is the problem and I found this and it may solve the problem or you can use alternatives tools such as bbFreeze, pyInstaller or cx_Freeze

Yet answered 15/10, 2020 at 6:0 Comment(8)
Thank you for your answer, but it is about making PySide2 available on macos. This part works and if I use python3 app.py it runs fine. My problem is that I would like to convert it to a standard macos app, the same I could build an installable stand alone program for Windows with PyInstaller and InnoSetup.Esmaria
@SergeBallesta I already updated the answer and make sure that you run your code in the same virtual environment.Yet
I can already use python setup.py py2app -A. The problem arises when I remove the -A to try to build a standalone application.Esmaria
Did you use the same environment? if so, try to use the alternatives tools which can be found in the answers such as pyInstaller or cx_Freeze. see docs.python-guide.org/shipping/freezingYet
What I would like is an example of working environment with all involved versions, and if possible a tested example on macOS Catalina.Esmaria
See this tutorial youtube.com/watch?v=DVOoHL2Bp_o&ab_channel=SamanthaCruzYet
Let us continue this discussion in chat.Yet
In the example that I mentioned to "see this example and include any file that you have", I found that you should include the external library which is can be like this OPTIONS = {'argv_emulation': True, 'includes': ['sip', 'PyQt4', 'PyQt4.QtCore', 'PyQt4.QtGui', 'simplejson'], 'excludes': ['PyQt4.QtDesigner', 'PyQt4.QtNetwork', 'PyQt4.QtOpenGL', 'PyQt4.QtScript', 'PyQt4.QtSql', 'PyQt4.QtTest', 'PyQt4.QtWebKit', 'PyQt4.QtXml', 'PyQt4.phonon']} This will solve the problem and if not, try it on another device. rk.edu.pl/en/building-mac-os-x-applications-py2appYet
R
0

I have successfully build apps using fbs. It is intended to build Python+PyQt5 apps (you can also use PySide2) and it should work on Mac OS as well (according to the documentation/tutorial).

Using PyInstaller it failed very often for including PyQt5 dependencies (especially when I was working with pyqtgraph), but with fbs it works great.

Remediosremedy answered 15/10, 2020 at 10:13 Comment(1)
Can you confirm that you were able to successfully run the linked tutorial on macos Catalina? On my system, the command fbs run correctly displays a nice window, but fbs freeze breaks with 7136 ERROR: Can not find path ./libshiboken2.abi...Esmaria

© 2022 - 2024 — McMap. All rights reserved.