import numpy as np
from qtpy import QtGui, QtCore, QtWidgets
import sharppy.sharptab as tab
from sharppy.sharptab.constants import *
import platform
__all__ = ['backgroundSlinky', 'plotSlinky']
## Written by Greg Blumberg - CIMMS
## wblumberg@ou.edu
[docs]class backgroundSlinky(QtWidgets.QFrame):
'''
Draw the background frame and lines for the Storm Slinky.
Draws onto a QPixmap.
'''
def __init__(self):
super(backgroundSlinky, self).__init__()
self.initUI()
[docs] def initUI(self):
## window configuration settings,
## sich as padding, width, height, and
## min/max plot axes
self.lpad = 5; self.rpad = 0
self.tpad = 0; self.bpad = 20
self.fpad = 5
self.wid = self.size().width() - self.rpad
self.hgt = self.size().height() - self.bpad
self.tlx = self.rpad; self.tly = self.tpad
self.brx = self.wid; self.bry = self.hgt
## center the frame on the slinky
self.center_frame()
if self.physicalDpiX() > 75:
fsize = 7
else:
fsize = 9
self.font_ratio = 0.0512
self.title_font = QtGui.QFont('Helvetica', round(self.size().height() * self.font_ratio)+2)
self.plot_font = QtGui.QFont('Helvetica', round(self.size().height() * self.font_ratio))
self.title_metrics = QtGui.QFontMetrics( self.title_font )
self.plot_metrics = QtGui.QFontMetrics( self.plot_font )
self.os_mod = 0
if platform.system() == "Windows":
self.os_mod = self.plot_metrics.descent()
## get the pixel height of the font
self.title_height = self.title_metrics.xHeight() + self.fpad
self.plot_height = self.plot_metrics.xHeight() + self.fpad
## initialize the QPixmap that the frame and data will be drawn on
self.plotBitMap = QtGui.QPixmap(self.width(), self.height())
self.plotBitMap.fill(self.bg_color)
## plot the background
self.plotBackground()
[docs] def center_frame(self):
'''
Center the slinky in the window.
'''
self.centerx = self.wid / 2; self.centery = self.hgt / 2
self.mag = 7000.*1.7
self.scale = (self.brx - self.tlx) / self.mag
[docs] def resizeEvent(self, e):
'''
Handles the event the window is resized.
'''
self.initUI()
[docs] def draw_frame(self, qp):
'''
Draw the background frame.
Parameters
----------
qp: QtGui.QPainter object
'''
## set a new pen to draw with
pen = QtGui.QPen(self.fg_color, 2, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.setFont(self.title_font)
## draw the borders in white
qp.drawLine(self.tlx, self.tly, self.brx, self.tly)
qp.drawLine(self.brx, self.tly, self.brx, self.bry)
qp.drawLine(self.brx, self.bry, self.tlx, self.bry)
qp.drawLine(self.tlx, self.bry, self.tlx, self.tly)
yval = self.bry - self.title_height - self.os_mod - 2
rect0 = QtCore.QRect(self.lpad, yval, 20, self.title_height)
qp.drawText(rect0, QtCore.Qt.TextDontClip | QtCore.Qt.AlignLeft, 'Storm Slinky')
[docs] def draw_axes(self, qp):
'''
Draw the X, Y Axes.
Parameters
----------
qp: QtGui.QPainter object
'''
## initialize a white pen to draw the frame axes
pen = QtGui.QPen(QtGui.QColor('#003366'), 2, QtCore.Qt.SolidLine)
qp.setPen(pen)
## draw the frame axes
qp.drawLine(self.centerx, self.tly, self.centerx, self.bry)
qp.drawLine(self.tlx, self.centery, self.brx, self.centery)
[docs] def plotBackground(self):
'''
Draws the background frame onto the QPixmap.
'''
qp = QtGui.QPainter()
qp.begin(self.plotBitMap)
qp.setRenderHint(qp.Antialiasing)
qp.setRenderHint(qp.TextAntialiasing)
## draw the frame
self.draw_axes(qp)
self.draw_frame(qp)
qp.end()
[docs] def xy_to_pix(self, x, y):
'''
Function to convert (x, y) to pixel (xx, yy) coordinates.
--------
x: the x distance component
y: the y distance component
'''
xx = self.centerx + (x * self.scale)
yy = self.centery - (y * self.scale)
return xx, yy
[docs]class plotSlinky(backgroundSlinky):
'''
Plots the data on the frame. Inherits from the
backgroundSlinky class and draws on the QPixmap
that is initialized there.
'''
def __init__(self, **kwargs):
'''
Initializes data variables needed to draw
the slinky by taking in a Profile object.
Parameters
----------
prof: a Profile Object
'''
self.bg_color = QtGui.QColor('#000000')
self.fg_color = QtGui.QColor('#ffffff')
self.use_left = False
super(plotSlinky, self).__init__()
self.prof = None
self.pcl = None
self.low_level_color = QtGui.QColor(RED)
self.mid_level_color = QtGui.QColor("#00FF00")
self.upper_level_color = QtGui.QColor(YELLOW)
self.trop_level_color = QtGui.QColor("#00FFFF")
[docs] def setProf(self, prof):
self.prof = prof
if self.use_left:
self.smu = self.prof.srwind[2]
self.smv = self.prof.srwind[3]
else:
self.smu = self.prof.srwind[0]
self.smv = self.prof.srwind[1]
self.pcl = None
self.clearData()
self.plotBackground()
self.plotData()
self.update()
[docs] def setParcel(self, pcl):
self.pcl = pcl
if self.prof is not None:
self.slinky_traj, self.updraft_tilt = tab.params.parcelTraj(self.prof, pcl, self.smu, self.smv)
self.clearData()
self.plotBackground()
self.plotData()
self.update()
[docs] def setPreferences(self, update_gui=True, **prefs):
self.bg_color = QtGui.QColor(prefs['bg_color'])
self.fg_color = QtGui.QColor(prefs['fg_color'])
self.low_level_color = QtGui.QColor(prefs['0_3_color'])
self.mid_level_color = QtGui.QColor(prefs['3_6_color'])
self.upper_level_color = QtGui.QColor(prefs['6_9_color'])
self.trop_level_color = QtGui.QColor(prefs['9_12_color'])
if update_gui:
if self.use_left:
self.smu = self.prof.srwind[2]
self.smv = self.prof.srwind[3]
else:
self.smu = self.prof.srwind[0]
self.smv = self.prof.srwind[1]
self.slinky_traj, self.updraft_tilt = tab.params.parcelTraj(self.prof, self.pcl, self.smu, self.smv)
self.clearData()
self.plotBackground()
self.plotData()
self.update()
[docs] def setDeviant(self, deviant):
self.use_left = deviant == 'left'
if self.use_left:
self.smu = self.prof.srwind[2]
self.smv = self.prof.srwind[3]
else:
self.smu = self.prof.srwind[0]
self.smv = self.prof.srwind[1]
self.slinky_traj, self.updraft_tilt = tab.params.parcelTraj(self.prof, self.pcl, self.smu, self.smv)
self.clearData()
self.plotBackground()
self.plotData()
self.update()
[docs] def resizeEvent(self, e):
'''
Handles when the window is resized.
Parameters
----------
e: an Event object
'''
super(plotSlinky, self).resizeEvent(e)
self.plotData()
[docs] def paintEvent(self, e):
'''
Handles painting the QPixmap onto the frame.
Parameters
----------
e: an Event object
'''
## this function handles painting the plot
super(plotSlinky, self).paintEvent(e)
## create a new painter obkect
qp = QtGui.QPainter()
qp.begin(self)
qp.drawPixmap(0,0,self.plotBitMap)
qp.end()
[docs] def plotData(self):
'''
Draws the data onto the QPixmap.
'''
if self.prof is None or self.pcl is None:
return
qp = QtGui.QPainter()
qp.begin(self.plotBitMap)
qp.setRenderHint(qp.Antialiasing)
qp.setRenderHint(qp.TextAntialiasing)
self.plotSlinky(qp)
self.plotSMV(qp)
self.plotTilt(qp)
qp.end()
[docs] def clearData(self):
'''
Handles the clearing of the pixmap
in the frame.
'''
self.plotBitMap = QtGui.QPixmap(self.width(), self.height())
self.plotBitMap.fill(self.bg_color)
[docs] def plotSMV(self, qp):
'''
Draws the line representing the Storm Motion Vector.
Parameters
----------
qp: a QtGui.QPainter object
'''
## set the pen
pen = QtGui.QPen(self.fg_color, 2, QtCore.Qt.SolidLine)
qp.setPen(pen)
## scale the vector to be visible in the window
if tab.utils.QC(self.smu) and tab.utils.QC(self.smv):
wdir, wspd = tab.utils.comp2vec(self.smu, self.smv)
u, v = tab.utils.vec2comp(wdir, 3000)
## convert the unit space to pixel space
motion_x, motion_y = self.xy_to_pix(u,v)
center_x, center_y = self.xy_to_pix(0,0)
qp.drawLine(motion_x,motion_y, center_x,center_y)
[docs] def plotTilt(self, qp):
'''
Plots the data for the updraft tilt.
Parameters
----------
qp: a QtGui.QPainter object
'''
## initialize a pen
pen = QtGui.QPen(self.fg_color, 1, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.setFont(self.title_font)
## draw the text
rect0 = QtCore.QRect(self.brx-30, self.tly+2, 30, self.title_height)
qp.drawText(rect0, QtCore.Qt.TextDontClip | QtCore.Qt.AlignRight, tab.utils.INT2STR(self.updraft_tilt) + ' deg ')
[docs] def plotSlinky(self, qp):
'''
Plots the circles of the Storm Slinky.
Parameters
----------
qp: a QtGui.QPainter object
'''
## set the pen
pen = QtGui.QPen(self.trop_level_color, 1, QtCore.Qt.SolidLine)
## if there is no storm slinky, don't plot it!
if self.slinky_traj is np.ma.masked:
return
has_el = self.pcl.bplus > 1e-3 and tab.utils.QC(self.pcl.elhght)
## loop through the parcel tradjectory in reverse
for tradj in self.slinky_traj[:]:
## get the x, y, and z location of the updraft at each height
x = tradj[0]
y = tradj[1]
z = tradj[2]
if not tab.utils.QC(x) or not tab.utils.QC(y):
continue
## set the various colors
if has_el and z == self.slinky_traj[-1][2]:
pen = QtGui.QPen(QtGui.QColor("#FF00FF"), 1, QtCore.Qt.SolidLine)
elif z < 3000:
pen = QtGui.QPen(self.low_level_color, 1, QtCore.Qt.SolidLine)
elif z < 6000:
pen = QtGui.QPen(self.mid_level_color, 1, QtCore.Qt.SolidLine)
elif z < 9000:
pen = QtGui.QPen(self.upper_level_color, 1, QtCore.Qt.SolidLine)
elif z < 12000:
pen = QtGui.QPen(self.trop_level_color, 1, QtCore.Qt.SolidLine)
else:
continue
## draw the circle
qp.setPen(pen)
xx, yy = self.xy_to_pix(x,y)
center = QtCore.QPointF(xx, yy)
qp.drawEllipse(center, 5, 5)
if __name__ == '__main__':
app_frame = QtGui.QApplication([])
tester = plotSlinky()
tester.setGeometry(100,100,121,138)
tester.show()
app_frame.exec_()