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

975 lines
39 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.
#
# Aviation_Populate
#
# Author: lefebvre
#
# Aviation_Populate - Version 20181114
#
# Last Modified: 14 November 2018
#
# ----------------------------------------------------------------------------
# The MenuItems list defines the GFE menu item(s) under which the
# Procedure is to appear.
# Possible items are: Populate, Edit, Consistency, Verify, Hazards
MenuItems = ["Populate"]
VariableList = []
import time
import SmartScript
from math import *
import numpy as np
import tkinter as tk
import AbsTime, TimeRange
import Aviation_EDASConfig as PopulateConfig
class Procedure (SmartScript.SmartScript):
def __init__(self, dbss):
SmartScript.SmartScript.__init__(self, dbss)
self._dbss = dbss
# Called when the cancel button is clicked
def cancelCommand(self):
self._tkmaster.destroy()
return
def runCommand(self):
self.populateAviationGrids()
return
def runDismissCommand(self):
self.populateAviationGrids()
self.cancelCommand()
return
# Copied from the original CR version of Aviation_Populate.
# We should replace this with a version that's vectorized for better performance.
def calcCloudBaseRH(self, modelName, version, topo, timeRange):
self.levels = ["MB1000","MB975", "MB950", "MB925", "MB900",
"MB875", "MB850", "MB825", "MB800", "MB775",
"MB750", "MB725", "MB700", "MB675", "MB650",
"MB625", "MB600", "MB575", "MB550", "MB525",
"MB500", "MB450", "MB400", "MB350", "MB300"
]
self.cloudRH = [98.0, 96.0, 94.0, 92.0, 90.0,
88.0, 85.0, 83.0, 80.0, 78.0,
75.0, 73.0, 70.0, 68.0, 65.0,
63.0, 60.0, 58.0, 55.0, 53.0,
50.0, 45.0, 40.0, 35.0, 30.0,
]
# Fetch the proper D2D databaseID
if version == "Previous":
versionNum = -1
else:
versionNum = 0
databaseID = self.findDatabase("D2D_" + modelName, versionNum)
trList = self.getWEInventory(databaseID, "rh", "MB500", timeRange)
gridTRList = []
MaxCloudBase=250
for tr in trList:
CloudBaseRH=self.newGrid(MaxCloudBase)
sounding = self.makeNumericSounding(databaseID, "rh", self.levels, tr, noDataError=0)
print("Making Sounding at ", tr)
if sounding is not None:
gh_c, rh_c = sounding
for i in range(rh_c.shape[0]):
rh = rh_c[i]
height = gh_c[i]/0.3048
height = height - topo
height = np.where(np.less(height, 1.0), 0.0, height)
rh = np.where(np.less(height, 1.0), 0.0, rh)
CloudBaseRH = np.where(np.logical_and(np.equal(CloudBaseRH, MaxCloudBase),
np.greater_equal(rh, self.cloudRH[i])), height/100.0, CloudBaseRH)
else:
print("Got None sounding back at:", tr)
continue
grid = np.clip(CloudBaseRH, 1, 250)
gridTRList.append((grid, tr))
return gridTRList
# This method will fetch the fromElement and copy it to the toElement
# for the specified database.
def copyGridsFromOtherElement(self, dbid, fromElement, toElement, timeRange):
fromInv = self.getWEInventory(dbid, fromElement, "SFC", timeRange)
for tr in fromInv:
grid = self.getGrids(dbid, fromElement, "SFC", tr)
self.createGrid("Fcst", toElement, "SCALAR", grid, tr)
return len(fromInv)
# The main mthod that does the grid copying and calculations
def populateAviationGrids(self):
# Calculate the timeRange for the generic elements
start = self._baseTime + (self._genStartTime * 3600)
end = self._baseTime + (self._genEndTime * 3600)
# Make the timeRanges for the generic and convective populate steps
if self._timeButtonSelected.get() == "Choose -->":
genTimeRange = TimeRange.TimeRange(AbsTime.AbsTime(start),
AbsTime.AbsTime(end))
elif self._timeButtonSelected.get() == "GFE Selected\nTime":
genTimeRange = self._timeRange
if self._convState == "Off":
# Populate the generic elements first
if self._selectedPrevCur.get() == "Previous":
selectedDBID = self._dbIDDict[self._selectedGenSource][1]
elif self._selectedPrevCur.get() == "Current":
selectedDBID = self._dbIDDict[self._selectedGenSource][0]
else:
self.statusBarMsg("Unknown model version defined. Aborting", "S")
return
# Copy and interpolate the generic elements over the selected timeRange
supportedWEs = []
for we in self._genElementsSelected:
if we in self._modelWEDict[selectedDBID.modelName()]:
supportedWEs.append(we)
self.copyCmd(supportedWEs, selectedDBID, genTimeRange)
self.interpolateCmd(self._genElementsSelected, genTimeRange, "GAPS", "SYNC", interval=1, duration=1)
else: # convective state on
# Populate the convective elements if needed.
selectedDBID = self._dbIDDict[self._selectedConSource][0] # Fetch the dbid from the dict
if (self._selectedConSource == "CONSShort") & (self._selectedConAlgorithm == "CloudBaseRH"):
self.statusBarMsg("Error!!!! CONNShort does not currently support the CloudBaseRH algorithm.", "S")
return
if self._selectedConAlgorithm == "CloudBaseRH":
self._activeLabel.config(text="Calculating", fg="red")
self._master.update()
cloudBaseGrids = self.calcCloudBaseRH(self._selectedConSource, self._selectedPrevCur.get(), self.getTopo(), genTimeRange)
self.deleteCmd([self._conElementSelected], genTimeRange) # remove the grids first
for grid, tr in cloudBaseGrids:
self.createGrid("Fcst", self._conElementSelected, "SCALAR", grid, tr)
self._activeLabel.config(text=" ")
else:
self.deleteCmd([self._conElementSelected], genTimeRange) # remove the grids first
copyFromWE = self._selectedConAlgorithm
copyCount = self.copyGridsFromOtherElement(selectedDBID, copyFromWE, self._conElementSelected, genTimeRange)
if copyCount == 0:
self.statusBarMsg("No grids found for " + selectedDBID.modelName() + " " + copyFromWE, "S")
self.interpolateCmd([self._conElementSelected], genTimeRange, "GAPS", "SYNC", interval=1, duration=1)
# Finally Populate with Obs grids to back fill and make a gap for interpolation
obsDB = self.findDatabase(self._obsSource)
obsTR = TimeRange.TimeRange(AbsTime.AbsTime(self._baseTime - (self._backfillHours * 3600)),
AbsTime.AbsTime(self._baseTime))
# Copy the Obs into the Fcst
if self._convState == "Off":
elementsSelected = self._genElementsSelected
else:
elementsSelected = [self._conElementSelected]
# Back fill grids from the Obs database
for weName in elementsSelected:
# simple case just copy the grids
if weName in self._modelWEDict[self._obsSource]:
self.copyCmd([weName], obsDB, obsTR)
return
# 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=4, column = 0, columnspan=6, pady=20)
# Run button
self._runButton = tk.Button(self._bottomButtonFrame, text="Run",
command=self.runCommand)
self._runButton.grid(row=0, column=0, padx=20)
# Run/Dismiss buttom
self._runDismissButton = tk.Button(self._bottomButtonFrame, text="Run/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
# Calls a procedure to fetch the currently selected timeRange. This allows the tool
# to be run multiple times without closing.
def getSelectedTimeRange(self):
varDict = {}
self.callProcedure("ReturnGFETimeRange", varDict=varDict)
if "SelectedTimeRange" in varDict:
start, end = varDict["SelectedTimeRange"]
timeRange = TimeRange.TimeRange(AbsTime.AbsTime(start), AbsTime.AbsTime(end))
return timeRange
else:
self.statusBarMsg("Error retrieving Selected TimeRange. Make sure ReturnGFETimeRange tool is installed.", "S")
return None
# Called when the cursor enter the GUI. Just fetches the current timeRange
def enterEvent(self, event=None):
# Reset the baseTime so the GUI stays current
self.calcBaseTime()
timeRange = self.getSelectedTimeRange()
if timeRange is None:
return
if self._timeRange != timeRange:
self._timeRange = timeRange
# If we're using the GFE time, figure out the start and end time
# in hours and update the sliders.
if self._timeButtonSelected.get() == "GFE Selected\nTime":
self.toggleTimeScaleState(True)
now = int(time.time() / 3600) * 3600
gfeStart = self._timeRange.startTime().unixTime()
gfeEnd = self._timeRange.endTime().unixTime()
self._genStartTime = int((gfeStart - now) / 3600)
self._genEndTime = int((gfeEnd - now) / 3600)
self._genSTScale.set(self._genStartTime)
self._genETScale.set(self._genEndTime)
self.updateStartEndTimeLabels()
self.toggleTimeScaleState(False)
return
# Called when a element button is selected.
def genElementSelected(self, event, elementName):
# Make a list of the elements that are supported
supportedWEs = self._modelWEDict[self._selectedGenSource]
if elementName not in supportedWEs:
return
# toggle on
if elementName not in self._genElementsSelected and \
self._genElementDict[elementName].cget("state") == tk.ACTIVE:
self._genElementsSelected.append(elementName)
self._genElementDict[elementName].select()
elif elementName in self._genElementsSelected and \
self._genElementDict[elementName].cget("state") == tk.ACTIVE:
if elementName in self._genElementsSelected:
self._genElementsSelected.remove(elementName)
self._genElementDict[elementName].deselect()
return
# Make the element buttons. The frame parameter is the topLevel frame.
def makeGenElementButtons(self, frame):
# Make the element frame for selecting the source
elementFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3, bg=self._bgColor)
elementFrame.grid(row=0, column=2, sticky=tk.N+tk.E+tk.S+tk.W)
# make a label
label = tk.Label(elementFrame, text="Select Element")
label.grid(row=0, column=0)
# Make each source button
row = 1
for i in range(len(self._config["genElementList"])):
weName = self._config["genElementList"][i]
def cbHandler(event, self=self, buttonName=weName):
return self.genElementSelected(event, buttonName)
self._genElementDict[weName] = tk.Checkbutton(elementFrame,
text=weName)
self._genElementDict[weName].grid(row=row, sticky=tk.W)
self._genElementDict[weName].bind("<ButtonRelease-1>", cbHandler)
self._genElementDict[weName].bind("<ButtonRelease-2>", cbHandler)
if weName in self._config["genElementDefaultList"]:
self._genElementDict[weName].select()
self._genWidgetList.append((self._genElementDict[weName], tk.NORMAL))
row = row + 1
return
# Called when the Previous or Current version buttons are selected.
def prevCurSelected(self):
# Tk variables are used to track the state of this button. Nothing to do here.
return
# Make the Current/Previous version buttons. The frame parameter is the topLevel frame.
def makePrevCurrentButtons(self, frame):
# Make the Current/Previous frame for selecting the source
prevCurFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3, bg=self._bgColor)
prevCurFrame.grid(row=0, column=1, sticky=tk.N+tk.E+tk.S+tk.W)
# Make each source button
label = tk.Label(prevCurFrame, text="Model Run")
label.grid(row=0, column=0)
buttonList = ["Previous", "Current"]
self._prevCurButtonDict = {}
self._selectedPrevCur = tk.StringVar()
self._selectedPrevCur.set(self._config["prevCurDefault"])
row = 1
for buttonName in buttonList:
self._prevCurButtonDict[buttonName] = tk.Radiobutton(prevCurFrame, text=buttonName,
padx=10, pady=2, command=self.prevCurSelected,
variable=self._selectedPrevCur, value = buttonName)
self._prevCurButtonDict[buttonName].grid(row=row, sticky=tk.W)
if buttonName == self._config["prevCurDefault"]:
self._prevCurButtonDict[buttonName].select()
self._selectedPrevCur.set(buttonName)
tkState = tk.NORMAL
self._prevCurButtonDict[buttonName].config(state=tkState)
self._genWidgetList.append((self._prevCurButtonDict[buttonName], tkState))
row = row + 1
return
def genSourceButtonClicked(self):
self._selectedGenSource = self._genSourceButtonSelected.get()
# Enable or disable the element buttons based on the elements the selected model supports
for weName, widget in self._genElementDict.items():
if weName in self._modelWEDict[self._selectedGenSource]:
widget.config(state=tk.NORMAL)
else: # not available for this model so deselect and remove from selected list
widget.deselect()
widget.config(state=tk.DISABLED)
if weName in self._genElementsSelected:
self._genElementsSelected.remove(weName)
return
# Creates the button for the generic sources. It will disable buttons when that
# source is not found.
def makeGenSourceRadioButtons(self, frame):
# Make the source frame for selecting the source
genFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3, bg = self._bgColor)
genFrame.grid(row=0, column=0, sticky=tk.N+tk.E+tk.S+tk.W)
# Add a label at the top
label = tk.Label(genFrame, text=" Select Model")
label.grid(row=0, column=0)
self._genSourceButtonSelected = tk.StringVar()
self._genSourceButtonSelected.set(self._config["genSources"][0])
self._genSourceButtonDict = {}
row = 1
for buttonName in self._config["genSources"]:
self._genSourceButtonDict[buttonName] = tk.Radiobutton(genFrame, text=buttonName,
padx=10, pady=2, command=self.genSourceButtonClicked,
variable=self._genSourceButtonSelected,
value = buttonName)
self._genSourceButtonDict[buttonName].grid(row=row, sticky=tk.W)
if buttonName == self._config["defaultGenSource"]:
self._genSourceButtonDict[buttonName].invoke()
if buttonName in self._genModelsMissing:
tkState = tk.DISABLED
else:
tkState = tk.NORMAL
self._genSourceButtonDict[buttonName].config(state=tkState)
self._genWidgetList.append((self._genSourceButtonDict[buttonName], tkState))
row = row + 1
return
# Formats the time string for display.
def makeTimeStr(self, intTime):
dayStr = str(time.gmtime(intTime).tm_mday).zfill(2)
hourStr = str(time.gmtime(intTime).tm_hour).zfill(2)
return "\n " + dayStr + "." + hourStr + "Z "
# Updates the start end tne time labels in the GUI
def updateStartEndTimeLabels(self):
# self._baseTime is truncated to 00Z
start = self._baseTime + self._genStartTime * 3600
end = self._baseTime + self._genEndTime * 3600
startStr = self.makeTimeStr(start)
endStr = self.makeTimeStr(end)
self._startTimeLabel.config(text=startStr)
self._endTimeLabel.config(text=endStr)
return
# Called when the generic start time is moved.
def genStartChanged(self, event):
self._genStartTime = self._genSTScale.get()
if self._genStartTime > self._startHours:
self._genStartTime = self._startHours
self._genSTScale.set(self._genStartTime)
# reset the endTime if needed
if self._genStartTime >= self._genEndTime:
self._genEndTime = self._genStartTime + 1
if self._genEndTime > self._endHours:
self._genEndTime = self._endHours
self._genETScale.set(self._genEndTime)
self.updateStartEndTimeLabels()
return
# Called when the generic end time is moved.
def genEndChanged(self, event):
self._genEndTime = self._genETScale.get()
if self._genEndTime > self._endHours:
self._genEndTime = self._endHours
if self._genEndTime <= 0:
self._genEndTime = 1
self._genETScale.set(self._genEndTime)
#reset startTime if needed
if self._genEndTime <= self._genStartTime:
self._genStartTime = self._genEndTime - 1
if self._genStartTime <= 0:
self._genStartTime = 0
self._genSTScale.set(self._genStartTime)
self.updateStartEndTimeLabels()
return
# Called when either time selector is selected.
def timeButtonSelected(self):
if self._timeButtonSelected.get() == "Choose -->":
self.toggleTimeScaleState(True)
else:
self.enterEvent()
self.toggleTimeScaleState(False)
return
# Toggles the time scales to active (True) or disabled (False)
def toggleTimeScaleState(self, activate):
if activate:
tkState = tk.NORMAL
else:
tkState = tk.DISABLED
for w in self._timeWidgetList:
w.config(state=tkState)
return
# Creates the generic time scales on the GUI
def makeGenTimeScales(self, frame):
self._genTimeFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3, bg=self._bgColor)
self._genTimeFrame.grid(row=1, column=0, columnspan=3, sticky=tk.E+tk.W)
# Selector frame
selFrame = tk.Frame(self._genTimeFrame, relief=tk.GROOVE, bd=3, bg=self._bgColor)
selFrame.grid(row=0, column=0, sticky=tk.N+tk.S, rowspan=2)
# Create Selector buttons
buttonList = ["GFE Selected\nTime", "Choose -->"]
self._timeButtonDict = {}
self._timeButtonSelected = tk.StringVar()
self._timeButtonSelected.set(self._timeButtonDefault)
row = 0
for buttonName in buttonList:
self._timeButtonDict[buttonName] = tk.Radiobutton(selFrame,
text=buttonName, padx=10, pady=2, command=self.timeButtonSelected,
variable=self._timeButtonSelected, value=buttonName)
self._timeButtonDict[buttonName].grid(row=row)
row = row + 1
# Make the time frame, time scales and time labels
timeScaleFrame = tk.Frame(self._genTimeFrame, relief=tk.GROOVE, bd=3, bg=self._bgColor)
timeScaleFrame.grid(row=1, column=1, sticky=tk.E+tk.W, columnspan=2)
# Create start and end time scales with labels
self._startLabel = tk.Label(timeScaleFrame, text="\nStart")
self._startLabel.grid(row=1, column=0)
self._endLabel = tk.Label(timeScaleFrame, text="\nEnd")
self._endLabel.grid(row=2, column=0)
self._timeWidgetList.append(self._startLabel)
self._timeWidgetList.append(self._endLabel)
self._genSTScale = tk.Scale(timeScaleFrame, from_= 0,
to=self._startHours, orient=tk.HORIZONTAL,
command=self.genStartChanged,
resolution=1, length=190, sliderlength=20)
self._genSTScale.grid(row=1, column=1, sticky=tk.E+tk.W, columnspan=2)
self._genSTScale.set(self._defaultStartHour)
self._genStartTime = self._defaultStartHour
self._timeWidgetList.append(self._genSTScale)
self._genETScale = tk.Scale(timeScaleFrame, from_= 0,
to=self._endHours, orient=tk.HORIZONTAL,
command=self.genEndChanged,
resolution=1, length=190, sliderlength=20)
self._genETScale.grid(row=2, column=1, sticky=tk.E+tk.W, columnspan=2)
self._genEndTime = self._genETScale.set(self._defaultEndHour)
self._genEndTime = self._defaultEndHour
self._timeWidgetList.append(self._genETScale)
# And finally the start and end time labels
self._startTimeLabel = tk.Label(timeScaleFrame, text=" DD.HH Z", font=self._activeFont)
self._startTimeLabel.grid(row=1, column=3, sticky=tk.E)
self._endTimeLabel = tk.Label(timeScaleFrame, text=" DD.HH Z", font=self._activeFont)
self._endTimeLabel.grid(row=2, column=3, sticky=tk.E)
self.updateStartEndTimeLabels()
return
# Toggles the widgets in the generic frame
def toggleGenFrame(self, active):
if active:
for w, tkState in self._genWidgetList:
w.config(state=tkState, bg=self._bgColor)
else:
for w, tkState in self._genWidgetList:
w.config(state=tk.DISABLED)
return
# Toggles on/off all the convective widgets based on the value of active
def toggleConvectiveFrame(self, active):
if active:
self.toggleGenFrame(False)
for w, tkState in self._conWidgetList:
w.config(state=tkState, bg=self._bgColor)
else:
self.toggleGenFrame(True)
for w, tkState in self._conWidgetList:
w.config(state=tk.DISABLED)
return
# Called when the convective button is selected
def convButtonSelected(self, event, buttonName):
# If it's on, turn it off
if self._convState == "On":
self._convState = "Off"
self._convButton.config(fg='gray20')
self._convButton.config(activeforeground='gray20')
self._convButton.config(font=self._disabledFont)
self.toggleConvectiveFrame(False)
# If it's off, turn it on
elif self._convState == "Off":
self._convState = "On"
self._convButton.config(fg='black')
self._convButton.config(activeforeground='black')
self._convButton.config(font=self._activeFont)
self.toggleConvectiveFrame(True)
# Reset the algorithm button state
self.setAlgorithmButtonState()
return
# Creates the convective button in the GUI
def makeConvectiveButton(self, frame):
buttonName = "Alternative Cloud Base"
def cbHandler(event, self=self, buttonName=buttonName):
return self.convButtonSelected(event, buttonName)
if self._convState == "On":
font = self._activeFont
else:
font = self._disabledFont
self._convButton = tk.Button(frame, text=buttonName, font=font,
height=1, width=len(buttonName), relief=tk.RAISED, bd=3,
justify=tk.CENTER)
self._convButton.grid(row=0, column=0, columnspan=4)
self._convButton.bind("<ButtonRelease-1>", cbHandler)
self._convButton.bind("<ButtonRelease-2>", cbHandler)
return
# Resets the state of the algorithm buttons based on the selected convective source
def setAlgorithmButtonState(self):
if self._algButtonDict:
if self._selectedConSource == "CONSShort": # disable the CloudBaseRH button
if self._selectedConAlgorithm == "CloudBaseRH":
self._algButtonDict["CloudBaseCCL"].invoke()
self._algButtonDict["CloudBaseRH"].config(state=tk.DISABLED)
else:
for alg in self._config["algChoices"]:
self._algButtonDict[alg].config(state=tk.NORMAL)
return
def conSourceButtonClicked(self):
self._selectedConSource = self._conSourceButtonSelected.get()
# Reset the algorithm state
self.setAlgorithmButtonState()
return
# Creates the button for the convective sources. It will disable buttons when that
# source is not found.
def makeConSourceRadioButtons(self, frame):
# Make the source frame for selecting the source
conFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3, bg = self._bgColor)
conFrame.grid(row=3, column=0, sticky=tk.N+tk.E+tk.S+tk.W)
# Add a label at the top
label = tk.Label(conFrame, text=" Select One Model")
label.grid(row=0, column=0)
self._conSourceButtonSelected = tk.StringVar()
self._conSourceButtonSelected.set(self._config["conSources"][0])
row = 1
for buttonName in self._config["conSources"]:
self._conSourceButtonDict[buttonName] = tk.Radiobutton(conFrame, text=buttonName,
padx=10, pady=2, command=self.conSourceButtonClicked,
variable=self._conSourceButtonSelected,
value = buttonName)
self._conSourceButtonDict[buttonName].grid(row=row, sticky=tk.W)
if buttonName == self._config["defaultConSource"]:
self._conSourceButtonDict[buttonName].invoke()
self._selectedConSource = self._config["defaultConSource"]
if buttonName in self._conModelsMissing:
tkState = tk.DISABLED
else:
tkState = tk.NORMAL
self._conSourceButtonDict[buttonName].config(state=tkState)
self._conWidgetList.append((self._conSourceButtonDict[buttonName], tkState))
row = row + 1
return
# Called when a convective element button is selected.
def conElementSelected(self):
self._conElementSelected = self._conElementStringVar.get()
return
# Make the element buttons. The frame parameter is the topLevel frame.
def makeConElementButtons(self, frame):
# Make the element frame for selecting the source
conElementFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3, bg=self._bgColor)
conElementFrame.grid(row=3, column=1, sticky=tk.E+tk.W+tk.N)
# make a label
label = tk.Label(conElementFrame, text="Select Element")
label.grid(row=0, column=0)
# set up defaults for this menu
self._conElementStringVar = tk.StringVar()
self._conElementStringVar.set(self._config["conElementDefault"])
self._conElementSelected = self._config["conElementDefault"]
self._conElementDict = {}
row = 1
# Make each element button
for buttonName in self._config["conElementList"]:
self._conElementDict[buttonName] = tk.Radiobutton(conElementFrame, text=buttonName,
padx=10, pady=2, command=self.conElementSelected,
variable=self._conElementStringVar,
value = buttonName)
self._conElementDict[buttonName].grid(row=row, sticky=tk.W)
# Select the default value
if buttonName == self._config["conElementDefault"]:
self._conElementDict[buttonName].invoke()
tkState = tk.NORMAL
self._conElementDict[buttonName].config(state=tkState)
self._conWidgetList.append((self._conElementDict[buttonName], tkState))
row = row + 1
# make a label that indicates the tool is calculating
labelFrame = tk.Frame(conElementFrame, relief=tk.GROOVE, bd=3, bg=self._bgColor)
labelFrame.grid(row=row+1, sticky=tk.N+tk.S+tk.E+tk.W)
self._activeLabel = tk.Label(labelFrame, text=" ", padx=50)
self._activeLabel.grid(row=1, column=0)
return
def algButtonClicked(self):
self._selectedConAlgorithm = self._algButtonSelected.get()
return
# Creates the button for the algorithm choices.
def makeAlgRadioButtons(self, frame):
# Make the alg frame for selecting the algorithm
algFrame = tk.Frame(frame, relief=tk.GROOVE, bd=3, bg = self._bgColor)
algFrame.grid(row=3, column=2, sticky=tk.N+tk.E+tk.S+tk.W)
# Add a label at the top
label = tk.Label(algFrame, text="Select One Algorithm")
label.grid(row=0, column=0)
self._algButtonSelected = tk.StringVar()
self._algButtonSelected.set(self._config["algChoices"][0])
self._algButtonDict = {}
row = 1
for buttonName in self._config["algChoices"]:
self._algButtonDict[buttonName] = tk.Radiobutton(algFrame, text=buttonName,
padx=10, pady=2, command=self.algButtonClicked,
variable=self._algButtonSelected,
value = buttonName)
self._algButtonDict[buttonName].grid(row=row, sticky=tk.W)
if buttonName == self._config["defaultAlg"]:
self._algButtonDict[buttonName].invoke()
self._selectedConAlgorithm = self._config["defaultAlg"]
if buttonName == "CloudBaseRH" and self._selectedConSource == "CONSShort":
tkState = tk.DISABLED
else:
tkState = tk.NORMAL
self._conWidgetList.append((self._algButtonDict[buttonName], tkState))
row = row + 1
return
# High-level method that calls other methods to build the GUI a widget at a time
def setUpUI(self):
self._tkmaster = tk.Tk()
self._master = tk.Toplevel(self._tkmaster)
self._master.title("Aviation_Populate")
# Capture the "x" click to close the GUI
self._master.protocol('WM_DELETE_WINDOW', self.cancelCommand)
# self._topFrame = tk.Frame(self._master, bg=self._bgColor)
self._topFrame = tk.Frame(self._master)
self._topFrame.grid()
self._tkmaster.withdraw() # remove the master from the display
self._topFrame.bind("<Enter>", self.enterEvent)
# Make the widgets for the generic sources
self._genWidgetList = []
self._timeWidgetList = []
self._genElementDict = {}
self.makeGenSourceRadioButtons(self._topFrame)
self.makePrevCurrentButtons(self._topFrame)
self.makeGenElementButtons(self._topFrame)
self.makeGenTimeScales(self._topFrame)
# Put the next set of widgets in a frame
self._conFrame = tk.Frame(self._topFrame, relief=tk.GROOVE, bd=3, bg=self._bgColor)
self._conFrame.grid(row=2, column=0, columnspan=3)
# Initialize Alternative CloudBase state data
self._conWidgetList = []
self._conSourceButtonDict = {}
self._conElementDict = {}
self._algButtonDict = {}
# Make the Alternative CloudBase widgets
self.makeConvectiveButton(self._conFrame)
self.makeConSourceRadioButtons(self._conFrame)
self.makeConElementButtons(self._conFrame)
self.makeAlgRadioButtons(self._conFrame)
#self.makeConTimeScales(self._conFrame)
if self._convState == "Off":
self.toggleConvectiveFrame(False)
self.makeBottomButtons(self._topFrame)
return
#####################################################################################
# Returns a dictionary modelName : list of databaseIDs for the specified modelList.
def makeDatabaseIDDict(self, modelList):
dbIDDict = {}
allDBs = self.availableDatabases()
for dbid in allDBs:
# Skip all D2D databases
if "_D2D_" in dbid.modelIdentifier():
continue
modelName = dbid.modelName()
if modelName in modelList:
if modelName in dbIDDict:
dbIDDict[modelName].append(dbid)
else:
dbIDDict[modelName] = [dbid]
# Sort the dbids in place newest to oldest.
for dbids in dbIDDict.values():
dbids.sort(key=lambda x: x.modelTime(), reverse=True)
return dbIDDict
# Fetches the inventory in the form of a timeRange list
def getWEInventory(self, modelName, weName, level, timeRange=None):
if timeRange is None:
timeRange = TimeRange.allTimes()
trList = []
# getGridInfo will just die if the modelName or weName is not valid
# so wrap it in a try block and return [] if it fails
try:
gridInfo = self.getGridInfo(modelName, weName, level, timeRange)
except:
return trList
for g in gridInfo:
if timeRange.overlaps(g.gridTime()):
trList.append(g.gridTime())
return trList
def makeModelWEDict(self, modelList, weList):
modelWEDict = {}
allDBs = self.availableParms()
for weName, level, dbid in allDBs:
modelName = dbid.modelName()
if modelName not in modelList or "_D2D_" in dbid.modelIdentifier():
continue
if weName not in weList:
continue
if modelName in modelWEDict and weName not in modelWEDict[modelName]:
modelWEDict[modelName].append(weName)
else:
modelWEDict[modelName] = [weName]
return modelWEDict
def calcBaseTime(self):
self._baseTime = int(time.time() / 3600) * 3600
return
# Make a temporary dialog to see if the user wants to continue.
def dialogPrompt(self):
root = tk.Tk()
# root.withdraw() # This removes the toot window, but causes the dialog
# to appear on other screen. Both windows are removed by destroy
import tkinter.messagebox as msg
result = msg.askyesno("Aviation_Populate","Aviation_Populate may be already running. Continue?")
root.destroy()
return result
# Called when the tool first starts, helps ensure the tool can't be started again.
# Starting twice causes CAVE to hang
def lockProcedure(self):
# Since CAVE hangs when procedures like these are started twice.
# The presence of this object indicates the tool is already running.
try:
# getObject throws an exception if the object is not found
self.getObject("Aviation_Populate", "Aviation_Populate")
keepGoing = self.dialogPrompt()
return keepGoing
except: # not found
# Didn't find the object so save it now to prevent another execution of this procedure
self.saveObject("Aviation_Populate", "StringObject", "Aviation_Populate")
return True
def unlockProcedure(self):
# Remove the object now that we're done.
self.deleteObject("Aviation_Populate", "Aviation_Populate")
return
# Main method that sets up the GUI and enters the event loop
def execute(self, timeRange):
self._config = PopulateConfig.config["Populate"]
# Lock so procedure can't be run again without closing first.
if not self.lockProcedure():
return
# Save the selected timeRange
self._timeRange = timeRange
# set up some constants for this tool
self._activeFont = "Helvetica 12 bold"
self._disabledFont = "Helvetica 12 normal"
self._bgColor = "#d9d9d9"
self._genElementsSelected = self._config["genElementDefaultList"]
self._obsSource = "Obs"
self._backfillHours = 6
self._timeButtonDefault = "Choose -->" # "GFE Selected"
self._startHours = 35
self._endHours = 36
self._defaultStartHour = 0
self._defaultEndHour = 36
self._interpGap = 1
self.calcBaseTime()
# Convective source, element and algorithm configuration
self._convState = "Off"
self._selectedConvState = self._convState
self._conElementDefault = self._config["conElementDefault"]
self._conElementSelected = self._conElementDefault
# Make a dictionary of dbids
self._dbIDDict = self.makeDatabaseIDDict(self._config["genSources"] + self._config["conSources"])
# Identify any model we didn't find.
self._genModelsMissing = []
for modelName in self._config["genSources"]:
if modelName not in self._dbIDDict:
self._genModelsMissing.append(modelName)
self._conModelsMissing = []
for modelName in self._config["conSources"]:
if modelName not in self._dbIDDict:
self._conModelsMissing.append(modelName)
weList = self._config["genElementList"] + self._config["conElementList"]
self._modelWEDict = self.makeModelWEDict(self._config["genSources"] + self._config["conSources"] + [self._obsSource], weList)
# Call the main method to set up all the GUI widgets
self.setUpUI()
tk.mainloop()
# Clean up the lock
self.unlockProcedure()
return