Commits

Anonymous committed be211b7

Converted WST example to new nib loader.

  • Participants
  • Parent commits c80108b

Comments (0)

Files changed (4)

File pyobjc/ChangeLog

+2002-11-15  Bill Bumgarner  <bbum@codefab.com>
+	* Fixed a minor bug in NibLoader.py when specifying NIBs without
+	the .nib extension
+
 2002-11-15 Ronald Oussoren <oussoren@cistron.nl>
 	- Modules/objc: Remove sel_class_method and sel_allocator from
 	  selector objects, and introduce a sel_flags.
 	  instead of calling objc_class.__getattr__.
 
 2002-11-15  Bill Bumgarner  <bbum@codefab.com>
+	* Changed the names of various methods from loadNib to
+	loadClassesForNib to better follow the Cocoa APIs.
+	* Just rewrote the NibLoader.py;  added the new version.
+
 	* Modified NibLoader to not generate a class for FirstResponder.
 	* Integrated Just van Rossum's NibLoader script into the module.
 

File pyobjc/Examples/WebServicesTool/WSTApplicationDelegateClass.py

 from Foundation import NSObject
 from WSTConnectionWindowControllerClass import WSTConnectionWindowController
 
-class WSTApplicationDelegate (NSObject):
+from AppKit import NibLoader
+
+NibLoader.loadClassesForNibFromBundle( "MainMenu" )
+
+class WSTApplicationDelegate:
+  __metaclass__ = NibLoader.NibClassBuilder
+
   def newConnectionAction_(self, sender):
     WSTConnectionWindowController.connectionWindowController().showWindow_(sender)
 

File pyobjc/Examples/WebServicesTool/WSTConnectionWindowControllerClass.py

 import string
 import traceback
 
+from AppKit import NibLoader
+NibLoader.loadClassesForNibFromBundle( "WSTConnection" )
+
 kWSTReloadContentsToolbarItemIdentifier = "WST: Reload Contents Toolbar Identifier"
 kWSTPreferencesToolbarItemIdentifier = "WST: Preferences Toolbar Identifier"
 kWSTUrlTextFieldToolbarItemIdentifier = "WST: URL Textfield Toolbar Identifier"
     
     self._toolbarItems.setObject_forKey_(toolbarItem, anIdentifier)
 
