Gauges and Meters - Logarithmic Rotary Gauge.
Hello,
A few posts back, I included source code to some gauges I had whipped together. After some constructive advice from John Hunter (Thanks!), I’ve had time to polish them a bit and include the logarithmic ones as promised.
···
===============================================================================
#!/usr/bin/env python
‘’’
The Log_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
raw_value = -4.0
limits = [ left, right ]
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()
fig.add_axes(gauge)
show()
NOTE: The limits you specify must be of this form
for any value of 'n': 1.0*10^n
‘’’
from future import division
from matplotlib.figure import Figure
from matplotlib.axes import Axes
import math
import types
from math import pi
class Log_Gauge(Axes):
def __init__(self, raw_value, raw_limits, raw_zones, attribute_name, field_names, file_name, resolution, x_length, y_length, *args, **kwargs):
Axes.__init__(self, *args, **kwargs)
#Perform Checking
if( raw_limits[0] == raw_limits[1] ):
raise ValueError('identical_limits_exception: %s' % raw_limits)
if( raw_limits[1] > raw_limits[0] ):
self.graph_positive = True
else: #Swap the limits around
self.graph_positive = False
raw_limits[0], raw_limits[1] = raw_limits[1] = raw_limits[0]
if not( math.log10(raw_limits[0]) % 1.0 == 0 and math.log10(raw_limits[1]) % 1.0 == 0 ):
raise ValueError('bad_limits_exception:%s' % raw_limits)
for zone in raw_zones:
if( zone[0] > zone[1] ): #Swap the zones so zone[1] > zone[0]
zone[0], zone[1] = zone[1], zone[0]
if( zone[1] < raw_limits[0] or zone[0] > raw_limits[1] ):
raise ValueError('bad_zone_exception' % zone)
if( zone[0] < raw_limits[0] ):
zone[0] = raw_limits[0]
if( zone[1] > raw_limits[1] ):
zone[1] = raw_limits[1]
#Stuff all of the variables into self.
self.raw_value = raw_value
self.raw_limits = raw_limits
self.raw_zones = raw_zones
self.attribute_name = attribute_name
self.field_names = field_names
self.file_name = file_name
self.resolution = resolution
self.x_length = x_length
self.y_length = y_length
#Draw the arch
for zone in raw_zones:
self.draw_arch( zone, False )
self.draw_arch( None, True )
self.draw_ticks()
self.draw_needle()
self.draw_bounding_box()
self.text(0.0, 0.3, attribute_name, size=10, va='center', ha='center')
#The black dot
p = self.plot([0.0],[0.0],'.', color='#000000')
def draw_arch( self, zone, border ):
if( border ):
start = self.raw_limits[0]
end = self.raw_limits[1]
else:
start = zone[0]
end = zone[1]
colour = zone[2]
x_vect = []
y_vect = []
if( self.graph_positive ):
start_value = int(180 - (start - self.raw_limits[0]) * (180.0/(self.raw_limits[1]-self.raw_limits[0])))
end_value = int(180 - (end - self.raw_limits[0]) * (180.0/(self.raw_limits[1]-self.raw_limits[0])))
else:
start_value = int( (end - self.raw_limits[0]) * (180.0/(self.raw_limits[1]-self.raw_limits[0])))
end_value = int( (start - self.raw_limits[0]) * (180.0/(self.raw_limits[1]-self.raw_limits[0])))
if( self.graph_positive ):
start_value = (math.log10(start) - math.log10(self.raw_limits[1])) * 180.00 / -(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
end_value = (math.log10(end) - math.log10(self.raw_limits[1])) * 180.00 / -(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
else:
start_value = (math.log10(end) - math.log10(self.raw_limits[0])) * 180.00 / +(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
end_value = (math.log10(start) - math.log10(self.raw_limits[0])) * 180.00 / +(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
#Draw the arch
theta = start_value
radius = 0.85
while (theta >= end_value):
x_vect.append( radius * math.cos(theta * (pi/180)) )
y_vect.append( radius * math.sin(theta * (pi/180)) )
theta -= 1
theta = end_value
radius = 1.0
while (theta <= start_value):
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.0)
else:
p = self.fill(x_vect, y_vect, colour, linewidth=0.0, alpha=0.4)
def draw_needle( self ):
x_vect = []
y_vect = []
if self.raw_value == None:
self.text(0.0, 0.4, "N/A", size=10, va='bottom', ha='center')
else:
self.text(0.0, 0.4, "%.2f" % self.raw_value, size=10, va='bottom', ha='center')
#Clamp the value to the limits
if( self.raw_value < self.raw_limits[0] ):
self.raw_value = self.raw_limits[0]
if( self.raw_value > self.raw_limits[1] ):
self.raw_value = self.raw_limits[1]
theta = 0
length = 0.95
if( self.graph_positive ):
angle = (math.log10(self.raw_value) - math.log10(self.raw_limits[1])) * 180.00 / -(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
else:
angle = (math.log10(self.raw_value) - math.log10(self.raw_limits[0])) * 180.00 / +(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
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 ):
if( self.graph_positive ):
angle = 180.0
else:
angle = 0.0
i = self.raw_limits[0]
step = self.raw_limits[0]
x_vect = []
y_vect = []
while( i < self.raw_limits[1] ):
while( i < (step * 10) ):
x_vect = []
y_vect = []
value = math.log10(i)
if( self.graph_positive ):
angle = (value - math.log10(self.raw_limits[1])) * 180.00 / -(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
else:
angle = (value - math.log10(self.raw_limits[0])) * 180.00 / +(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
x_pos = 1.1 * math.cos( angle * (pi/180.0))
y_pos = 1.1 * math.sin( angle * (pi/180.0))
mantissa = int(i / math.pow(10, math.ceil(math.log10(i))-1))
if( mantissa == 10 or mantissa == 1 ):
if( type(self.raw_limits[2]) 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
else:
tick_length = 0.05
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")
i += step
i = step * 10
step = step * 10
i = self.raw_limits[1]
value = math.log10(i)
if( self.graph_positive ):
angle = (value - math.log10(self.raw_limits[1])) * 180.00 / -(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
else:
angle = (value - math.log10(self.raw_limits[0])) * 180.00 / +(math.log10(self.raw_limits[1]) - math.log10(self.raw_limits[0]))
x_pos = 1.1 * math.cos( angle * (pi/180.0))
y_pos = 1.1 * math.sin( angle * (pi/180.0))
mantissa = int(i / math.pow(10, math.ceil(math.log10(i))-1))
if( mantissa == 10 ):
if( type(self.raw_limits[2]) 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 ):
x_vect = [
self.x_length/2,
self.x_length/2,
-self.x_length/2,
-self.x_length/2,
self.x_length/2,
]
y_vect = [
-0.1,
self.y_length,
self.y_length,
-0.1,
-0.1,
]
p = self.plot(x_vect, y_vect, 'r-', linewidth=0)
def make_widget( raw_value, raw_limits, raw_zones, attribute_name, field_names, file_name, resolution=72 ):
from pylab import figure, show, savefig
x_length = 2.4 # Length of the Primary axis
y_length = 1.6 # Length of the Secondary axis
fig_height = y_length
fig_width = x_length
fig = figure( figsize=(fig_width, fig_height) )
rect = [(0.0/fig_width), (0.2/fig_height), (x_length/fig_width), (y_length/fig_height)]
gauge = Log_Gauge( raw_value,
raw_limits, raw_zones,
attribute_name, field_names,
file_name, resolution,
x_length, y_length,
fig, rect,
xlim=( -0.1, x_length+0.1 ),
ylim=( -0.4, y_length+0.1 ),
xticks=[],
yticks=[],
)
gauge.set_axis_off()
fig.add_axes(gauge)
show()
fig.canvas.print_figure( file_name,dpi=resolution )
#make_widget( 0.01, [0.001,10.0], [[0.001,0.01,‘r’],[0.01,0.1,‘y’],[0.1,10.0,‘g’]], “Rx MOS (24h)”, [‘WLL to LAS’,‘LAS to WLL’,‘WLL to LAS’,‘LAS to WLL’], ‘log_gauge.png’, 100)
‘’’
if name==‘main’:
from pylab import figure, show
raw_value = -4.0
limits = [0.0001,1.0]
zone_colour = [[0.0001,0.001,'r'],[0.001,0.1,'y'],[0.1,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 = Log_Gauge(fig, rect,
xlim=( -0.1, graph_width+0.1 ),
ylim=( -0.4, graph_height+0.1 ),
xticks=[],
yticks=[],
)
gauge.set_axis_off()
fig.add_axes(gauge)
#show()
fig.canvas.print_figure('log_gauge',dpi=72)
‘’’