Marco's Blog

All content strictly personal opinions.
en eo

Adding Google Maps behavior to Weight Table

2008-04-26 5 min read Software marco

I woke up this morning and checked a bug report on the Tcl weight table utility I wrote. I have now several years of (admittedly intermittent) weight data, so now the utility loads a little slowly, and then you have to grab the scrollbar to move around the many years.

As you quickly learn, idioms and paradigms move on the Internet. I remember the day I picked up a mouse in a computer store in Germany, not knowing what to do with it, in a scene reminiscent of Scotty in Star Trek IV. Nowadays we are so used to a mouse, we don’t even know how to use a computer without it (nor is there a realistic way to use X or Windows or Mac OS X without one, thanks to keyboard-hating developers).

Once, maps were an image with scroll buttons on the sides. That’s how worked (later sold to Yahoo!, I believe), then MapQuest. That paradigm led the way for a long time: it is understandable, requires no prior knowledge, and doesn’t force you to reload a lot. The downside? It’s atrociously slow and doesn’t really give you the map you want.

Enters Google, the downright scary service that understands intuitive. Unlike my forlorn look when I picked up the mouse for my Atari ST, when I clicked on the map and it moved under my fingers (cursor), I almost cried with elation. FINALLY someone that gets it! That’s what I wanted all along, it was just a matter of technical complexity and bandwidth that didn’t get me there.

Fact is, objects on the screen are to a certain extent real to us. We want to operate on them as we would with real life objects. If I see a portion of something under a glass magnifier, I either move the magnifier or the something if I want to change my view. That’s exactly what Google does.

I’ll leave it to Google to explain to me why they didn’t implement the other obvious analogy, that is the selection of a rectangle for the zoom area. That’s been around for a while, and I am not sure I understand Google’s reticence to implement it. After all, the gesture is obvious (single mouse click, selection, then other single mouse click) and Google maps has this annoying habit of showing you the entire world if it thinks that a search result in Japan applies to your search for sushi bars in San Francisco.

All of that is beside the point, though. What I wanted to see was how much it would take to change the behavior of weight.tcl to implement the paradigm. Scrolling the canvas is done only horizontally (time coordinate) and is supported fairly well by the canvas commands. I thought it might take maybe a few dozen lines of code, and a few hours of tinkering around.

So I go into the code and look at the part where events are tied to scripts. In QT, you connect signals to slots (nice analogy), in Tk, you bind events to scripts (the conceptual difference between scripts and slots is mainly due to the OO nature of QT).

I tried it. I looked at the man page for bind once, then went to write my script. I started the app, and it failed on me because of a stupid mistake (forgot the unit of measurement). On second try, it worked right off the bat. And all I had done was adding the following lines:

-- gewicht.tcl (revision 27)</span>
++ gewicht.tcl (working copy)</span>
@@ -572,6 +572,10 @@</span>
set xb %x</span>
set yb %y</span>
+bind .c &lt;B1-Motion&gt; {</span>
+ .c xview scroll \[expr $xb – %x\] units</span>
+ set xb %x</span>
bind .c &lt;ButtonRelease-1&gt; {</span>
set nx %x</span>
set ny %y</span>

You see? I added four lines (three significant ones) and off I went. Amazing, no?

{xtypo_quote_right}bind .c <B1-Motion>{/xtypo_quote_right}What do the three lines do? The first line simply defines when to trigger the event: the object of our attention is the canvas named .c, the event that triggers the script is B1-Motion. B1 is a modifier to the actual event, (mouse) Motion. All together this means that the script should be run if and when the mouse is moved over the canvas .c and the mouse button 1 is pressed. It’s extremely cryptic, I admit, but very concise.

{xtypo_quote_right}.c xview scroll [expr $xb – %x] units{/xtypo_quote_right} Next, we do the actual work. We stored the last x coordinate in the variable xb (it’s set for the first time in a different script for the first pressing of the mouse button), and now we tell the canvas to scroll itself. That’s the way Tk does things, it is fundamentally an object oriented framework in which you tell things to do stuff. So we tell the canvas to scroll itself by as many units (sign sensitive) as the mouse moved. That’s synchronous scrolling, by the way – the mouse stays in place on the canvas, and you have the impression you are dragging the graph with your mouse.

{xtypo_quote_right}set xb %x{/xtypo_quote_right} The next line harbors the only criticism to the framework: instead of being able to get the differential movement of the mouse, I can only ask for absolute positioning. That’s kinda dumb, because with mouse motion the only thing that really matters is direction and speed, which is all relative motion. So we have to store the value in a temporary (static) variable and make use of it next time an event is triggered.

All in all, a fantastically simple change to implement, and a famously wonderful framework to do so. It’s not a surprise that Tk has become the universal framework of choice for pretty much all scripting languages that need a graphical interface, be it Python, Perl, or Tcl…