-class WSTConnectionWindowController(NSWindowController):
-    _progressIndicator = IBOutlet("progressIndicator")
-    _statusTextField = IBOutlet("statusTextField")
-    _methodsTable = IBOutlet("methodsTable")
-    _urlTextField = IBOutlet("urlTextField")
-    _methodDescriptionTextView = IBOutlet("methodDescriptionTextView")
+class WSTConnectionWindowController:
+    __metaclass__ = NibLoader.NibClassBuilder
+
     __slots__ = ('_toolbarItems',
         '_toolbarDefaultItemIdentifiers',
         '_toolbarAllowedItemIdentifiers',
     def awakeFromNib(self):
         self.retain() # balanced by autorelease() in windowWillClose_
         
-        self._statusTextField.setStringValue_("No host specified.")
-        self._progressIndicator.setStyle_(NSProgressIndicatorSpinningStyle)
-        self._progressIndicator.setUsesThreadedAnimation_(YES)
-        self._progressIndicator.setDisplayedWhenStopped_(NO)
+        self.statusTextField.setStringValue_("No host specified.")
+        self.progressIndicator.setStyle_(NSProgressIndicatorSpinningStyle)
+        self.progressIndicator.setUsesThreadedAnimation_(YES)
+        self.progressIndicator.setDisplayedWhenStopped_(NO)
         
         self.createToolbar()
         
 
         lastURL = NSUserDefaults.standardUserDefaults().stringForKey_("LastURL")
         if lastURL and len(lastURL):
-            self._urlTextField.setStringValue_(lastURL)
+            self.urlTextField.setStringValue_(lastURL)
         
     def createToolbarItems(self):
         addToolbarItem(self, kWSTReloadContentsToolbarItemIdentifier, "Reload", "Reload", "Reload Contents", None, "reloadVisibleData:", NSImage.imageNamed_("Reload"), None)
         addToolbarItem(self, kWSTPreferencesToolbarItemIdentifier, "Preferences", "Preferences", "Show Preferences", None, "orderFrontPreferences:", NSImage.imageNamed_("Preferences"), None)
-        addToolbarItem(self, kWSTUrlTextFieldToolbarItemIdentifier, "URL", "URL", "Server URL", None, None, self._urlTextField, None)
+        addToolbarItem(self, kWSTUrlTextFieldToolbarItemIdentifier, "URL", "URL", "Server URL", None, None, self.urlTextField, None)
         
         self._toolbarDefaultItemIdentifiers.addObject_(kWSTReloadContentsToolbarItemIdentifier)
         self._toolbarDefaultItemIdentifiers.addObject_(kWSTUrlTextFieldToolbarItemIdentifier)
     def setStatusTextFieldMessage_(self, aMessage):
         if not aMessage:
             aMessage = "Displaying information about %d methods." % len(self._methods)
-        self._statusTextField.setStringValue_(aMessage)
-        self._statusTextField.display()
+        self.statusTextField.setStringValue_(aMessage)
+        self.statusTextField.display()
 
     def reloadVisibleData_(self, sender):
-        url = self._urlTextField.stringValue()
+        url = self.urlTextField.stringValue()
         self._methods = []
         self._methodSignatures = {}
         self._methodDescriptions = {}
         
         if url and len(url):
             self._server = xmlrpclib.ServerProxy(url)
-            self._progressIndicator.startAnimation_(sender)
+            self.progressIndicator.startAnimation_(sender)
             self.setStatusTextFieldMessage_("Retrieving method list...")
             try:
                 self._methods = self._server.listMethods()
                     self._methodPrefix = "system."
                 except:
                     self.setStatusTextFieldMessage_("Server failed to respond to listMethods query.  See below for more information.")
-                    self._progressIndicator.stopAnimation_(sender)
+                    self.progressIndicator.stopAnimation_(sender)
                     self._server = None
                     self._methodPrefix = None
                     
                     exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
-                    self._methodDescriptionTextView.setString_("Exception information\n\nType: %s\n\nValue: %s\n\nTraceback:\n\n %s\n" % (exceptionType, exceptionValue, string.join(traceback.format_tb( exceptionTraceback ), '\n' )))
+                    self.methodDescriptionTextView.setString_("Exception information\n\nType: %s\n\nValue: %s\n\nTraceback:\n\n %s\n" % (exceptionType, exceptionValue, string.join(traceback.format_tb( exceptionTraceback ), '\n' )))
                     
                     return
                     
             self._methods.sort(lambda x, y: cmp(x, y))
-            self._methodsTable.reloadData()
-            self._methodsTable.display()
+            self.methodsTable.reloadData()
+            self.methodsTable.display()
             self.setStatusTextFieldMessage_("Retrieving information about %d methods." % len(self._methods))
             self.window().setTitle_(url)
             NSUserDefaults.standardUserDefaults().setObject_forKey_(url, "LastURL")
             for aMethod in self._methods:
                 index = index + 1
                 if not (index % 5):
-                    self._methodsTable.reloadData()
-                    self._methodsTable.display()
+                    self.methodsTable.reloadData()
+                    self.methodsTable.display()
                 self.setStatusTextFieldMessage_("Retrieving signature for method %s (%d of %d)." % (aMethod , index, len(self._methods)))
                 methodSignature = getattr(self._server, self._methodPrefix + "methodSignature")(aMethod)
                 signatures = None
                     signatures = signature
                 self._methodSignatures[aMethod] = signatures
             self.setStatusTextFieldMessage_(None)
-            self._progressIndicator.stopAnimation_(sender)
-            self._methodsTable.reloadData()
+            self.progressIndicator.stopAnimation_(sender)
+            self.methodsTable.reloadData()
         else:
             self.window().setTitle_("Untitled.")
             self.setStatusTextFieldMessage_("No URL specified.")
     
     def selectMethodAction_(self, sender):
-        selectedRow = self._methodsTable.selectedRow()
+        selectedRow = self.methodsTable.selectedRow()
         selectedMethod = self._methods[selectedRow]
         
         if  not self._methodDescriptions.has_key(selectedMethod):
-            self._progressIndicator.startAnimation_(sender)
+            self.progressIndicator.startAnimation_(sender)
             self.setStatusTextFieldMessage_("Retrieving signature for method %s..." % selectedMethod)
             methodDescription = getattr(self._server, self._methodPrefix + "methodHelp")(selectedMethod)
             if not methodDescription:
                 methodDescription = "No description available."
             self._methodDescriptions[selectedMethod] = methodDescription
-            self._progressIndicator.stopAnimation_(sender)
+            self.progressIndicator.stopAnimation_(sender)
         else:
             methodDescription = self._methodDescriptions[selectedMethod]
        
         self.setStatusTextFieldMessage_(None)
-        self._methodDescriptionTextView.setString_(methodDescription)
+        self.methodDescriptionTextView.setString_(methodDescription)
 
     def numberOfRowsInTableView_(self, aTableView):
         return len(self._methods)

File pyobjc/Lib/AppKit/NibLoader.py

 #!/usr/bin/env python
 
+"""
 
+loadClassesForNib(nibPath):
+
+
+loadClassesForNibFromBundle(nibName[, sourceBundle=<mainbundle>]):
+
+
+NibClassBuilder -- metaclass
+
+  class PyModel(NSTbleSource):
+      __metaclass__ = NibClassBuilder
+      ...
+
+
+NibInfo
+
+"""
+import sys
 import os
 import objc
 
 
+__all__ = ["NibClassBuilder", "loadClassesForNib", "loadClassesForNibFromBundle", "NibInfo"]
+
+
 NSDictionary = objc.lookup_class("NSDictionary")
 NSObject = objc.lookup_class("NSObject")
 NSBundle = objc.lookup_class("NSBundle")
 
+
 class NibLoaderError(Exception): pass
 
 
-class _NibClassBuilder(object):
+class ClassInfo:
+
+	__slots__ = ("nib", "name", "super", "actions", "outlets")
+
+	def merge(self, other):
+		assert self.name == other.name
+		if self.super != other.super:
+			raise NibLoaderError, \
+					"Incompatible superclass for %s" % self.name
+		self.outlets = mergeLists(self.outlets, other.outlets)
+		self.actions = mergeLists(self.actions, other.actions)
+
+	def __cmp__(self, other):
+		s = [getattr(self, x) for x in self.__slots__]
+		o = [getattr(other, x) for x in self.__slots__]
+		return cmp(s, o)
+
+
+class NibInfo(object):
 
 	def __init__(self):
-		self._classes = {}
-	
-	def addNib(self, nibPath):
+		self.classes = {}
+
+	def keys(self):
+		return self.classes.keys()
+
+	def len(self):
+		return len(self.classes)
+
+	def __iter__(self):
+		return iter(self.classes)
+
+	def __getitem__(self, name):
+		return self.classes[name]
+
+	def get(self, name, default=None):
+		return self.classes.get(name, default)
+
+	def loadClassesForNib(self, nibPath):
+		nibName = os.path.basename(nibPath)
 		nibInfo = NSDictionary.dictionaryWithContentsOfFile_(
 				os.path.join(nibPath, 'classes.nib'))
 		if nibInfo is None:
 			raise NibLoaderError, "Invalid NIB info"
 		if nibInfo['IBVersion'] != '1':
 			raise NibLoaderError, "Unsupported NIB version"
-		classes = self._classes
-		for classInfo in nibInfo['IBClasses']:
-			self._addClass(classInfo)
-	
-	def _addClass(self, classInfo):
-		classes = self._classes
-		name = classInfo['CLASS']
-		if name == "FirstResponder": return
+		for rawClsInfo in nibInfo['IBClasses']:
+			self._addClass(nibName, rawClsInfo)
+
+	def loadClassesForNibFromBundle(self, nibName, sourceBundle=None):
+		if not sourceBundle:
+			sourceBundle = NSBundle.mainBundle()
+
+		if nibName[-4:] == '.nib':
+			nibPath = sourceBundle.pathForResource_ofType_(nibName, None)
+		else:
+			nibPath = sourceBundle.pathForResource_ofType_(nibName, 'nib')
+
+		if not nibPath:
+			raise NibLoaderError, ("Could not find nib named '%s' "
+					"in bundle '%s'" % (nibName, sourceBundle))
+		self.loadClassesForNib(nibPath)
+
+	def _addClass(self, nibName, rawClsInfo):
+		classes = self.classes
+		name = rawClsInfo['CLASS']
+		if name == "FirstResponder":
+			# a FirstResponder never needs to be made
+			return
+
+		clsInfo = ClassInfo()
+		clsInfo.nib = nibName
+		clsInfo.name = name
+		clsInfo.super = rawClsInfo.get('SUPERCLASS', 'NSObject')
+		clsInfo.actions = [a + "_" for a in rawClsInfo.get('ACTIONS', ())]
+		clsInfo.outlets = list(rawClsInfo.get('OUTLETS', ()))
+
 		if not classes.has_key(name):
-			classes[name] = classInfo
+			classes[name] = clsInfo
 		else:
-			curcls = classes[name]
-			curcls['OUTLETS'] = _mergelists(
-				curcls.get('OUTLETS', ()),
-				classInfo.get('OUTLETS', ()))
-			curcls['ACTIONS'] = _mergelists(
-				curcls.get('ACTIONS', ()),
-				classInfo.get('ACTIONS', ()))
-			if curcls['SUPERCLASS'] != classInfo['SUPERCLASS']:
-				raise NibLoaderError, \
-					"Incompatible superclass for %s" % name
-	
+			classes[name].merge(clsInfo)
+
 	def makeClass(self, name, bases, methods):
-		classInfo = self._classes.get(name)
-		if classInfo is None:
+		clsInfo = self.classes.get(name)
+		if clsInfo is None:
 			raise NibLoaderError, ("No class named '%s' found in "
 					"nibs" % name)
-		superName = classInfo['SUPERCLASS']
-		actions = classInfo.get('ACTIONS', {})
-		outlets = classInfo.get('OUTLETS', {})
-		
+
 		try:
-			superClass = objc.lookup_class(superName)
+			superClass = objc.lookup_class(clsInfo.super)
 		except objc.nosuchclass_error:
 			raise NibLoaderError, ("Superclass '%s' for '%s' not "
-					"found." % (superName, name))
+					"found." % (clsInfo.super, name))
 		bases = (superClass,) + bases
 		metaClass = superClass.__class__
