August 21, 2008.
So lets say you're writing a PyObjC application, and you're deciding how you'll represent your data. You fire open a nib (xib, these days) and stare at
NSOutlineView for a while before picking one of the two. Lets say you chose
NSOutlineView. You're going to need a way to create new nodes. Well, no sweat, just throw an
NSButton into the fray and use the
NSAddTemplate icon to make it clear what it does.
Here comes the big question though: how do you remove data from your shiny new
NSOutlineView? The easy answer is to add another button (perhaps with the
NSRemoveTemplate icon, you audacious rascal). If you're feeling really ambitious you may even bind a key to that button so that the delete key works.
Let a little time pass, though, and you'll start to have a nagging feeling. Something just doesn't feel right. You're never using that remove button, you're always pressing the delete key. Hell, once you meant to hit add and accidentally deleted something. You're finally ready to accept the truth: you just want to press the delete key, and you don't want no stinking remove button.
To accomplish that you'll need to subclass
NSTableView if thats what you're using). It'll look like this:
from Foundation import * class WRLOutlineView(NSOutlineView): def keyDown_(self, event): key = event.charactersIgnoringModifiers() flags = event.modifierFlags() & 0x00FF if ord(key) == 127 and flags == 0: # Do your removal magic here. pass else: super(DeletableOutlineView,self).keyDown_(event)
Its a pretty simple trick, but can be a bit confusing to figure out. Specifically, it can be bewildering to try to figure out what non-textual characters are in Python. Here it was difficult to figure out how to reference the delete character so that I could check to see if the incoming key was a match. Thankfully the builtin function
ord turns ascii characters into their integer value, which made the comparison easy.
For completeness, here is the code I was using to actually remove the node from the table. At the line
# Do your removal magic here. I had the code:
I was using a custom datasource and delegate, and in the delegate the
removeList_ method looked like this:
@objc.IBAction def removeList_(self,sender): item = self.outlineView.itemAtRow_(self.outlineView.selectedRow()) parent = self.outlineView.parentForItem_(item) self.data.remove(item) self.outlineView.reloadItem_reloadChildren_(parent,True)
In that snippet
self.data is an instance of a class containing my tree structure, and
self.data.remove is a method that will recursively search the tree structure to remove the node supplied as the argument.
I'll be continuing to scribble down these short PyObjC tips and tricks as I run into them, so let me know if there is anything you're particularly interested in looking at and I'll merrily investigate.
I'm always glad to hear your questions and comments.