PhotoBomb

Ubuntu Opportunistic Developers Week March 2010 - SHOWCASE: Photobomb - Rick Spencer - Mar 3 2010

(03:02:05 PM) rickspencer3: hi all
(03:02:07 PM) rickspencer3: soooo
(03:02:17 PM) rickspencer3: I'm here to talk about photobomb
(03:02:46 PM) rickspencer3: jono thought some folks might be interested in hearing about some of the technology that I used and such
(03:03:25 PM) rickspencer3: to make it easier to walk through, I put some screenshots and some notes:
(03:03:28 PM) rickspencer3: here:
(03:03:28 PM) rickspencer3: http://theravingrick.blogspot.com/2010/03/photobomb-featured-app-for-opp-dev-week.html
(03:04:06 PM) rickspencer3: shall I begin with photobomb?
(03:05:18 PM) rickspencer3: I wrote the first iteration of Photobomb one Saturday afternoon.
(03:05:34 PM) rickspencer3: does everyone know what it does?
(03:05:43 PM) rickspencer3: it's basically a hacked together photo editor
(03:06:09 PM) rickspencer3: that let's you mash up images from sources like your feeds, your computer, the web, your web cam, etc...
(03:06:13 PM) rickspencer3: do some fun stuff
(03:06:20 PM) rickspencer3: and then you can save the image or tweet it
(03:06:34 PM) rickspencer3: I wrote this totally just for the fun of it in my spare time
(03:06:42 PM) rickspencer3: during January, I set the goal of writing one new feature a night
(03:06:48 PM) rickspencer3: so I got pretty far with it that way
(03:06:56 PM) rickspencer3: of course, it's a bit buggy still
(03:07:00 PM) rickspencer3: but it's kinda fun to use
(03:07:14 PM) rickspencer3: anyone who wants to contribute to it, fork it, whatever
(03:07:22 PM) rickspencer3: by all means, have a go
(03:07:24 PM) rickspencer3: anyway ...
(03:07:37 PM) rickspencer3: I used Quickly to get the project started, so it was just a matter of laying out some UI in Glade, and then figuring out how to manipulate images.
(03:07:52 PM) rickspencer3: I used Python Imaging Library (PIL) to create a cropped version of that little squirrel, and to paste it onto the picture.
(03:08:00 PM) rickspencer3: PIL is a complete and power library. It was a bit tough figuring out the masking to crop the squirrel image as desired, but I eventually managed.
(03:08:16 PM) rickspencer3: I ended up moving away from PIL, but from what I saw, I was impressed
(03:08:22 PM) rickspencer3: any questions do far?
(03:09:12 PM) ClassBot: mhall119 asked: where can we get the code?
(03:09:24 PM) rickspencer3: it's in a junk branch
(03:09:27 PM) ***rickspencer3 gets
(03:09:43 PM) rickspencer3: https://code.edge.launchpad.net/~rick-rickspencer3/+junk/photobomb
(03:09:46 PM) rickspencer3: party on!
(03:09:54 PM) rickspencer3: I think it might be in my ppa too
(03:10:08 PM) rickspencer3: ok
(03:10:17 PM) rickspencer3: so following along to the next screen shot
(03:10:51 PM) rickspencer3: I added the ability to choose an image to stick in there
(03:11:04 PM) rickspencer3: and photobomb with that little squirrel
(03:11:12 PM) rickspencer3: It turned out to be tedious to write code to open an image file.
(03:11:18 PM) rickspencer3: So I added a prompt to quickly.widgets to handle opening files.
(03:11:23 PM) rickspencer3: Having your own library of reusable widgets is quite a luxury.
(03:11:30 PM) rickspencer3: if you look closely at the screenshot ...
(03:11:35 PM) rickspencer3: Note that I didn't know the gtk.FileChooser API that well, so made a couple of early mistakes, such as handling the existing file replace functionality myself.
(03:11:47 PM) rickspencer3: Didn't realize that the API could handle it
(03:12:02 PM) rickspencer3: but in any case, the shell of photobomb is PyGtk of course
(03:12:25 PM) rickspencer3: and I had to learn a bit about some areas of Gtk that I hadn't used too much
(03:12:32 PM) rickspencer3: any questions about that one?
(03:13:23 PM) ClassBot: mhall119 asked: Did you already know Glade before you started this?
(03:13:26 PM) rickspencer3: oh yes
(03:13:27 PM) rickspencer3: quite
(03:13:37 PM) rickspencer3: at first, you think Glade is your enemy
(03:13:46 PM) rickspencer3: but after a while, you learn it is our friend
(03:13:52 PM) rickspencer3: but Glade can be a bit hard to learn
(03:14:01 PM) rickspencer3: and it isn't designed to do everything
(03:14:11 PM) rickspencer3: so writing gtk.UIBuilder code is still part of hte process
(03:14:24 PM) rickspencer3: quickly makes this a bit easier, by giving each window a builder function
(03:14:30 PM) rickspencer3: I mean builder member
(03:14:33 PM) rickspencer3: so you can go:
(03:14:54 PM) rickspencer3: self.builder.get_object("hbox1").pack_start(my_widget)
(03:15:16 PM) ClassBot: mhall119 asked: does photobomb run on Karmic?  or just Lucid?
(03:15:20 PM) rickspencer3: well
(03:15:26 PM) rickspencer3: it's packaged for Lucid
(03:15:36 PM) rickspencer3: because it uses quickly-widgets
(03:15:43 PM) rickspencer3: which is in the quidgets project
(03:15:48 PM) rickspencer3: however, if you want to run it
(03:15:52 PM) rickspencer3: it shouldn't be too hard
(03:15:59 PM) rickspencer3: just branch photobomb
(03:16:07 PM) rickspencer3: then branch quidgets
(03:16:34 PM) rickspencer3: then stick quidgets/quickly dir in photobomb/photobomb, and it might work
(03:17:03 PM) rickspencer3: or just put quidgets somewhere on disk and set the python path when you run it
(03:17:34 PM) rickspencer3: !PYTHONPATH="~/quidgets/quickly" python photobomb/bin/photobomb
(03:17:35 PM) rickspencer3: for example
(03:17:50 PM) ClassBot: mhall119 asked: Any good tutorials on learning to make Glade my friend?
(03:17:55 PM) rickspencer3: mmm
(03:17:58 PM) rickspencer3: not really
(03:18:04 PM) rickspencer3: there are a bunch of tutorials
(03:18:23 PM) rickspencer3: but many are out of date, since PyGtk moved form LibGlade to UI Builder
(03:18:39 PM) rickspencer3: if you have Quickly installed, there is a bit in there to help you get started
(03:18:52 PM) rickspencer3: #ubuntu-app-devel is a good place to ask specific gladey questions
(03:19:12 PM) rickspencer3: unless it's about workign with a quickly template, in which case #quickly might be better
(03:19:21 PM) rickspencer3: ok
(03:19:26 PM) rickspencer3: so next section
(03:19:40 PM) rickspencer3: When I tried to implement some captioning and other features, I came to realize that PIL was just not designed for the kind of stuff I wanted to do with it.
(03:19:51 PM) rickspencer3: When I switched to GooCanvas, it became much easier to add features.
(03:19:56 PM) rickspencer3: I was able to add resizing, rotation, and drag to move in a matter of an hour or so.
(03:20:39 PM) rickspencer3: A couple of days later I fixed an issue with dragging rotated items that resulted from the craziness involved with items in goocanvas maintaining their own coordinate system seperate from the goocanvas they are in.
(03:21:10 PM) rickspencer3: questions about PIL, GooCanvas, or anything else so far?
(03:21:48 PM) ClassBot: mhall119 asked: it doesn't look like you did much with glade, do you find yourself doing most UI building directly in python?
(03:21:52 PM) rickspencer3: yes
(03:22:00 PM) rickspencer3: I genreally use glade to set up containers
(03:22:05 PM) rickspencer3: then I target those container from code
(03:22:15 PM) rickspencer3: especially for custom images and such'
(03:22:23 PM) rickspencer3: also, I use subclassing a lot
(03:22:36 PM) rickspencer3: and I haven't done much work to make my own widgets work within glade
(03:22:54 PM) rickspencer3: ok
(03:22:54 PM) rickspencer3: I could then go and the ability for users to use a pen tool in photobomb.
(03:23:00 PM) rickspencer3: This required managing mouse events and creating a PolyLine object from the collected mouse points.
(03:23:08 PM) rickspencer3: Later I found that a Path worked better than a PolyLine for creating the Ink. I still have not figured out how to add points to an existing Path.
(03:23:12 PM) rickspencer3: As a result, Photobomb recreates the Path with each mouse move. This gets slow if the Path is not short.
(03:23:35 PM) rickspencer3: I did a session on GooCanvas yesterday, but can take some quick questions on it today
(03:24:43 PM) rickspencer3: I used the gtk.ColorChooserDialog for color selection.
(03:24:48 PM) rickspencer3: I also added color button and ink width button.
(03:24:57 PM) rickspencer3: originally, These hosted a goocanvas, and I just added a path to each and set the properties of the Path to display the selected color and width.
(03:25:20 PM) rickspencer3: later I changed the color button to change it's background to show the correct color
(03:25:25 PM) rickspencer3: I mean, selected color
(03:25:41 PM) rickspencer3: there's a "color selection" button in Gtk, but it didn't quite suite my UI
(03:25:53 PM) rickspencer3: in terms of getting settings from users
(03:25:53 PM) rickspencer3: I had already created quickly.prompts to make it easy to collect a line width from the user. (quickly.prompts.integer()).
(03:26:05 PM) rickspencer3: I could handle this with a GooCanvas.Widget object embedded in situ, but that's a lot of work and using quickly.prompts.string() is a one-liner.
(03:26:12 PM) rickspencer3: questions so far?
(03:26:54 PM) rickspencer3: following along, the next thing I implemented was adding text to the goocanvas
(03:26:58 PM) rickspencer3: I added a gtk.STOCK_EDIT button to collect text from the user.
(03:27:07 PM) rickspencer3: well, that was just to manage the correct icon
(03:28:19 PM) rickspencer3: since i wanted buttons to be compact
(03:28:40 PM) rickspencer3: I used toolbar buttons, and just setting them to stock worked well
(03:28:54 PM) rickspencer3: later, I had to hack around some limitations to how "stock" works
(03:29:22 PM) rickspencer3: I used a quickly.prompts.string() to collect the string to display from the user.
(03:29:52 PM) rickspencer3: next I added the some feedback about the current selection
(03:29:52 PM) rickspencer3: To display selection, I simply used a GooCanvas.Rect with a bit of buffer around the selected object.
(03:30:00 PM) rickspencer3: I could have added stock selection points from gtk, but that sounded like a lot of work.
(03:30:07 PM) rickspencer3: questions so far?
(03:32:27 PM) rickspencer3: I added a bit of opacity to the selection box. This works better, as you can see what is behind the selected item
(03:32:53 PM) rickspencer3: Then I added opacity to other selectable items.
(03:32:58 PM) rickspencer3: I sniped some code from segphault's grabbersnap app to figure out how to set up rgba colors.
(03:33:04 PM) rickspencer3: It's a bit of a hack, but seems to work well.
(03:33:16 PM) rickspencer3: At this point in the project I also switched from having the user open individual images to add, to iterating through the Pictures directory and adding a button for each encountered image.
(03:33:21 PM) rickspencer3: I accomplished this asynchronously by putting this on a thread, using quickly.widgets.asynch_task_progressbox()
(03:33:29 PM) rickspencer3: This simplifies threaded coding a bit, but I am finding that threads + Python + PyGtk don't work out as well as I want, no matter how carefully I code it
(03:33:54 PM) rickspencer3: happy to take questions now if you've gottem'
(03:34:42 PM) rickspencer3: so moving on to the web cam feature
(03:34:49 PM) rickspencer3: Web Cam support is an important feature for Photobomb,
(03:35:00 PM) rickspencer3: but the available APIs looked daunting.
(03:35:04 PM) rickspencer3: That is, until I discovered that Pygame had a simplified web cam interface.
(03:35:14 PM) rickspencer3: I created a quickly.widget to handle web cam integration and added that widget to Photobomb.
(03:35:20 PM) rickspencer3: Note that this means quickly.widgets depends on PyGame.
(03:35:26 PM) rickspencer3: I think I shall move the webcam widget out of the default install and make it available in some other manner.
(03:35:35 PM) rickspencer3: Having another source of images meant that I needed to add a gtk.Notebook to handle switching between the two sources.
(03:36:10 PM) rickspencer3: I later went on to use icons for the notebook tabs, but labels worked fine when there were only 2 sources
(03:36:15 PM) rickspencer3: Questions so far?
(03:36:48 PM) ClassBot: w1nGNUtz asked: It seems some programs take too long to evolve. How could it be if you manage to do this so quickly?
(03:36:52 PM) rickspencer3: hmm
(03:36:55 PM) rickspencer3: interesting quetsion
(03:37:01 PM) rickspencer3: I guess I just did one new feature a night
(03:37:07 PM) rickspencer3: I refactored as I went
(03:37:15 PM) rickspencer3: to support adding new features
(03:37:22 PM) rickspencer3: this was a really fun project to work on
(03:37:34 PM) rickspencer3: so I don't know
(03:37:51 PM) rickspencer3: I guess PyGtk + GooCanvas are just really productive libraries
(03:38:03 PM) ClassBot: titeuf_87 asked: is there any reasons you went with pygame instead of gstreamer?
(03:38:19 PM) rickspencer3: for the simple reason that I discovered the PyGame api, and it looked like I could do it
(03:38:46 PM) rickspencer3: I would switch to gstreamer if I saw sample code that convinced me it would be easy to do
(03:38:57 PM) rickspencer3: I would love to break the PyGame dependency
(03:39:16 PM) rickspencer3: ok
(03:39:21 PM) rickspencer3: so Web image searching
(03:39:25 PM) rickspencer3: I added this next
(03:39:37 PM) rickspencer3: so you could search for a term, and then get back a list of related images
(03:39:52 PM) rickspencer3: I ended up using the Yahoo! image search api for this
(03:40:02 PM) rickspencer3: for the sole reason that the API was dead simple to use and well documented
(03:40:38 PM) rickspencer3: around this time, someone picked up another program I wrote, and added good support for extracting image urls from a web page
(03:41:08 PM) rickspencer3: so I sniped that code and made it so that if you entered a URL
(03:41:21 PM) rickspencer3: instead of doing an image search, it would scrape images from that page
(03:41:37 PM) rickspencer3: while I was at it, I also let the user type in a url directly to a photo and loaded that
(03:41:48 PM) rickspencer3: I think the web image search functionality is really fun to use
(03:41:59 PM) rickspencer3: like, "it would be funny if a picture of a cat were here"
(03:42:06 PM) rickspencer3: type "cats"
(03:42:18 PM) rickspencer3: and then you can get the cat, use the clipping tool, and drag it where you want
(03:42:31 PM) rickspencer3: I'll pause to see if there are questions now
(03:42:52 PM) rickspencer3: ok
(03:43:05 PM) rickspencer3: so the next major change I made was from toolbar buttons to normal buttons
(03:43:08 PM) rickspencer3: the motivation was this:
(03:43:14 PM) rickspencer3: Changing size, rotation, and opacity required the user to click the button once for each increment.
(03:43:21 PM) rickspencer3: So rotating something to be upside required the user to go "click click click click", etc...
(03:43:27 PM) rickspencer3: What I wanted was the user to click the button, and hold it down as the image rotated, and then to stop when it was positioned as desired.
(03:43:39 PM) rickspencer3: Sadly, there is no "key-down" event on gtk.Toolbars.
(03:43:50 PM) rickspencer3: well, gtk.ToolButtons, I mean
(03:43:56 PM) rickspencer3: So I had to switch Photobomb from using toolbars to using regular gtk.Button.
(03:44:06 PM) rickspencer3: I achieved the layout by using a gtk.Table
(03:44:21 PM) rickspencer3: I also created a new quickly.widget.PressAndHold widget to add press and hold functionality
(03:44:28 PM) rickspencer3: I accomplish the asynch behavior with gobject.timeout_add(250, self.__tick).
(03:44:35 PM) rickspencer3: At each tick the button emits a signal telling the consuming app to do something.
(03:44:45 PM) rickspencer3: Questions?
(03:45:07 PM) ClassBot: M49 asked: Does it check license metadata for pictures?
(03:45:10 PM) rickspencer3: nope
(03:45:21 PM) rickspencer3: that might be a nice feature to add though
(03:45:30 PM) rickspencer3: especially if this is something that concerns you
(03:46:08 PM) rickspencer3: but I don't think it's pertinent to the way I envisioned folks using photobomb
(03:46:18 PM) rickspencer3: basically, making funny pictures from themselves and their friends
(03:46:34 PM) rickspencer3: ok
(03:46:43 PM) rickspencer3: last section is on the Social Network Integration
(03:46:49 PM) rickspencer3: this was almost all achieved with Gwibber
(03:46:58 PM) rickspencer3: Because Gwibber uses desktopcouch to store messages in Lucid,
(03:47:05 PM) rickspencer3:  I could add the feature to grovel through the messages and extract images coming from social feeds to display.
(03:47:17 PM) rickspencer3: Of course, this will only work in Lucid ;)
(03:47:25 PM) rickspencer3: I also sniped Gwibber's fb credentials from desktopcouch, and used that and some FML to get images from Facebook.
(03:47:59 PM) rickspencer3: so your gwibber tab of the notebook loads up all the images in feeds you subscribe to, and also any images in facebook ...
(03:48:22 PM) rickspencer3: that are tagged with any of your friends so long as the image has been modified in the last 5 days
(03:48:31 PM) rickspencer3: I think this aspect of photobomb is really fun
(03:48:48 PM) rickspencer3: converging your personal feeds into an app that is designed to mash 'em all up
(03:48:57 PM) rickspencer3: I used the Gwibber API to add broadcasting Photobomb images.
(03:49:04 PM) rickspencer3: The GooCanvas renders a png that is saved to a temp location.
(03:49:13 PM) rickspencer3: Then pycurl uploads the image to imgur.com, which returns a url.
(03:49:19 PM) rickspencer3: Gwibber's GwibberPosterVBox to broadcast the URL.
(03:49:26 PM) rickspencer3: So that
(03:49:30 PM) rickspencer3: s the photobomb showcase
(03:49:49 PM) rickspencer3: I would love finish off photobomb sometime and let folks have fun with it
(03:49:53 PM) rickspencer3: but I've been a bit busy
(03:50:10 PM) rickspencer3: there are few bugs here and there, some of them quite annoying, as they involve frozen UI
(03:50:17 PM) rickspencer3: so, 10 minutes left
(03:50:27 PM) rickspencer3: happy to discuss anything now, answer any questions
(03:51:13 PM) rickspencer3: ok
(03:51:20 PM) rickspencer3: I'm going to call that a wrap, then
(03:51:24 PM) rickspencer3: thanks all for coming!
(03:51:34 PM) rickspencer3: and if anyone wants to contribute to photobomb in any way
(03:51:45 PM) rickspencer3: I am totally open to that'
(03:51:58 PM) rickspencer3: it's more fun working on these things together ;)

MeetingLogs/OpWeek1003/PhotoBomb (last edited 2010-03-03 21:03:46 by pool-71-182-100-128)