-		
-		if hasattr(outlets, "keys"):
-			outlets = outlets.keys()
-		for o in outlets:
+
+		for o in clsInfo.outlets:
 			if not methods.has_key(o):
 				methods[o] = objc.IBOutlet(o)
 
-		if hasattr(actions, "keys"):
-			actions = actions.keys()
-		for a in actions:
-			a = a + "_"
+		for a in clsInfo.actions:
 			if not methods.has_key(a):
-				methods[a] = _dummyAction
-		
+				methods[a] = _actionStub
+
 		return metaClass(name, bases, methods)
 
+	def printOverview(self, file=None):
+		classes = self.classes.values()
+		classes.sort()  # see ClassInfo.__cmp__
+		nib = None
+		INDENT = "   "
+		for clsInfo in classes:
+			if nib != clsInfo.nib:
+				if nib:
+					print >>file
+				nib = clsInfo.nib
+				print >>file, "%s:" % nib
+			print >>file, INDENT + "%s(%s):" % (clsInfo.name, clsInfo.super)
+			for attrName in ["actions", "outlets"]:
+				attrs = getattr(clsInfo, attrName)
+				label = attrName.capitalize()
+				attrs.sort()
+				print >>file, 2 * INDENT + "%s:" % label
+				if not attrs:
+					print >>file, 3 * INDENT + "<none>"
+				else:
+					for name in attrs:
+						print >>file, 3 * INDENT + name
 
