Random notes from mg
Wed, 12 Oct 2011
Last weekend I attended the N9 Hackathon in Vienna.
Nokia kindly sponsored all food and accommodation costs and, at the very end,
surprised me with an entirely unexpected gift of a N9 phone.
Vienna: great transportation system, delicious food (either that, or I was
always extremely hungry when I ate), huge portions, restaurants open until
midnight. Shame I didn't have time to see the city itself.
The N9 is a gorgeous phone; much more
so in real life than in pictures.
After some hassle upgrading the Qt
SDK (the provided upgrade tool managed to somehow remove the Qt Creator
IDE while purporting to upgrade it; I had to reinstall the entire SDK) and
flashing my N950 with Beta 2 firmware
(Qt SDK 1.1.3 produces apps incompatible with the old Beta 1 firmware of the
N950) I started prototyping a time tracking application in QML,
while learning QML and the N9
design guidelines at the same time.
Converting the pretty pictures into QML was harder than I expected, but at
the end of the second day I had something that looked
like a native N9 application.
Most useful reference pages were:
- The QML
syntax introduction (which felt incomplete, but was almost adequate in the end).
- The list of Harmattan-specific QML components.
- The list of standard QML components.
- The UI
building blocks pages mentioned above (pretty pictures! pretty colours! I like shiny things!).
- Harmattan
Qt Components User Interface Guidelines: pixel and font sizes of the
standard UI elements. (Ignore the "import UIConstants.js" red herrings;
it's an internal thing apparently, and you can't use it
directly from your 3rd-party apps. Unless you find and copy UIConstants.js
into your project, after figuring out if the licence allows it, which
seemed too much of a hassle for me to even start. So I hardcoded all
the numbers directly, like a bad programmer who doesn't know about
constants.)
- The TutorialApplication
sample, finding the sources of which was unexpectedly difficult
-- a straight git clone of the qt-components
repository gives you something too recent to run with the older
qt-components version on the N9. I ended up using apt-get source
qt-components in Scratchbox to download the source
tarball of version 1.0~git20110525-1+0m6. Look in
qt-components/examples/meego/.
Finally, workflow. QML is parsed at run time (application startup time,
unless you delay the loading), which means no recompilation ought to be
required to make changes, which means short development feedback cycles ought
to be possible. So I was not happy about having to wait several seconds after
hitting Run in Qt Creator, while it built a .deb, copied it to the device over
wifi or USB networking, installed and ran it there. Deploying to the Qt
Simulator is quicker, but not as much as I think it ought to be. Plus, the
Qt Simulator apparently cannot simulate the landscape mode of the N9,
and it lies about the default font size of QML Text elements (if you
do not specify a pixelSize, text elements will look all right in the simulator,
but ridiculously tiny on the N9).
In the end I cobbled up a shell
script that rsyncs my updated QML files to the device and runs a short
Python
script (over ssh) to launch them. You need rsync and PySide installed on
the device for this, obviously, as well as having
SSH set up for passwordless logins. As a bonus, I can now do QML development
during lunch, directly on the device, with vim,
enjoying proper syntax
highlighting.
:)
Oh, and my code is up
on Github.
Fri, 29 Jul 2011
Andrew Olmsted built
the first FBReader packages for Harmattan, after tweaking the build
system a bit. The desktop version of FBReader already used Qt 4, and
ran almost unmodified, but with some bugs (segfault on task switch) and ugly
UI.
I started with the Ubuntu packages for FBReader, since they used a more
sane build system for .debs (compared to upstream's funky
shell script). Some tweaks were needed to make it build in Scratchbox:
since GTK+ and Hildon libraries aren't available on Harmattan, I had to disable
the building of -gtk and -maemo versions of libzlui. I also got to learn
a new tool — quilt.
Fixing the segfault took a couple of days of debugging, studying the source
code
of both FBReader and Qt itself, and asking for help on IRC. Turns out
FBReader was holding an active QPainter instance for too long, and its backing
pixmap got destroyed (or, rather, converted from an OpenGL texture to a plain
X11 pixmap) during a task switch, causing the crash. I'm probably describing
this wrong BTW, but, in any case, adding QPainter::begin() and QPainter::end()
calls in the paintEvent handler fixed the segfault.
Next, a small tweak in the .desktop file to make FBReader a single-instance
application: change Exec=FBReader to Exec=single-instance
/usr/bin/FBReader (I'm paraphrasing slightly).
Then, a more ambitious goal: making FBReader intercept volume keys and
use them for scrolling. Google gave me a pointer
to QmKeys, which was the
wrong API to use here, but gave me a lead to qmkeyd2, which appears to be an
open
source daemon, which gave me a lead to sysuid, another open
source daemon, which in turn gave me a lead to libresourceqt,
and that was the right API at last.
Volume keys generate regular key events for XF86AudioRaiseVolume and
XF86AudioLowerVolume, but they're also intercepted by qmkeyd2, which tells
all subscribers (and sysuid is one) about them. Which subscriber gets to
react is determined by the resource policy framework. So what I needed to do
in FBReader was acquire the ScaleButtonResource when FBReader starts up or gets
focus, and release it when FBReader quits or goes into background. That
also required some IRC help until I discovered installEventFilter()
and the ApplicationActivate/ApplicationDeactivate
events. And QApplication::instance().
The various tools available in the developer firmware were invaluable:
openssh, gdb, valgrind, strace, xev, xprop, lsof, netstat. Also, I would not
have achieved my second goal without being able to look at the sources of Meego
system components (qmkeyd, sysuid). Yay open source!
Here are my
changes to the source code. You can find my modified Debian packaging
files, as well as prebuilt binary packages (with full
debug info, for gdb goodness), in my experimental harmattan apt repository.
The UI is still ugly and non-native, but it doesn't matter much in fullscreen
mode :) .
Note to self: when next building fbreader, make sure the 2 megabyte
tags file doesn't end up in the .diff.gz. And speaking of crud
in source packages, the vim
package I built for Harmattan the other day contains the entire 50 meg
.hg in the .orig.tar.gz. I need to figure out how to
tell dh_make to omit it.
Tue, 19 Jul 2011
Last Thursday I received a package containing something called the Nokia N950 development kit. Sweet sweet
hardware, shame it's not going to be sold to end users. The software is
visibly an unfinished pre-release version, but shows great potential. There
are almost no 3rd-party apps, which is why Nokia is loaning these N950s to
random developers.
I intend to port GTimeLog to it.
Although my more immediate need is to have FBReader, so that I can stop carrying both
this one and my N900 with me everywhere. Also, vim would be nice.
I've already hacked up Lithuanian
support to the virtual and hardware keyboards, thanks to the very nice design
of Maliit. As a comparison, I've
had my N900 for a year and a half, and I still can't type Lithuanian on it.
XKB is not fun.
Sun, 19 Dec 2010
objgraph got
zodbbrowser got
- support for all ZODB databases, not just those with a
Zope 3/Bluebream-style root folder/local site.
- the ability to cope better with broken objects (due to the way ZODB
works, not having some of your modules on the Python pack can break
unpickling; zodbbrowser now handles this kind of situation better).
- assorted smaller improvements.
- a slow but inevitable shift of focus from "use it as a plugin for your
Zope 3 app" to "it's a standalone tool for inspecting ZODB contents".
(Both use cases are still supported, and will be for the foreseeable
future.)
imgdiff got
- its first public release.
- some experimental code to actually find and highlight the differing
parts of the images:
This works better when both images are the same size, although there's
experimental (and somewhat buggy) code to try all possible alignments.
I could use some help here; image processing is not something I'm
familiar with, and searching
StackOverflow didn't help beyond reminding me of the existence
of PIL's ImageChops.difference(), which is for same-sized images only.
Many of the results there are about comparing photos, where things like
lighting levels matter. What I need is a diff for computer-generated
images, where some things may be shifted around a bit, by different
amounts, but are essentially the same. Are there any two-dimensional
sequence diff algorithms?
Sat, 07 Aug 2010
Dozer is mostly known for
its memory profiling capabilities, but the as-yet unreleased version has
more. I've talked
about log capturing, now it's time for
Profiling
This WSGI middleware profiles every request with the cProfile module.
To see the profiles, visit a hidden URL /_profiler/showall:

