Goal: Iterate through all my (OS X Safari) browser windows and make a list of titles and urls which is then placed in the clipboard ready to be pasted into an email or blog post.
This is an update to Tab Dumping in Safari. That still works well as the basis for extending any Cocoa-based application at runtime, but it relies on SIMBL, which while it is a great bit of code, essentially is abusing the InputManager interface. Some developers and users shun such hacks, and at least one Apple application checks for them at startup and warns you from using them.
I have been running the WebKit nightlies, which are like Safari, but with newer code and features (most importantly to me right now, a Firebug-like developer toolkit). WebKit warns at startup that if you’re running extensions (such as SIMBL plugins) it may make the application less stable. I was running both Saft and my own tab dumping plugin, and WebKit was crashing a lot. So I removed those and the crashes went away. I miss a handful of the Saft extensions (but not having to update it for every Safari point release), and I found I really miss my little tab-dumping tool.
I toyed with the idea of rewriting it as a service, which would then be available from the services menu, but couldn’t figure out how to access the application’s windows and tabs from the service. So I tried looking at Safari’s scriptable dictionary, using the AppleScript Script Editor. Long ago, John Gruber had written about the frustration with Safari’s tabs not being scriptable, but a glance at the scripting dictionary showed me this was no longer the case (and probably hasn’t been for years, I haven’t kept track).
I am a complete n00b at AppleScript. I find the attempt at English-like syntax just confuses (and irritates) me no end. But what I wanted looked achievable with it, so I armed myself with some examples from Google searches, and Apple’s intro pages and managed to get what I wanted working. It may not be the best possible solution (in fact I suspect the string concatenation may be one of the most pessimal methods), but it Works For Me™.
In Script Editor, paste in the following:
set url_list to ""
-- change WebKit to Safari if you are not running nightlies
tell application "WebKit"
set window_list to windows
repeat with w in window_list
try
set tab_list to tabs of w
repeat with t in tab_list
set url_list to url_list & name of t & "\n"
set url_list to url_list & URL of t & "\n\n"
end repeat
on error
-- not all windows have tabs
end try
end repeat
set the clipboard to url_list
end tell
I had to use AppleScript Utility to add the script menu to my menu bar. From there it was easy to create script folders that are specific to both WebKit and Safari and save a copy of the script (with the appropriate substitution, see comment in script) into each folder. Now I can copy the title and URL of all my open tabs onto the clipboard easily again, without any InputManager hacks.
I had some recollection that is a way to do this from Python, so I looked and found Appscript. I was able to install this with a simple easy_install appscript and quickly ported most of the applescript to Python. The only stumbling block was that I couldn’t find a way to access the clipboard with appscript, and I didn’t want to have to pull in the PyObjC framework just to write to the clipboard. So I used subprocess to call the command-line pbcopy utility.
#!/usr/local/bin/python
from appscript import app
import subprocess
tab_summaries = []
for window in app('WebKit').windows.get():
try:
for tab in window.tabs.get():
name = tab.name.get().encode('utf-8')
url = tab.URL.get().encode('utf-8')
tab_summaries.append('%s\n%s' % (name, url))
except:
# not all windows have tabs
pass
clipboard = subprocess.Popen('pbcopy', stdin=subprocess.PIPE)
clipboard.stdin.write('\n\n'.join(tab_summaries))
The remaining hurdle was simply to put the Python script I’d written into the same Scripting folder as my AppleScript version. For me this was ~/Library/Scripts/Applications/WebKit/. When run from the scripts folder, your usual environment is not inherited, so the #! line must point to the version of Python you are using (and which has Appscript installed). You should also make the script executable. Adding .py or any other extension is not necessary.
Overall, while I found AppleScript to be very powerful, and not quite as painful as I remembered, I found the Python version (warts and all) to be easier to work with. Combined with the fact that the script folder will run non-Applescript scripts, this opens up new worlds for me. I have hesitated in the past to write a lot of SIMBL-based plugins, tempting though it may be, because they are hacks, and they run in every Cocoa-based application. But adding application-specific (or universal) scripts, in Python, is pure, unadulterated goodness.
]]>Current features of Drawing Board:
There is basic file handling, which may be useful as example code for learning Cocoa programming using Python. I’m still working at adding drawing tools besides freehand drawing, and I have ideas for a lot of other things, but the main idea is to keep the program from getting in your way–to keep as close to possible as sketching with a pencil on paper, but to make the process of creating simple animations easier.
Two features that are pretty close, and are important to the goal of the project, are export as Flash and export as Quicktime. Those will be coming sooner, rather than later.
The project page is at http://livingcode.org/project/drawingboard and you can find links there to the binary download, the source repository, and the bug/feature tracker. I’ve also set up Google groups for the Living Code projects: http://groups.google.com/group/livingcode-users and http://groups.google.com/group/livingcode-developer for ongoing discussions.
A few people have seen me demo this program at the Vancouver Python Workshop and at Bar Camp Vancouver and expressed an interest, so I hope it can be of use, both in learning to program OS X with Python, and for creating animations. Please let me know what you find useful and what could be improved!
]]>The VanPyZ meeting will be at the Uniserve office, where Paul and I now work, Suite 1550, 1055 West Hastings Street, in Vancouver, BC. Mark Mayo took time out from his new baby (congratulations, Mark!) to create an Upcoming event for it. We’ll be going out for drinks afterwards, so come hang out with the Vancouver pythoneers.
]]>Before diving into the software, just a note on the hardware. I’m going with a black Macbook for several reasons. First, it looks really cool, and I can’t deny that appeals to me. Second, it is somewhat smaller and lighter than the smallest Macbook Pros, and since I carry my laptop to and from work every day by foot or bicycle, that matters (I’m getting two power bricks so I can leave one in each location to save even more weight). Third, the wifi reception is much better. Encasing an antenna in plastic appears to work better than encasing it in aluminum, go figure. Macbook Pros are more powerful, but only a bit, and mainly for heavy-duty 3D, which isn’t really my thing. We’ll see how much this matters in practice. White vs. Black Macbook: the white ones are rumored to stain really easily, plus did I mention that the black ones look really freaking cool? The Macbook is cheaper than the Macbook Pro, but that’s not a big factor in why I’m choosing it (since once again, work will own it, just a different work–more on that later). The Macbook also gets better battery life, is slightly less crotch-scaldingly hot, and so far I’m liking the keyboard a lot.
I used to use TigerLaunch (free, open source), and still do on other Macs in the family, but once you’ve tried Quicksilver (free, beta) there’s no going back. I’m still working on becoming a Quicksilver power user, but just the app-launching and file-searching facility is worth using this tool for. TigerLaunch is great for its simplicity, and Quicksilver is great for making you wonder how you ever got along without it. I’ve heard wonderful things about PathFinder ($34.95USD) and keep meaning to try it, but haven’t gotten to it yet. First I need to learn how to write Quicksilver plugins in Python…
I used to use Pico on Unix and BBEdit Lite on Mac OS 7, back in the day. I couldn’t stand either vi or emacs. I wanted a T-shirt that read, “I’d rather die than use vi.” Then I worked at a company where the only editor you could count on being installed was vi and I began to get its keystrokes etched into the memory of my fingers. I’m not even that good at it, but sometimes vi can be so damn fast. Of course, I don’t use classic vi, but a nice modern Vim (free, open source). I’ve tried to switch back to Mac editors a few times: TextWrangler (free, the OS X incarnation of BBEdit Lite), SubEthaEdit ($35USD, great for collaborative editing), Smultron (free, open source). These are all great editors, but each one lacked something that kept me from switching over completely. I had been hearing about TextMate (€39EUR) for some time, but had trouble getting excited over a commercial editor when there were so many free ones to choose from. I finally gave it a serious try and I’m hooked. It still isn’t as easy to search as vi is, but the way it can be expanded on with plugins and the general fit and feel are great.
Safari is shaping up to be a great browser, fast and powerful. There are a few details that are missing, but nearly all of them are satisfied with the Saft ($12USD) extension, which provides great ad filtering, improves the way windows and tabs are handled, and gives shortcuts for accessing search sites from the address bar, among many other features. Safari itself takes two kinds of plugins, Netscape-style plugins and WebKit plugins. Unfortunately, these are only triggered when their target mime types are loaded, so they can’t readily be used to extend the way the application itself works. So all of the Safari extensions that I’m aware of are implemented as InputManagers (see my post on TabDump for more on InputManagers). My TabDump extension is the other extension that I find indispensable, which is why I wrote it, but more important than TabDump itself is the example it gives for writing your own extensions to Safari or any other (Cocoa) application. That’s what I love about OS X, you can get into it and take control of your own machine, make it your own. You can do that with Linux, of course, but the high-level of Cocoa applications make a huge difference. Back to browsers, I also recommend the Flip4Mac plugin, to allow your browser to play Windows Media files directly. Finally, I also keep Firefox (free, open-source) around for its advanced features. It’s not my main browser, but it has features that show where the browser (and all applications) will be going in the future. I may have to do a separate post just about the cool features coming in Firefox 2 (currently in beta) and Firefox 3. Other good browsers to keep around for testing are Camino (the Firefox engine with a Cocoa UI) and Opera (free). For more Safari plugins and extensions than you could ever use, check the listings at Pimp My Safari.
I realize that Safari now supports RSS feeds in some fashion, but it really isn’t a newsreader. I’m not crazy about trying to put every possible application into the browser. If you have to work betwwen Windows, OS X, and Linux, or some combination thereof, then things like GMail and Bloglines can be a godsend, otherwise you’re probably better off with a dedicated desktop app. NetNewsWire ($29.95USD) is a desktop app that is so good, it was the most popular newsreader (by far) for a long time, even though it only ran on OS X. Since being acquired by NewsGator, it’s still as good, but now synchronizes your feeds with the NewsGator site, so you get the best of both worlds: a top-notch desktop app, and a webapp that stays sychronized with it. I have a feeling that in the near future, nearly all applications will work this way. And if you can’t afford the price, they still offer NetNewsWire Lite for free (and I happily used it for a couple of years before upgrading).
Apple gives you a decent email program with OS X, nothing too fancy. It keeps getting better (mostly) with each new release. There has been some complaints about Mail.app in OS 10.4 changing to a proprietary format (which they did in order to integrate with Spotlight) and I’ll have more to say about that in a future post. I have tried using Gmail (when I was mostly using Windows at work) and Thunderbird (free, open-source), and coming back to Mail.app is like a breath of fresh air. For nearly everything I do, Mail.app is better. It’s far from perfect, and sites like HawkWings specialize in plugins and extensions for Mail.app, but it sure works for me. I do wonder why email programs are still so hard to get right. Since email is basically the oldest use of the internet (and networks generally), and the original “killer app,” if we don’t know how to do email yet, what hope do we have for anything that’s actually complicated? Of course, perhaps because they have been around for so long, choice of email programs tends to be very personal, so all I can say is, Mail.app works for me. I also recommend the AttachmentScannerPlugin by James Eagan, who also provides a tutorial on writing plugins for Mail.app which is generally applicable to extending Cocoa programs and using their undocumented private APIs. Also, he uses Python and PyObjC to write the plugin, which makes me happy.
Regular reader(s) were probably wondering how I was going to get that plug for Python and PyObjC in there, eh?
]]>Vancouver is such a great place. There’s the standard stuff: Great weather, beautiful beaches, forests and mountains. Then there is all the rest: lots of interesting geeks of various stripes, cool places to work, small conferences to attend. I’ve had so much more fun at the Vancouver Python Workshop and Northern Voices than at big anonymous events like JavaOne and OOPSLA. There’s just no contest. And BarCamp is all about being a small, intimate event–that appears to be its whole entire purpose. I can hardly wait.
]]>Last weekend I attended the Vancouver Python Workshop here in town. I love this conference because it’s small and local, but attracts a great crowd. There were a few folks I know mainly from the edu-sig mailing list. Guido was there to give the keynote, I got to talk with Ian Bicking a fair bit, and I had never realized that Toby Donaldson is a fellow Vancouverite.
Guido (van Rossum, creator of Python, for anyone reading this who is not soaking in Python every day) gave a keynote on the state of the upcoming Python 3000. Since I make an effort to keep up with this there wasn’t much in the way of surprises here, but it’s always good to hear it from the BDFL’s mouth. I’m excited by what’s happening in Python lately, both in language changes and in the libraries, and I think Python 3000 will be a move in the right direction when it comes.
Jim Hugunin, initiator of the Numeric extension, Jython (Python on the Java virtual machine), and most recently IronPython (Python on Microsoft’s dotNet virtual machine) also gave a keynote. He showed how having Python running in the same runtime as your C# code allows you to have stack traces all the way down, which is an advantage over PyObjC. It also helps to be binding to a language which is garbage collected. In PyObjC they go to lengths to make the runtime behave like garbage-collected Python, but there are still some edge cases that can sneak in and bite you. Of course, Apple’s announcement of garbage-collected Objective-C in XCode 3 next year should fix that. Jim’s an engaging speaker and seems like a really nice guy. And since both Google (”do no evil”) and Microsoft (”evil empire”) were represented, balance was maintained in the Force.
After the keynotes there was a reception at the Steamworks brew pub, which is a great place. Staying up late drinking may not be the best way to kick off a conference that starts at 8 am the next day though. I was home by midnight, but I heard from many others that they were out until 3 am. Ouch.
This was an intense day for me. I was scheduled to give a presentation, but then Paul (Prescod, my friend and co-worker, and one of the conference organizers) asked me to also give a lightning talk and be on a panel discussion. Since I didn’t have time to prepare much for these I was a bit nervous. On top of already being nearly sick with stage fright getting ready for the presentation I did prepare for.
Guido hadn’t had time for questions after his keynote the night before, so the conference kicked off with a Q&A session for him. After that was the lightning talks, and I gave the first one. Aside from not having time to prepare, I had never given a lightning talk before. I demo’d Drawing Board, the animation tool I’ve been working on for my kids, which filled my five minutes pretty easily. I got some nice feedback about it too, which is especially gratifying since after I’ve been working on something for awhile I tend to only see all the problems I know it has, rather than what is good about it. Drawing Board has been a real struggle for me, both in learning PyObjC and the “Cocoa Way” of doing things, and trying to push it into new territory, so I was pleased with how its first public demo was received.
After the lightning talks I attended the beginning of Paul’s tutorial for newcomers to Python to try and pick up some tips. After lurking on the edu-sig mailing list, I’m trying to organized some Python classes at my daughter’s school for the kids there. Paul’s approach was more towards people already programming in other languages, so I didn’t get much there that I could use, but it gave me some things to think about at least.
Ian Bicking’s talk on WSGI got extended from one 45-minute slot to two, and I only got so sit in on the first half, because it overlapped with my presentation. I loved his hand-drawn slides and his take on the “Internet is a series of tubes” meme. He’s a good presenter and WSGI is important stuff for Python, giving it the kind of basic web framework that Servlets gives to Java, which Python has desperately needed for a long time. Now if I could just wrap my brain around Paste I think I could achieve web enlightenment…
My talk, “OS X, Python, and Kids” went well. I used the whole 45 minutes (not like last time I presented when I went nervously through my presentation in 10 minutes and was then left embarrassed on stage), took some good questions, and people seemed to be engaged. I’ve posted my slides, with notes based on what I was talking about during the presentation, here (3MB PDF). The feedback from this presentation, where I used several of my projects as examples of what you can accomplish with PyObjC, was very good, at least from the folks who came up to me. A couple of people even sounded like they were on the fence about whether to switch to Macs and I might have given them a push.
After my talk, and lunch, was the panel discussing how to embed C, C++, C#, and other languages in Python. We had Jim Hugunin (IronPython), Tom Weir (using SWIG on a proprietary project), Samuele Pedroni (of PyPy), and me. There was no moderator, and while I’ve followed the various tools to wrap libraries for Python, and I’ve used a lot of libraries that are wrapped for Python, I haven’t actually done much wrapping myself, so I tried to be a moderator and get the conversation going. SWIG seemed to be used by the most people in the room, and while as a user of SWIG I’ve struggled with it, since any project that uses it seems to be dependent on a very specific version which you then have to find, download, and install. So I’m not a big SWIG fan. On the other hand, folks who use it for wrapping C code can freeze the version they’re using and more or less rely on it, so it works great for them (although I guess debugging is still a bear). I still thing SWIG is part of the problem, not part of the solution.
At the end of day one was a great BBQ at Locarno Beach. Daniela and I took the kids, then we stayed on the beach to watch the final night of the Celebration of Light fireworks. It was Mexico’s turn to light up the sky and they did a spectacular job, winning the four-night competition with Italy, China, and the Czech Repulic. So then there was an encore display, and then we tried to get home through the gridlock that follows fireworks in Vancouver. We got home by midnight, washed the sand of the kids, and put them to bed.
After two late nights (for me) and early mornings (for weekends) I’m beginning to feel really beat. I’m also coming down from the adrenaline high I was on yesterday, before and after presenting. I was also distracted by trying to implement the Turtle module in the standard library from Tkinter to PyObjC (and simultaneously port my Kutia turtle program from Tkinter to PyObjC). I actually got pretty far with it, but probably need to post some questions on the PyObjC mailing list to get it finished.
The morning started with a status report on PyPy from Samuele Pedroni, one of the furthest travellers to the conference, coming in from Italy. I’m fascinated by the progress PyPy has made, going from thousands of times slower than CPython to only a couple of times slower. PyPy is Python implemented in Python, specifically implemented in a subset of Python which can be efficiently compiled to C, called RPython. This is similar to how Squeak Smalltalk is implemented (a small, compilable subset of Python is used to implement the rest). The advantage of PyPy is that because the language definition itself is relatively high level, they can quickly implement all kinds of interesting things, like Stackless Python, compiling python to dotNet or Java, even experimenting with compiling Python to Javascript. It’s a very interesting project and a lot of fun to watch. I’m rooting for them.
Next was Wilson Fowlie’s presentation on PyParsing. I had lunch with Wilson and he’s a great guy. He shouldn’t be so self-deprecating in his presentation because he had great information and it was an interesting topic. Parsing libraries are an area where Python has an excess of riches, and it can be difficult to decide what to use. Wilson made his choice and is happy with it, and makes a pretty convincing case for choosing PyParsing. It would be interesting to see projects such as reStructured Text, PyMarkdown, and PyTextile all built on top of the same basic parsing engine. Next time I have a file type to parse I will give PyParsing a try, based on this Wilson’s presentation.
After lunch, James Thiele gave a talk on embedding domain specific languages in Python, mainly revolving around the hooks Python gives you for importing libraries. By using the import hooks you can import code as Python objects which is not Python code. This is pretty cool, especially when coupled with the previous talk on Parsing.
There was a panel discusson on Little Languages, which both Wilson and James were on, but by now fatigue and distraction were taking their toll and I wasn’t a very good audience.
After lunch there was a presentation by Leonardo Almeida on Python and Zope in Brazil, which was basically that they are both widely used, in both business and government, but companies that use them don’t want to talk about it because they regard Python as their secret weapon.
David Ostrowski gave a talk on teaching with Python in graduate computer science classes, but I didn’t really enjoy the presentation. I was too tired and his points about Python were too familiar. I didn’t hear anything new, but it all appeared to be new to him. More power to him, I guess.
There were some good lightning talks at the end of the conference, including Brian Quinlan showing a hack he’d whipped up after the earlier talk on importing domain specific languages. He showed how to use the import hooks and the csv module to load comma separated value files as native python datatypes.
To close the conference, Ian Caven gave a great show about how he and a partner built up a successful business using Python (and OS X). They restore movies for DVD release and they use hundreds of Macs running in parallel, with all the processing managed by Python. It’s a great story and Ian is a great presenter, so that was a high note to end the conference on.
Looking forward to next time, for sure. Thanks to all the organizers!
]]>Pastels is an example project for creating an OS X screensaver in Python using PyObjC. By extension it could be used as an example for building nearly any plugin or bundle for OS X. It started when I had an idea for drawing a simple squiggle, over and over, while cycling the colours and moving the squiggle around. I was very pleased with how it turned out.
Project page: http://livingcode.org/project/pastels/
It’s also my first attempt at hosting an open-source project at Google with their new hosting program. If it works out well I will add more of my projects there, which will save me trying to set up and configure Subversion on Dreamhost for public access (probably not difficult, but one more thing I don’t have to do means more time for writing example code and tutorials).
I’m working on the tutorial text to go along with this project, so ask any questions you have and I’ll try to get to them in the tutorial.
If you are seeing this on my site (as opposed to the Atom feed), there are some changes I’m making to the site that I’d like to point out. I’ve added pages for projects and mini-projects which use the same stylesheet and includes as the rest of the site. I know the stylesheet is uglyless than completely attractive right now–the first thing was to get everything factored and consistent, then to make it pretty. The projects page only has one item on it (Pastels), but that should be changing now that I have the infrastructure set up the way I want it. Nearly all the projects I mentioned in my presentation at the Vancouver Python Workshop will get their own pages soon. More about that in my next post.
]]>I’ve been meaning to post about the article, but I keep getting caught up preparing my presentation for the Vancouver Python Workshop on Saturday (the workshop starts Friday August 3rd and goes through Sunday August 5th). My talk this year is on using [PyObjC] to create applications and plugins for OS X using Python. I’ll get the slides up after, as soon as I can. I’m also planning on doing a shorter version of this talk at Bar Camp Vancouver which is 6 pm Friday, August 25 to 6 pm Saturday, August 26.
And I should have mentioned the Google talk at the Vancouver High Performance Computing User Group before it happened on July 27th. Narayanan ‘Shiva’ Shivakumar came up from their Seattle office to present mostly old information from their published papers such as The Google File System, MapReduce, and BigTable (video). The talk over beers after was fun, and it was good to see my friend Mark and find out he has a blog, even if it’s over my head much of the time.
Well, that’s my update dump. More stuff on actually using PyObjC coming Real Soon Now.
]]>def screenShot(self): rect = NSScreen.mainScreen().frame() image = NSImage.alloc().initWithSize_((rect.size.width, rect.size.height)) window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_( rect, NSBorderlessWindowMask, NSBackingStoreNonretained, False) view = NSView.alloc().initWithFrame_(rect) window.setLevel_(NSScreenSaverWindowLevel + 100) window.setHasShadow_(False) window.setAlphaValue_(0.0) window.setContentView_(view) window.orderFront_(self) view.lockFocus() screenRep= NSBitmapImageRep.alloc().initWithFocusedViewRect_(rect) image.addRepresentation_(screenRep) view.unlockFocus() window.orderOut_(self) window.close() return image
I cribbed this several months ago from some example Cocoa code, but forgot to make a note of where I got it. If anyone recognizes this pattern, please let me know so I can attribute it.
[Update 2006-03-10]
Nils Soffer pointed out:
A simpler solution:try:screenshot = 'screenshort.pdf'os.system('screencapture -i %s' % screenshot)image = NSImage.alloc().initWithContentsOfFile_(screenshot)finally:os.remove(screenshot):-)
Which reminds me, you can also use the command-line screencapture -c to go to the clipboard instead of a file. This might be faster, but I haven’t tested it yet. I still think there should be a Cocoa method to grab the underlying framebuffer of the screen. Besides the -c flag to send the image to the clipboard there is also a -C flag to capture the cursor as well.
[Update 2008-01-22]
Digging through old mail as I move this entry from my custom blog setup to Wordpress, I found where I got this code from originally. It’s from the Irate Scotsman’s Screen Sharing code. Thanks, Matt!
]]>My quiet time is going to continue for a bit longer, but while I won’t apologize, I will at least explain. Here’s what I’m doing instead with the couple of hours I have between putting the kids to bed and going to bed myself.
More news as it happens.
]]>