awips2/cave/com.raytheon.viz.gfe/localization/gfe/userPython/procedures/DefineBreakpoints.py
2022-05-05 12:34:50 -05:00

931 lines
34 KiB
Python

# ----------------------------------------------------------------------------
# This software is in the public domain, furnished "as is", without technical
# support, and with no warranty, express or implied, as to its usefulness for
# any purpose.
#
# DefineBreakpoints
#
# SOFTWARE HISTORY
#
# Date Ticket# Engineer Description
# ------------ ---------- ----------- ------------------------------------------
# May 9, 2019 21020 tlefebvr Original version
# May 16, 2019 21020 tlefebvr Code review changes
# May 28, 2019 21020 tlefebvr Fixed issue when no advisories available
# May 31, 2019 21020 tlefebvr Fixed Zone display issues and improved
# performance when updating.
# Aug. 2, 2019 21020 tlefebvr Clean-up based on code review.
# Aug. 14,2019 21020 tlefebvr Implemented contiguous deselection.
# Refactored updates to zone display for
# better performance.
# Aug. 22, 2019 21020 tlefebvr Code Review changes
# Sep. 18, 2019 21020 tlefebvr Final Code review changes
################################################################################
MenuItems = ["Populate"]
VariableList = []
import time
import tkinter as tk
import numpy as np
import AbsTime
import TimeRange
import TropicalUtility
import WindWWUtils
import ZoneMap
class Procedure (TropicalUtility.TropicalUtility):
def __init__(self, dbss):
TropicalUtility.TropicalUtility.__init__(self, dbss)
self._dbss = dbss
# Saves the specified stormInfo to the JSON files under pil.
def saveStormInfo(self, pil):
# Find the dict with the matching pil
for (stormPil, stormInfo) in self._stormInfoDicts.items():
if stormPil == pil:
# insert the bpDict
stormInfo["Breakpoints"] = self.makeBPDict()
zoneDict = self.makeZoneDict(stormInfo["Breakpoints"])
stormInfo["zoneDict"] = zoneDict
# Use TropicalUtility to save advisories.
self._saveAdvisory(pil, stormInfo)
break
return
# Returns the list of zones for the specified bpDict and hazard.
def getBPZones(self, bpDict, haz):
bpList = bpDict[haz]
zoneList = []
for bp in bpList:
zoneList = zoneList + self._bpDict[bp]
return zoneList
# Makes a dictionary of {haz : zoneList} based on the bpDict.
def makeZoneDict(self, bpDict):
hazZoneDict = {}
for haz in self._hazList:
bpList = bpDict[haz]
for bp in bpList:
hazZoneDict[haz] = self.getBPZones(bpDict, haz)
return hazZoneDict
# Makes a dictionary of {haz : breakpoint} list.
def makeBPDict(self):
hazBPDict = {}
for haz in self._hazList:
hazBPDict[haz] = self.makeBreakpointList(haz)
return hazBPDict
# Looks up the breakpoint name and returns the corresponding zoneList.
def breakpointToZoneList(self, bpName):
try:
return self._bpDict[bpName]
except KeyError:
print ("Unknown breakpoint", bpName, "passed to breakpointToZoneList.")
return []
# Called when the cancel button is clicked
def cancelCommand(self):
self._tkmaster.destroy()
# Called when run is selected. Just saves the stormInfo for the current pil.
def runCommand(self):
self.saveStormInfo(self._selectedPil)
# Called when Run/Dismiss button is clicked.
def runDismissCommand(self):
self.saveStormInfo(self._selectedPil)
self.cancelCommand()
# Create the Execute and Cancel buttons
def makeBottomButtons(self, frame):
# Make the frame
self._bottomButtonFrame = tk.Frame(frame, bg=self._bgColor)
self._bottomButtonFrame.grid(row=2, column = 0, columnspan=6, pady=20)
# Run button
self._runButton = tk.Button(self._bottomButtonFrame, text="Save",
command=self.runCommand)
self._runButton.grid(row=0, column=0, padx=20)
# Run/Dismiss button
self._runDismissButton = tk.Button(self._bottomButtonFrame, text="Save/Dismiss",
command=self.runDismissCommand)
self._runDismissButton.grid(row=0, column=1, padx=20)
# Cancel button
self._cancelButton = tk.Button(self._bottomButtonFrame, text="Cancel",
command=self.cancelCommand)
self._cancelButton.grid(row=0, column=2, padx=20)
return
# Returns the starting breakpoint name, if it's a segment. For singles, just return
# what is passed it.
def startBP(self, bpName):
bpName = bpName.strip()
parts = bpName.split(" - ")
bp = parts[0].strip()
pos = bp.find("(")
if pos > -1:
bp = bp[0:pos]
return bp
# Returns the ending breakpoint name, if it's a segment. For singles, just return
# what is passed it.
def endBP(self, bpName):
bpName = bpName.strip()
parts = bpName.split(" - ")
if len(parts) < 2:
return None
bp = parts[-1].strip()
pos = bp.find(")")
if pos > -1:
bp = bp[0:pos]
return bp
# Returns true if the specifed bp is a single (no " - ")
def singleBP(self, bpName):
return " - " not in bpName
# Find the index where the startBP is the bpStartName
def findStartBPIndex(self, bpList, bpStartName, availableIndexes):
for i in range(len(bpList)):
if not availableIndexes[i]:
continue
start = self.startBP(bpList[i])
if start is None:
return None
elif start == bpStartName:
return i
return None
# Sorts breakpoints into a list of linked breakpoints. Where
# one BP ends and another starts is a link. This code will
# sort the BPs into a list of linked BPs and a list of single
# BPs (orphans).
def sortBPKeys(self, bpList):
# Finds the BP index whose start matches the currentIndex end
def findLinkedIndex(currentIndex, availableIndexes):
target = self.endBP(bpList[currentIndex])
for i in range(len(bpList)):
if not availableIndexes[i]:
continue
if self.singleBP(bpList[i]):
continue
start = self.startBP(bpList[i])
if start == target:
return i
return None
# Finds the next available index that has not yet been used.
def findNextAvailableIndex(availableIndexes):
availArray = np.argwhere(availableIndexes)
if len(availArray) > 0:
return availArray[0][0]
return None
availableIndexes = np.ones(len(bpList), np.bool) # Keeps track of used list entries
finalList = []
orphanList = []
bpIndex = 0
# Loop over the whole list
innerList = [bpList[bpIndex]] # initialize with the first BP
availableIndexes[bpIndex] = False
while availableIndexes.any():
nextIndex = findLinkedIndex(bpIndex, availableIndexes)
if nextIndex is None: # end of the sequence
if len(innerList) == 1:
orphanList.append(bpList[bpIndex])
else: # longer than 1
finalList.append(innerList) # ordinary end of sequence
availableIndexes[bpIndex] = False
bpIndex = findNextAvailableIndex(availableIndexes)
if bpIndex is None:
break
innerList = [bpList[bpIndex]] # re-init the innerList
else: # found a next index
innerList.append(bpList[nextIndex])
availableIndexes[bpIndex] = False
bpIndex = nextIndex
availableIndexes[bpIndex] = False
return finalList, orphanList
# Returns the listbox that corresponds to the specified event.
def eventToListBox(self, event):
for haz in self._hazList:
if self._listBoxWidgets[haz] == event.widget:
return haz, self._listBoxWidgets[haz]
return None
# Paints the listbox entries various colors depending on their state.
def paintItems(self, hazard, item):
for haz in self._hazList:
# The selected item is already painted by Tk
if haz == hazard:
continue
if self.allowedComboException(haz, hazard):
if item in self._listBoxWidgets[haz].curselection():
# The other hazard is selected so, paint show both as selected
self._listBoxWidgets[haz].itemconfig(item, selectbackground="purple", foreground="black")
self._listBoxWidgets[hazard].itemconfig(item, selectbackground="purple", foreground="black")
continue
backColor = self._fadedColors[hazard]
foreColor = 'gray50'
self._listBoxWidgets[haz].itemconfig(item, background=backColor, foreground=foreColor)
# Clears the items in the listbox, depending on their state.
def clearItems(self, hazard, item):
backColor = "white"
foreColor = "black"
# First figure out what color the items should be if the hazard is one of the exceptions
if hazard in self._allowedHazCombos:
# See if the other hazard has the same selected item.
otherHaz = self._allowedHazCombos[0]
if hazard == self._allowedHazCombos[0]:
otherHaz = self._allowedHazCombos[1]
selItems = self._listBoxWidgets[otherHaz].curselection()
# If it's selected, set the colors differently
if item in selItems:
backColor = self._fadedColors[otherHaz]
foreColor = "gray50"
for haz in self._hazList:
if self.allowedComboException(haz, hazard):
if item in self._listBoxWidgets[haz].curselection():
# different colors for this case
bColor = self._boldColors[haz]
self._listBoxWidgets[haz].itemconfig(item, selectbackground=bColor, foreground="black")
self._listBoxWidgets[hazard].itemconfig(item, background=backColor, foreground="black")
self._listBoxWidgets[haz].itemconfig(item, background=backColor, foreground=foreColor)
# Returns true if the combination of hazards is allowed to occur at the same time.
def allowedComboException(self, haz1, haz2):
return haz1 in self._allowedHazCombos and haz2 in self._allowedHazCombos
# Returns the item that was selected based on the previous selections
def selectedItem(self, previous, current):
for c in current:
if c not in previous:
return c
return None
# Returns the item that was deselected based on the previous selections
def deselectedItem(self, previous, current):
for p in previous:
if p not in current:
return p
return None
def selectListBoxItem(self, listBox, haz, selectedItem):
if self.isAvailable(haz, selectedItem):
self.paintItems(haz, selectedItem)
self._lastItemSelected = selectedItem
else:
# De-select the item since it's already taken
listBox.selection_clear(selectedItem)
return
def deselectListBoxItem(self, listBox, haz, deselectedItem):
listBox.selection_set(deselectedItem)
self.clearItems(haz, deselectedItem)
listBox.selection_clear(deselectedItem)
self._lastItemDeselected = deselectedItem
return
# Determine if this hazards can be selected.
def isAvailable(self, hazard, item):
for haz in self._hazList:
if haz == hazard:
continue
if self.allowedComboException(haz, hazard):
continue
hazSelections = self._listBoxWidgets[haz].curselection()
if item in hazSelections:
return False
return True
#Returns true if the shift button was down for the specified event.
def shiftButtonDown(self, event):
return event.state & 0x0001
# A selection with a shift down is the contiguous selection event
# where the tool automatically selects the items from the previous
# selection to the current selection. The Tk EXTENDED mode only
# allows one contiguous set at a time, which is a requirement,
# so a bit of extra code to figure out what was clicked and then
# select every item from start to end.
# Figure out the start and end items and select those.
def processContiguousSelection(self, event, listBox, haz):
# A contiguous click must be in the same hazard as the previous click
if self._lastHazSelected is None or haz != self._lastHazSelected:
return
if self._lastItemSelected is not None:
lastItem = self._lastItemSelected
elif self._lastItemDeselected is not None:
lastItem = self._lastItemDeselected
else: # neither were previously selected
return
nearest = listBox.nearest(event.y)
start = min(nearest, lastItem)
end = max(nearest, lastItem) + 1
# Disable the contiguous selection for islands
if start >= self._islandSeparatorItem or end-1 >= self._islandSeparatorItem:
return
for i in range(start, end):
if self._lastItemSelected is not None:
if self.isAvailable(haz, i):
self.selectListBoxItem(listBox, haz, i)
listBox.selection_set(i)
self.updateZoneDisplay(haz, i, True) # add the hazard
else:
self.deselectListBoxItem(listBox, haz, i)
self.updateZoneDisplay(haz, i, False) # remove the hazard
self._lastHazSelected = None
self._lastItemSelected = None
# Update the breakpoint grid
self.displayZones()
return
def listBoxSelected(self, event):
haz, listBox = self.eventToListBox(event)
selItems = listBox.curselection()
# If a separator was selected, deselect and return
if self._islandSeparatorItem in selItems:
listBox.selection_clear(self._islandSeparatorItem)
return
# Find the item that was selected or de-selected.
deselected = self.deselectedItem(self._hazSelections[haz], selItems)
selected = self.selectedItem(self._hazSelections[haz], selItems)
if self.shiftButtonDown(event):
self.processContiguousSelection(event, listBox, haz)
else:
if selected is not None:
self.selectListBoxItem(listBox, haz, selected)
self.updateZoneDisplay(haz, selected, True) # add the hazard
self._lastItemSelected = selected
self._lastItemDeselected = None
elif deselected is not None:
self.deselectListBoxItem(listBox, haz, deselected)
self.updateZoneDisplay(haz, deselected, False) # add the hazard
self._lastItemDeselected = deselected
self._lastItemSelected = None
# Update the breakpoint grid
self.displayZones()
self._lastHazSelected = haz
self._hazSelections[haz] = listBox.curselection() # save the current selections
self.updateBreakpointDisplay()
return
# Makes the listbox GUI for a hazard.
def makeListBox(self, frame, bpList, orphanedBPs, label, mode):
# Make the listbox. A strange interaction with X windows causes other listbox
# selections to be unselected when any listbox selection is made. The
# "exportselection = 0" flag fixes this annoyance.
color = self._boldColors[label]
self._listBoxWidgets[label] = tk.Listbox(frame, selectmode=mode, height=20, width=35,
selectbackground=color, exportselection=0)
self._listBoxWidgets[label].grid(padx=5, pady=5)
for bp in bpList:
self._listBoxWidgets[label].insert(tk.END, bp)
# Add in the separator before the list of islands
self._listBoxWidgets[label].insert(tk.END, self._islandSeparator)
self._islandSeparatorItem = self._listBoxWidgets[label].size() - 1
# Add in the islands and other orphans
for bp in orphanedBPs:
self._listBoxWidgets[label].insert(tk.END, bp)
self._listBoxWidgets[label].grid(row=0, column=0)
self._listBoxWidgets[label].grid(row=0, column=0)
self._listBoxWidgets[label].bind("<<ListboxSelect>>", self.listBoxSelected)
self._listBoxWidgets[label].bind("<Shift-Button-1>", self.listBoxSelected)
scrollbar = tk.Scrollbar(frame)
scrollbar.grid(row= 0, column=1, sticky=tk.NS)
scrollbar.config(command=self._listBoxWidgets[label].yview)
self._listBoxWidgets[label].config(yscrollcommand=scrollbar.set)
label = tk.Label(frame, text=self._listLabels[label], font=self._activeFont)
label.grid(row=1)
return
# Returns the list of selected breakpoints.
def makeBreakpointList(self, haz):
selectedItems = self._listBoxWidgets[haz].curselection()
bpList = []
for s in selectedItems:
bpList = bpList + [self._listBoxWidgets[haz].get(s)]
return bpList
# Returns a list of tuples that represent contiguous BP segments
def getContiguousBPSegments(self, listBox):
bpSegments = []
selectedItems = listBox.curselection()
if len(selectedItems) == 0:
return bpSegments
start = selectedItems[0]
end = selectedItems[0]
for item in range(1, len(selectedItems)):
i = selectedItems[item]
if i - end == 1:
end = i
continue
else:
bpSegments.append((start, end))
start = i
end = i
bpSegments.append((start, end))
return bpSegments
# Makes the string that gets displayed in the GUI
def makeBPString(self, haz):
listBox = self._listBoxWidgets[haz]
bpSegments = self.getContiguousBPSegments(listBox)
bpList = []
finalStr = ""
for start, end in bpSegments:
if start >= self._islandSeparatorItem: # an island item
for i in range(start, end+1):
bpList.append(listBox.get(i))
else: # A regular item
startBP = self.startBP(listBox.get(start))
endBP = self.endBP(listBox.get(end))
if startBP is not None and endBP is not None:
bpList.append(startBP + "-" + endBP)
finalStr = ", ".join(bpList) # Concatenate the list into a single string
return finalStr
# Updates the breakpoints display
def updateBreakpointDisplay(self):
for haz, textBox in self._bpTextboxes.items():
hazBPs = self.makeBPString(haz)
# update the text box
textBox.delete(0.0, tk.END)
textBox.insert(0.0, hazBPs)
return
# Fetches the item number based on the label
def getListBoxItem(self, listBox, textStr):
for i in range(listBox.size()):
if listBox.get(i) == textStr:
return i
return None
# Deselects the specified pil button
def deselectPilButton(self, pil):
self._pilButtons[pil].configure(bg=self._deselectedColor)
self._pilButtons[pil].configure(activebackground=self._deselectedColor)
for haz in self._hazList:
listBox = self._listBoxWidgets[haz]
for item in listBox.curselection():
self.clearItems(haz, item)
listBox.selection_clear(item)
return
# Selects the list items that are defined in the stormInfoDicts.
def selectPilButton(self, pil):
self._pilButtons[pil].configure(bg=self._selectedColor)
self._pilButtons[pil].configure(activebackground=self._selectedColor)
self.fetchStormInfo()
if pil in self._stormInfoDicts:
bpDict = self._stormInfoDicts[pil]["Breakpoints"]
for haz, bpList in bpDict.items():
listBox = self._listBoxWidgets[haz]
for bp in bpList:
item = self.getListBoxItem(listBox, bp)
self.selectListBoxItem(listBox, haz, item)
listBox.selection_set(item)
self._hazSelections[haz] = listBox.curselection() # save the current selections
# Scroll this list to the first item
if len(self._hazSelections[haz]) > 0:
listBox.selection_set(self._hazSelections[haz][0])
# Update the entire zone display
self.createZoneDisplay()
return
# Called when a pil button is selected
def pilButtonSelected(self, pil):
# Deselect the old pil button and list items
if self._selectedPil != "":
self.deselectPilButton(self._selectedPil)
# Select the new button and restore list selections
self._selectedPil = pil
self.selectPilButton(self._selectedPil)
self._lastHazSelected = None
self._lastItemSelected = None
self.updateBreakpointDisplay()
return
# Makes the pil button.
def makePilButton(self, frame, pil):
self._pilButtons[pil] = tk.Button(frame, text=pil, width=5, command=lambda: self.pilButtonSelected(pil))
row = self._pilList.index(pil)
self._pilButtons[pil].grid(row=row, column=0, sticky=tk.NW, pady=5)
if pil == self._selectedPil:
self.selectPilButton(pil)
# Creates and plots the label
def makeHazardLabel(self, frame, haz, row):
label = tk.Label(frame, text=haz, font=self._activeFont)
label.grid(row=row, column=1)
# Selects the button that displays the zone areas
def selectDisplayZonesButton(self):
self._displayZonesButton.configure(bg=self._selectedColor)
self._displayZonesButton.configure(activebackground=self._selectedColor)
# Deselects the button that displays the zone areas
def deselectDisplayZonesButton(self):
self._displayZonesButton.configure(bg=self._deselectedColor)
self._displayZonesButton.configure(activebackground=self._deselectedColor)
# Called when the "Display Zones" button is clicked.
def displayZonesSelected(self):
if self._displayZones:
self.resetZoneDisplay()
self.displayZones()
self._displayZones = False
self.deselectDisplayZonesButton()
else:
self._displayZones = True
self.selectDisplayZonesButton()
self.pilButtonSelected(self._selectedPil)
self.createZoneDisplay()
# Creates the GUI areas that allows the user to display the selected zones.
def makeHazardArea(self, frame):
# Make buttons for the advisory pil
self._pilFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3)
self._pilFrame.grid(row=1, column=0, padx=10)
self._pilButtons = {}
for pil in self._pilList:
self.makePilButton(self._pilFrame, pil)
self._textboxFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3)
self._textboxFrame.grid(row=1, column=2, padx=10)
for haz in self._hazList:
label = tk.Label(self._textboxFrame, text=haz, font=self._activeFont)
row = self._hazList.index(haz)
label.grid(row=row, column=1)
# Make the text widget. Selected breakpoints will go there
self._bpTextboxes[haz] = tk.Text(self._textboxFrame, height=2,
width=150, wrap=tk.WORD, exportselection=0)
self._bpTextboxes[haz].grid(row=row, column=2, columnspan=5, sticky=tk.W)
self._displayZoneFrame = tk.Frame(frame)#, relief=tk.GROOVE, bd=3)
self._displayZoneFrame.grid(row=2, column=2, sticky=tk.W, padx=10)
label = "Display Zones"
self._displayZonesButton = tk.Button(self._displayZoneFrame, text=label, width=len(label), command=lambda: self.displayZonesSelected())
row = len(self._hazList)
self._displayZonesButton.grid(row=row)
label = tk.Label(self._displayZoneFrame, text=" Display 'BreakpointHazards' grid to see selected zones.")
label.grid(row=row, column=3)
if self._displayZones:
self.selectDisplayZonesButton()
else:
self.selectDisplayZonesButton()
return
# Return the other of the allowed combo, if any
def getOtherAllowedHazard(self, haz):
for hazard in self._allowedHazCombos:
if hazard != haz:
return hazard
return None
# Return True if the other allowed combo is selected
def hazardComboSelected(self, haz, item):
if haz not in self._allowedHazCombos:
return False
otherHazard = self.getOtherAllowedHazard(haz)
otherSelections = self._listBoxWidgets[otherHazard].curselection()
if item in otherSelections:
return True
return False
# Display the grid that allows the user to see the selected zones.
def displayZones(self):
weName = "BreakpointHazards"
now = int(self._gmtime().unixTime() / (3600 * 6)) * 3600 * 6
timeRange = TimeRange.TimeRange(AbsTime.AbsTime(now),
AbsTime.AbsTime(now + (3600 * 24)))
self.createGrid("Fcst", weName, "DISCRETE", (self._bpHazGrid, self._fullHazList), timeRange,
discreteKeys=self._fullHazList, discreteOverlap=1, discreteAuxDataLength=5,
defaultColorTable="SITE/NHA/ProposedBPHazs")
return
# Resets the zones display grid
def resetZoneDisplay(self):
hazIndex = self.getIndex("<None>", self._fullHazList)
self._bpHazGrid = self.newGrid(hazIndex, dtype=np.int8)
self.displayZones()
# Creates the zones display grid.
def createZoneDisplay(self):
if not self._displayZones:
return
weName = "BreakpointHazards"
hazKeys = ["<None>"] + self._hazList + ["TR.W^HU.A"]
self._bpHazGrid = self.empty(np.int8)
bpDict = self.makeBPDict() # haz : breakpointList
zoneDict = self.makeZoneDict(bpDict) # haz : zoneList
maskDict = {}
for hazKey in self._hazList:
if hazKey in zoneDict:
zoneList = zoneDict[hazKey]
mask = self._zoneMap.maskFromZoneList(zoneList)
maskDict[hazKey] = mask
else:
maskDict[hazKey] = self.empty(np.bool)
hazIndex = self.getIndex(hazKey, self._fullHazList)
self._bpHazGrid[maskDict[hazKey]] = hazIndex
# Check for and set the allowed combination
comboMask = maskDict["HU.A"] & maskDict["TR.W"]
if comboMask.any():
hazIndex = self.getIndex("TR.W^HU.A", hazKeys)
self._bpHazGrid[comboMask] = hazIndex
# Display the grid
self.displayZones()
return
# Updates the zone display with the latest selection.
def updateZoneDisplay(self, haz, item, addHazard):
if not self._displayZones:
return
listBox = self._listBoxWidgets[haz]
bpName = listBox.get(item)
zoneList = self.breakpointToZoneList(bpName)
mask = self._zoneMap.maskFromZoneList(zoneList)
# See if this was part of an allowed combination
comboSelected = self.hazardComboSelected(haz, item)
hazardsToAdd = []
if addHazard:
if comboSelected:
hazardsToAdd.append(self.getIndex("TR.W^HU.A", self._fullHazList))
else:
hazardsToAdd.append(self.getIndex(haz, self._fullHazList))
else: # removing the hazard
if comboSelected:
otherHazard = self.getOtherAllowedHazard(haz)
hazardsToAdd.append(self.getIndex(otherHazard, self._fullHazList))
else:
hazardsToAdd.append(self.getIndex("<None>", self._fullHazList))
for hazIndex in hazardsToAdd:
self._bpHazGrid[mask] = hazIndex
return
# Returns the advisory names (AT1, ...) based on teh current set of
# JSON files.
def getAdvisoryNames(self):
fileNames = self._getStormAdvisoryNames() # fetch the JSON fileNames
# Strip the .json
finalList = [fileName.replace(".json", "") for fileName in fileNames]
return finalList
# Returns all the storminfo objects in a dictionary.
def fetchStormInfo(self):
# Fetch all the storm info dictionaries
stormInfoDictList = self._WindWWUtils.getStormInfoDicts()
self._stormInfoDicts = {}
for stormInfo in stormInfoDictList:
# Add the Breakpoints key if we don't have it.
if "Breakpoints" not in stormInfo:
stormInfo["Breakpoints"] = {}
self._stormInfoDicts[stormInfo["pil"]] = stormInfo
# Create the GUI upon startup.
def setUpUI(self):
self._tkmaster = tk.Tk()
self._master = tk.Toplevel(self._tkmaster)
self._master.title("Select Breakpoints")
# Capture the "x" click to close the GUI
self._master.protocol('WM_DELETE_WINDOW', self.cancelCommand)
self._topFrame = tk.Frame(self._master)
self._topFrame.grid()
self._tkmaster.withdraw() # remove the master from the display
self._listBoxWidgets = {}
self._listboxFrames = {}
self._bpFrame = tk.Frame(self._topFrame)
self._bpFrame.grid(row=0, column=0)
for haz in self._hazList:
column = self._hazList.index(haz)
self._listboxFrames[haz] = tk.Frame(self._bpFrame)
self._listboxFrames[haz].grid(row=0, column=column)
self.makeListBox(self._listboxFrames[haz], self._linkedBPs, self._orphanedBPs, haz, tk.MULTIPLE)
self._hazardFrame = tk.Frame(self._topFrame)
self._hazardFrame.grid(row=1, column=0, columnspan=3, sticky=tk.W, padx=20, pady=10)
self._bpTextboxes = {}
self.makeHazardArea(self._hazardFrame)
# Make the Run, Run/Dismiss, Cancel buttons
self.makeBottomButtons(self._topFrame)
self.updateBreakpointDisplay()
self.createZoneDisplay()
return
# Main method that sets up the GUI and enters the event loop
def execute(self):
# set up some constants for this tool
self._activeFont = "Helvetica 12 bold"
self._disabledFont = "Helvetica 12 normal"
self._hazLabelFont = "Helvetica 10 bold"
self._bgColor = "#d9d9d9"
self._selectedColor = "red"
self._deselectedColor = "#d9d9d9"
self._hazList = ["HU.W", "HU.A", "TR.W", "TR.A"]
self._fullHazList = ["<None>", "HU.W", "HU.A", "TR.W", "TR.A", "TR.W^HU.A"]
self._allowedHazCombos = ["TR.W", "HU.A"]
self._listTypes = ["start", "end", "single"]
self._islandSeparator = "***** ISLANDS *****"
self._bpHazGrid = self.empty(np.bool)
self._pilList = self.getAdvisoryNames()
if len(self._pilList) == 0:
self.statusBarMsg("No Advisory files found. Please run StormInfo first.", "U")
return
self._pilList.sort()
self._boldColors = {
"HU.W" : "#FF0000", #(255, 0, 0), # red
"HU.A": "#FF0000", #(255, 125, 125), # light red
"TR.W" : "#8888FF", #(0, 0, 255), # blue
"TR.A" : "#8888FF", #(125, 125, 255), # light blue
"TR.W^HU.A" : "#FF00FF", #(255, 0, 255),
}
self._fadedColors = {
"HU.W" : "#FFDDDD", #(255, 0, 0), # light red
"HU.A": "#FFDDDD", #(255, 125, 125), # light red
"TR.W" : "#DDDDFF", #(0, 0, 255), # light blue
"TR.A" : "#DDDDFF", #(125, 125, 255), # light blue
"TR.W^HU.A" : "#FF00FF", #(255, 0, 255), # light purple
}
self._selectedPil = self._pilList[0]
self._displayZones = True
self._lastHazSelected = None
self._lastItemSelected = None
self._lastItemDeselected = None
self._listLabels = {
"HU.W" : "Hurricane Warning",
"HU.A" : "Hurricane Watch",
"TR.W" : "Tropical Storm Warning",
"TR.A" : "Tropical Storm Watch",
}
self._WindWWUtils = WindWWUtils.WindWWUtils(self._dbss)
self._hazSelections = {}
for haz in self._hazList:
self._hazSelections[haz] = ()
self._zoneMap = ZoneMap.ZoneMap(self._dbss)
# initialize the selected breakpoints
self._selectedBPs = {}
for haz in self._hazList:
self._selectedBPs[haz] = []
self.fetchStormInfo()
self._bpDict = WindWWUtils.bpDict
bpList = list(self._bpDict.keys())
self._linkedBPs, self._orphanedBPs = self.sortBPKeys(bpList)
for l in self._linkedBPs:
if len(l) <= 20:
self._orphanedBPs = l + self._orphanedBPs
self._linkedBPs = self._linkedBPs[0]
self._bpListDict = {}
for listType in self._listTypes:
if listType in ["start", "end"]:
self._bpListDict[listType] = self._linkedBPs
elif listType in ["single"]:
self._bpListDict[listType] = self._orphanedBPs
self.setUpUI()
tk.mainloop()
return