Commits

Ronald Oussoren committed 69f1b5d

Use mknibwrapper on all GUI examples except WebServicesTool

Comments (0)

Files changed (19)

+2002-10-31 Ronald Oussoren <oussoren@cistron.nl>
+	* Use 'nibwrapper' technique in all GUI examples.
+	* Actually remove old version of classnib.py and add the
+	  version in the new location (as used by Tools/mknibwrapper)
+
 2002-10-31  Bill Bumgarner  <bbum@codefab.com>
 	* Fixed a typo in NSOutlineViewDataSource.
 	* Fixed the classnib.py script slightly.

pyobjc/Examples/CurrencyConverter/CurrencyConverter.py

 from Foundation import NSObject
 from AppKit import NSApplicationMain
 from objc import *
+from nibwrapper import ConverterBase, ConverterControllerBase
 
-class Converter (NSObject):
+class Converter (ConverterBase):
 	def convertAmount(self, amt, rate):
 		return amt*rate
 		
-class ConverterController (NSObject):
+class ConverterController (ConverterControllerBase):
 
 	# First define the IB Outlets, the 'ivar' calls below define new
 	# instance variables in the objective-C class (e.g. visible
 	# for introspection in objective-C)
-	converter   = IBOutlet('converter')
-	dollarField = IBOutlet('dollarField')
-	rateField   = IBOutlet('rateField')
-	totalField  = IBOutlet('totalField')
-	extraField  = IBOutlet('extraField')
-
-	def dummy(self):
-		return "dummy"
 
 	def awakeFromNib(self):
-		print "awakeFromNib"
-
 		# Provide some defaults for the user...
 		self.dollarField.setFloatValue_(2.0)
 		self.rateField.setFloatValue_(3.0)
 
 	def convert_(self, sender):
-		print 'DBG PYTHON: ConverterController.convert_(%s, %s)' % (self, sender)
 		rate = self.rateField.floatValue()
-		print '  rate=', rate
 		amt = self.dollarField.floatValue()
-		print '  amt=', amt
 
 		total = self.converter.convertAmount(rate, amt)
-		print '  total=', total
 		self.totalField.setFloatValue_(total)
 		self.rateField.selectText_(self)
-		print "Done"
 
 
 sys.exit(NSApplicationMain(sys.argv))

pyobjc/Examples/CurrencyConverter/nibwrapper.py

+# THIS FILE IS GENERATED. DO NOT EDIT!!!
+# Interface classes for using NIB files
+
+from objc import IBOutlet
+from Foundation import NSObject
+
+class ConverterBase (NSObject):
+	"Base class for class 'Converter'"
+	pass
+
+class ConverterControllerBase (NSObject):
+	"Base class for class 'ConverterController'"
+	converter = IBOutlet("converter")
+	totalField = IBOutlet("totalField")
+	rateField = IBOutlet("rateField")
+	dollarField = IBOutlet("dollarField")
+
+	def convert_(self, sender): pass
+
+

pyobjc/Examples/CurrencyConverter/setup-app.py

 objc.builder.build_applet(
 	app_name= 'CurrencyConverterPY',
 	main_py = 'CurrencyConverter.py',
-	extra_files = ['English.lproj/MainMenu.nib'])
+	extra_files = ['English.lproj/MainMenu.nib'],
+	extra_src = ['nibwrapper.py'] )
 # NOTE: We'd like to use just 'English.lproj', but buildtools.py gets 
 # confused by that....

pyobjc/Examples/TableModel2/TableModel.py

 import sys
 import os.path
+from nibwrapper import PyModelBase
 
 sys.path.insert(0, os.path.join(sys.path[0], "pyobjc"))
 
 import Foundation
 import AppKit
 
-class PyModel (Foundation.NSObject, AppKit.NSTableDataSource):
+class PyModel (PyModelBase, AppKit.NSTableDataSource):
 	__slots__  = ('rowcount')
 
 	def init(self):

