ControlledPreferences

Demonstrates how to use Cocoa Bindings to simplify storing and retrieving user preferences. Also demonstrates how to use an NSValueTransformer to archive/unarchive a non-property-list type automatically (NSColor, in this case).

Originally from “Cocoa Bindings Examples and Hints”, converted to PyObjC by u.fiedler.

Sources

ControlledPreferences.py

#
#  ControlledPreferences
#
#

from PyObjCTools import AppHelper

# import classes required to start application
import PreferencesPanelController
import FontNameToDisplayNameTransformer
import FontSampleDisplayView

# start the event loop
AppHelper.runEventLoop()

FontNameToDisplayNameTransformer.py

#
#  FontNameToDisplayNameTransformer.py
#  ControlledPreferences
#
#  Converted by u.fiedler on 04.02.05.
#  with great help from Bob Ippolito - Thank you Bob!
#
#  The original version was written in Objective-C by Malcolm Crawford
#  at http://homepage.mac.com/mmalc/CocoaExamples/controllers.html

from Foundation import *
from AppKit import *

class FontNameToDisplayNameTransformer(NSValueTransformer):
    """
    Takes as input the fontName of a font as stored in user defaults,
    returns the displayed font name of the font to show to the user.
    """
    def transformedValueClass(cls):
        return NSString
    transformedValueClass = classmethod(transformedValueClass)

    def allowsReverseTransformation(cls):
        return False
    allowsReverseTransformation = classmethod(allowsReverseTransformation)

    def transformedValue_(self, aValue):
        font = NSFont.fontWithName_size_(aValue, 12)
        return font.displayName()

FontSampleDisplayView.py

#
#  FontSampleDisplayView.py
#  ControlledPreferences
#
#  Converted by u.fiedler on 04.02.05.
#  with great help from Bob Ippolito - Thank you Bob!
#
#  The original version was written in Objective-C by Malcolm Crawford
#  at http://homepage.mac.com/mmalc/CocoaExamples/controllers.html

from Foundation import *
from AppKit import *
from PyObjCTools.KeyValueCoding import *

class FontSampleDisplayView(NSView):
    """
    Display WordOfTheDay with the preferred font and color
    """
    def drawRect_(self, rect):
        defaults = NSUserDefaultsController.sharedUserDefaultsController().values()
        favoriteColor = NSUnarchiver.unarchiveObjectWithData_(getKey(defaults, u'FavoriteColor'))
        fontName = getKey(defaults, u'FontName')
        fontSize = getKey(defaults, u'FontSize')
        favoriteFont = NSFont.fontWithName_size_(fontName, fontSize)
        wordOfTheDay = getKey(defaults, u'WordOfTheDay')

        # Do the actual drawing
        myBounds = self.bounds() # = (x, y), (bw, bh)
        NSDrawLightBezel(myBounds, myBounds)
        favoriteColor.set()
        NSRectFill(NSInsetRect(myBounds, 2, 2))
        attrsDictionary = {NSFontAttributeName: favoriteFont}
        attrString = NSAttributedString.alloc().initWithString_attributes_(wordOfTheDay, attrsDictionary)
        attrSize = attrString.size() # = (bw, bh)
        attrString.drawAtPoint_(
            NSMakePoint(
                (attrSize.width / -2) + (myBounds.size.width / 2),
                (attrSize.height / -2) + (myBounds.size.height / 2),
            ),
        )

    def initWithFrame_(self, frameRect):
        self = super(FontSampleDisplayView, self).initWithFrame_(frameRect)
        dnc = NSNotificationCenter.defaultCenter()
        dnc.addObserver_selector_name_object_(self, "redisplay:", NSUserDefaultsDidChangeNotification, None)
        return self

    def redisplay_(self, sender):
        self.setNeedsDisplay_(True)

PreferencesPanelController.py

#
#  PreferencesPanelController.py
#  ControlledPreferences
#
#  Converted by u.fiedler on 04.02.05.
#  with great help from Bob Ippolito - Thank you Bob!
#
#  The original version was written in Objective-C by Malcolm Crawford
#  at http://homepage.mac.com/mmalc/CocoaExamples/controllers.html


from FontNameToDisplayNameTransformer import FontNameToDisplayNameTransformer
from Foundation import *
from AppKit import *
from PyObjCTools.KeyValueCoding import *

class PreferencesPanelController (NSWindowController):

    @objc.IBAction
    def changeTextFont_(self, sender):
        "The user changed the current font selection, so update the default font"

        # Get font name and size from user defaults
        defaults = NSUserDefaultsController.sharedUserDefaultsController().values()
        fontName = getKey(defaults, u'FontName')
        fontSize = getKey(defaults, u'FontSize')

        # Create font from name and size; initialize font panel
        font = NSFont.fontWithName_size_(fontName, fontSize)
        if font is None:
            font = NSFont.systemFontOfSize_(NSFont.systemFontSize())
        NSFontManager.sharedFontManager().setSelectedFont_isMultiple_(font, False)
        NSFontManager.sharedFontManager().orderFrontFontPanel_(self)

        # Set window as firstResponder so we get changeFont: messages
        self.window().makeFirstResponder_(self.window())


    @objc.IBAction
    def changeFont_(self, sender):
        "This is the message the font panel sends when a new font is selected"
        # Get selected font
        fontManager = NSFontManager.sharedFontManager()
        selectedFont = fontManager.selectedFont()
        if selectedFont is None:
            selectedFont = NSFont.systemFontOfSize_(NSFont.systemFontSize())
        panelFont = fontManager.convertFont_(selectedFont)

        # Get and store details of selected font
        # Note: use fontName, not displayName.  The font name identifies the font to
        # the system, we use a value transformer to show the user the display name
        fontSize = panelFont.pointSize()

        defaults = NSUserDefaultsController.sharedUserDefaultsController().values()
        defaults.setValue_forKey_(panelFont.fontName(), u"FontName")
        defaults.setValue_forKey_(fontSize, u"FontSize")


"""
Set up initial values for defaults:
Create dictionary with keys and values for WordOfTheDay, FontName,
FontSize, and FavoriteColor.  Mostly straightforward, but:

Store the fontName of the font as the default; the textfield displays
the font's displayName using a value transformer.

The color must be archived -- you can't store NSColors directly in NSUserDefaults.
"""

dictionary = {}
dictionary[u'WordOfTheDay'] = u'Today'
systemFont = NSFont.systemFontOfSize_(NSFont.systemFontSize())
dictionary[u"FontName"] = systemFont.fontName()
dictionary[u"FontSize"] = systemFont.pointSize()
archivedColor = NSArchiver.archivedDataWithRootObject_(NSColor.greenColor())
dictionary[u'FavoriteColor'] = archivedColor
NSUserDefaultsController.sharedUserDefaultsController().setInitialValues_(dictionary)

# Create and register font name value transformer
transformer = FontNameToDisplayNameTransformer.alloc().init()
NSValueTransformer.setValueTransformer_forName_(transformer, u'FontNameToDisplayNameTransformer')

setup.py

"""
Script for building the example:

Usage:
    python setup.py py2app
"""
from distutils.core import setup
import py2app

setup(
    name="ControlledPreferences",
    app=["ControlledPreferences.py"],
    data_files=["English.lproj"],
)

Resources

Table Of Contents

Resources

Support development