Cocoa is cool and Objective-C is a great language, but I like/prefer Python and the PyObjC bridge makes my life easier and programming actually interesting/fun for me.
This Xcode project is an example of how easy it is to create useful custom views in PyObjC with an example of a graph view that displays a circular queue/buffer. Everything is done starting with an Xcode project which is available for download (76KB compressed archive) and modification. Here is a preview of what you can use out of the box:


The easiest and smallest part of the project is the RingBuffer class that implements a very simple circular queue/buffer and provides direct access to the list that stores the information:
# # RingBuffer.py class RingBuffer: def __init__(self, size): """ init with # of elements in the queue """ self.data = [ None for i in xrange(size) ] def append(self, x): """ take away one and put one in """ self.data.pop(0) self.data.append(x) def get(self): """ return the list so we can manipulate it """ return self.data
There's not a lot of rocket science there, but it's amazing how much code you can eliminate by taking advantage of Python's inherent capabilities. In this case, list initialization is a breeze and removal/insertion of new elements could not be easier.
Next up is the CBGraphView class itself:
It seems that 2009 is starting off with a Twitter phishing problem. Users are being "tricked" into clicking on links that then do all sorts of bad things.
While these attacks can come via "regular" URLs, we will eventually see phishers use sites like TinyURL to mask their destination, making it that much harder on desktop and in-stream protection mechanisms.

If you run OS X 10.5 (Leopard), I offer you Throw Them Back (592K compressed archive) as a way to stay a step ahead of the bad guys. This version (0.9) has two modes. The most useful one is where you start the app up, then copy a URL to the clipboard and invoke the global shortcut key (Command-Control-J :: this will be a configurable option in the next release) which will take the URL, expand it (if necessary) and run it through the PhishTank screening service to see if it is a "bad" site. PhishTank is run by OpenDNS and is a great service, but it is not comprehensive (no service would be). While you can pretty much trust the items tagged as phishing (indicated by a fish icon that will pop up in the window), you should not assume that a lack of notification equals an OK site.
If you are determined to press on even if you do not recognize an expanded URL (or are unsure about a standard URL), use the arrows next to the URL fields to bring up a drawer that will render the site filtered through Google Mobile. That should take care of any malicious code and let you see if this is a site you really want to visit.
The URL you pasted is put back on the clipboard, so you can use it anywhere you like and you can copy the short or expanded URL from the displayed fields.

The second mode is where you just enter or paste a URL right into the field in the window. While that is useful, it is not nearly as speedy.
I have a bunch of items on the TO DO list, including:
I welcome feedback and suggestions in the comments or on Twitter (@hrbrmstr).
| If you do like the software, please consider clicking the button on the right. I'm seriously contemplating making version 1.0 (this is version 0.9) something you have to pay for to get all the additional features. If you act now, you will receive free upgrades forever for just $3.99USD. Version 1.0 pricing will be $4.99USD. |
from objc import YES, NO, IBOutlet, IBAction
from Foundation import *
from AppKit import *
'''
a great deal of work is done in the nib/xib file.
it has the window and button and the menu that is set as
the status item. each menu item is wired either to a method
below or one in the first responder.
'''
class xAppDelegate(NSObject):
statusBar = None # holds our NSStatusBar
mStatusMenu = None # we store our status bar menu item here
mbImage = None # the image that will be our initial status item
redImage = None # an alternate status item
statusSub = objc.IBOutlet() # the menu that will be attached to the status bar item
prefsWindow = objc.IBOutlet() # the mock "prefs" window
button = objc.IBOutlet() # button in the mock "prefs" window that will turn status item red
# show "preferences" - called from menu item
@IBAction
def editPreferences_(self, sender):
self.prefsWindow.makeKeyAndOrderFront_(sender)
NSLog("Editing Preferences")
# returns our status bar icon to the starting image
@IBAction
def makeItNormal_(self, sender):
self.mStatusMenu.setImage_(self.mbImage)
NSLog("making it normal")
# turns our status bar icon to all red - called from button press
@IBAction
def makeItRed_(self, sender):
self.mStatusMenu.setImage_(self.redImage)
NSLog("making it normal")
# when we load the nib, get the images as well
def awakeFromNib(self):
self.mbImage = NSImage.imageNamed_("image.jpg")
self.redImage = NSImage.imageNamed_("red.jpg")
# we wired up the "Quit" item in our status bar menu to "terminate:" in the first responder
# this gets called when we really are going down
def applicationWillTerminate_(self, notification):
NSLog("We're going down")
# when the app instantiates our class, set the menu bar
def applicationDidFinishLaunching_(self, sender):
statusBar = NSStatusBar.systemStatusBar() # grabs the system status bar
# we may change the status bar item to a larger image, so variable length
self.mStatusMenu = statusBar.statusItemWithLength_(NSVariableStatusItemLength)
self.mStatusMenu.setImage_(self.mbImage) # set the initial image
self.mStatusMenu.setHighlightMode_(True) # we want visual feedback when clicked
#self.mStatusMenu.setTitle_("Status Menu") # if you uncomment this, you get a "word" menu vs image menu
self.mStatusMenu.setMenu_(self.statusSub) # sets the menu from the NIB
The source code is commented fairly well, but upon launch, the awakeFromNib and applicationDidFinishLaunching methods will be called and you should be able to follow what is happening. As the code states, a great deal of work is done in the nib/xib file where everything is wired up:

