 # Gauges again, source code this time.

Apparently the mailing list doesn't do attachments. So here

> is the source instead: - Jamil Khan

Hi Jamil -- thanks for contributing these. They look very snazzy --
nice design!

As they are they will be useful, but to make them maximally useful, I
think they should be refactored using the matplotlib API and not the
pylab interface, since gauges are typically used in GUI application
rather than one-off scripts and most application developers use the
API rather than pylab. It is a fairly easy migration -- I'll include
an example refactoring of gauge.py below. For more details on the
API, see

Are you interested in doing this? If we get these cleaned up a bit
and documented, they would be a nice start to a new "toolkit"

Here is the modified gauge.py, which defines a Gauge as a custom
Axes. This can now be used from the pylab interface or the matplotlib
API. One other things to note: all of your setp calls could be
removed by simply passing the kwargs to the relevant function. Eg,

p = plot(x_vect, y_vect, 'b-')
setp(p, color='black')
setp(p, linewidth=1.5)

you could do

p = plot(x_vect, y_vect, 'b-')
setp(p, color='black',linewidth=1.5)

or event better

p = plot(x_vect, y_vect, 'b-', color='black',linewidth=1.5)

Another little trick; in several places you do things like

temp = limits
limits = limits
limits = temp

With the magic of tuples, you can avoid the temporary and do this in
one line

limits, limits = limits = limits

Also, do you intend to do integer division in lines like

graph_width/2

If not, you will want to do one of 'from __future__ import division'
or graph_width/2.

For efficiency and to save typing, you may want to replace blocks like

x_vect = []
x_vect.append( (graph_width/2) )
x_vect.append( (graph_width/2) )
x_vect.append(-(graph_width/2) )
x_vect.append(-(graph_width/2) )
x_vect.append( (graph_width/2) )

with

x_vect = [
graph_width/2,
graph_width/2,
-graph_width/2,
-graph_width/2,
graph_width/2,
]

Final comment: your code uses tabs rather than spaces for indentation,
which can cause problems and is nonstandard. Perhaps you could
configure your editor to use spaces for indentation?

Thanks for the contribution!

JDH

Example refactoring of gauge.py

#!/usr/bin/env python
"""

The Gauge widget draws a semi-circular gauge. You supply limits,
shaded regions, names and the current value, and invoke it like this:

from pylab import figure, show

current_value = -4.0
limits = [-1.0,1.0,1,0.1]
zone_colour = [[-1.0,0.0,'r'],[0.0,0.5,'y'],[0.5,1.0,'g']]
attribute_name = "Rx MOS (24h)"

graph_height = 1.6
graph_width = 2.4
fig_height = graph_height
fig_width = graph_width

fig = figure(figsize=(fig_width, fig_height ))

rect = [(0.0/fig_width), (0.2/fig_height),
(graph_width/fig_width), (graph_height/fig_height)]

gauge = Gauge(fig, rect,
xlim=( -0.1, graph_width+0.1 ),
ylim=( -0.4, graph_height+0.1 ),
xticks=[],
yticks=[],
)
gauge.set_axis_off()

show()

"""
from __future__ import division
from matplotlib.figure import Figure
from matplotlib.axes import Axes
import math
import types

from math import pi

class Gauge(Axes):
def __init__(self, *args, **kwargs):
Axes.__init__(self, *args, **kwargs)

#Perform Checking
if( limits == limits ):
raise ValueError('identical_limits_exception: %s'%limits)
if( limits > limits ):
graph_positive = True
else: #Swap the limits around
graph_positive = False
limits, limits = limits = limits

#There must be an integer number of minor ticks for each major tick
if not( ((limits/limits) % 1.0) * limits == 0 ):

if( limits <= 0 or
limits <= 0 or
limits < limits or
limits > abs(limits-limits) ):
for zone in zone_colour:
if( zone > zone ): #Swap the zones so zone > zone
zone, zone = zone, zone
if( zone < limits or zone > limits ):
if( zone < limits ):
zone = limits
if( zone > limits ):
zone = limits

#Draw the arch
for zone in zone_colour:
self.draw_arch(limits, zone, zone, zone, False, graph_positive)
self.draw_arch(limits, limits, limits, 'black', True, graph_positive)
self.draw_ticks(limits, graph_positive)
self.draw_needle(current_value, limits, graph_positive)
self.draw_bounding_box(graph_width, graph_height)
self.text(0.0, 0.2, attribute_name, size=10, va='bottom', ha='center')

#The black dot.
p = self.plot([0.0],[0.0],'.', color='#000000')

def draw_arch(self, limits, start_angle, end_angle, colour, border, graph_positive):
x_vect = []
y_vect = []
if( graph_positive ):
start = int(180 - (start_angle - limits) * (180.0/(limits-limits)))
end = int(180 - (end_angle - limits) * (180.0/(limits-limits)))
else:
start = int( (end_angle - limits) * (180.0/(limits-limits)))
end = int( (start_angle - limits) * (180.0/(limits-limits)))

