Launching PyQt apps in macOS at login

Heads up! You've already completed this tutorial.

josh | 2021-01-03 02:14:11 UTC | #1

I wish to release a PyQt app using fbs for macOS. The app will be a utility that sits in the menubar and needs to launch on login across all user accounts.

I have found some tutorials and packages online for doing this with Swift/Objective-C, but there is not much guidance on how to do this with Python (PyQt/fbs).

Can anyone shed some light on this or point me to a resource that could be helpful?

Alternatively, I could create a Swift app that executes the PyQt app on login, but I want to avoid this if possible.


martin | 2021-01-10 02:58:21 UTC | #2

Hi @josh welcome to the forum!

I've never done this before, but it should be possible from Python. When trying to do something like this it's often a good idea to see how you would go about doing it from the command line.

If you search for adding a startup application from the Terminal you can find this answer on Stack Exchange. That describes launch daemons (start at boot) and launch agents (start at login) and gives instructions for the former, but it's the latter you want for user applications. Rather than add these to /System/Library they need to go in the per-user library folder, under /Users/<username>/Library/LaunchAgents.

If you have an existing .plist you can copy and edit that, or just use the following. This is a .plist which will run the Moonsweeper example app installed in the /Applications folder.

python
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>com.learnpyqt.moonsweeper</string>
        <key>LimitLoadToSessionType</key>
        <string>Aqua</string>
        <key>ProgramArguments</key>
        <array>
                <string>/Applications/Moonsweeper.app/Contents/MacOS/Moonsweeper</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>StandardErrorPath</key>
        <string>/dev/null</string>
        <key>StandardOutPath</key>
        <string>/dev/null</string>
</dict>
</plist>

Save this in the users launch agent folder, e.g. as ~/Library/LaunchAgents/com.learnpyqt.moonsweeper and it will launch the specified app at startup.

fyi -- If your application isn't signed, this might not work. For development you can disable Gatekeeper with sudo spctl --master-disable -- fine for development, but not something for your users.

How to do this from an application?

If you want to make it possible for your users to toggle "Run at startup" in the interface, the approach is identical to what's described here -- you just just need to write/delete the file from within your app. Make sure you're writing to the user's ~/Library folder, not the system one (you'd need permissions for that).

It doesn't actually matter what you use for the filename for the .plist but its a good idea to keep this consistent with your application namespace. That way you know only you would be writing the file, so can delete it if it's there without any worries.


josh | 2021-01-10 02:57:48 UTC | #3

Thanks @martin -- that was super helpful!

I did this manually and it works without needing to sign the app or disable Gatekeeper.

However, the plist file generated by fbs does not work -- not even if the RunAtLoad key is added with a value of true. So, if anyone is stuck on this, use the plist template given by @martin and adjust it to your app.


Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Launching PyQt apps in macOS at login was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.