tukaan / Idared

Utilities and extensions for Tukaan on macOS
MIT License
2 stars 0 forks source link

How do I use the functions defined here? #2

Open Moosems opened 1 year ago

rdbende commented 1 year ago

@Moosems I couldn't figure out too much with the menus but I added a Quicklook feature :D

image

Moosems commented 1 year ago

That, sir, is awesome!

Moosems commented 1 year ago

@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:

  1. Install the pyobjc library if you haven't already:
pip install pyobjc
  1. Create a new Python file, for example, menu_bar_app.py, and import the required libraries:
import AppKit
from AppKit import NSApplication, NSApplicationPasteboard, NSApplicationWorkspace, NSMainNibBundle, NSMenu, NSMenuItem, NSWindowController
  1. Define a class that inherits from 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))
  1. Define the actions for the menu items:
    def open_app(self, sender):
        print("Open app")

    def quit_app(self, sender):
        self.app.terminate_(sender)
        self.app.hide_(sender)
  1. Instantiate the NSApplication object and set the main nib:
    NSApp = NSApplication.sharedApplication()
    NSApp.setActivationPolicy_(NSApplication.activationPolicyRegular)

    NSApp.setMainNib_(NSMainNibBundle.instance())
    NSApp.run()
  1. Finally, create a 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.

rdbende commented 1 year ago

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...

Moosems commented 1 year ago

Fair enough, I'll probably implement it in my own app at some point lol.

rdbende commented 1 year ago

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.

Moosems commented 1 year ago

That is definitely a concern of mine but theoretically it could work like this:

  1. An NSApplication can be created with app = NSApplication.sharedApplication() and a blank menu quickly set.
  2. The Tk instance gets created and overrides the menu
  3. The NSApplication gets deleted and leaves just the Tk instance running

I wish there was some better way to do this to be honest, as I am uncannily disturbed by the delay with the default Menu.

rdbende commented 1 year ago

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?

Moosems commented 1 year ago

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?

Moosems commented 5 months ago

@rdbende, is there a way to bind to NSApplication and the NS system from C? M

rdbende commented 5 months ago

PyObjC?