back to homepage

A Digital Ruler in Python

I occasionally need to measure something in the corporeal world but never seem to have a ruler handy, so I wrote a bit of Python code that uses the turtle graphics module to draw a to-life scale digital ruler. The turtle module is a wrapper around a Tkinter canvas and is meant for introducing kids to programming so it isn't powerful, but for the simple use case of drawing a ruler turtle can handle it. It's been a while since I've used the module so the code isn't very clean.

digital ruler

The code shown below should draw the above image of a standard yellow ruler onto an unresizable pop up window.

#!/usr/bin/python3

from turtle import *
from tkinter import *

PPM = 4.857685009487666
PPI = PPM * 25.4

WIDTH = PPM * 303
HEIGHT = 100
START_OFFSET = PPM

root = Tk()
root.title('turtle ruler')
root.resizable(False, False)
canvas = Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()
screen = TurtleScreen(canvas)
turtle = RawTurtle(screen)

screen.bgcolor('#f1aa00')
screen.tracer(0, 0)
screen.setworldcoordinates(0, 0, WIDTH, HEIGHT)

turtle.forward(START_OFFSET)
for i in range(int(WIDTH / PPM)):
    turtle.left(90)

    travel = 12
    if i % 10 == 0:
        travel = 24
    elif i % 5 == 0:
        travel = 18
    turtle.forward(travel)
    if i % 10 == 0:
        turtle.write(i//10, align='center')
    turtle.back(travel)

    turtle.right(90)
    turtle.forward(PPM)

turtle.penup()
turtle.goto(0, HEIGHT+1)
turtle.pendown()

turtle.forward(START_OFFSET)
for i in range(int(WIDTH / (PPI/6))+1):
    turtle.right(90)

    travel = 12
    if i % 6 == 0:
        travel = 24
    elif i % 3 == 0:
        travel = 18
    turtle.forward(travel)
    if i % 6 == 0:
        turtle.penup()
        turtle.forward(16)
        turtle.pendown()
        turtle.write(i//6, align='center')
        turtle.penup()
        turtle.backward(16)
        turtle.pendown()
    turtle.back(travel)

    turtle.left(90)
    turtle.forward(PPI/6)

turtle.penup()
turtle.home()
canvas.bind('<Button-1>', lambda event: root.destroy(), None)
canvas.tk.mainloop()

To draw accurate measurements the code needs to know either the PPM (Pixels Per Millimeter) or PPI (Pixels Per Inch). I figured out the PPM on my Linux PC by evoking xrandr:

$ xrandr
Screen 0: minimum 320 x 200, current 2560 x 1440, maximum 16384 x 16384
HDMI-1 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 527mm x 296mm
   2560x1440     59.95*+
   1920x1440     60.00
   1920x1200     59.95
   1920x1080     60.00    50.00    59.94
   1920x1080i    60.00    50.00    59.94
   1680x1050     59.88
   1400x1050     59.95
   1280x1024     75.02    60.02
   1440x900      59.90
   1152x864      75.00
   1280x720      60.00    50.00    59.94
   1440x576      50.00
   1024x768      75.03    70.07    60.00
   832x624       74.55
   800x600       72.19    75.00    60.32    56.25
   720x576       50.00
   720x576i      50.00
   720x480       60.00    59.94
   720x480i      60.00    59.94
   640x480       75.00    72.81    66.67    60.00    59.94
   720x400       70.08
DP-1 disconnected (normal left inverted right x axis y axis)

And you can see that at the very top it says that my monitor is 2560px across and 527mm across, so with some basic math $2560\text{px}/527\text{mm} \approx 4.857\,\text{PPM}$. To get the PPI the code just does a simple conversion from millimeter to inch.

As I stated above, it's been a while since I've used turtle so some of the code is probably redundant and can be achieved in an easier way. I have to use RawTurtle and create my own window because I need the screen to be unresizable and while we could try to make use of turtle's internal functions and hack together something like

from turtle import *

turtle = Turtle()
screen = Screen()

screen.getcanvas()._root().resizable(False, False)

screen.exitonclick()

There are several problems with this approach and besides I don't want the border turtle always draws on initialization. It's much easier to make our own Tkinter window and canvas and pass it to turtle.

BTW turtle's source code is just 4000 lines and very easy to read. Many times during the short development of the ruler code, I consulted the source code.