So I prepared a little retrospective of Photobomb development to walk through in the session, taking questions as I go. Fortunately I was blogging daily as I added features, so it was easy to pull out images to jog my memory.
This posting is the notes for the session, which is later today.
Inspiration
I wrote the first iteration of Photobomb one Saturday afternoon. 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. I used Python Imaging Library (PIL) to create a cropped version of that little squirrel, and to paste it onto the picture. 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.
Creating a File Dialog Widget
It turned out to be tedious to write code to open an image file. Also, a lot of copy and pasting. So I added a prompt to quickly.widgets to handle opening files. Having your own library of reusable widgets is quite a luxury. 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. Didn't realize that the API could handle it.
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. When I switched to GooCanvas, it became much easier to add features.
I added a gtk.STOCK_EDIT button to collect text from the user. I used a quickly.prompts.string() to collect the string to display from the user.
To display selection, I simply used a GooCanvas.Rect with a bit of buffer around the selected object. I could have added stock selection points from gtk, but that sounded like a lot of work.
I added a bit of opacity to the selection box. This works better.
Then I added opacity to other selectable items. I sniped some code from segphault's grabbersnap app to figure out how to set up rgba colors. It's a bit of a hack, but seems to work well.
I used the Yahoo! API to do image searching. I chose Yahoo! only because the API was quite easy to figure out.
Inspiration
I wrote the first iteration of Photobomb one Saturday afternoon. 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. I used Python Imaging Library (PIL) to create a cropped version of that little squirrel, and to paste it onto the picture. 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.
Creating a File Dialog Widget
It turned out to be tedious to write code to open an image file. Also, a lot of copy and pasting. So I added a prompt to quickly.widgets to handle opening files. Having your own library of reusable widgets is quite a luxury. 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. Didn't realize that the API could handle it.
Switch from PIL to GooCanvas
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. When I switched to GooCanvas, it became much easier to add features.
I was able to add resizing, rotation, and drag to move in a matter of an hour or so. A couple of days later I fixed an issue with dragging rotated items that resulted from
I used Inkscape to create svg files for the custom icons. I simply sized the documents how I wanted, and told the toolbar buttons to load the icons from the path. The delete icon used gtk.STOCK_DELETE.
I used the gtk.ColorChooserDialog for color selection.
I also added color button and ink width button. 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. I had already created quickly.prompts to make it easy to collect a line width from the user. (quickly.prompts.integer()).
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.
Ink in GoogCanvas
For example, I could then go and the ability for users to use a pen tool in photobomb. This required managing mouse events and creating a PolyLine object from the collected mouse points. 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. As a result, Photobomb recreates the Path with each mouse move. This gets slow if the Path is not short.SVG Icons with Inkscape
I used Inkscape to create svg files for the custom icons. I simply sized the documents how I wanted, and told the toolbar buttons to load the icons from the path. The delete icon used gtk.STOCK_DELETE.
I used the gtk.ColorChooserDialog for color selection.
I also added color button and ink width button. 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. I had already created quickly.prompts to make it easy to collect a line width from the user. (quickly.prompts.integer()).
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.
Text
I added a gtk.STOCK_EDIT button to collect text from the user. I used a quickly.prompts.string() to collect the string to display from the user.
Selection
To display selection, I simply used a GooCanvas.Rect with a bit of buffer around the selected object. I could have added stock selection points from gtk, but that sounded like a lot of work.
Opacity and Directory Iteration
I added a bit of opacity to the selection box. This works better.
Then I added opacity to other selectable items. I sniped some code from segphault's grabbersnap app to figure out how to set up rgba colors. It's a bit of a hack, but seems to work well.
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.
Web Cam
Web Cam support is an important feature for Photobomb, but the available APIs were daunting. That is, until I discovered that Pygame had a simplified web cam interface. I created a quickly.widget to handle web cam integration and added that widget to Photobomb. Note that this means quickly.widgets depends on PyGame. I think I shall move the webcam widget out of the default install and make it available in some other manner.Having another source of images meant that I needed to add a gtk.Notebook to handle switching between the two sources.
Image Searching
I used the Yahoo! API to do image searching. I chose Yahoo! only because the API was quite easy to figure out.
Press and Hold
Changing size, rotation, and opacity required the user to click the button once for each increment. So rotating something to be upside required the user to go "click click click click", etc...
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.
Sadly, there is no "key-down" event on gtk.Toolbars. So I had to switch Photobomb from using toolbars to using regular gtk.Button. I also created a new quickly.widget.PressAndHold widget to handle this.
I accomplish the asynch behavior with gobject.timeout_add(250, self.__tick). At each tick the button emits a signal telling the consuming app to do something.
Awesome post, Rick. I've been waiting for some insight into Quickly and their widgets since I used a text book to learn python recently after 20 years away from developing.
ReplyDeleteThank you.
Know this sounds lame, but where do I get Photobomb, the app, quickly?
ReplyDelete