pyobjc/Examples/TableModel2/TableModel2.pbproj/project.pbxproj

 		080E96DDFE201D6D7F000001 = {
 			children = (
 				F67591E9034123C701336B42,
+				F57653070371679F01A801DC,
 			);
 			isa = PBXGroup;
 			name = Classes;
 //4A2
 //4A3
 //4A4
+//F50
+//F51
+//F52
+//F53
+//F54
+		F57653070371679F01A801DC = {
+			isa = PBXFileReference;
+			path = nibwrapper.py;
+			refType = 4;
+		};
+//F50
+//F51
+//F52
+//F53
+//F54
 //F60
 //F61
 //F62

pyobjc/Examples/TableModel2/nibwrapper.py

+# THIS FILE IS GENERATED. DO NOT EDIT!!!
+# Interface classes for using NIB files
+
+from objc import IBOutlet
+from Foundation import NSObject
+
+class PyModelBase (NSObject):
+	"Base class for class 'PyModel'"
+	pass
+

pyobjc/Examples/Todo/CalendarMatrix.py

 from Foundation import *
 from AppKit import *
 from objc import IBOutlet
+from nibwrapper import CalendarMatrixBase
 
 gNumDaysInMonth = ( 0, 31, 28, 31, 30, 21, 30, 31, 31, 30, 31, 30, 31 )
 
 def isLeap(year):
 	return (((year % 4) == 0 and ((year % 100) != 0)) or (year % 400) == 0)
 
-class CalendarMatrix (NSMatrix):
-	_lastMonthButton = IBOutlet('lastMonthButton')
-	_monthName       = IBOutlet('monthName')
-	_nextMonthButton = IBOutlet('nextMonthButton')
-
+class CalendarMatrix (CalendarMatrixBase):
 	__slots__ = ('_selectedDay', '_startOffset')
 
 	def initWithFrame_(self, frameRect):
 		currentYear = thisDate.yearOfCommonEra()
 		currentMonth = thisDate.monthOfYear()
 
-		print sender, self._nextMonthButton, self._lastMonthButton
-
-		if sender is self._nextMonthButton:
-			print "NEXT MONTH", sender, self._nextMonthButton
+		if sender is self.nextMonthButton:
 			if currentMonth == 12:
 				currentMonth = 1
 				currentYear += 1
 			else:
 				currentMonth += 1
 		else:
-			print "PREVIOUS MONTH"
 			if currentMonth == 1:
 				currentMonth = 12
 				currentYear -= 1
 					0,
 					0,
 					NSTimeZone.localTimeZone())
-		self._monthName.setStringValue_(
+		self.monthName.setStringValue_(
 			firstOfMonth.descriptionWithCalendarFormat_("%B %Y"))
 		daysInMonth = gNumDaysInMonth[currentMonth]
 

pyobjc/Examples/Todo/English.lproj/ToDoDocument.nib/classes.nib

             SUPERCLASS = NSMatrix; 
         }, 
         {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
-        {CLASS = MySound; LANGUAGE = ObjC; SUPERCLASS = Sound; }, 
+	{CLASS = MySound; LANGUAGE = ObjC; SUPERCLASS = Sound; },
         {CLASS = SelectionNotifyMatrix; LANGUAGE = ObjC; SUPERCLASS = NSMatrix; }, 
         {
             ACTIONS = {itemStatusClicked = id; }; 
         }
     ); 
     IBVersion = 1; 
-}
+}

pyobjc/Examples/Todo/English.lproj/ToDoInfoWindow.nib/classes.nib

         }
     ); 
     IBVersion = 1; 
-}
+}

pyobjc/Examples/Todo/InfoWindowController.py

 from AppKit import *
 from objc import selector, IBOutlet
 from ToDoDocument import *
+from nibwrapper import InfoWindowControllerBase
 
 NOTIFY_TAG     = 0
 RESCHEDULE_TAG = 1
 
 _sharedInfoWindowController = None
 
-class InfoWindowController (NSWindowController):
+class InfoWindowController (InfoWindowControllerBase):
 
 	__slots__ = ('_inspectingDocument', )
 
-	dummyView = IBOutlet('dummyView')
-	infoDate  = IBOutlet('infoDate')
-	infoItem  = IBOutlet('infoItem')
-	infoNotes = IBOutlet('infoNotes')
-	infoNotifyAMPM = IBOutlet('infoNotifyAMPM')
-	infoNotifyHour = IBOutlet('infoNotifyHour')
-	infoNotifyMinute = IBOutlet('infoNotifyMinute')
-	infoNotifyOtherHours = IBOutlet('infoNotifyOtherHours')
-	infoNotifySwitchMatrix = IBOutlet('infoNotifySwitchMatrix')
-	infoPopUp = IBOutlet('infoPopUp')
-	infoSchedComplete = IBOutlet('infoSchedComplete')
-	infoSchedDate     = IBOutlet('infoSchedDate')
-	infoSchedMatrix   = IBOutlet('infoSchedMatrix')
-	infoWindowViews   = IBOutlet('infoWindowViews')
-	notesView         = IBOutlet('notesView')
-	notifyView        = IBOutlet('notifyView')
-	reschedView       = IBOutlet('reschedView')
-
 	def switchClicked_(self, sender):
 		dueSecs = 0
 		idx = 0

pyobjc/Examples/Todo/SelectionNotifyMatrix.py

 from AppKit import *
 from Foundation import *
