awips2/cave/com.raytheon.viz.avnconfig/localization/aviation/python/Selection.py
Max Schenkelberg 6f60751ec6 Issue #2033 moved avnfps and text workstation files into respective plugins.
Change-Id: If95cb839ad81ca2a842ff7f6926847ac3928d8f2

Former-commit-id: 77e1a4d8f5237e5fae930c1e00589c752f8b3738
2013-08-15 12:21:43 -05:00

1189 lines
38 KiB
Python

##
# This software was developed and / or modified by Raytheon Company,
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
#
# U.S. EXPORT CONTROLLED TECHNICAL DATA
# This software product contains export-restricted data whose
# export/transfer/disclosure is restricted by U.S. law. Dissemination
# to non-U.S. persons whether in the United States or abroad requires
# an export license or other authorization.
#
# Contractor Name: Raytheon Company
# Contractor Address: 6825 Pine Street, Suite 340
# Mail Stop B8
# Omaha, NE 68106
# 402.291.0100
#
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
# further licensing information.
##
#
# Name:
# Selection.py
# GFS1-NHD:A9058.0000-SCRIPT;3
#
# Status:
# DELIVERED
#
# History:
# Revision 3 (DELIVERED)
# Created: 20-MAR-2007 13:36:53 OBERFIEL
# Updated text input fields and printer dialogs to be
# consistent. Frame tag removed.
#
# Revision 2 (DELIVERED)
# Created: 16-FEB-2006 14:32:56 TROJAN
# removed unnecessary imports, renamed shadowed variables
#
# Revision 1 (APPROVED)
# Created: 13-FEB-2006 10:22:43 TROJAN
# stdr 945
#
# Change Document History:
# 1:
# Change Document: GFS1-NHD_SPR_7284
# Action Date: 02-MAY-2007 10:53:18
# Relationship Type: In Response to
# Status: CLOSED
# Title: AvnFPS: Climate tools input fields should be consistent.
#
#
import exceptions, math
import Tkinter as tk
import Pmw
import Avn
##############################################################################
def hour_value_validator(text):
if len(text) > 5:
return Pmw.ERROR
text = text.strip()
if len(text) < 5:
return Pmw.PARTIAL
try:
h, m = text.split(':')
if len(h) != 2 or len(m) != 2:
raise ValueError
if not 0 <= int(h) < 24:
raise ValueError
if not 0 <= int(m) < 59:
raise ValueError
except (IndexError, ValueError):
return Pmw.ERROR
return Pmw.OK
def hour_range_validator(text):
if len(text) > 11:
return Pmw.ERROR
text = text.strip()
if len(text) < 11 or '-' not in text:
return Pmw.PARTIAL
try:
from_, to = text.split('-')
if len(from_) != 5 or len(to) != 5:
return Pmw.ERROR
except (IndexError, ValueError):
return Pmw.ERROR
for x in from_, to:
if x[2] != ':':
return Pmw.ERROR
h = int(x[:2])
if not 0 <= h < 24:
return Pmw.ERROR
m = int(x[3:])
if not 0 <= m < 60:
return Pmw.ERROR
return Pmw.OK
def date_value_validator(text):
if len(text) > 4:
return Pmw.ERROR
text = text.strip()
if len(text) < 4:
return Pmw.PARTIAL
try:
m, d = text[:2], text[2:]
if len(m) != 2 or len(d) != 2:
raise ValueError
if not 0 < int(m) <= 12:
raise ValueError
if not 0 < int(d) <= 31:
raise ValueError
except (IndexError, ValueError):
return Pmw.ERROR
return Pmw.OK
def date_range_validator(text):
if len(text) > 9:
return Pmw.ERROR
text = text.strip()
if len(text) < 9 or '-' not in text:
return Pmw.PARTIAL
try:
from_, to = text.split('-')
if len(from_) != 4 or len(to) != 4:
raise ValueError
int(from_)
int(to)
except (IndexError, ValueError):
return Pmw.ERROR
for x in from_, to:
month = int(x[:2])
if not 1 <= month <= 12:
return Pmw.ERROR
day = int(x[2:])
if not 1 <= day <= 31:
return Pmw.ERROR
return Pmw.OK
def wind_dir_value_validator(text):
if len(text) > 4:
return Pmw.ERROR
text = text.strip().upper()
if len(text) < 3:
return Pmw.PARTIAL
if text == 'CALM':
return Pmw.OK
if text.startswith('c'):
return Pmw.PARTIAL
try:
if not 0 <= int(text) <= 360:
raise ValueError
except ValueError:
return Pmw.ERROR
return Pmw.OK
def wind_dir_range_validator(text):
if len(text) > 7:
return Pmw.ERROR
text = text.strip()
if len(text) < 7 or '-' not in text:
return Pmw.PARTIAL
try:
from_, to = text.split('-')
if len(from_) != 3 or len(to) != 3:
raise ValueError
if not 0 < int(from_) <= 360 or not 0 < int(to) <= 360:
raise ValueError
except ValueError:
return Pmw.ERROR
return Pmw.OK
def wind_speed_value_validator(text):
if len(text) > 5:
return Pmw.ERROR
text = text.strip()
if text.endswith('+'):
text = text[:-1]
try:
if not 0.0 <= float(text) <= 99.0:
raise ValueError
except ValueError:
return Pmw.PARTIAL
return Pmw.OK
def wind_speed_range_validator(text):
if len(text) > 11:
return Pmw.ERROR
text = text.strip()
if text.endswith('+'):
text = text[:-1]
if '-' not in text or text.endswith('-'):
return Pmw.PARTIAL
try:
from_, to = text.split('-')
if not 0.0 <= float(from_) <= 99.0 or not 0.0 < float(to) <= 99.0:
raise ValueError
except ValueError:
return Pmw.ERROR
return Pmw.OK
def ceiling_value_validator(text):
if len(text) > 5:
return Pmw.ERROR
text = text.strip()
try:
if not 0 <= int(text) <= 30000:
raise ValueError
except ValueError:
return Pmw.PARTIAL
return Pmw.OK
def ceiling_range_validator(text):
if len(text) > 11:
return Pmw.ERROR
text = text.strip().upper()
if '-' not in text or text.endswith('-'):
return Pmw.PARTIAL
try:
from_, to = text.split('-')
if from_.endswith('+'):
from_ = from_[:-1]
if to.endswith('+'):
to = from_[:-1]
if not 0 <= int(from_) <= 30000:
raise ValueError
if to == 'UNL':
return Pmw.OK
elif not 0 <= int(to) <= 30000:
raise ValueError
except ValueError:
return Pmw.ERROR
return Pmw.OK
def visibility_value_validator(text):
if len(text) > 5:
return Pmw.ERROR
text = text.strip()
try:
if not 0 <= float(text) <= 20.0:
raise ValueError
except ValueError:
return Pmw.PARTIAL
return Pmw.OK
def visibility_range_validator(text):
if len(text) > 11:
return Pmw.ERROR
text = text.strip()
if text.endswith('+'):
text = text[:-1]
if '-' not in text or text.endswith('-'):
return Pmw.PARTIAL
try:
from_, to = text.split('-')
if not 0 <= float(from_) <= 20.0:
raise ValueError
if not 0 <= float(to) <= 20.0:
raise ValueError
except ValueError:
return Pmw.ERROR
return Pmw.OK
##############################################################################
class _BaseLine(Pmw.MegaWidget):
def __init__(self, parent=None, **kw):
optiondefs = (
('background', 'grey', None),
('width', 250, Pmw.INITOPT),
('height', 60, Pmw.INITOPT),
('margin', 10, Pmw.INITOPT),
('valuecolor', 'blue', Pmw.INITOPT),
('rangecolor', 'red', Pmw.INITOPT),
('valuevalidator', None, Pmw.INITOPT),
('rangevalidator', None, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
Pmw.MegaWidget.__init__(self, parent)
# Create the components.
interior = self.interior()
label = self.createcomponent('title',
(), None,
tk.Label, (interior,),
relief='raised',
borderwidth=1,
text=self['title'],
)
label.grid(row=0, column=0, sticky='ew')
frame = self.createcomponent('frame',
(), None,
tk.Frame, (interior,),
relief='raised',
borderwidth=1,
)
frame.grid(row=2, column=0, sticky='news')
self._valueentry = self.createcomponent('value',
(), None,
Pmw.EntryField, (frame,),
labelpos='w',
label_text='value:',
label_justify='right',
validate=self['valuevalidator'],
entry_width=4,
command=self.draw_value,
)
self._valueentry.pack(side='left', fill='x', expand=1, padx=3)
self._rangeentry = self.createcomponent('range',
(), None,
Pmw.EntryField, (frame,),
labelpos='w',
label_text='range:',
label_justify='right',
validate=self['rangevalidator'],
entry_width=9,
command=self.draw_range,
)
self._rangeentry.pack(side='left', fill='x', expand=1, padx=3)
Pmw.alignlabels([self._valueentry, self._rangeentry])
c = self._canvas = self.createcomponent('canvas',
(), None,
tk.Canvas, (interior,),
width=self['width'],
height=self['height'],
)
c.grid(row=1, column=0)
# Draw labeled scale
self.yc = self['height']/2
self.x0, self.x1 = self['margin'], self['width']-self['margin']
c.create_line(self.x0, self.yc, self.x1, self.yc, fill='black',
width=1)
for v, text in self.ticks():
x = self.value2x(v)
c.create_line(x, self.yc, x, self.yc-5, fill='black', width=1)
if text:
c.create_text(x, self.yc-5, text=text, anchor='s')
c.bind('<Any-ButtonRelease>', self.btnUp)
c.bind('<B1-Motion>', self.btnMove)
c.bind('<B2-Motion>', self.btnMove)
self._cursor = c.cget('cursor')
self._movePending = False
# Check keywords and initialise options.
self.initialiseoptions()
def __move_value(self):
# redraws value as an arrow on the X axis
x = self.value2x(self._value)
self._canvas.coords(self._canvas.find_withtag('arrow'),
x, self.yc, x+5, self.yc-8, x-5, self.yc-8)
def __move_range(self):
# redraws range as a shaded rectangle on the X axis
c = self._canvas
x0, x1 = self.value2x(self._range[0]), self.value2x(self._range[1])
y0, y1 = self.yc+2, self.yc+20
c.coords(c.find_withtag('range'), x0, y0, x1, y1)
c.coords(c.find_withtag('from'), x0, y0, x0, y1)
c.coords(c.find_withtag('to'), x1, y0, x1, y1)
def __moveCompressed(self):
c = self._canvas
x = max(self.x0, c.canvasx(self._event.x))
current_value = self.x2value(x)
items = c.find_withtag('active')
if not items:
return
if items[0] == c.find_withtag('arrow')[0]:
if self._value != current_value:
oldx = self.value2x(self._value)
self.set_value(current_value)
x = self.value2x(self._value)
self.__move_value()
if self._button == 2:
dx = x - oldx
x0, y0, x1, y1 = c.coords(c.find_withtag('range'))
x0 = max(self.x0, min(self.x1, x0+dx))
x1 = max(self.x0, min(self.x1, x1+dx))
from_, to = self.x2value(x0), self.x2value(x1)
self.set_range(from_, to)
self.__move_range()
elif items[0] == c.find_withtag('from')[0]:
if self._range[0] != current_value:
self.set_range(current_value, self._range[1])
self.__move_range()
elif items[0] == c.find_withtag('to')[0]:
if self._range[1] != current_value:
self.set_range(self._range[0], current_value)
self.__move_range()
c.update_idletasks()
self._movePending = False
#########################################################################
# Public methods
def btnDown(self, event):
# sets active tag
c = self._canvas
c.addtag_withtag('active', 'current')
c.lift('active')
self._movePending = False
self._button = event.num
c.configure(cursor='arrow')
def btnUp(self, event):
# deletes active tag
c = self._canvas
c.configure(cursor=self._cursor)
tags = c.find_withtag('active')
if not tags:
return
c.dtag('active')
def btnMove(self, event):
# saves mouse coordinates, schedules __moveCompressed()
tags = self._canvas.find_withtag('active')
if not tags:
return
self._event = event
if not self._movePending:
timerId = self._canvas.after_idle(self.__moveCompressed)
self._movePending = True
def set_value(self, value):
# sets value in valueentry widget
self._value, s = self.scale(self.normalize(value))
self._valueentry.setvalue(s)
def get_value(self):
# returns value from valueentry widget
if not self._valueentry.valid():
raise ValueError('Invalid entry: '+\
str(self._valueentry.getvalue()))
return self.parse(self._valueentry.getvalue().strip().upper())
def set_range(self, from_, to):
# sets value in rangeentry widget
if from_ > to:
from_, to = to, from_
from_, s_from = self.scale(self.normalize(from_))
to, s_to = self.scale(self.normalize(to))
self._range = from_, to
self._rangeentry.setvalue(s_from+'-'+s_to)
def get_range(self):
if not self._rangeentry.valid():
raise ValueError('Invalid entry: '+\
str(self._rangeentry.getvalue()))
return [self.parse(s.strip()) for s in \
self._rangeentry.getvalue().split('-')]
def draw_value(self, value=None):
# draws value as an arrow on the X axis
if value is None:
self.set_value(self.get_value())
else:
self.set_value(value)
if not self._valueentry.valid():
return
c = self._canvas
c.delete('arrow')
x = self.value2x(self._value)
c.create_polygon(x, self.yc, x+5, self.yc-8, x-5, self.yc-8,
fill=self['valuecolor'], width=1, tag='arrow')
c.tag_bind('arrow', '<ButtonPress-1>', self.btnDown)
c.tag_bind('arrow', '<ButtonPress-2>', self.btnDown)
def draw_range(self, from_=None, to=None):
# draws range as a shaded rectangle on the X axis
if from_ is None or to is None:
self.set_range(*self.get_range())
else:
self.set_range(from_, to)
if not self._rangeentry.valid():
return
c = self._canvas
c.delete('range', 'from', 'to')
x0, x1 = self.value2x(self._range[0]), self.value2x(self._range[1])
color = self['rangecolor']
c.create_rectangle(x0, self.yc+2, x1, self.yc+20, outline=color,
fill=color, stipple='gray25', tag='range')
c.create_line(x0, self.yc+2, x0, self.yc+20, fill=color, tag='from')
c.create_line(x1, self.yc+2, x1, self.yc+20, fill=color, tag='to')
c.tag_bind('from', '<ButtonPress-1>', self.btnDown)
c.tag_bind('to', '<ButtonPress-1>', self.btnDown)
def ticks(self):
raise Avn.AvnError('Not implemented')
def normalize(self, value):
raise Avn.AvnError('Not implemented')
def parse(self, text):
raise Avn.AvnError('Not implemented')
def scale(self, value):
raise Avn.AvnError('Not implemented')
def value2x(self, value):
raise Avn.AvnError('Not implemented')
def x2value(self, x):
raise Avn.AvnError('Not implemented')
##############################################################################
class Ceiling(_BaseLine):
'''Widget to select ceiling value and match range'''
UNLIMITED = 99999.0
LIMITED = 25000.0
def __init__(self, parent=None, **kw):
optiondefs = (
('title', 'Ceiling', None),
('top', 5000.0, Pmw.INITOPT),
('shift', 100.0, Pmw.INITOPT),
('valuevalidator', ceiling_value_validator, Pmw.INITOPT),
('rangevalidator', ceiling_range_validator, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
_BaseLine.__init__(self, parent)
def ticks(self):
ticks = [(0.0, '0'), (200.0, '2'), (600.0, '6'), (1000.0, '10'), \
(3100.0, '31')]
return ticks + [(self['top'], '%d' % (self['top']//100)), \
(self.LIMITED, '%d+' % (self['top']//100+1)), \
(self.UNLIMITED, 'UNL')]
def get_range(self):
range = _BaseLine.get_range(self)
if range[0] == self.LIMITED:
range[0] = self['top']+100.0
return range
def normalize(self, value):
if value < 0.0:
value = 0.0
if value <= self['top']:
return value
elif value <= self.LIMITED:
return self.LIMITED
else:
return self.UNLIMITED
def scale(self, value):
if value <= self['shift']:
value = 0.0
if value <= self['top']:
return value, '%.0f' % value
elif value == self.LIMITED:
return self.LIMITED, '%.0f+' % (self['top']+100)
elif value == self.UNLIMITED:
return self.UNLIMITED, 'UNL'
else:
print '==== BAD thing happened #1 ===='
def parse(self, text):
if text == 'UNL':
return self.UNLIMITED
elif text.endswith('+'):
return self.LIMITED
else:
return self.normalize(float(text))
def value2x(self, value):
value = self.normalize(value)
if value <= self['shift']:
value = self['shift']
if value <= self['top']:
v = math.sqrt((value-self['shift'])/ \
(self['top']-self['shift']))*0.8
elif value <= self.LIMITED:
v = 0.9
else:
v = 1.0
return self.x0 + v*(self.x1-self.x0)
def x2value(self, x):
v = (x-self.x0)/(self.x1-self.x0)
if v <= 0.8:
return (v/0.8)**2*(self['top']-self['shift']) + self['shift']
elif v <= 0.9:
return self.LIMITED
else:
return self.UNLIMITED
##############################################################################
class Visibility(_BaseLine):
'''Widget to select visibility value and match range'''
UNLIMITED = 20.0
def __init__(self, parent=None, **kw):
optiondefs = (
('title', 'Visibility', None),
('top', 10.0, Pmw.INITOPT),
('shift', 0.1, Pmw.INITOPT),
('valuevalidator', visibility_value_validator, Pmw.INITOPT),
('rangevalidator', visibility_range_validator, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
_BaseLine.__init__(self, parent)
def ticks(self):
ticks = [(0.0, '0'), (0.25, '1/4'), (1.0, '1'), (3.0, '3'), \
(5.0, '5'), (6.0, '6')]
return ticks + [(self['top'], '%d' % (self['top']//1.0)), \
(self.UNLIMITED, '%d+' % ((self['top']+1)//1.0))]
def normalize(self, value):
if value < 0.0:
value = 0.0
if value <= self['top']:
return value
else:
return self.UNLIMITED
def scale(self, value):
if value <= self['shift']:
value = 0.0
if value < 1.0:
return value, '%.2f' % value
elif value <= self['top']:
return value, '%.1f' % value
elif value == self.UNLIMITED:
return self.UNLIMITED, '%d+' % ((self['top']+1)//1.0)
else:
print '=====', value
print '==== BAD thing happened #1 ===='
def parse(self, text):
if text.endswith('+'):
return self.UNLIMITED
else:
return self.normalize(float(text))
def value2x(self, value):
value = self.normalize(value)
if value <= self['shift']:
value = self['shift']
if value <= self['top']:
v = math.sqrt((value-self['shift'])/ \
(self['top']-self['shift']))*0.9
else:
v = 1.0
return self.x0 + v*(self.x1-self.x0)
def x2value(self, x):
v = (x-self.x0)/(self.x1-self.x0)
if v <= 0.9:
return (v/0.9)**2*(self['top']-self['shift']) + self['shift']
else:
return self.UNLIMITED
##############################################################################
class WindSpeed(_BaseLine):
'''Widget to select wind speed value and match range'''
UNLIMITED = 50.0
def __init__(self, parent=None, **kw):
optiondefs = (
('title', 'Wind Speed', None),
('top', 30.0, Pmw.INITOPT),
('shift', 2.5, Pmw.INITOPT),
('valuevalidator', wind_speed_value_validator, Pmw.INITOPT),
('rangevalidator', wind_speed_range_validator, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
_BaseLine.__init__(self, parent)
def ticks(self):
ticks = [(0.0, '0'), (4.0, '4'), (10.0, '10')]
return ticks + [(self['top'], '%d' % (self['top']//1.0)), \
(self.UNLIMITED, '%d+' % ((self['top']+1)//1.0))]
def normalize(self, value):
if value < 0.0:
value = 0.0
if value <= self['top']:
return value
else:
return self.UNLIMITED
def scale(self, value):
if value <= self['shift']:
value = 0.0
if value <= self['top']:
return value, '%.1f' % value
elif value == self.UNLIMITED:
return self.UNLIMITED, '%d+' % ((self['top']+1)//1.0)
else:
print '=====', value
print '==== BAD thing happened #1 ===='
def parse(self, text):
if text.endswith('+'):
return self.UNLIMITED
else:
return self.normalize(float(text))
def value2x(self, value):
value = self.normalize(value)
if value <= self['shift']:
value = self['shift']
if value <= self['top']:
v = math.sqrt((value-self['shift'])/(self['top']-self['shift']))*0.9
else:
v = 1.0
return self.x0 + v*(self.x1-self.x0)
def x2value(self, x):
v = (x-self.x0)/(self.x1-self.x0)
if v <= 0.9:
return (v/0.9)**2*(self['top']-self['shift']) + self['shift']
else:
return self.UNLIMITED
##############################################################################
class _BaseCircle(Pmw.MegaWidget):
def __init__(self, parent=None, **kw):
optiondefs = (
('background', 'grey', None),
('size', 110, Pmw.INITOPT),
('margin', 20, Pmw.INITOPT),
('valuecolor', 'blue', Pmw.INITOPT),
('rangecolor', 'red', Pmw.INITOPT),
('valuevalidator', None, Pmw.INITOPT),
('rangevalidator', None, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
Pmw.MegaWidget.__init__(self, parent)
# Create the components.
interior = self.interior()
interior.grid_columnconfigure(2, weight=1)
interior.grid_rowconfigure(1, weight=1)
frame = self.createcomponent('frame',
(), None,
tk.Frame, (interior,),
relief='raised',
borderwidth=1,
)
frame.grid(row=0, column=1, sticky='news')
label = self.createcomponent('title',
(), None,
tk.Label, (frame,),
borderwidth=1,
text=self['title'],
)
label.pack(side='top')
self._valueentry = self.createcomponent('value',
(), None,
Pmw.EntryField, (frame,),
labelpos='w',
label_text='value:',
label_justify='right',
validate=self['valuevalidator'],
entry_width=12,
command=self.draw_value,
)
self._valueentry.pack(side='top', fill='x', expand=1)
self._rangeentry = self.createcomponent('range',
(), None,
Pmw.EntryField, (frame,),
labelpos='w',
label_text='range:',
label_justify='right',
validate=self['rangevalidator'],
entry_width=12,
command=self.draw_range,
)
self._rangeentry.pack(side='top', fill='x', expand=1)
Pmw.alignlabels([self._valueentry, self._rangeentry])
c = self._canvas = self.createcomponent('canvas',
(), None,
tk.Canvas, (interior,),
width=self['size'],
height=self['size'],
)
c.grid(row=0, column=0)
# Draw labelled circle
x0 = y0 = self['margin']
x1 = y1 = self['size'] - self['margin']
self._bbox = x0, y0, x1, y1
c.create_oval(self._bbox, outline='black')
self.xc = self.yc = self['size']/2
self.rad = self.xc - self['margin']
rad1 = self.rad + 5
for v, (text, anchor) in self.ticks():
fi = math.radians(90-self.value2fi(v))
x, y = math.cos(fi), -math.sin(fi)
x0 = self.xc + x*self.rad
x1 = self.xc + x*rad1
y0 = self.yc + y*self.rad
y1 = self.yc + y*rad1
c.create_line(x0, y0, x1, y1, fill='black')
if text:
c.create_text(x1, y1, text=text, anchor=anchor)
c.bind('<Any-ButtonRelease>', self.btnUp)
c.bind('<B1-Motion>', self.btnMove)
c.bind('<B2-Motion>', self.btnMove)
self._cursor = c.cget('cursor')
self._movePending = False
# Check keywords and initialise options.
self.initialiseoptions()
def __rotate_value(self):
c = self._canvas
c.coords(c.find_withtag('arrow'), *self.value_coords())
def __move_range(self):
c = self._canvas
fi_from = self.value2fi(self._range[0])
fi_to = self.value2fi(self._range[1])
extent = fi_to-fi_from
if extent < 0:
extent += 360
c.itemconfigure(c.find_withtag('range'), start=90-fi_to, extent=extent)
xe, ye = self._fi2xy(fi_from)
c.coords(c.find_withtag('from'), self.xc, self.yc, xe, ye)
xe, ye = self._fi2xy(fi_to)
c.coords(c.find_withtag('to'), self.xc, self.yc, xe, ye)
def _fi2xy(self, fi):
fi = math.radians(fi)
x = self.xc + self.rad*math.sin(fi)
y = self.yc - self.rad*math.cos(fi)
return x, y
def _xy2fi(self, x, y):
return math.degrees(math.atan2(x-self.xc, -y+self.yc))
def __moveCompressed(self):
c = self._canvas
x0, y0 = c.canvasx(self._event.x), c.canvasy(self._event.y)
current_fi = self.fi2value(self._xy2fi(x0, y0))
items = c.find_withtag('active')
if not items:
return
if items[0] == c.find_withtag('arrow')[0]:
if self._value != current_fi:
old_value = self._value
self.set_value(current_fi)
delta = self._value - old_value
self.__rotate_value()
if self._button == 2:
from_, to = self._range[0]+delta, self._range[1]+delta
self.set_range(from_, to)
self.__move_range()
elif items[0] == c.find_withtag('from')[0]:
if self._range[0] != current_fi:
self.set_range(current_fi, self._range[1])
self.__move_range()
elif items[0] == c.find_withtag('to')[0]:
if self._range[1] != current_fi:
self.set_range(self._range[0], current_fi)
self.__move_range()
c.update_idletasks()
self._movePending = False
def ticks(self):
raise Avn.AvnError('Not implemented')
def normalize(self, value):
raise Avn.AvnError('Not implemented')
def parse(self, text):
raise Avn.AvnError('Not implemented')
def scale(self, value):
raise Avn.AvnError('Not implemented')
def value2fi(self, value):
raise Avn.AvnError('Not implemented')
def fi2value(self, fi):
raise Avn.AvnError('Not implemented')
##########################################################################
# Public methods
def btnDown(self, event):
c = self._canvas
c.addtag_withtag('active', 'current')
c.lift('active')
self._movePending = False
self._button = event.num
c.configure(cursor='arrow')
def btnUp(self, event):
c = self._canvas
c.configure(cursor=self._cursor)
tags = c.find_withtag('active')
if not tags:
return
c.dtag('active')
def btnMove(self, event):
tags = self._canvas.find_withtag('active')
if not tags:
return
self._event = event
if not self._movePending:
timerId = self._canvas.after_idle(self.__moveCompressed)
self._movePending = True
def value_coords(self):
fi = math.radians(self.value2fi(self._value))
x = math.sin(fi)
y = -math.cos(fi)
x0 = self.xc - x*self.rad/5
y0 = self.yc - y*self.rad/5
x1 = self.xc + x*(self.rad-5)
y1 = self.yc + y*(self.rad-5)
fi1 = fi+math.pi/12
x2 = x1 - math.sin(fi1)*10
y2 = y1 - -math.cos(fi1)*10
fi2 = fi-math.pi/12
x3 = x1 - math.sin(fi2)*10
y3 = y1 - -math.cos(fi2)*10
return x0, y0, x1, y1, x2, y2, x3, y3, x1, y1
def set_value(self, value):
self._value, s = self.scale(self.normalize(value))
self._valueentry.setvalue(s)
def get_value(self):
if not self._valueentry.valid():
raise ValueError('Invalid entry: '+ \
str(self._valueentry.getvalue()))
return self.parse(self._valueentry.getvalue().strip().upper())
def set_range(self, from_, to):
from_, s_from = self.scale(self.normalize(from_))
to_, s_to = self.scale(self.normalize(to))
self._range = from_, to
self._rangeentry.setvalue(s_from+'-'+s_to)
def get_range(self):
if not self._rangeentry.valid():
raise ValueError('Invalid entry: '+ \
str(self._rangeentry.getvalue()))
return [self.parse(s.strip()) for s in \
self._rangeentry.getvalue().split('-')]
def draw_value(self, value=None):
if value is None:
self.set_value(self.get_value())
else:
self.set_value(value)
if not self._valueentry.valid():
return
c = self._canvas
c.delete('arrow')
kwds = {'width': 2, 'fill': self['valuecolor'], 'tag': 'arrow'}
c.create_line(*self.value_coords(), **kwds)
if c.gettags('range'):
c.tag_raise('arrow', 'range')
c.tag_bind('arrow', '<ButtonPress-1>', self.btnDown)
c.tag_bind('arrow', '<ButtonPress-2>', self.btnDown)
def draw_range(self, from_=None, to=None):
if from_ is None or to is None:
self.set_range(*self.get_range())
else:
self.set_range(from_, to)
if not self._rangeentry.valid():
return
c = self._canvas
c.delete('range', 'from', 'to')
fi_from = self.value2fi(self._range[0])
fi_to = self.value2fi(self._range[1])
extent = fi_to-fi_from
if extent < 0:
extent += 360
c.create_arc(self._bbox, start=90-fi_to, extent=extent,
outline=self['rangecolor'], fill=self['rangecolor'],
stipple='gray25', tag='range')
if c.gettags('arrow'):
c.tag_lower('range', 'arrow')
xe, ye = self._fi2xy(fi_from)
c.create_line(self.xc, self.yc, xe, ye, fill=self['rangecolor'],
tag='from')
c.tag_bind('from', '<ButtonPress-1>', self.btnDown)
xe, ye = self._fi2xy(fi_to)
c.create_line(self.xc, self.yc, xe, ye, fill=self['rangecolor'],
tag='to')
c.tag_bind('to', '<ButtonPress-1>', self.btnDown)
##############################################################################
class HourOfDay(_BaseCircle):
'''Widget to select hour of the day and match range'''
def __init__(self, parent=None, **kw):
optiondefs = (
('title', 'Hour', None),
('valuevalidator', hour_value_validator, Pmw.INITOPT),
('rangevalidator', hour_range_validator, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
_BaseCircle.__init__(self, parent)
self._canvas.create_oval(self.xc-3, self.yc-3, self.xc+3, self.yc+3,
fill=self['valuecolor'], outline=self['valuecolor'])
def ticks(self):
# wind specific
def _tick(hour):
if hour%4 == 0:
a = {0: 's', 4: 'w', 8: 'w', 12: 'n', 16: 'e', 20: 'e'}[hour]
return hour, ('%02d' % hour, a)
else:
return hour, (None, None)
return map(_tick, range(24))
def normalize(self, value):
return value%24
def parse(self, text):
h, m = [int(x) for x in text.split(':')]
return h + m/60.0
def scale(self, value):
h, m = int(value), int(value%1.0*60)
return value, '%02d:%02d' % (h, m)
def value2fi(self, value):
# wind specific
return 15*self.normalize(value)
def fi2value(self, fi):
# wind specific
return fi/15.0
##############################################################################
class Date(_BaseCircle):
'''Widget to select julian day of year and match range'''
def __init__(self, parent=None, **kw):
optiondefs = (
('title', 'Date', None),
('valuevalidator', date_value_validator, Pmw.INITOPT),
('rangevalidator', date_range_validator, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
self.days = [sum(days[:n]) for n in range(1, len(days)+1)]
_BaseCircle.__init__(self, parent)
self._canvas.create_oval(self.xc-3, self.yc-3, self.xc+3, self.yc+3,
fill=self['valuecolor'], outline=self['valuecolor'])
def ticks(self):
return zip([(d0+d1)/2 for d0, d1 in Avn.window(self.days)],
[('J', 's'), ('F', 'w'), ('M', 'w'), ('A', 'w'), ('M', 'w'), \
('J', 'n'), ('J', 'n'), ('A', 'n'), ('S', 'e'), ('O', 'e'), \
('N', 's'), ('D', 's')])
def normalize(self, value):
return value%365
def parse(self, text):
month, day = int(text[:2]), int(text[2:4])
if month == 2 and day == 29:
day = 28
return self.days[month-1] + day - 1
def scale(self, value):
for m, (d0, d1) in enumerate(Avn.window(self.days)):
if d0 <= value < d1:
month, day = m+1, value-d0+1
break
return value, '%02d%02d' % (month, day)
def value2fi(self, value):
# wind specific
return 360.0/365.0*self.normalize(value)
def fi2value(self, fi):
# wind specific
return fi*365.0/360.0
##############################################################################
class WindDirection(_BaseCircle):
'''Widget to select wind direction and match range'''
def __init__(self, parent=None, **kw):
optiondefs = (
('title', 'Wind Direction', None),
('valuevalidator', wind_dir_value_validator, Pmw.INITOPT),
('rangevalidator', wind_dir_range_validator, Pmw.INITOPT),
)
self.defineoptions(kw, optiondefs)
_BaseCircle.__init__(self, parent)
self._last_value = 180
def btnDown3(self, event):
# toggle calm/variable wind
if self._value == 0:
self.draw_value(self._last_value)
self.draw_range(*self._last_range)
else:
self._last_value = self._value
self._last_range = self._range
self.set_value('CALM')
self.draw_value()
self.set_range(1, 360)
self.draw_range()
def set_value(self, value):
# wind specific, overrides method in the base class
if type(value) in [type(1), type(1.0)]:
self._value, s = self.scale(self.normalize(value))
else:
self._value, s = 0, 'CALM'
self._valueentry.setvalue(s)
def draw_value(self, value=None):
# wind specific, overrides method in the base class
if value is None:
self.set_value(self.get_value())
else:
self.set_value(value)
if not self._valueentry.valid():
return
c = self._canvas
c.delete('arrow')
if self._value == 0:
# Calm or variable wind
kwds = {'width': 2, 'outline': self['valuecolor'], 'tag': 'arrow'}
c.create_oval(self.xc-8, self.yc-8, self.xc+8, self.yc+8, **kwds)
c.tag_bind('wind', '<ButtonPress-3>', self.btnDown3)
else:
kwds = {'width': 2, 'fill': self['valuecolor'], 'tag': 'arrow'}
c.create_line(*self.value_coords(), **kwds)
if c.gettags('range'):
c.tag_raise('arrow', 'range')
c.tag_bind('arrow', '<ButtonPress-1>', self.btnDown)
c.tag_bind('arrow', '<ButtonPress-2>', self.btnDown)
c.tag_bind('arrow', '<ButtonPress-3>', self.btnDown3)
def value_coords(self):
# wind specific, overrides method in the base class
fi = math.radians(self.value2fi(self._value))
x = math.sin(fi)
y = -math.cos(fi)
r = self.rad - 5
x0, y0 = self.xc, self.yc
x1 = self.xc + x*r
y1 = self.yc + y*r
fi1 = fi-0.6*math.pi
x2 = x1 - math.sin(fi1)*12
y2 = y1 - -math.cos(fi1)*12
return x0, y0, x1, y1, x2, y2
def ticks(self):
return [(0, ('N', 's')), (45, ('NE', 's')), (90, ('E', 'w')), \
(135, ('SE', 'n')), (180, ('S', 'n')), (225, ('SW', 'n')), \
(270, ('W', 'e')), (315, ('NW', 's'))]
def normalize(self, value):
return value%360 or 360
def parse(self, text):
if text == 'CALM':
return 'CALM'
else:
return self.normalize(int(text))
def scale(self, value):
return value, '%03d' % value
def value2fi(self, value):
return self.normalize(value)
def fi2value(self, fi):
return fi%360.0