Re: [Pyobjc-dev] Attempt at writing Cocoa services with pyobjc
Brought to you by:
ronaldoussoren
From: <bb...@ma...> - 2003-01-06 15:02:15
|
On Monday, Jan 6, 2003, at 07:43 US/Eastern, Dinu Gherman wrote: > Hi, > > After recently seeing the release of pyobjc 0.8 I decided it was > high time to have some fun writing *real* Python Cocoa programs! Good -- the more feedback, the better! > I started using pyobjc on a little Cocoa Services tool, much in > the spirit of URLServices for those who know it. Unfortunately, > I can't find a URL for downloading it, but I can provide my copy > of it to anybody interested. > > There are two problems I'm having so far with that first toy pro- > ject, after just creating a "Python Cocoa App." with PB: You are working too hard. :-) Neither of these problems are specific to Cocoa-Python -- they would both be issues if you were building an ObjC or Java Cocoa App. > 1. Removing the PB Resources folder containing the MainMenu.nib > and InfoPlist.strings results in complaints like "Unable to load nib > file: MainMenu, exiting". The NIB file isn't needed in this case as > there is neither a GUI nor an app "owner" object, hence I also re- > moved the MyAppDelegate.py thinking that would be ok. Apparently > there must be something else to get rid of the need for a NIB file. > Any ideas? A Cocoa application generally has the concept of a 'Main NIB file'. That is, a NIB file that is automatically loaded at application launch that contains things like the main menu and the primary controller object. In the case of Cocoa/Python, the default 'controller' object is an instance of MyAppDelegate. The instance appears in MainMenu.nib. If you truly want to remove MainMenu.nib, then you need to also adjust the configuration of the application target such that it will no longer attempt to load the MainMenu at app launch. Then, remove the reference to MyAppDelegate from __main__.py or set the inheritance of MyAppDelegate to something appropriate [NSObject] and remove the AutoBaseClass/NibClassBuilder stuff from MyAppDelegate.py. You will need to instantiate MyAppDelegate by hand-- likely in __main__.py. But I don't think you want to to do that-- if you want a UI-less app, you are far better off simply removing the single window from MainMenu.nib. If you don't want the dock icon [bad idea, more often than not], then use the NSUIElement hack [search google]. Ahhh... I see... you want to create a services engine that runs completely in the background. I'll leave the above in anyway (it is still useful). More below.... > 2. In the Python main file I fumbled to port the code from the > URLServices project inside this function: > > int main(int argc, const char **argv) > { > NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; > URLServices *serviceProvider = [[URLServices alloc] init]; > > NSRegisterServicesProvider(serviceProvider, @"URLServices"); > > NS_DURING > [[NSRunLoop currentRunLoop] configureAsServer]; > [[NSRunLoop currentRunLoop] run]; > NS_HANDLER > NSLog(@"%@", localException); > NS_ENDHANDLER > > [serviceProvider release]; > [pool release]; > > exit(0); > return 0; > } > > > to something like the following: > > # import classes required to start application > import PyServices > > from Foundation import NSAutoreleasePool, NSRunLoop, NSLog > from AppKit import NSRegisterServicesProvider > > # stuff ported from URLServices' main.m > ## pool = NSAutoreleasePool.alloc().init() > serviceProvider = PyServices.PyServices.alloc().init() > NSRegisterServicesProvider(serviceProvider, "PyServices") > # NS_DURING > NSRunLoop.currentRunLoop().configureAsServer() > NSRunLoop.currentRunLoop().run() > # NS_HANDLER > NSLog("%@", localException) > # NS_ENDHANDLER > serviceProvider.release() > ## pool.release() > > # pass control to the AppKit (from template __main__.py) > # import sys > # sys.exit(AppKit.NSApplicationMain(sys.argv)) > > where it seems that using the autorelease pool is a bad idea (com- > plains with "Cannot retain an autorelease pool") and replacing the > NSApplicationMain function with an NSRunLoop does not quite seem > to work like expected, resulting in crash reports being dumped. Yes -- this is a bug in the bridge, sort of. There is a category on NSAutoreleasePool that allows you to push and pop pools, as needed. @interface NSAutoreleasePool (PyObjCSupport) + (void) pyobjcPushPool; + (void) pyobjcPopPool; @end So, just call... NSAutoreleasePool.pyobjcPushPool() ... instead of NSAutoreleasePool.alloc().init(). PyObjC maps Objective-C exceptions to Python and vice-versa. That is, you can do: try: NSRunLoop.currentRunLoop().configureAsServer() NSRunLoop.currentRunLoop().run() except: ... handle exception as you would any other Python exception ... > I'm barely beginning to really play with pyobjc and so I don't have > a detailed idea yet of how it works, while at the same time I'm un- > dusting some Cocoa knowledge... Since it is only some 15 KB I dare > to attach my initial code here right away. > > If anybody can give a helping hand this could be a nice demo for > pyobjc since text, URL and file management is *much* easier in Py- > thon than in the Foundation Kit... Agreed. I haven't seen URLServices -- is there any reason not to have some kind of a UI? I.e. does it need preferences, documentation or any kind of a UI? If so, I would punt on creating an 'invisible' service -- they are handy for a lot of things, but it also means that the user cannot easily kill/quit the service. If the service is every invoked from a disk image or other removable media, the media cannot be ejected until the service is killed. b.bum |