+from nibwrapper import SelectionNotifyMatrixBase
 
 RowSelectedNotification = "RowSelectedNotification"
 
-class  SelectionNotifyMatrix (NSMatrix):
+class  SelectionNotifyMatrix (SelectionNotifyMatrixBase):
 	def mouseDown_(self, theEvent):
 		super(SelectionNotifyMatrix, self).mouseDown_(theEvent)
 

pyobjc/Examples/Todo/ToDoDocument.py

 from ToDoCell import *
 from ToDoItem import *
 from SelectionNotifyMatrix import *
+from nibwrapper import ToDoDocumentBase
 
 ToDoItemChangedNotification = "ToDoItemChangedNotification"
 
-class  ToDoDocument (NSDocument):
-	_calendar   = IBOutlet("calendar")
-	_dayLabel   = IBOutlet("dayLabel")
-	_itemList   = IBOutlet("itemList")
-	_statusList = IBOutlet("statusList")
+class  ToDoDocument (ToDoDocumentBase):
 
 	__slots__ = ('_dataFromFile', '_activeDays', '_currentItems', '_selectedItem', '_selectedItemEdited')
 
 		# NSDocument.windowControllerDidLoadNib_(self, aController)
 
 		self.setHasUndoManager_(0)
-		self._itemList.setDelegate_(self)
+		self.itemList.setDelegate_(self)
 		
-		index = self._statusList.cells().count()
+		index = self.statusList.cells().count()
 		while index:
 			index -= 1
 
 			aCell = ToDoCell.alloc().init()
 			aCell.setTarget_(self)
 			aCell.setAction_('itemStatusClicked:')
-			self._statusList.putCell_atRow_column_(aCell, index, 0)
+			self.statusList.putCell_atRow_column_(aCell, index, 0)
 			aCell.release()
 		
 		if self._dataFromFile:
 		else:
 			self.loadDocWithData_(None)
 
-		NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, 'rowSelected:', RowSelectedNotification, self._itemList)
-		NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, 'rowSelected:', RowSelectedNotification, self._statusList)
+		NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, 'rowSelected:', RowSelectedNotification, self.itemList)
+		NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, 'rowSelected:', RowSelectedNotification, self.statusList)
 	
 	def loadDocWithData_(self, data):
 		if data:
 		self.selectItemAtRow_(0)
 		self.updateLists()
 