-def _mergelists(l1, l2):
+
+def _actionStub(self, sender): pass
+
+
+def mergeLists(l1, l2):
 	r = {}
 	for i in l1:
 		r[i] = 1
 		r[i] = 1
 	return r.keys()
 
-def _dummyAction(self, sender): pass
 
+class NibClassBuilder(type):
+	def __new__(cls, name, bases, methods):
+		return _nibInfo.makeClass(name, bases, methods)
 
-_nibClassBuilder = _NibClassBuilder()
+_nibInfo = NibInfo()
 
-addNib = _nibClassBuilder.addNib
+loadClassesForNib =  _nibInfo.loadClassesForNib
+loadClassesForNibFromBundle =  _nibInfo.loadClassesForNibFromBundle
 
-def addNibFromBundle( nibName, sourceBundle = None ):
-	if not sourceBundle:
-		sourceBundle = NSBundle.mainBundle()
 
-	if nibName[-4:] == '.nib':
-		nibPath = sourceBundle.pathForResource_ofType_( nibName, None )
+#
+# The rest of this file is a simple command line tool.
+#
+
+commandline_doc = """\
+NibLoader.py [-th] nib1 [...nibN]
+  Print an overview of the classes found in the nib file(s) specified,
+  listing their superclass, actions and outlets.
+  -t Instead of printing the overvies, perform a simple test.
+  -h Print this text."""
+
+def usage(msg, code):
+	if msg:
+		print msg
+	print commandline_doc
+	sys.exit(code)
+
+def test(nibFiles):
+	for nibPath in nibFiles:
+		print "Loading", nibPath
+		loadClassesForNib(nibPath)
+	print
+	classNames = _nibInfo.keys()
+	classNames.sort()
+	for className in classNames:
+		try:
+			# instantiate class, equivalent to
+			# class %(className):
+			#     __metaclass__ = NibClassBuilder
+			cls = NibClassBuilder(className, (), {})
+		except NibLoaderError, why:
+			print "*** Failed class: %s; NibLoaderError: %s" % (
+					className, why[0])
+		else:
+			print "Created class: %s, superclass: %s" % (cls.__name__,
+					cls.__bases__[0].__name__)
+
+def printOverview(nibFiles):
+	for nibPath in nibFiles:
+		info = NibInfo()
+		info.loadClassesForNib(nibPath)
+		info.printOverview()
+		print
+
+def commandline():
+	import getopt
+
+	try:
+		opts, nibFiles = getopt.getopt(sys.argv[1:], "th")
+	except getopt.error, msg:
+		usage(msg, 1)
+
+	doTest = 0
+	for opt, val in opts:
+		if opt == "-t":
+			doTest = 1
+		elif opt == "-h":
+			usage("", 0)
+	
+	if not nibFiles:
+		usage("No nib file specified.", 1)
+	
+	if doTest:
+		test(nibFiles)
 	else:
-		nibPath = sourceBundle.pathForResource_ofType_( nibName, '.nib' )
-
-	if not nibPath:
-		raise NibLoaderError, "Could not find nib named '%s' in bundle '%s'" % ( nibName, sourceBundle )
-
-	addNib( nibPath )
-
-
-class _NibLoader(type):
-	
-	def __new__(meta, name, bases, methods):
-		if not bases:
-			return type.__new__(meta, name, bases, methods)
-		else:
-			assert bases[0].__class__ is meta
-			return _nibClassBuilder.makeClass(name, bases[1:], methods)
-
-
-class NibLoader:
-	__metaclass__ = _NibLoader
+		printOverview(nibFiles)
 
 
 if __name__ == "__main__":
-	import sys
-	for nibPath in sys.argv[1:]:
-		print "loading", nibPath
-		addNib(nibPath)
-	
-	classNames = _nibClassBuilder._classes.keys()
-	classNames.sort()
-	for name in classNames:
-		print "making test class for", name
-		try:
-			cls = type(name, (NibLoader,), {})
-		except NibLoaderError, why:
-			print "****** NibLoaderError", why[0]
-		else:
-			print "Class: %s, superclass: %s" % (cls, cls.__bases__[0])
-		print
+	commandline()