Open Moosems opened 2 years ago
That, sir, is awesome!
@rdbende, I was messing with phind and asked it for its thoughts on this topic, it gave the following responses:
How can I use an NSApplication from AppKit in Python and make it work side by side with tkinter?
Using AppKit in Python and making it work side by side with Tkinter can be achieved by running them in separate threads. This will allow both frameworks to run concurrently without blocking each other. However, it is important to note that using Tkinter on macOS has some known issues, as mentioned in Source 14. It is recommended to use an updated third-party Tcl/Tk 8.5 instead of the Apple-supplied Tcl/Tk.
Here's an example of how to run AppKit and Tkinter in separate threads:
import threading
import AppKit
import tkinter as tk
def run_appkit():
app = NSApplication.sharedApplication()
app.activateIgnoringOtherApps_(True)
# Add your AppKit code here
def run_tkinter():
root = tk.Tk()
# Add your Tkinter code here
root.mainloop()
if __name__ == "__main__":
appkit_thread = threading.Thread(target=run_appkit)
tkinter_thread = threading.Thread(target=run_tkinter)
appkit_thread.start()
tkinter_thread.start()
appkit_thread.join()
tkinter_thread.join()
In this example, we create two separate functions, run_appkit
and run_tkinter
, which contain the AppKit and Tkinter code, respectively. We then create two separate threads, appkit_thread
and tkinter_thread
, to run these functions concurrently.
However, this approach has some drawbacks:
An alternative approach would be to use a different Python GUI framework that is compatible with macOS and does not have the known issues with Tkinter. For example, you could use PyQt or Kivy, which are cross-platform Python GUI frameworks that do not depend on Tcl/Tk.
In summary, while it is possible to run AppKit and Tkinter side by side using separate threads, it might not be the most efficient or reliable solution. It is recommended to consider alternative Python GUI frameworks for macOS applications.
How can I make a app for MacOS use NSMenu to instantly get a Menubar without waiting and getting a default Menu in Python? I need to only use builtin Python libraries or use special code for MacOS.
To create a macOS app with a menu bar instantly without waiting and getting a default menu in Python, you can use the AppKit
framework, which is a macOS-specific version of the AppKit framework available for Objective-C and Swift. You can use AppKit
in Python by using the pyobjc
library. Here's a step-by-step guide to create a menu bar app using Python and AppKit
:
pyobjc
library if you haven't already:pip install pyobjc
menu_bar_app.py
, and import the required libraries:import AppKit
from AppKit import NSApplication, NSApplicationPasteboard, NSApplicationWorkspace, NSMainNibBundle, NSMenu, NSMenuItem, NSWindowController
NSWindowController
and override the windowDidLoad
method to create the menu bar:class MenuBarApp(NSWindowController):
def windowDidLoad(self):
# Create a new NSMenu and add NSMenuItem objects to it
main_menu = NSMenu.new(title="", title_display_string="Main Menu")
file_menu = NSMenu.new(title="", title_display_string="File")
window_menu = NSMenu.new(title="", title_display_string="Window")
# Add NSMenuItem objects to the NSMenu
open_item = NSMenuItem.new(title="Open", action="open_app")
quit_item = NSMenuItem.new(title="Quit", action="quit_app")
# Add submenus
main_menu.addItem_(NSMenuItem.separatorItem())
main_menu.addItem_(file_menu)
main_menu.addItem_(window_menu)
file_menu.addItem_(open_item)
file_menu.addItem_(quit_item)
# Set the application menu
self.app.setMainMenu_(main_menu)
# Set the application menu items
self.app.setAppMenu_(NSApp.servicesMenu)
# Set the application status item
self.app.setActivationPolicy_(NSApplication.activationPolicyRegular)
# Add a custom status bar item
status_bar_item = NSStatusItem.new(self.app.statusBar(), 0)
status_bar_item.setLength_(NSMakeSize(100, 22))
status_bar_item.setView_(NSView.new(NSRect.zero))
def open_app(self, sender):
print("Open app")
def quit_app(self, sender):
self.app.terminate_(sender)
self.app.hide_(sender)
NSApplication
object and set the main nib: NSApp = NSApplication.sharedApplication()
NSApp.setActivationPolicy_(NSApplication.activationPolicyRegular)
NSApp.setMainNib_(NSMainNibBundle.instance())
NSApp.run()
NSMainNibBundle
instance:NSMainNibBundle = NSObject.new(NSNibLoadingPolicy.auto)
NSMainNibBundle.initialize(NSNibLoadingPolicy.allJoined)
Now, when you run the menu_bar_app.py
script, you should see a menu bar with the "Open" and "Quit" options. When you click on the "Open" option, it will print "Open app" in the terminal.
Please note that this approach uses Objective-C code written in Python using the pyobjc
library, which may not be as efficient as writing native Objective-C code. However, it allows you to create a macOS menu bar app using Python without relying on InterfaceBuilder or storyboards.
For a more detailed example of creating a macOS menu bar app using Python and AppKit
, you can refer to Source 4.
I will give all this a try but it has some interesting ideas. If there is a pre-built python lib for this, that could make this much simpler.
Looks like I can't avoid AI anywhere, phhhh.
So honestly, I don't think it's worth it to reimplement all the menu commands with some macOS API magic, just because a hundred millisecond lag when loading...
Fair enough, I'll probably implement it in my own app at some point lol.
The second thing is that Tk uses an NsApplication under the hood, and I'm curious how the two would play together, and whether one doesn't want to overwrite the menu set by the another.
That is definitely a concern of mine but theoretically it could work like this:
NSApplication
can be created with app = NSApplication.sharedApplication()
and a blank menu quickly set.Tk
instance gets created and overrides the menuNSApplication
gets deleted and leaves just the Tk
instance runningI wish there was some better way to do this to be honest, as I am uncannily disturbed by the delay with the default Menu
.
The Tk instance gets created and overrides the menu
And the Tk instance will override it with the same thing, as it does now. Or no?
I don't know. Maybe there's a way to try and get the Tk instance to not override until I explicitly choose to and keep the NSApplication one until the new one is ready to be set?
@rdbende, is there a way to bind to NSApplication and the NS system from C? M
@Moosems I couldn't figure out too much with the menus but I added a Quicklook feature :D