#Draw the arch
theta = start
while (theta >= end):
x_vect.append( radius * math.cos(theta * (pi/180)) )
y_vect.append( radius * math.sin(theta * (pi/180)) )
theta -= 1

theta = end
while (theta <= start):
x_vect.append( radius * math.cos(theta * (pi/180)) )
y_vect.append( radius * math.sin(theta * (pi/180)) )
theta += 1

if( border ):
#Close the loop
x_vect.append(-0.85)
y_vect.append(0.0)

p = self.plot(x_vect, y_vect, 'b-', color='black', linewidth=1.5)
else:
p = self.fill(x_vect, y_vect, colour, linewidth=0.0, alpha=0.4)

def draw_needle(self, current_value, limits, graph_positive):
x_vect = []
y_vect = []

if current_value == None:
self.text(0.0, 0.4, "N/A", size=10, va='bottom', ha='center')
else:
self.text(0.0, 0.4, "%.2f" % current_value, size=10, va='bottom', ha='center')

#Clamp the value to the limits
if( current_value < limits ):
current_value = limits
if( current_value > limits ):
current_value = limits

theta = 0
length = 0.95
if( graph_positive ):
angle = 180.0 - (current_value - limits) *(180.0/abs(limits-limits))
else:
angle = (current_value - limits) *(180.0/abs(limits-limits))

while (theta <= 270):
x_vect.append( length * math.cos((theta + angle) * (pi/180)) )
y_vect.append( length * math.sin((theta + angle) * (pi/180)) )
length = 0.05
theta += 90

p = self.fill(x_vect, y_vect, 'b', alpha=0.4)

def draw_ticks(self, limits, graph_positive):
if( graph_positive ):
angle = 180.0
else:
angle = 0.0
i = 0
j = limits

while( i*limits + limits <= limits ):
x_vect = []
y_vect = []
if( i % (limits/limits) == 0 ):
x_pos = 1.1 * math.cos( angle * (pi/180.0))
y_pos = 1.1 * math.sin( angle * (pi/180.0))
if( type(limits) is types.FloatType ):
self.text( x_pos, y_pos, "%.2f" % j, size=10, va='center', ha='center', rotation=(angle - 90))
else:
self.text( x_pos, y_pos, "%d" % int(j), size=10, va='center', ha='center', rotation=(angle - 90))
tick_length = 0.15
j += limits
else:
tick_length = 0.05
i += 1
x_vect.append( 1.0 * math.cos( angle * (pi/180.0)))
x_vect.append( (1.0 - tick_length) * math.cos( angle * (pi/180.0)))
y_vect.append( 1.0 * math.sin( angle * (pi/180.0)))
y_vect.append( (1.0 - tick_length) * math.sin( angle * (pi/180.0)))
p = self.plot(x_vect, y_vect, 'b-', linewidth=1, alpha=0.4, color="black")
if( graph_positive ):
angle -= limits * (180.0/abs(limits-limits))
else:
angle += limits * (180.0/abs(limits-limits))
if( i % (limits/limits) == 0 ):
x_pos = 1.1 * math.cos( angle * (pi/180.0))
y_pos = 1.1 * math.sin( angle * (pi/180.0))
if( type(limits) is types.FloatType ):
self.text( x_pos, y_pos, "%.2f" % j, size=10, va='center', ha='center', rotation=(angle - 90))
else:
self.text( x_pos, y_pos, "%d" % int(j), size=10, va='center', ha='center', rotation=(angle - 90))

def draw_bounding_box(self, graph_width, graph_height):
x_vect = [
graph_width/2,
graph_width/2,
-graph_width/2,
-graph_width/2,
graph_width/2,
]

y_vect = [
-0.1,
graph_height,
graph_height,
-0.1,
-0.1,
]

p = self.plot(x_vect, y_vect, 'r-', linewidth=0)

if __name__=='__main__':
from pylab import figure, show

current_value = -4.0
limits = [-1.0,1.0,1,0.1]
zone_colour = [[-1.0,0.0,'r'],[0.0,0.5,'y'],[0.5,1.0,'g']]
attribute_name = "Rx MOS (24h)"

graph_height = 1.6
graph_width = 2.4
fig_height = graph_height
fig_width = graph_width

fig = figure(figsize=(fig_width, fig_height ))

rect = [(0.0/fig_width), (0.2/fig_height),
(graph_width/fig_width), (graph_height/fig_height)]

gauge = Gauge(fig, rect,
xlim=( -0.1, graph_width+0.1 ),
ylim=( -0.4, graph_height+0.1 ),
xticks=[],
yticks=[],
)
gauge.set_axis_off()