-		self._dayLabel.setStringValue_(
-			self._calendar.selectedDay().descriptionWithCalendarFormat_timeZone_locale_(
+		self.dayLabel.setStringValue_(
+			self.calendar.selectedDay().descriptionWithCalendarFormat_timeZone_locale_(
 				"To Do on %a %B %d %Y", 
 				NSTimeZone.defaultTimeZone(), 
 				None))
 		else:
 			self._activeDays = NSMutableDictionary.alloc().init()
 
-		date = self._calendar.selectedDay()
+		date = self.calendar.selectedDay()
 		self.setCurrentItems_(self._activeDays.objectForKey_(date))
 
 	def setCurrentItems_(self, newItems):
 		if newItems:
 			self._currentItems = newItems.mutableCopy()
 		else:
-			numRows, numCols = 6,1 # self._itemList.getNumberOfRows_columns_()
+			numRows, numCols = 6,1 # self.itemList.getNumberOfRows_columns_()
 			self._currentItems = NSMutableArray.alloc().initWithCapacity_(numRows)
 
 			for d in range(numRows):
 				self._currentItems.addObject_("")
 	
 	def updateLists(self):
-		numRows = self._itemList.cells().count()
+		numRows = self.itemList.cells().count()
 
 		for i in range(numRows):
 			if self._currentItems:
 				else:
 					due = None
 
-				self._itemList.cellAtRow_column_(i, 0).setStringValue_(thisItem.itemName())
-				self._statusList.cellAtRow_column_(i, 0).setTimeDue_(due)
-				self._statusList.cellAtRow_column_(i, 0).setTriState_(thisItem.status())
+				self.itemList.cellAtRow_column_(i, 0).setStringValue_(thisItem.itemName())
+				self.statusList.cellAtRow_column_(i, 0).setTimeDue_(due)
+				self.statusList.cellAtRow_column_(i, 0).setTriState_(thisItem.status())
 			else:
-				self._itemList.cellAtRow_column_(i, 0).setStringValue_("")
-				self._statusList.cellAtRow_column_(i, 0).setTitle_("")
-				self._statusList.cellAtRow_column_(i, 0).setImage_(None)
+				self.itemList.cellAtRow_column_(i, 0).setStringValue_("")
+				self.statusList.cellAtRow_column_(i, 0).setTitle_("")
+				self.statusList.cellAtRow_column_(i, 0).setImage_(None)
 
 	def saveDocItems(self):
 		if self._currentItems:
 		if not self._selectedItemEdited:
 			return
 
-		row = self._itemList.selectedRow()
-		newName = self._itemList.selectedCell().stringValue()
+		row = self.itemList.selectedRow()
+		newName = self.itemList.selectedCell().stringValue()
 
 		if isinstance(self._currentItems.objectAtIndex_(row), ToDoItem):
 			prevNameAtIndex = self._currentItems.objectAtIndex_(row).itemName()
 			elif prevNameAtIndex != newName:
 				self._currentItems.objectAtRow_(row).setItemName_(newName)
 		elif newName != "":
-			newItem = ToDoItem.alloc().initWithName_andDate_(newName, self._calendar.selectedDay())
+			newItem = ToDoItem.alloc().initWithName_andDate_(newName, self.calendar.selectedDay())
 			self._currentItems.replaceObjectAtIndex_withObject_(row, newItem)
 			newItem.release()
 
 			self.setCurrentItems_(self._activeDays.objectForKey_(date))
 		else:
 			print "calenderMatrix:didChangeToDate: -> no _activeDays"
-		self._dayLabel.setStringValue_(
+		self.dayLabel.setStringValue_(
 			date.descriptionWithCalendarFormat_timeZone_locale_(
 			"To Do on %a %B %d %Y", NSTimeZone.defaultTimeZone(),
 			None))
 		self.selectedItemAtRow_(0)
 
 	def selectedItemAtRow_(self, row):
-		self._itemList.selectCellAtRow_column_(row, 0)
+		self.itemList.selectCellAtRow_column_(row, 0)
 
 	def controlTextDidBeginEditing_(self, notif):
 		self._selectedItemEdited = 1
 		return NSArchiver.archivedDataWithRootObject_(self._activeDays)
 
 	def loadRepresentation_ofType_(self, data, aType):
-		if self_calendar:
+		if selfcalendar:
 			self.loadDocWithData_(data)
 		else:
 			self._dataFromFile = data.retain()
 			None)
 
 	def selectItemAtRow_(self, row):
-		self._itemList.selectCellAtRow_column_(row, 0)
+		self.itemList.selectCellAtRow_column_(row, 0)
 
 if __name__ == "__main__":
 	x = ToDoDocument.alloc()

pyobjc/Examples/Todo/TodoAppDelegate.py

 from Foundation import NSObject
 from InfoWindowController import InfoWindowController
+from nibwrapper import ToDoAppDelegateBase
 
-class ToDoAppDelegate (NSObject):
+class ToDoAppDelegate (ToDoAppDelegateBase):
 	def showInfo_(self, sender):
 		InfoWindowController.sharedInfoWindowController().showWindow_(sender)

pyobjc/Examples/Todo/nibwrapper.py

+# THIS FILE IS GENERATED. DO NOT EDIT!!!
+# Interface classes for using NIB files
+
+from objc import IBOutlet
+from Foundation import NSObject
+from AppKit import NSDocument, NSMatrix, NSWindowController
+
+class ToDoAppDelegateBase (NSObject):
+	"Base class for class 'ToDoAppDelegate'"
+	def showInfo_(self, sender): pass
+
+
+class ToDoDocumentBase (NSDocument):
+	"Base class for class 'ToDoDocument'"
+	calendar = IBOutlet("calendar")
+	itemList = IBOutlet("itemList")
+	dayLabel = IBOutlet("dayLabel")
+	statusList = IBOutlet("statusList")
+
+	def itemStatusClicked_(self, sender): pass
+
+
+class InfoWindowControllerBase (NSWindowController):
+	"Base class for class 'InfoWindowController'"
+	infoWindowViews = IBOutlet("infoWindowViews")
+	infoItem = IBOutlet("infoItem")
+	reschedView = IBOutlet("reschedView")
+	infoNotifyMinute = IBOutlet("infoNotifyMinute")
+	infoPopUp = IBOutlet("infoPopUp")
+	infoNotifyOtherHours = IBOutlet("infoNotifyOtherHours")
+	notesView = IBOutlet("notesView")
+	dummyView = IBOutlet("dummyView")
+	infoNotifyHour = IBOutlet("infoNotifyHour")
+	infoDate = IBOutlet("infoDate")
+	notifyView = IBOutlet("notifyView")
+	infoSchedMatrix = IBOutlet("infoSchedMatrix")
+	infoSchedDate = IBOutlet("infoSchedDate")
+	infoSchedComplete = IBOutlet("infoSchedComplete")
+	infoNotifyAMPM = IBOutlet("infoNotifyAMPM")
+	infoNotifySwitchMatrix = IBOutlet("infoNotifySwitchMatrix")
+	infoNotes = IBOutlet("infoNotes")
+
+	def swapInfoWindowView_(self, sender): pass
+
+	def switchClicked_(self, sender): pass
+
+
+class CalendarMatrixBase (NSMatrix):
+	"Base class for class 'CalendarMatrix'"
+	monthName = IBOutlet("monthName")
+	lastMonthButton = IBOutlet("lastMonthButton")
+	nextMonthButton = IBOutlet("nextMonthButton")
+
+	def choseDay_(self, sender): pass
+
+	def monthChanged_(self, sender): pass
+
+
+class SelectionNotifyMatrixBase (NSMatrix):
+	"Base class for class 'SelectionNotifyMatrix'"
+	pass
+

pyobjc/Examples/Todo/setup-app.py

 objc.builder.build_applet(
 	app_name= 'ToDo',
 	main_py = 'main.py',
-	extra_src = [ fn for fn in os.listdir('.') if fn.endswith('.py') and fn != 'main.py' ],
+	extra_src = [ fn for fn in os.listdir('.') if fn.endswith('.py') and fn not in ('main.py', 'setup-app.py') ],
 	info_plist = 'Info.plist',
 	raw=False,
 	extra_files = ['English.lproj' ] + images + icons 

pyobjc/Lib/AppKit/classnib.py

+"""
+This module contains functions and classes for dealing with the classes.nib
+file in a NIB 'file'.
+
+Usage for this module:
+	NIBFILE1 = "MainMenu.nib"
+	NIBFILE2 = "DocumentWindow.nib"
+
+	info1 = parse_classes_nib(NIBFILE1)
+	info2 = parse_classes_nib(NIBFILE2)
+	fp = open('nibclasses.py', 'w')
+	generate_wrapper_module(fp, [info1, info2])
+
+Using the generated module:
+	import nibclasses
+
+	class MyNibDefinedClass (nibclasses.MyNibDefinedClassBase):
+		def someAction_(self, sender):
+			pass
+
+
+The generated module contains base-classes for all classes defined in the
+NIB file. The base class serves as documentation for the list of methods
+that need to be implemented in subclasses, as wel as a way to avoid working
+with explicit method signatures.
+"""
+#
+#An example classes.nib:
+#{
+#    IBClasses = (
+#        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+#        {
+#            ACTIONS = {nextImage = id; };
+#            CLASS = PyModel;
+#            LANGUAGE = ObjC;
+#            OUTLETS = {Image = id; nextButton = id; };
+#            SUPERCLASS = NSObject;
+#        }
+#    );
+#    IBVersion = 1;
+#}
+#
+import sys
+
+
+def parse_classes_nib(nibfile):
+	"""
+	Parse a classes.nib file inside a NIB file. Returns a list of
+	class information.
+	"""
+	import os
+	d = NSDictionary.dictionaryWithContentsOfFile_(
+		os.path.join(nibfile, 'classes.nib'))
+
+	if not d:
+		raise ValueError, "Invalid or non-existing NIB: %s"%nibfile
+	return d
+
+def generate_wrapper_module(outfp, classinfolist):
+	"""
+	Generate the wrapper module given the class data in one or more
+	NIB files.
+	"""
+	generator = ClassNibGenerator(outfp)
+	for n in classinfolist:
+		generator.add_classnib(n)
+	generator.generate()
+
+
+#
+#
+# Beyond this are classes and functions used to implement the public functions,
+# all of which are defined above here.
+#
+#
+
+import objc
+
+NSBundle = objc.lookup_class('NSBundle')
+NSDictionary = objc.lookup_class('NSDictionary')
+
+def _mergelists(l1, l2):
+	r = {}
+	for i in l1:
+		r[i] = 1
+	for i in l2:
+		r[i] = 1
+	return r.keys()
+
+class ClassNibGenerator:
+	"""
+	Class for generating a python module containing the utility classes 
+	needed to use the NIB file. For every class X in the classes.nib we
+	generate class 'XBase'. The user is supposed to subclass 'X' from this
+	class.
+	"""
+	def __init__(self, fp):
+		self._fp = fp
+		self._didGenerate = 0
+		self._classes = []
+	
+	def add_classnib(self, nibinfo):
+		if not nibinfo.has_key('IBVersion'):
+			raise ValueError, "Invalid NIB info"
+		if nibinfo['IBVersion'] != '1':
+			raise ValueError, "Unsupported NIB version"
+
+		self._classes.extend(nibinfo['IBClasses'])
+
+	def generate(self):
+		assert not self._didGenerate
+
+		self._unique_classes()
+		self._generate_header()
+
+		for cls in self._classes:
+			if cls['CLASS'] == 'FirstResponder':
+				# This is special, ignore
+				continue
+
+			self._generate_class(cls)
+
+	def _frameworkForClass(self, clsname):
+		"""
+		Return framework containing the class, as a python package.
+		"""
+		cls = objc.lookup_class(clsname)
+
+		path = NSBundle.bundleForClass_(cls).bundlePath()
+		if path == '/System/Library/Frameworks/Foundation.framework':
+			return 'Foundation'
+		elif path == '/System/Library/Frameworks/AppKit.framework':
+			return 'AppKit'
+		else:
+			return ''
+
+		
+	def _generate_header(self):
+		"""
+		Generate header of the sub-module. 
+		TODO: Import the right modules, even if some of the superclasses
+		are in 'foreign' frameworks
+		"""
+		frameworks = {}
+
+		for cls in self._classes:
+			try:
+				frameworks[self._frameworkForClass(cls['SUPERCLASS'])].append(cls['SUPERCLASS'])
+			except KeyError:
+				frameworks[self._frameworkForClass(cls['SUPERCLASS'])] = [cls['SUPERCLASS']]
+			except objc.error:
+				continue
+
+		self._fp.write("# THIS FILE IS GENERATED. DO NOT EDIT!!!\n")
+		self._fp.write("# Interface classes for using NIB files\n")
+		self._fp.write("\n")
+		self._fp.write("from objc import IBOutlet\n")
+		for pkg in frameworks.keys():
+			self._fp.write('from %s import %s\n'%(
+				pkg, ', '.join(_mergelists(frameworks[pkg],()))))
+		self._fp.write('\n')			
+
+
+
+	def _unique_classes(self):
+		"""
+		Merge the class-lists from the various classnibs. It should
+		be possible to define the same class in multiple nibs, as long
+		as they are consistent enough.
+		"""
+		clsdict = {}
+		for cls in self._classes:
+			nm = cls['CLASS']
+			if not clsdict.has_key(nm):
+				clsdict[nm] = cls
+			else:
+				curcls = clsdict[nm]
+				curcls['OUTLETS'] = _mergelists(
+					curcls.get('OUTLETS', ()),
+					cls.get('OUTLETS', ()))
+				curcls['ACTIONS'] = _mergelists(
+					curcls.get('ACTIONS', ()),
+					cls.get('ACTIONS', ()))
+				if curcls['SUPERCLASS'] != cls['SUPERCLASS']:
+					raise ValueError, \
+						"Incompatible superclass for %s"%nm
+		self._classes = clsdict.values()	
+
+	def _generate_class(self, classinfo):
+		"""
+		Generate a python class for a class description in the NIBInfo
+
+		Fields in the classinfo:
+		{
+			'OUTLETS': {'Image': 'id', 'nextButton': 'id'}, 
+			'SUPERCLASS': 'NSObject', 
+			'CLASS': 'PyModel', 
+			'ACTIONS': {'nextImage': 'id'}, 
+			'LANGUAGE': 'ObjC'
+		}
+		"""
+		if classinfo['LANGUAGE'] != 'ObjC':
+			raise ValueError, 'Classes.nib not for Objective-C'
+
+		clsname = classinfo['CLASS']
+		supername = classinfo['SUPERCLASS']
+		actions = classinfo.get('ACTIONS', ())
+		outlets = classinfo.get('OUTLETS', ())
+		try:
+			fw = self._frameworkForClass(supername)
+		except objc.error:
+			sys.stderr.write(
+				'WARN: Skipping %s: no superclass %s\n'%(
+					clsname, supername))
+			return
+
+		#if fw:
+		#	supername = '%s.%s'%(fw, supername)
+	
+		self._fp.write('class %sBase (%s):\n'%(clsname, supername))
+		self._fp.write('\t"Base class for class \'%s\'"\n'%clsname)
+		if not actions and not outlets:
+			self._fp.write('\tpass\n')
+	
+		if outlets:
+			for o in outlets.keys():
+				# Might want to check type (outlets[o])
+				self._fp.write('\t%s = IBOutlet("%s")\n'%(o, o))
+
+		if outlets:
+			self._fp.write('\n')
+
+		if actions:
+			for a in actions.keys():
+				self._fp.write('\tdef %s_(self, sender): pass\n\n'%a)
+
+		self._fp.write('\n')
+
+
+
+if __name__ == '__main__':
+	import sys
+	classinfo = parse_classes_nib('English.lproj/MainMenu.nib')
+	generate_wrapper_module(sys.stdout, [classinfo])

pyobjc/Lib/objc/classnib.py

-"""
-This module contains functions and classes for dealing with the classes.nib
-file in a NIB 'file'.
-
-Usage for this module:
-	NIBFILE1 = "MainMenu.nib"
-	NIBFILE2 = "DocumentWindow.nib"
-
-	info1 = parse_classes_nib(NIBFILE1)
-	info2 = parse_classes_nib(NIBFILE2)
-	fp = open('nibclasses.py', 'w')
-	generate_wrapper_module(fp, [info1, info2])
-
-Using the generated module:
-	import nibclasses
-
-	class MyNibDefinedClass (nibclasses.MyNibDefinedClassBase):
-		def someAction_(self, sender):
-			pass
-
-
-The generated module contains base-classes for all classes defined in the
-NIB file. The base class serves as documentation for the list of methods
-that need to be implemented in subclasses, as wel as a way to avoid working
-with explicit method signatures.
-"""
-#
-#An example classes.nib:
-#{
-#    IBClasses = (
-#        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
-#        {
-#            ACTIONS = {nextImage = id; };
-#            CLASS = PyModel;
-#            LANGUAGE = ObjC;
-#            OUTLETS = {Image = id; nextButton = id; };
-#            SUPERCLASS = NSObject;
-#        }
-#    );
-#    IBVersion = 1;
-#}
-#
-
-from Foundation import NSDictionary
-
-def parse_classes_nib(nibfile):
-	"""
-	Parse a classes.nib file inside a NIB file. Returns a list of
-	class information.
-	"""
-	import os
-	return NSDictionary.dictionaryWithContentsOfFile_(os.path.join(nibfile, 'classes.nib'))
-
-def generate_wrapper_module(outfp, classinfolist):
-	"""
-	Generate the wrapper module given the class data in one or more
-	NIB files.
-	"""
-	generator = ClassNibGenerator(outfp)
-	for n in classinfolist:
-		generator.add_classnib(n)
-	generator.generate()
-
-#
-#
-# Beyond this are classes and functions used to implement the public functions,
-# all of which are defined above here.
-#
-#
-
-import objc
-NSBundle = objc.lookup_class('NSBundle')
-
-def _mergelists(l1, l2):
-	r = {}
-	for i in l1:
-		r[i] = 1
-	for i in l2:
-		r[i] = 1
-	return r.keys()
-
-class ClassNibGenerator:
-	"""
-	Class for generating a python module containing the utility classes 
-	needed to use the NIB file. For every class X in the classes.nib we
-	generate class 'XBase'. The user is supposed to subclass 'X' from this
-	class.
-	"""
-	def __init__(self, fp):
-		self._fp = fp
-		self._didGenerate = 0
-		self._classes = []
-	
-	def add_classnib(self, nibinfo):
-		if not nibinfo.has_key('IBVersion'):
-			raise ValueError, "Invalid NIB info"
-		if nibinfo['IBVersion'] != '1':
-			raise ValueError, "Unsupported NIB version"
-
-		self._classes.extend(nibinfo['IBClasses'])
-
-	def generate(self):
-		assert not self._didGenerate
-
-		self._unique_classes()
-		self._generate_header()
-
-		for cls in self._classes:
-			if cls['CLASS'] == 'FirstResponder':
-				# This is special, ignore
-				continue
-
-			self._generate_class(cls)
-
-	def _frameworkForClass(self, clsname):
-		"""
-		Return framework containing the class, as a python package.
-		"""
-		cls = objc.lookup_class(clsname)
-
-		path = NSBundle.bundleForClass_(cls).bundlePath()
-		if path == '/System/Library/Frameworks/Foundation.framework':
-			return 'Foundation'
-		elif path == '/System/Library/Frameworks/AppKit.framework':
-			return 'AppKit'
-		else:
-			return ''
-
-		
-	def _generate_header(self):
-		"""
-		Generate header of the sub-module. 
-		TODO: Import the right modules, even if some of the superclasses
-		are in 'foreign' frameworks
-		"""
-		frameworks = {}
-
-		for cls in self._classes:
-			try:
-				frameworks[self._frameworkForClass(cls['SUPERCLASS'])].append(cls['SUPERCLASS'])
-			except KeyError:
-				frameworks[self._frameworkForClass(cls['SUPERCLASS'])] = [cls['SUPERCLASS']]
-		self._fp.write("# THIS FILE IS GENERATED. DO NOT EDIT!!!\n")
-		self._fp.write("# Interface classes for using NIB files\n")
-		self._fp.write("\n")
-		self._fp.write("from objc import IBOutlet\n")
-		for pkg in frameworks.keys():
-			self._fp.write('from %s import %s\n'%(
-				pkg, ', '.join(_mergelists(frameworks[pkg],()))))
-		self._fp.write('\n')			
-
-
-
-	def _unique_classes(self):
-		"""
-		Merge the class-lists from the various classnibs. It should
-		be possible to define the same class in multiple nibs, as long
-		as they are consistent enough.
-		"""
-		clsdict = {}
-		for cls in self._classes:
-			nm = cls['CLASS']
-			if not clsdict.has_key(nm):
-				clsdict[nm] = cls
-			else:
-				curcls = clsdict[nm]
-				curcls['OUTLETS'] = _mergelists(
-					curcls.get('OUTLETS', ()),
-					cls.get('OUTLETS', ()))
-				curcls['ACTIONS'] = _mergelists(
-					curcls.get('ACTIONS', ()),
-					cls.get('ACTIONS', ()))
-				if curcls['SUPERCLASS'] != cls['SUPERCLASS']:
-					raise ValueError, \
-						"Incompatible superclass for %s"%nm
-		self._classes = clsdict.values()	
-
-	def _generate_class(self, classinfo):
-		"""
-		Generate a python class for a class description in the NIBInfo
-
-		Fields in the classinfo:
-		{
-			'OUTLETS': {'Image': 'id', 'nextButton': 'id'}, 
-			'SUPERCLASS': 'NSObject', 
-			'CLASS': 'PyModel', 
-			'ACTIONS': {'nextImage': 'id'}, 
-			'LANGUAGE': 'ObjC'
-		}
-		"""
-		if classinfo['LANGUAGE'] != 'ObjC':
-			raise ValueError, 'Classes.nib not for Objective-C'
-
-		clsname = classinfo['CLASS']
-		supername = classinfo['SUPERCLASS']
-		actions = classinfo.get('ACTIONS', ())
-		outlets = classinfo.get('OUTLETS', ())
-		fw = self._frameworkForClass(supername)
-		if fw:
-			supername = '%s'%(supername)
-	
-		self._fp.write('class %sBase (%s):\n'%(clsname, supername))
-		self._fp.write('\t"Base class for class \'%s\'"\n'%clsname)
-		if not actions and not outlets:
-			self._fp.write('\tpass\n')
-
-		for o in outlets:
-			self._fp.write('\t%s = IBOutlet("%s")\n'%(o, o))
-		if outlets:
-			self._fp.write('\n')
-
-		for a in actions:
-			self._fp.write('\tdef %s_(self, sender): pass\n\n'%a)
-
-		self._fp.write('\n')
-
-
-
-if __name__ == '__main__':
-	import sys
-	for nibFile in sys.argv[1:]:
-		classinfo = parse_classes_nib(nibFile)
-		generate_wrapper_module(sys.stdout, [classinfo])

pyobjc/Tools/mknibwrapper

+#!/usr/bin/env python
+"""
+mknibwrapper [--module=nibwrapper] [--output-directory=.] [--help] file.nib...
+
+This tool can be used to generate the classes defined in a NIB file.
+"""
+import sys
+import getopt
+import os
+
+from AppKit.classnib import parse_classes_nib, generate_wrapper_module
+
+PRG=sys.argv[0]
+MODNAME='nibwrapper'
+BASEDIR='.'
+
+def usage(fp):
+	fp.write("Usage:\n")
+	fp.write("\t%s [--module=nibwrapper] [--output-directory=.] [--help] x.nib...\n", PRG)
+
+try:
+	opts, args = getopt.getopt(sys.argv[1:],
+		'm:h?o:', [ 'help', 'module=', 'output-directory=' ])
+except getopt.error, msg:
+	sys.stderr.write("%s: %s\n"%(PRG, msg))
+	usage(sys.stderr)
+	sys.exit(1)
+
+for key, value in opts:
+	if key in [ '-h', '-?', '--help' ]:
+		usage(sys.stdout)
+		sys.exit(0)
+	elif key in [ '-m', '--module' ]:
+		MODNAME = value
+	elif key in [ '-d', '--output-directory' ]:
+		BASEDIR = value
+	else:
+		raise InternalError, "Unhandled option: %s"%key
+
+if not args:
+	sys.stderr.write("%s: No NIB file specified\n"%PRG)
+	usage(sys.stderr)
+	sys.exit(1)
+
+class_data = [ parse_classes_nib(a) for a in args ]
+
+fname = os.path.join(BASEDIR, MODNAME.replace('.', '/')) + '.py'
+generate_wrapper_module(open(fname, 'w'), class_data)