Shallow Thoughts : tags : python
Akkana's Musings on Open Source, Science, and Nature.
Tue, 17 Aug 2010

We were talking about fonts again on IRC, and how there really isn't
any decent font viewer on Linux that lets you group fonts into categories.
Any time you need to choose a font -- perhaps you know you need one
that's fixed-width, script, cartoony, western-themed --
you have to go through your entire font list, clicking
one by one on hundreds of fonts and saving the relevant ones somehow
so you can compare them later. If you have a lot of fonts installed,
it can take an hour or more to choose the right font for a project.
There's a program called fontypython that does some font categorization,
but it's hard to use: it doesn't operate on your installed fonts, only
on fonts you copy into a special directory. I never quite understood
that; I want to categorize the fonts I can actually use on my system.
I've been wanting to write a font categorizer for a long time, but
I always trip up on finding documentation on getting Python to render fonts.
But this time, when I googled, I found jan bodnar's
ZetCode
Pango tutorial, which gave me all I needed and I was off and running.
Fontasia is initially a font viewer. It shows all your fonts in a list
on the left, with a preview on the right. But it also lets you add
categories: just type the category name in the box and click
Add category and a button for that category will appear,
with the current font added to it. A font can be in multiple categories.
Once you've categorized your fonts, a menu at the top of the window
lets you show just the fonts in a particular category. So if you're
working on a project that needs a Western-style font, show that
category and you'll see only relevant fonts.
You can also show only the fonts you've categorized -- that way you can
exclude fonts you never use -- I don't speak Tamil or Urdu so I don't
really need to see those fonts when I'm choosing a font. Or you can
show only the uncategorized fonts: this is useful when you add
some new fonts to your system and need to go through them and categorize
them.
I'm excited about fontasia. It's only a few days old and already used
it several times for real-world font selection problems.
If you want to try it, it's here:
Fontasia: View and
categorize fonts.
Tags: fonts, programming, python
[
11:20 Aug 17, 2010
More programming |
permalink to this entry
]
Sat, 10 Jul 2010
How many times have you wanted an easy way of making arrows in GIMP?
I need arrows all the time, for screenshots and diagrams. And there
really isn't any easy way to do that in GIMP. There's a script-fu for
making arrows in the Plug-in registry,
but it's fiddly and always takes quite a few iterations to get it right.
More often, I use a collection of arrow brushes I downloaded from somewhere
-- I can't remember exactly where I got my collection, but there are
lots of options if you google gimp arrow brushes -- then
use the free rotate tool to rotate the arrow in the right direction.
The topic of arrows came up again on #gimp yesterday, and Alexia Death
mentioned her script-fu in
GIMP Fx Foundary
that "abuses the selection" to make shapes, like stars and polygons.
She suggested that it would be easy to make arrows the same way, using
the current selection as a guide to where the arrow should go.
And that got me thinking about Joao Bueno's neat Python plug-in demo that
watches the size of the selection and updates a dialog every time the
selection changes. Why not write an interactive Python script that
monitors the selection and lets you change the arrow by changing the
size of the selection, while fine-tuning the shape and size of the
arrowhead interactively via a dialog?
Of course I had to write it. And it works great! I wish I'd written
this five years ago.
This will also make a great demo for my OSCON 2010 talk on
Writing
GIMP Scripts and Plug-ins, Thursday July 22. I wish I'd had it for
Libre Graphics Meeting last month.
It's here: GIMP
Arrow Designer.
Tags: gimp, programming, python, oscon2010
[
10:25 Jul 10, 2010
More gimp |
permalink to this entry
]
Fri, 16 Apr 2010
I needed a way to send the output of a Python program to two places
simultaneously: print it on-screen, and save it to a file.
Normally I'd use the Linux command tee for that:
prog | tee prog.out saves a copy of the output to the
file prog.out as well as printing it. That worked fine until
I added something that needed to prompt the user for an answer.
That doesn't work when you're piping through tee: the output gets
buffered and doesn't show up when you need it to, even if you try
to flush() it explicitly.
I investigated shell-based solutions: the output I need is on
sterr, while Python's raw_input() user prompt uses stdout, so
if I could get the shell to send stderr through tee without stdout,
that would have worked. My preferred shell, tcsh, can't do this at all,
but bash supposedly can. But the best examples I could find on the
web, like the arcane
prog 2>&1 >&3 3>&- | tee prog.out 3>&-
didn't work.
I considered using /dev/tty or opening a pty, but those calls only work
on Linux and Unix and the program is otherwise cross-platform.
What I really wanted was a class that acts like a standard
Python file object,
but when you write to it it writes to two places: the log file and stderr.
I found an example of someone
trying
to write a Python tee class, but it didn't work: it worked for
write() but not for print >>
I am greatly indebted to KirkMcDonald of #python for finding the problem.
In the Python source implementing >>,
PyFile_WriteObject (line 2447) checks the object's type, and if it's
subclassed from the built-in file object, it writes
directly to the object's fd instead of calling
write().
The solution is to use composition rather than inheritance. Don't make your
file-like class inherit from file, but instead include a
file object inside it. Like this:
import sys
class tee :
def __init__(self, _fd1, _fd2) :
self.fd1 = _fd1
self.fd2 = _fd2
def __del__(self) :
if self.fd1 != sys.stdout and self.fd1 != sys.stderr :
self.fd1.close()
if self.fd2 != sys.stdout and self.fd2 != sys.stderr :
self.fd2.close()
def write(self, text) :
self.fd1.write(text)
self.fd2.write(text)
def flush(self) :
self.fd1.flush()
self.fd2.flush()
stderrsav = sys.stderr
outputlog = open(logfilename, "w")
sys.stderr = tee(stderrsav, outputlog)
And it works! print >>sys.stderr, "Hello, world" now
goes to the file as well as stderr, and raw_input still
works to prompt the user for input.
In general, I'm told, it's not safe to inherit from
Python's built-in objects like file, because they tend
to make assumptions instead of making virtual calls to your
overloaded methods. What happened here will happen for other objects too.
So use composition instead when extending Python's built-in types.
Tags: programming, python
[
08:48 Apr 16, 2010
More programming |
permalink to this entry
]
Fri, 08 Jan 2010
We just had the second earthquake in two days, and I was chatting with
someone about past earthquakes and wanted to measure the distance to
some local landmarks. So I fired up
PyTopo as the easiest way
to do that. Click on one point, click on a second point and it prints
distance and bearing from the first point to the second.
Except it didn't. In fact, clicks weren't working at all. And although
I have hacked a bit on parts of pytopo (the most recent project was
trying to get scaling working properly in tiles imported from OpenStreetMap),
the click handling isn't something I've touched in quite a while.
It turned out that there's a regression in PyGTK: mouse button release
events now need you to set an event mask for button presses as well as
button releases. You need both, for some reason. So you now need code
that looks like this:
drawing_area.connect("button-release-event", button_event)
drawing_area.set_events(gtk.gdk.EXPOSURE_MASK |
# next line wasn't needed before:
gtk.gdk.BUTTON_PRESS_MASK |
gtk.gdk.BUTTON_RELEASE_MASK )
An easy fix ... once you find it.
I filed
bug 606453
to see whether the regression was intentional.
I've checked in the fix to the
PyTopo
svn repository on Google Code.
It's so nice having a public source code repository like that!
I'm planning to move Pho to Google Code soon.
Tags: programming, python, pygtk, mapping
[
13:20 Jan 08, 2010
More programming |
permalink to this entry
]
Wed, 25 Nov 2009
Continuing the discussion of those funny characters you sometimes
see in email or on web pages, today's Linux Planet article
discusses how to convert and handle encoding errors, using
Python or the command-line tool recode:
Mastering
Characters Sets in Linux (Weird Characters, part 2).
Tags: writing, linux, unicode, i18n, charsets, ascii, programming, python
[
14:06 Nov 25, 2009
More writing |
permalink to this entry
]
Wed, 11 Nov 2009
I almost always write my
presentation
slides using HTML. Usually I use Firefox to present them; it's
the browser I normally run, so I know it's installd and the slides
all work there. But there are several disadvantages to using Firefox:
- In fullscreen mode, it has a small "minimized urlbar" at the
top of the screen that I've never figured out to banish -- not only
is it visible to users, but it also messes up the geometry of
the slides (they have to be 762 pixels high rather than 768);
- It's very heavyweight, bad when using a mini laptop or netbook;
- Any personal browsing preferences, like no-animation,
flashblock or noscript, apply to slides too unless explicitly
disabled, which I've forgotten to do more than once before a talk.
Last year, when I was researching lightweight browsers, one of the
ones that impressed me most was something I didn't expect: the demo
app that comes with
pywebkitgtk
(package python-webkit on Ubuntu).
In just a few lines of Python, you can create your own browser with
any UI you like, with a fully functional content area.
Their current demo even has tabs.
So why not use pywebkitgtk to create a simple fullscreen
webkit-based presentation tool?
It was even simpler than I expected. Here's the code:
#!/usr/bin/env python
# python-gtk-webkit presentation program.
# Copyright (C) 2009 by Akkana Peck.
# Share and enjoy under the GPL v2 or later.
import sys
import gobject
import gtk
import webkit
class WebBrowser(gtk.Window):
def __init__(self, url):
gtk.Window.__init__(self)
self.fullscreen()
self._browser= webkit.WebView()
self.add(self._browser)
self.connect('destroy', gtk.main_quit)
self._browser.open(url)
self.show_all()
if __name__ == "__main__":
if len(sys.argv) <= 1 :
print "Usage:", sys.argv[0], "url"
sys.exit(0)
gobject.threads_init()
webbrowser = WebBrowser(sys.argv[1])
gtk.main()
That's all! No navigation needed, since the slides include javascript
navigation to skip to the next slide, previous, beginning and end.
It does need some way to quit (for now I kill it with ctrl-C)
but that should be easy to add.
Webkit and image buffering
It works great. The only problem is that webkit's image loading turns out
to be fairly poor compared to Firefox's. In a presentation where most
slides are full-page images, webkit clears the browser screen to
white, then loads the image, creating a noticable flash each time.
Having the images in cache, by stepping through the slide show then
starting from the beginning again, doesn't help much (these are local
images on disk anyway, not loaded from the net). Firefox loads the
same images with no flash and no perceptible delay.
I'm not sure if there's a solution. I asked some webkit developers and
the only suggestion I got was to rewrite the javascript in the slides
to do image preloading. I'd rather not do that -- it would complicate
the slide code quite a bit solely for a problem that exists only in
one library.
There might be some clever way to hack double-buffering in the app code.
Perhaps something like catching the 'load-started' signal,
switching to another gtk widget that's a static copy of the current
page (if there's a way to do that), then switching back on 'load-finished'.
But that will be a separate article if I figure it out. Ideas welcome!
Tags: programming, hack, python, web, speaking
[
16:12 Nov 11, 2009
More programming |
permalink to this entry
]
Tue, 20 Oct 2009
For years I've been reading daily news feeds on a series of PalmOS
PDAs, using a program called
Sitescooper
that finds new pages on my list of sites, downloads them, then runs
Plucker to translate them into Plucker's
open Palm-compatible ebook format.
Sitescooper has an elaborate series of rules for trying to get around
the complicated formatting in modern HTML web pages. It has an
elaborate cache system to figure out what it's seen before.
When sites change their design (which most news sites seem to
do roughly monthly), it means going in and figuring out the new
format and writing a new Sitescooper site file. And it doesn't
understand RSS, so you can't use the simplified RSS that most
sites offer. Finally, it's no longer maintained; in fact, I was
the last maintainer, after the original author lost interest.
Several weeks ago, bma tweeted
about a Python RSS reader he'd hacked up using the feedparser
package. His reader targeted email, not Palm, but finding out
about feedparser was enough to get me started. So I wrote
FeedMe
(Carla Schroder came up with the all-important name).
I've been using it for a couple of weeks now and I'm very happy
with the results. It's still quite rough, of course, but it's
already producing better files than Sitescooper did, and it
seems more maintainable. Time will tell.
Of course it needs to be made more flexible, adjusted so that
it can produce formats besides Plucker, and so on. I'll get to it.
And the only site I miss now, because it doesn't offer an RSS feed,
is Linux Planet.
Maybe I'll find a solution for that eventually.
Tags: RSS, python, programming, palm, plucker
[
20:08 Oct 20, 2009
More programming |
permalink to this entry
]
Mon, 03 Aug 2009
During OSCON a couple of weeks ago, I kept wishing I could do
Twitter searches for a pattern like #oscon in a cleaner way than
keeping a tab open in Firefox where I periodically hit Refresh.
Python-twitter doesn't support searches, alas, though it is part
of the Twitter API. There's an experimental branch of python-twitter
with searching, but I couldn't get it to work. But it turns out
Gwibber is also written in Python, and I was able to lift some
JSON code from Gwibber to implement a search. (Gwibber itself,
alas, doesn't work for me: it bombs out looking for the Gnome
keyring. Too bad, looks like it might be a decent client.)
I hacked up a "search for OSCON" program and used it a little during
the week of the conference, then got home and absorbed in catching
up and preparing for next week's GetSET summer camp, where I'm
running an astronomy workshop and a Javascript workshop for high
school girls. That's been keeping me frazzled, but I found a little
time last night to clean up the search code and release
Twit 0.3
with search and a few other new command-line arguments.
No big deal, but it was nice to take a hacking break from all this
workshop coordinating. I'm definitely happier program than I am
organizing events, that's for sure.
Tags: twitter, python, programming
[
17:23 Aug 03, 2009
More programming |
permalink to this entry
]
Thu, 09 Jul 2009
I finally dragged myself into 2009 and tried Twitter.
I'd been skeptical, but it's actually fairly interesting and not
that much of a time sink. While it's true that some people tweet
about every detail of their lives -- "I'm waiting for a bus" /
"Oh, hooray, the bus is finally here" / "I got a good seat in the
second row of the bus" / "The bus just passed Second St. and two
kids got on" / "Here's a blurry photo from my phone of the Broadway Av.
sign as we pass it"
-- it's easy enough to identify those people and un-follow them.
And there are tons of people tweeting about interesting stuff.
It's like a news ticker, but customizable -- news on the latest
protests in Iran, the latest progress on freeing the Mars Spirit
Rover, the latest interesting publication on dinosaur fossils,
and what's going on at that interesting conference halfway around
the world.
The trick is to figure out how you want the information delivered.
I didn't want to have to leave a tab open in Firefox all the time.
There was an xchat plug-in that sounded perfect -- I have an xchat
window up most of the time I'm online -- but it turned out it works
by picking one of the servers you're connected to, making a private
channel and posting things there. That seemed abusive to the server
-- what if everyone on Freenode did that?
So I wanted a separate client. Something lightweight and simple.
Unfortunately, all the Twitter clients available for Linux either
require that I install a lot of infrastructure first (either Adobe
Air or Mono), or they just plain didn't work (a Twitter client
where you can't click on links? Come on!)
But then I tried out the Python-Twitter bindings, and they were so
easy to use I decided to write them up for my next Linux Planet article,
which came out today:
Write
Your Own Linux Twitter Client In Less Time Than It Takes To Find One!.
The article shows how to use the bindings to write a bare-bones
client. But of course, I've been hacking on the client all along,
so the one I'm actually using has a lot more features like *ahem*
letting you click on links. And letting you block threads, though
I haven't actually tested that since I haven't seen any threads
I wanted to block since my first day.
You can download the
current version of
Twit, and anyone who's interested can
follow me on Twitter.
I don't promise to be interesting -- that's up to you to decide --
but I do promise not to tweet about every block of my bus ride.
Tags: writing, programming, python, twitter
[
15:09 Jul 09, 2009
More writing |
permalink to this entry
]
Sat, 20 Jun 2009
On my last Mojave trip, I spent a lot of the evenings hacking on
PyTopo.
I was going to try to stick to OpenStreetMap and other existing mapping
applications like TangoGPS, a neat little smartphone app for
downloading OpenStreetMap tiles that also runs on the desktop --
but really, there still isn't any mapping app that works well enough
for exploring maps when you have no net connection.
In particular, uploading my GPS track logs after a day of mapping,
I discovered that Tango really wasn't a good way of exploring them,
and I already know Merkaartor, nice as it is for entering new OSM
data, isn't very good at working offline. There I was, with PyTopo
and a boring hotel room; I couldn't stop myself from tweaking a bit.
Adding tracklogs was gratifyingly easy. But other aspects of the
code bother me, and when I started looking at what I might need to
do to display those Tango/OSM tiles ... well, I've known for a while
that some day I'd need to refactor PyTopo's code, and now was the time.
Surprisingly, I completed most of the refactoring on the trip.
But even after the refactoring, displaying those OSM tiles turned out
to be a lot harder than I'd hoped, because I couldn't find any
reliable way of mapping a tile name to the coordinates of that tile.
I haven't found any documentation on that anywhere, and Tango and
several other programs all do it differently and get slightly
different coordinates. That one problem was to occupy my spare time
for weeks after I got home, and I still don't have it solved.
But meanwhile, the rest of the refactoring was done, nice features
like track logs were working, and I've had to move on to other
projects. I am going to finish the OSM tile MapCollection class,
but why hold up a release with a lot of useful changes just for that?
So here's PyTopo 0.8,
and the couple of known problems with the new features will have to wait
for 0.9.
Tags: programming, python, gtk, pygtk, mapping
[
19:49 Jun 20, 2009
More programming |
permalink to this entry
]
Fri, 19 Jun 2009
A silly little thing, but something that Python books mostly don't
mention and I can never find via Google:
How do you find all the methods in a given class, object or module?
Ideally the documentation would tell you. Wouldn't that be nice?
But in the real world, you can't count on that,
and examining all of an object's available methods can often give
you a good guess at how to do whatever you're trying to do.
Python objects keep their symbol table in a dictionary
called __dict__ (that's two underscores on either end of the word).
So just look at object.__dict__. If you just want the
names of the functions, use object.__dict__.keys().
Thanks to JanC for suggesting dir(object) and help(object), which
can be more helpful -- not all objects have a __dict__.
Tags: programming, python, tips
[
11:44 Jun 19, 2009
More programming |
permalink to this entry
]
Sun, 14 Jun 2009
Part 3 of "Graphical Python Programming With PyGTK"
uses object-oriented Python to clean up the code from Part 2,
and also adds handling of key events to get rid of that silly
Quit button.
PythonGTK
Programming part 3: Screensaver, Objects, and User Input
Tags: writing, python, programming
[
11:18 Jun 14, 2009
More writing |
permalink to this entry
]
Mon, 01 Jun 2009
Someone on the OSM newbies list asked how he could strip
waypoints out of a GPX track file. Seems he has track logs of an
interesting and mostly-unmapped place that he wants to add to
openstreetmap, but there
are some waypoints that shouldn't be included, and he wanted a
good way of separating them out before uploading.
Most of the replies involved "just edit the XML." Sure, GPX files
are pretty simple and readable XML -- but a user shouldn't ever have
to do that! Gpsman and gpsbabel were also mentioned, but they're not
terribly easy to use either.
That reminded me that I had another XML-parsing task I'd been wanting
to write in Python: a way to split track files from my Garmin GPS.
Sometimes, after a day of mapping, I end up with several track
segments in the same track log file. Maybe I mapped several different
trails; maybe I didn't get a chance to upload one day's mapping before
going out the next day. Invariably some of the segments are of zero
length (I don't know why the Garmin does that, but it always does).
Applications like merkaartor don't like this one bit, so I
usually end up editing the XML file and splitting it into
segments by hand. I'm comfortable with XML -- but it's still silly.
I already have some basic XML parsing as part
of PyTopo and Ellie, so I know the parsing very easy to do.
So, spurred on by the posting on OSM-newbies,
I wrote a little GPX parser/splitter called
gpxmgr.
gpxmgr -l file.gpx can show you how many track logs are
in the file; gpxmgr -w file.gpx can write new files for
each non-zero track log. Add -p if you want to be prompted for
each filename (otherwise it'll use the name of the track log,
which might be something like "ACTIVE\ LOG\ #2").
How, you may wonder, does that help the original
poster's need to separate out waypoints from track files?
It doesn't. See, my GPS won't save tracklogs and
waypoints in the same file, even if you want them that way;
you have to use two separate gpsbabel commands to upload a track
file and a waypoint file. So I don't actually know what a
tracklog-plus-waypoint file looks like.
If anyone wants to use gpxmgr to manage waypoints as well as tracks,
send me a sample GPX file that combines them both.
Tags: mapping, programming, python
[
19:43 Jun 01, 2009
More mapping |
permalink to this entry
]
Thu, 28 May 2009
Part 2 of Graphical Python Programming With PyGTK gets into how to
do some cool Qix screensaver-style graphics, in:
Graphical
Python Programming With PyGTK, part 2: Write Your Own Screensaver.
There's also a digg link.
Tags: writing, python, programming
[
17:09 May 28, 2009
More writing |
permalink to this entry
]
Thu, 14 May 2009
This week's Linux Planet article is another one on Python and
graphical toolkits, but this time it's a little more advanced:
Graphical
Python Programming With PyGTK.
This one started out as a fun and whizzy screensaver sort of program
that draws lots of pretty colors -- but I couldn't quite fit it all
into one article, so that will have to wait for the sequel two weeks
from now.
Tags: writing, python, programming
[
18:53 May 14, 2009
More writing |
permalink to this entry
]
Thu, 23 Apr 2009
Latest Linux Planet article: How to write a "blobify" GIMP plug-in
in Python to make text look three-dimensional.
Creating
a Fancy 3D-Effect GIMP Plugin in Python.
Tags: gimp, writing, python
[
10:46 Apr 23, 2009
More writing |
permalink to this entry
]
Thu, 09 Apr 2009
Latest Linux Planet article: Part 1 of a two-parter on
Writing
GIMP scripts in Python.
As usual, there's a
Digg
link too.
Tags: gimp, writing, python
[
21:21 Apr 09, 2009
More writing |
permalink to this entry
]
Thu, 26 Mar 2009
Latest on Linux Planet: another introductory programming article,
this time on Python's tkinter library:
GUI
Programming in Python For Beginners.
(As usual, there's a
Digg link and also a
Reddit one.)
Tags: writing, python, programming
[
15:36 Mar 26, 2009
More writing |
permalink to this entry
]
Tue, 03 Mar 2009
Ever since I got the GPS I've been wanting something that plots the
elevation data it stores. There are lots of apps that will show me
the track I followed in latitude and longitude, but I couldn't find
anything that would plot elevations.
But GPX (the XML-based format commonly used to upload track logs)
is very straightforward -- you can look at the file and read the
elevations right out of it. I knew it wouldn't be hard to write
a script to plot them in Python; it just needed a few quiet hours.
Sounded like just the ticket for a rainy day stuck at home with
a sore throat.
Sure enough, it was fairly easy. I used xml.dom.minidom to
parse the file (I'd already had some experience with it in
gimplabels
for converting gLabels templates), and pylab from
matplotlib
for doing the plotting. Easy and nice looking.
I even threw in the nice "conditional main" code from
Matt
Harrison's SCALE7x Python talk, so it should be callable from other
Python code.
Here's the page and a screenshot:
Ellie: plot elevation
from a GPS track.
Tags: programming, mapping, python
[
16:57 Mar 03, 2009
More programming |
permalink to this entry
]
Sat, 28 Feb 2009
I was making a minor tweak to my
garmin script
that uses gpsbabel to read in tracklogs and waypoints from my GPS
unit, and I needed to look up the syntax of how to do some little
thing in sh script. (One of the hazards of switching languages a
lot: you forget syntax details and have to look things up a lot,
or at least I do.)
I have quite a collection of scripts in various languages in my
~/bin (plus, of course, all the scripts normally installed in
/usr/bin on any Linux machine) so I knew I'd have lots of examples.
But there are scripts of all languages sharing space in those
directories; it's hard to find just sh examples.
For about the two-hundredth time, I wished, "Wouldn't it be nice
to have a command that can search for patterns only in files that
are really sh scripts?"
And then, the inevitable followup ... "You know, that would be
really easy to write."
So I did -- a little python hack called langgrep that takes a language,
grep arguments and a file list, looks for a shebang line and only greps
the files that have a shebang matching the specified language.
Of course, while writing langgrep I needed langgrep, to look up
details of python syntax for things like string.find (I can never
remember whether it's string.find(s, pat) or s.find(pat); the python
libraries are usually nicely object-oriented but strings are an
exception and it's the former, string.find). I experimented with
various shell options -- this is Unix, so of course there are plenty
of ways of doing this in the shell, without writing a script. For instance:
grep find `egrep -l '#\\!.*python' *`
grep find `file * | grep python | sed 's/:.*//'`
i in foo; file $i|grep python && grep find $i; done # in sh/bash
These are all pretty straightforward, but when I try to make them
into tcsh aliases things get a lot trickier. tcsh lets you make
aliases that take arguments, so you can use !:1 to mean the first
argument, !2-$ to mean all the arguments starting with the second
one. That's all very well, but when you put them into a shell alias
in a file like .cshrc that has to be parsed, characters like ! and $
can mean other things as well, so you have to escape them with \.
So the second of those three lines above turns into something like
alias greplang "grep \!:2-$ `file * | grep \!:1 | sed 's/:.*//'`"
except that doesn't work either, so it probably needs more escaping
somewhere. Anyway, I decided after a little alias hacking that
figuring out the right collection of backslash escapes would
probably take just as long as writing a python script to do the
job, and writing the python script sounded more fun.
So here it is: my
langgrep
script. (Awful name, I know; better ideas welcome!)
Use it like this (if python is the language you're looking for,
find is the search pattern, and you want -w to find only "find"
as a whole word):
langgrep python -w find ~/bin/*
Tags: programming, python, shell
[
09:57 Feb 28, 2009
More programming |
permalink to this entry
]
Sat, 16 Aug 2008
Last night Joao and I were on IRC helping someone who was learning
to write gimp plug-ins. We got to talking about pixel operations and
how to do them in Python. I offered my arclayer.py as an example of
using pixel regions in gimp, but added that C is a lot faster for
pixel operations. I wondered if reading directly from the tiles
(then writing to a pixel region) might be faster.
But Joao knew a still faster way. As I understand it, one major reason
Python is slow at pixel region operations compared to a C plug-in is
that Python only writes to the region one pixel at a time, while C can
write batches of pixels by row, column, etc. But it turns out you
can grab a whole pixel region into a Python array, manipulate it as
an array then write the whole array back to the region. He thought
this would probably be quite a bit faster than writing to the pixel
region for every pixel.
He showed me how to change the arclayer.py code to use arrays,
and I tried it on a few test layers. Was it faster?
I made a test I knew would take a long time in arclayer,
a line of text about 1500 pixels wide. Tested it in the old arclayer;
it took just over a minute to calculate the arc. Then I tried Joao's
array version: timing with my wristwatch stopwatch, I call it about
1.7 seconds. Wow! That might be faster than the C version.
The updated, fast version (0.3) of arclayer.py is on my
arclayer page.
If you just want the trick to using arrays, here it is:
from array import array
[ ... setting up ... ]
# initialize the regions and get their contents into arrays:
srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,
False, False)
src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])
dstRgn = destDrawable.get_pixel_rgn(0, 0, newWidth, newHeight,
True, True)
p_size = len(srcRgn[0,0])
dest_pixels = array("B", "\x00" * (newWidth * newHeight * p_size))
[ ... then inside the loop over x and y ... ]
src_pos = (x + srcWidth * y) * p_size
dest_pos = (newx + newWidth * newy) * p_size
newval = src_pixels[src_pos: src_pos + p_size]
dest_pixels[dest_pos : dest_pos + p_size] = newval
[ ... when the loop is all finished ... ]
# Copy the whole array back to the pixel region:
dstRgn[0:newWidth, 0:newHeight] = dest_pixels.tostring()
Good stuff!
Tags: gimp, python, programming, performance
[
21:02 Aug 16, 2008
More gimp |
permalink to this entry
]
Sun, 25 May 2008
A user on the One Laptop Per Child (OLPC, also known as the XO)
platform wrote to ask me how to use crikey on that platform.
There are two stages to getting crikey running on a new platform:
- Build it, and
- Figure out how to make a key run a specific program.
The crikey page
contains instructions I've collected for binding keys in various
window managers, since that's usually the hard part.
On normal Linux machines the first step is normally no problem.
But apparently the OLPC comes with gcc but without make or the X
header files. (Not too surprising: it's not a machine aimed at
developers and I assume most people developing for the machine
cross-compile from a more capable Linux box.)
We're still working on that (if my correspondant gets it working,
I'll post the instructions), but while I was googling for
information about the OLPC's X environment I stumbled upon
a library I didn't know existed: python-xlib.
It turns out it's possible to do most or all of what crikey does
from Python. The OLPC is Python based; if I could write crikey
in Python, it might solve the problem.
So I whipped up a little key event generating script as a test.
Unfortunately, it didn't solve the OLPC problem (they don't include
python-xlib on the machine either) but it was a fun exercises, and
might be useful as an example of how to generate key events in
python-xlib. It supports both event generating methods: the X Test
extension and XSendEvent. Here's the script:
/pykey-0.1.
But while I was debugging the X Test code, I had to solve a bug that
I didn't remember ever solving in the C version of crikey. Sure
enough, it needed the same fix I'd had to do in the python version.
Two fixes, actually. First, when you send a fake key event through
XTest, there's no way to specify a shift mask. So if you need a
shifted character like A, you have to send KeyPress Shift, KeyPress a.
But if that's all you send, XTest on some systems does exactly what
the real key would do if held down and never released: it
autorepeats. (But only for a little while, not forever. Go figure.)
So the real answer is to send KeyPress Shift, KeyPress a, KeyRelease
a, KeyRelease Shift. Then everything works nicely. I've updated
crikey accordingly and released version 0.7 (though since XTest
isn't used by default, most users won't see any change from 0.6).
In the XSendEvent case, crikey still doesn't send the KeyRelease
event -- because some systems actually see it as another KeyPress.
(Hey, what fun would computers be if they were consistent and
always predictable, huh?)
Both C and Python versions are linked off the
crikey page.
Tags: programming, crikey, X11, python
[
14:50 May 25, 2008
More programming |
permalink to this entry
]
Fri, 12 Oct 2007
On a recent Mojave desert trip, we tried to follow a minor dirt road
that wasn't mapped correctly on any of the maps we had, and eventually
had to retrace our steps. Back at the hotel, I fired up my trusty
PyTopo on the East
Mojave map set and tried to trace the road. But I found that as I
scrolled along the road, things got slower and slower until it
just wasn't usable any more.
PyTopo was taking up all of my poor laptop's memory. Why?
Python is garbage collected -- you're not supposed to have
to manage memory explicitly, like freeing pixbufs.
I poked around in all the sample code and man pages I had available
but couldn't find any pygtk examples that seemed to be doing any
explicit freeing.
When we got back to civilization (read: internet access) I did
some searching and found the key. It's even in the
PyGTK
Image FAQ, and there's also some discussion in a
mailing
list thread from 2003.
Turns out that although Python is supposed to handle its own garbage
collection, the Python interpreter doesn't grok the size of a pixbuf
object; in particular, it doesn't see the image bits as part of the
object's size. So dereferencing lots of pixbuf objects doesn't trigger
any "enough memory has been freed that it's time to run the garbage
collector" actions.
The solution is easy enough: call gc.collect() explicitly
after drawing a map (or any other time a bunch of pixbufs have been
dereferenced).
So there's a new version of PyTopo, 0.6
that should run a lot better on small memory machines, plus
a new collection format (yet another format from
the packaged Topo! map sets) courtesy of Tom Trebisky.
Oh ... in case you're wondering, the ancient USGS maps from
Topo! didn't show the road correctly either.
Tags: programming, python, gtk, pygtk
[
21:21 Oct 12, 2007
More programming |
permalink to this entry
]
Tue, 04 Sep 2007
I left the water on too long in the garden
again. I keep doing
that: I'll set up something where I need to check back in five minutes or
fifteen minutes, then I get involved in what I'm doing and 45 minutes
later, the cornbread is burnt or the garden is flooded.
When I was growing up, my mom had a little mechanical egg timer.
You twist the dial to 5 minutes or whatever, and it goes
tick-tick-tick and then DING! I could probably
find one of those to buy (they're probably all digital now
and include clocks and USB plugs and bluetooth ports) but since the
problem is always that I'm getting distracted by something on the
computer, why not run an app there?
Of course, you can do this with shell commands. The simple solution
is:
(sleep 300; zenity --info --text="Turn off the water!") &
But the zenity dialogs are small -- what if I don't notice it? --
and besides, I have to multiply by 60 to turn a minute delay into
sleep seconds. I'm lazy -- I want the computer to do that for me!
Update: Ed Davies points out that "sleep 5m" also works.
A slightly more elaborate solution is at. Say something like:
at now + 15 minutes
and when it prompts for commands, type something like:
export DISPLAY=:0.0
zenity --info --text="Your cornbread is ready"
to pop up a window with a message.
But that's too much typing and has the same problem of the small
easily-ignored dialogs. I'd really rather have a great big red
window that I can't possibly miss.
Surely, I thought, someone has already written a nice egg-timer
application! I tried aptitude search timer and found several
apps such as gtimer, which is much more complicated than I wanted (you
can define named events and choose from a list of ... never mind, I
stopped reading there). I tried googling, but didn't have much luck
there either (lots of Windows and web apps, no Linux apps or
cross-platform scripts).
Clearly just writing the damn thing was going to be easier than
finding one.
(Why is it that every time I want to do something simple on a computer,
I have to write it? I feel so sorry for people who don't program.)
I wanted to do it in python, but what to use for the window that pops up?
I've used python-gtk in the past, but I've been meaning to check out
TkInter (the gui toolkit that's kinda-sorta part of Python) and
this seemed like a nice opportunity since the goal was so simple.
The resulting script:
eggtimer.
Call it like this:
eggtimer 5 Turn off the water
and in five minutes, it will pop up a huge red window the size of the
screen with your message in big letters. (Click it or hit a key to
dismiss it.)
First Impressions of TkInter
It was good to have an excuse to try TkInter and compare it with python-gtk.
TkInter has been recommended as something normally installed
with Python, so the user doesn't have to install anything extra.
This is apparently true on Windows (and maybe on Mac), but on
Ubuntu it goes the other way: I already had pygtk, because GIMP
uses it, but to use TkInter I had to install python-tk.
For developing I found TkInter irritating. Most
of the irritation concerned the poor documentation:
there are several tutorials demonstrating very basic uses, but
not much detailed documentation for answering questions like "What
class is the root Tk() window and what methods does it have?"
(The best I found -- which never showed up in google, but was
referenced from O'Reilly's Programming Python -- was
here.)
In contrast, python-gtk is
very well documented.
Things I couldn't do (or, at least, couldn't figure out how to do, and
googling found only postings from other people wanting to do the same thing):
- Button didn't respond to any of the obvious keys, like Return or
Space, and in fact setting key handlers on the button didn't work --
I ended up setting a key handler on the root window.
- I couldn't find a way to set the root window size and background
explicitly, so I had to set approximate window size by guessing at
the size of the internal padding of the button.
- There's an alternate to the root Tk() window called
Toplevel, which is documented and does allow setting window
size. Unfortunately, it also pops up an empty dialog without being
told to (presumably a bug).
- All of the tutorials I found for creating dialogs was wrong,
and I finally gave up on dialogs and just used a regular window.
- I couldn't fork and return control to the shell, because TkInter
windows don't work when called from a child process (for reasons no
one seems to be able to explain), so you have to run it in the
background with & if you want your shell prompt back.
I expect I'll be sticking with pygtk for future projects.
It's just too hard figuring things out with no documentation.
But it was fun having an excuse to try something new.
Tags: programming, python, tkinter
[
13:35 Sep 04, 2007
More programming |
permalink to this entry
]
Fri, 25 Aug 2006
Belated release announcement: 0.5b2 of my little map viewer
PyTopo
has been working well, so I released 0.5 last week with only a
few minor changes from the beta.
I'm sure I'll immediately find six major bugs -- but hey, that's
what point releases are for. I only did betas this time because
of the changed configuration file format.
I also made a start on a documentation page for the .pytopo file
(though it doesn't really have much that wasn't already written
in comments inside the script).
Tags: programming, python, gtk, pygtk, mapping
[
21:10 Aug 25, 2006
More programming |
permalink to this entry
]
Sat, 03 Jun 2006
A few months ago, someone contacted me who was trying to use my
PyTopo map display script for a different set of map data, the
Topo! National Parks series. We exchanged some email about the
format the maps used.
I'd been wanting to make PyTopo more general
anyway, and already had some hacky code in my local version to
let it use a local geologic map that I'd chopped into segments.
So, faced with an Actual User (always a good incentive!), I
took the opportunity to clean up the code, use some of Python's
support for classes, and introduce several classes of map data.
I called it 0.5 beta 1 since it wasn't well tested. But in the last
few days, I had occasion to do some map exploring,
cleaned up a few remaining bugs, and implemented a feature which
I hadn't gotten around to implementing in the new framework
(saving maps to a file).
I think it's ready to use now. I'm going to do some more testing:
after visiting the USGS
Open House today and watching Jim Lienkaemper's narrated
Virtual
Tour of the Hayward Fault,
I'm all fired up about trying again to find more online geologic
map data.
But meanwhile, PyTopo is feature complete and has the known
bugs fixed. The latest version is on
the PyTopo page.
Tags: programming, python, gtk, pygtk, mapping
[
17:25 Jun 03, 2006
More programming |
permalink to this entry
]
Tue, 21 Jun 2005
I updated my Debian sid system yesterday, and discovered today that
gnome-volume-control has changed their UI yet again. Now the window
comes up with two tabs,
Playback and
Capture; the
default tab,
Playback, has only one slider in it,
PCM,
and all the important sliders, like
Volume, are under
Capture. (I'm told this is some interaction with how ALSA
sees my sound chip.)
That's just silly. I've never liked the app anyway -- it takes
forever to come up, so I end up missing too much of any clip that
starts out quiet. All I need is a simple, fast window with
a single slider controlling master volume. But nothing like that
seems to exist, except panel applets that are tied to the panels
of particular window managers.
So I wrote one, in PyGTK. vol is
a simple script which shows a slider, and calls aumix
under the hood to get and set the volume. It's horizontal by
default; vol -h gives a vertical slider.
Aside: it's somewhat amazing that Python has no direct way
to read an integer out of a string containing more than just that
integer: for example, to read 70 out of "70,". I had to write a
function to handle that. It's such a terrific no-nonsense
language most of the time, yet so bad at a few things.
(And when I asked about a general solution in the python channel
at [large IRC network], I got a bunch of replies like "use
int(str[0:2])" and "use int(str[0:-1])".
Shock and bafflement ensued when I pointed out that 5, 100, and -27
are all integers too and wouldn't be handled by those approaches.)
Tags: programming, python, gtk, pygtk
[
14:54 Jun 21, 2005
More programming |
permalink to this entry
]
Wed, 13 Apr 2005
I needed to print some maps for one of my geology class field trips,
so I added a "save current map" key to PyTopo (which saves to .gif,
and then I print it with gimp-print). It calls
montage
from Image Magick.
Get yer PyTopo 0.3
here.
Tags: programming, python, imagemagick, mapping
[
16:56 Apr 13, 2005
More programming |
permalink to this entry
]
Sat, 09 Apr 2005
A few days ago, I mentioned my woes regarding Python sending spurious
expose events every time the drawing area gains or loses focus.
Since then, I've spoken with several gtk people, and investigated
several workarounds, which I'm writing up here for the benefit of
anyone else trying to solve this problem.
First, "it's a feature". What's happening is that the default focus
in and out handlers for the drawing area (or perhaps its parent class)
assume that any widget which gains keyboard focus needs to redraw
its entire window (presumably because it's locate-highlighting
and therefore changing color everywhere?) to indicate the focus
change. Rather than let the widget decide that on its own, the
focus handler forces the issue via this expose event. This may be a
bad decision, and it doesn't agree with the gtk or pygtk documentation
for what an expose event means, but it's been that way for long enough
that I'm told it's unlikely to be changed now (people may be depending
on the current behavior).
Especially if there are workarounds -- and there are.
I wrote that this happened only in pygtk and not C gtk, but I was
wrong. The spurious expose events are only passed if the CAN_FOCUS
flag is set. My C gtk test snippet did not need CAN_FOCUS,
because the program from which it was taken, pho, already implements
the simplest workaround: put the key-press handler on the window,
rather than the drawing area. Window apparently does not have
the focus/expose misbehavior.
I worry about this approach, though, because if there are any other
UI elements in the window which need to respond to key events, they
will never get the chance. I'd rather keep the events on the drawing
area.
And that becomes possible by overriding the drawing area's default
focus in/out handlers. Simply write a no-op handler which returns
TRUE, and set it as the handler for both focus-in and focus-out. This
is the solution I've taken (and I may change pho to do the same thing,
though it's unlikely ever to be a problem in pho).
In C, there's a third workaround: query the default focus handlers,
and disconnect() them. That is a little more efficient (you
aren't calling your nop routines all the time) but it doesn't seem to
be possible from pygtk: pygtk offers disconnect(), but there's no way to
locate the default handlers in order to disconnect them.
But there's a fourth workaround which might work even in pygtk:
derive a class from drawing area, and set the focus in and out
handlers to null. I haven't actually tried this yet, but it may be
the best approach for an app big enough that it needs its own UI classes.
One other thing: it was suggested that I should try using AccelGroups
for my key bindings, instead of a key-press handler, and then I could
even make the bindings user-configurable. Sounded great!
AccelGroups turn out to be very easy to use, and a nice feature.
But they also turn out to have undocumented limitations on what
can and can't be an accelerator. In particular, the arrow keys can't
be accelerators; which makes AccelGroup accelerators less than
useful for a widget or app that needs to handle user-initiated
scrolling or movement. Too bad!
Tags: programming, python, gtk, pygtk
[
20:52 Apr 09, 2005
More programming |
permalink to this entry
]
Wed, 06 Apr 2005
While on vacation, I couldn't resist tweaking
pytopo
so that I could use it to explore some of the areas we were
visiting.
It seems fairly usable now. You can scroll around, zoom in and out
to change between the two different map series, and get the
coordinates of a particular location by clicking. I celebrated
by making a page for it, with a silly tux-peering-over-map icon.
One annoyance: it repaints every time it gets a focus in or out,
which means, for people like me who use mouse focus, that it
repaints twice for each time the mouse moves over the window.
This isn't visible, but it would drag the CPU down a bit on a
slow machine (which matters since mapping programs are particularly
useful on laptops and handhelds).
It turns out this is a pygtk problem: any pygtk drawing area window
gets spurious Expose events every time the focus changes (whether or
not you've asked to track focus events), and it reports that the
whole window needs to be repainted, and doesn't seem to be
distinguishable in any way from a real Expose event.
The regular gtk libraries (called from C) don't do this, nor
do Xlib C programs; only pygtk.
I filed
bug 172842
on pygtk; perhaps someone will come up with a workaround, though
the couple of pygtk developers I found on #pygtk couldn't think
of one (and said I shouldn't worry about it since most people
don't use pointer focus ... sigh).
Tags: programming, python, gtk, pygtk, mapping
[
16:26 Apr 06, 2005
More programming |
permalink to this entry
]
Sun, 27 Mar 2005
I couldn't stop myself -- I wrote up a little topo map viewer in
PyGTK, so I can move around with arrow keys or by clicking near the
edges. It makes it a lot easier to navigate the map directory if
I don't know the exact starting coordinates.
It's called PyTopo,
and it's in the same
place as my earlier two topo scripts.
I think CoordsToFilename has some bugs; the data CD also has some
holes, and some directories don't seem to exist in the expected
place. I haven't figured that out yet.
Tags: programming, python, gtk, pygtk, mapping
[
17:53 Mar 27, 2005
More programming |
permalink to this entry
]
I've long wished for something like those topographic map packages
I keep seeing in stores. The USGS (US Geological Survey) sells
digitized versions of their maps, but there's a hefty setup fee
for setting up an order, so it's only reasonable when buying large
collections all at once.
There are various Linux mapping applications which do things like
download squillions of small map sections from online mapping sites,
but they're all highly GPS oriented and I haven't had much luck
getting them to work without one. I don't (yet?) have a GPS;
but even if I had one, I usually want to make maps for places I've
been or might go, not for where I am right now. (I don't generally
carry a laptop along on hikes!)
The Topo!
map/software packages sold in camping/hiking stores (sometimes
under the aegis of National Geographic
are very reasonably priced. But of course, the software is
written for Windows (and maybe also Mac), not much help to Linux
users, and the box gives no indication of the format of the data.
Googling is no help; it seems no Linux user has ever
tried buying one of these packages to see what's inside.
The employees at my local outdoor equipment store (Mel Cotton's)
were very nice without knowing the answer, and offered
the sensible suggestion of calling the phone number on the box,
which turns out to be a small local company, "Wildflower Productions",
located in San Francisco.
Calling Wildflower, alas, results in an all too familiar runaround:
a touchtone menu tree where no path results in the possibility of
contact with a human. Sometimes I wonder why companies bother to
list a phone number at all, when they obviously have no intention
of letting anyone call in.
Concluding that the only way to find out was to buy one, I did so.
A worthwhile experiment, as it turned out! The maps inside are
simple GIF files, digitized from the USGS 7.5-minute series and,
wonder of wonders, also from the discontinued but still useful
15-minute series.
Each directory contains GIF files covering the area of one
7.5 minute map, in small .75-minute square pieces,
including pieces of the 15-minute map covering the same area.
A few minutes of hacking with python and
Image Magick
resulted in a script to stitch together all images
in one directory to make one full USGS 7.5 minute map;
after a few hours of hacking, I can stitch
a map of arbitrary size given start and end longitude and latitude.
My initial scripts,
such as they are.
Of course, I don't yet have nicities like a key, or an interactive
scrolling window, or interpretation of the USGS digital elevation
data. I expect I have more work to do. But for now, just
being able to generate and print maps for a specific area is a huge boon,
especially with all the mapping we're doing in Field Geology class.
GIMP's "measure" tool will come in handy for measuring distances
and angles!
Tags: programming, python, gtk, pygtk
[
11:13 Mar 27, 2005
More programming |
permalink to this entry
]