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!
PyQt6 Crash Course — a new tutorial in your Inbox every day
Beginner-focused crash course explaining the basics with hands-on examples.
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.
<?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!