mingxiao
mingxiao

Reputation: 1812

osx open Proxies tab in network preferences programmatically

How can I programmatically open the 'Proxies' tab in 'Network' dialog box? System Preferences > Network > Advanced > Proxies

For those using Chrome, if you go to Menu > Settings > Show Advanced Settings > Change proxy settings... , the 'Network' box shows up, and its already on the 'Proxies' tab.

I want to achieve this using python.

Upvotes: 1

Views: 758

Answers (1)

abarnert
abarnert

Reputation: 366103

The way to do this is through Apple Events. If you open AppleScript Editor, you can Open Dictionary on System Preferences and see the commands:

tell application "System Preferences"
    reveal pane "com.apple.preference.network"
end tell

So, how do you do this from Python? There are three options:

  1. Create some AppleScript and run it via PyObjC, or via a wrapper like py-applescript.
  2. Use ScriptingBridge, Apple's AppleEvents-to-Python (and -Ruby and -ObjC) bridge.
  3. Use appscript, a third-party AppleEvents-to-Python (and …) bridge.

Appscript is a lot better, but it's effectively an abandoned project, and ScriptingBridge comes with Apple's version of Python. So, I'll show that first:

import ScriptingBridge
sp = ScriptingBridge.SBApplication.applicationWithBundleIdentifier_('com.apple.SystemPreferences')
panes = sp.panes()
pane = panes.objectWithName_('com.apple.preference.network')
anchors = pane.anchors()
dummy_anchor = anchors.objectAtIndex_(0)
dummy_anchor.reveal()

You may notice that the ScriptingBridge version is a lot more verbose and annoying than the AppleScript. There are a few reasons for this.

  1. ScriptingBridge isn't really an AppleEvent-Python bridge, it's an AppleEvent-ObjC bridge wrapped up in PyObjC, so you have to use horribleObjectiveCSyntax_withUnderscores_forEachParameterNamed_.

  2. It's inherently horribly verbose.

  3. The "obsolete" method of looking applications up by name isn't exposed in ScriptingBridge, so you have to find the bundle ID (or file:// URL) of the app and open that.

  4. Most importantly, ScriptingBridge doesn't expose the actual object model; it forces it into a CocoaScripting OO-style model and exposes that. So, while System Preferences knows how to reveal anything, the ScriptingBridge wrapper only knows how to call the reveal method on an anchor object.

While the last two are the most troublesome, the first two can be annoying as well. For example, even using bundle IDs and following the CocoaScripting model, here's what the equivalent looks like in AppleScript:

tell application "com.apple.SystemPreferences"
    reveal first anchor of pane "com.apple.preference.network"
end tell

… and in Python with appscript:

import appscript
sp = appscript.app('com.apple.SystemPreferences')
sp.panes['com.apple.preference.network'].anchors[1].reveal()

Meanwhile, in general, I wouldn't recommend any Python programmer move any of their logic into AppleScript, or try to write logic that crosses the boundaries (because I subscribe to the Geneva conventions against torture). So, I immediately start with ScriptingBridge or appscript in any case where we might need so much as an if statement. But in this case, as it turns out, we don't need that. So, using an AppleScript solution might be the best answer. Here's the code with py-applescript, or with nothing but what Apple gives you out of the box:

import applescript
scpt = 'tell app "System Preferences" to reveal pane "com.apple.preference.network"'
applescript.AppleScript(scpt).run()

import Foundation
scpt = 'tell app "System Preferences" to reveal pane "com.apple.preference.network"'
ascpt = Foundation.NSAppleScript.alloc()
ascpt.initWithSource_(scpt)
ascpt.executeAndReturnError_(None)

Upvotes: 3

Related Questions