TableModelWithSearch

A more advanced example of Key-Value Coding. This uses a custom NSArrayController.

Sources

FilteringArrayController.py

#
#  FilteringArrayController.py
#  TableModelWithSearch
#
#  Created by Bill Bumgarner on Sun Apr 04 2004.
#  Copyright (c) 2004 __MyCompanyName__. All rights reserved.
#

from objc import super
import objc
from Cocoa import NSArrayController
import re

kLiteralSearch = 'Literal Search'
kRegularExpressionSearch = 'Regular Expression Search'

def regexForSearchString(searchString, searchType):
    if not searchString:
        return None

    searchString = searchString.strip()
    if searchType == kLiteralSearch:
        searchString = re.escape(searchString.strip()) + r'(?i)'
    return re.compile(searchString)

def dictValueFilter(dicts, regex):
    for dct in dicts:
        for value in dct.values():
            if regex.search(value):
                yield dct
                break

class FilteringArrayController (NSArrayController):
    searchString = None
    lastRegex = None
    searchType = kLiteralSearch

    def arrangeObjects_(self, objects):
        supermethod = super(FilteringArrayController, self).arrangeObjects_
        try:
            regex = regexForSearchString(self.searchString, self.searchType)
        except:
            regex = self.lastRegex
        self.lastRegex = regex
        if regex is None:
            return supermethod(objects)
        return supermethod(list(dictValueFilter(objects, regex)))

    @objc.IBAction
    def performSearch_(self, sender):
        self.searchString = sender.stringValue()
        self.rearrangeObjects()

    @objc.IBAction
    def changeSearchType_(self, searchType):
        self.lastRegex = None
        self.searchString = None
        self.searchType = searchType
        self.rearrangeObjects()

TableModelWithSearchAppDelegate.py

from Foundation import NSObject, NSMutableArray
import os, pwd


def getPasswords():
    a = NSMutableArray()
    for pw in pwd.getpwall():
        a.append({
            'name': pw.pw_name,
            'password': pw.pw_passwd,
            'uid': str(pw.pw_uid),
            'gid': str(pw.pw_gid),
            'gecos': pw.pw_gecos,
            'home_dir': pw.pw_dir,
            'shell': pw.pw_shell,
        })

    return a

class TableModelWithSearchAppDelegate (NSObject):
    def passwords(self):
        if not hasattr(self, '_cachedpasswords'):
            self._cachedpasswords = getPasswords()
        return self._cachedpasswords

ToolbarCreator.py

#
#  ToolbarCreator.py
#  TableModelWithSearch
#
#  Created by Bill Bumgarner on Sun Apr 04 2004.
#  Copyright (c) 2004 __MyCompanyName__. All rights reserved.
#

import objc
from objc import super
from Cocoa import NSObject, NSMenuItem, NSMenu, NSToolbarItem, NSToolbar
from Cocoa import NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier
from Cocoa import NSToolbarPrintItemIdentifier, NSToolbarSeparatorItemIdentifier, NSToolbarSpaceItemIdentifier

kToolbarIdentifier = "TableModel Toolbar Identifier"
kSearchFieldItemIdentifier = "TableModel Search Field Identifier"

from FilteringArrayController import kLiteralSearch, kRegularExpressionSearch

class ToolbarCreator (NSObject):
    filteringArrayController = objc.IBOutlet()
    searchField = objc.IBOutlet()
    window = objc.IBOutlet()

    def awakeFromNib(self):
        self.toolbarItemCache = {}

        # create toolbar containing search field
        toolbar = NSToolbar.alloc().initWithIdentifier_(kToolbarIdentifier)
        toolbar.setDelegate_(self)
        toolbar.setAllowsUserCustomization_(True)
        toolbar.setAutosavesConfiguration_(True)

        searchFieldItem = NSToolbarItem.alloc().initWithItemIdentifier_(kSearchFieldItemIdentifier)
        self.searchFieldItem = searchFieldItem
        searchFieldItem.setLabel_("Search")
        searchFieldItem.setPaletteLabel_("Search Field")
        searchFieldItem.setToolTip_("Search")
        searchFieldItem.setView_(self.searchField)
        searchFieldItem.setMinSize_(self.searchField.bounds().size)
        maxSize = self.searchField.bounds().size
        maxSize.width = maxSize.width + 150
        searchFieldItem.setMaxSize_(maxSize)

        self.toolbarItemCache[kSearchFieldItemIdentifier] = searchFieldItem

        self.window.setToolbar_(toolbar)

        cellMenu = NSMenu.alloc().initWithTitle_('Search Menu')
        # note, bottom up!
        for v in [kRegularExpressionSearch, kLiteralSearch]:
            item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(v, 'changeSearchType:', '')
            item.setRepresentedObject_(v)
            item.setTarget_(self)
            cellMenu.insertItem_atIndex_(item, 0)
        self.searchField.cell().setSearchMenuTemplate_(cellMenu)
        # this better be the kLiteralSearch menu item
        self.changeSearchType_(item)

    @objc.IBAction
    def changeSearchType_(self, sender):
        obj = sender.representedObject()
        self.searchField.cell().setPlaceholderString_(obj)
        self.searchField.setStringValue_('')
        self.filteringArrayController.changeSearchType_(obj)

    def toolbarDefaultItemIdentifiers_(self, aToolbar):
        return [
            kSearchFieldItemIdentifier,
            NSToolbarFlexibleSpaceItemIdentifier,
            NSToolbarSeparatorItemIdentifier,
            NSToolbarCustomizeToolbarItemIdentifier,
        ]

    def toolbarAllowedItemIdentifiers_(self, aToolbar):
        return [
            kSearchFieldItemIdentifier,
            NSToolbarFlexibleSpaceItemIdentifier,
            NSToolbarSpaceItemIdentifier,
            NSToolbarSeparatorItemIdentifier,
            NSToolbarPrintItemIdentifier,
            NSToolbarCustomizeToolbarItemIdentifier,
        ]

    def toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar_(self, toolbar, itemIdentifier, flag):
        newItem = NSToolbarItem.alloc().initWithItemIdentifier_(itemIdentifier)
        item = self.toolbarItemCache[itemIdentifier]

        newItem.setLabel_( item.label() )
        newItem.setPaletteLabel_( item.paletteLabel() )
        if item.view():
            newItem.setView_( item.view() )
        else:
            newItem.setImage_( item.image() )

        newItem.setToolTip_( item.toolTip() )
        newItem.setTarget_( item.target() )
        newItem.setAction_( item.action() )
        newItem.setMenuFormRepresentation_( item.menuFormRepresentation() )

        if newItem.view():
            newItem.setMinSize_( item.minSize() )
            newItem.setMaxSize_( item.maxSize() )

        return newItem

main.py

#
#  __main__.py
#  TableModelWithSearch
#
#  Created by Bob Ippolito on Sun Apr 04 2004.
#  Copyright (c) 2004 Bob Ippolito. All rights reserved.
#

from PyObjCTools import AppHelper
from Foundation import NSProcessInfo

# import classes required to start application
import TableModelWithSearchAppDelegate
import ToolbarCreator
import FilteringArrayController

# start the event loop
AppHelper.runEventLoop(argv=[])

setup.py

"""
Script for building the example, alternative to the Xcode project

Usage:
    python3 setup.py py2app
"""
from setuptools import setup

setup(
    name="TableModelWithSearch",
    app=["main.py"],
    data_files=["English.lproj"],
    setup_requires=[
        "py2app",
        "pyobjc-framework-Cocoa",
    ]
)

Resources