reCAPTCHA WAF Session Token

Controlling macOS with Python — SitePoint

In this quick tip, excerpted from Useful Python, Stuart looks at ways to control the Windows OS with Python.

Working on a Mac, we can control almost everything about the system using pyobjc, the Python-to-Objective-C bridge. Apple makes most of its OS controllable via the AppKit module, and pyobjc gives Python access to all of this. This will be most useful if we already know the AppKit way to do the thing we want, but with a little exploration it’s possible to make our way through the operating system APIs.

Let’s try an example. First, we’ll need pyobjc, which can be installed with pip install pyobjc. This will install a whole list of operating system API bridges, allowing access to all sorts of aspects of macOS. For now, we’ll consider AppKit, which is the tool used to build and control running apps on a Mac desktop.

We can list all the applications currently running using AppKit:

<code class="lang-python"> <a href="https://yourselfhood.com/how-to-write-efficient-python-code-a-tutorial-for-beginners/"  class="lar_link" data-linkid="2205" data-postid="2301"  title="Python"   target="_blank" >Python</a> 3.9.6 (default, Oct 18 2022, 12:41:40) 
[Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from AppKit import NSWorkspace
>>> NSWorkspace.sharedWorkspace().runningApplications() 
(
    "<NSRunningApplication: 0x60000145c000 (com.apple.loginwindow - 148) LSASN:{hi=0x0;lo=0x6006}>",
    "<NSRunningApplication: 0x60000145c080 (com.apple.backgroundtaskmanagement.agent - 475) LSASN:{hi=0x0;lo=0xb00b}>",
    "<NSRunningApplication: 0x60000145c100 (com.apple.WindowManager - 474) LSASN:{hi=0x0;lo=0xc00c}>",
    "<NSRunningApplication: 0x60000145c180 (com.apple.CoreLocationAgent - 500) LSASN:{hi=0x0;lo=0xe00e}>",
    "<NSRunningApplication: 0x60000145c980 (com.apple.Terminal - 1302) LSASN:{hi=0x0;lo=0x24024}>",
    "<NSRunningApplication: 0x60000145ca00 (com.apple.Safari - 1303) LSASN:{hi=0x0;lo=0x25025}>",
    "<NSRunningApplication: 0x60000145cb80 (com.apple.Spotlight - 1310) LSASN:{hi=0x0;lo=0x28028}>",
    "<NSRunningApplication: 0x60000145cc00 (com.apple.finder - 1306) LSASN:{hi=0x0;lo=0x29029}>",
)
>>> 
</code>

This will give a long list of NSRunningApplication objects. Each one corresponds to a specific application currently running on the desktop. Many are “invisible” applications (things that are running but aren’t necessarily showing a window), but others are things that we might think of as actual applications that we can see—such as Safari, Terminal, and so on. NSRunningApplication is documented at developer.apple.com, where its properties can be seen. For example, each application has a localizedName and a bundleIdentifier:

<code class="lang-python">>>> for nsapp in NSWorkspace.sharedWorkspace().runningApplications():
...   print(f"{nsapp.localizedName()} -> {nsapp.bundleIdentifier()}")
... 
loginwindow -> com.apple.loginwindow
BackgroundTaskManagementAgent -> com.apple.backgroundtaskmanagement.agent
WindowManager -> com.apple.WindowManager
CoreLocationAgent -> com.apple.CoreLocationAgent
Terminal -> com.apple.Terminal
Safari -> com.apple.Safari
Spotlight -> com.apple.Spotlight
Finder -> com.apple.finder
</code>

We can also see that a NSRunningApplication object has an activate function, which we can call to activate that app as though we had clicked its icon in the Dock. So, to find Safari and then activate it, we would use that activate function. The call to activate requires a value for options, as the documentation describes, and that also needs to be imported from AppKit:

<code class="lang-python">>>> from AppKit import NSWorkspace, NSApplicationActivateIgnoringOtherApps
>>> safari_list = [x for x in NSWorkspace.sharedWorkspace().runningApplications()
    if x.bundleIdentifier() == 'com.apple.Safari']
>>> safari = safari_list[0]
>>> safari.activateWithOptions_(NSApplicationActivateIgnoringOtherApps)
</code>

Now Safari is activated.

Finding Python Versions of macOS APIs

Finding the name of something in Python that corresponds to the Objective-C name can be a little tricky. As shown in the code above, the Objective-C activate function is called activateWithOptions_ in Python. There’s a set of rules for this name translation, which the pyobjc documentation explains, but it can sometimes be quicker to use Python’s own dir() function to show all the properties of an object and then pick out the one that looks most plausible:

<code class="lang-python">>>> print(len(dir(safari)))
452
</code>

Ouch! Our safari instance of an NSRunningApplication has 452 properties! Well, the one we want is probably called something like “activate”, so:

<code class="lang-python">>>> print([x for x in dir(safari) if "activate" in x.lower()])
['activateWithOptions_', 'activateWithOptions_']
</code>

Aha! So activateWithOptions_ is the name of the function we need to call. Similarly, the name of the option we want to pass to that function is in AppKit itself:

<code class="lang-python">>>> [x for x in dir(AppKit) if "ignoringotherapps" in x.lower()]
['NSApplicationActivateIgnoringOtherApps']
</code>

This process can feel a little exploratory at times, but it’s possible to do anything that Objective-C can do from Python as well.

This article is excerpted from Useful Python, available on SitePoint Premium and from ebook retailers.

Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

WP Twitter Auto Publish Powered By : XYZScripts.com