What you see here is heavily tweaked in my fork branch
of Dozer; upstream version had no Cost column and
didn't vary the background of Time by age (that
last bit helps me see clumps of requests).
Here's what an individual profile looks like:

The call tree nodes can be expanded and collapsed by clicking on the
function name. There's a hardcoded limit of 20 nesting levels (upstream had a
limit of 15), sadly that appears not to be enough for practical purposes,
especially if you start profiling Zope 3 applications...
You can also take a look at the WSGI environment:

Sadly, nothing about the response is captured by Dozer. I'd've liked to
show the Content-Type and perhaps Content-Length in the profile list.
The incantation in development.ini is
[filter-app:profile]
use = egg:Dozer#profile
profile_path = /tmp/profiles
next = main
Create an empty directory /tmp/profiles and make sure other users
cannot write to it. Dozer stores captured profiles as Python
pickles, which are insecure
and allow arbitrary
command execution.
To enable the profiler, run paster like this:
$ paster serve development.ini -n profile
Bonus feature: call graphs
Dozer also writes a call graph in Graphviz "dot" format in the profile
directory. Here's the graph corresponding to the profile you saw earlier,
as displayed by the excellent XDot:

See the fork where the "hot" red path splits into two?

On the left we have Routes deciding to spend 120 ms (70% total time)
recompiling its route maps. On the right we have the actual request dispatch.
The actual controller action is called a bit further down:

Here it is, highlighted. 42 ms (24% total time), almost all of which is
spent in SQLAlchemy, loading the model object (a 2515 byte image stored as a
blob) from SQLite.
A mystery: pickle errors
When I first tried to play with the Dozer profiler, I was attacked by
innumerable exceptions. Some of those were due to a lack of configuration
(profile_path) or invalid configuration (directory not existing), or not
knowing the right URL (going to /_profiler raised TypeError). I
tried to make Dozer's profiler more forgiving or at least produce clearer
error messages in my fork branch,
e.g. going to /_profiler now displays the profile list.
However some errors were very mysterious: some pickles, written by Dozer
itself, could not be unpickled. I added a try/except that put those at the end
of the list, so you can see and delete them.

Does anybody have any clues as to why profile.py
might be writing out broken pickles?
Update: as Ben says in the comments, my changes have
been accepted upstream. Yay!