import numpy as np
from qtpy import QtGui, QtCore, QtWidgets
from qtpy.QtOpenGL import *
import sharppy.sharptab as tab
import sharppy.sharptab.utils as utils
from sharppy.sharptab.constants import *
## routine written by Kelton Halbert - OU School of Meteorology
## keltonhalbert@ou.edu
__all__ = ['backgroundThetae', 'plotThetae']
[docs]class backgroundThetae(QtWidgets.QFrame):
'''
Draw the background frame and lines for the Theta-E plot.
Draws the background on a QPixmap.
Inherits a QtWidgets.QFrame Object
'''
def __init__(self):
super(backgroundThetae, self).__init__()
self.initUI()
[docs] def initUI(self):
'''
Initializes window variables and the QPixmap
that gets drawn on.
'''
## window configuration settings,
## sich as padding, width, height, and
## min/max plot axes
self.lpad = 0; self.rpad = 0
self.tpad = 0; self.bpad = 20
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
## what are the minimum/maximum values expected
## for the data? This is used when converting
## to pixel coordinates.
self.pmax = 1025.; self.pmin = 400.
self.tmax = 360.; self.tmin = 300.
## do a DPI check for the font size
if self.physicalDpiX() > 75:
fsize = 6
else:
fsize = 7
self.font_ratio = 0.0512
self.label_font = QtGui.QFont('Helvetica', round(self.size().height() * self.font_ratio))
## initialize the QPixmap
self.plotBitMap = QtGui.QPixmap(self.width(), self.height())
self.clear()
## and draw the background
self.plotBackground()
[docs] def resizeEvent(self, e):
'''
Handles the event the window is resized.
Parameters
----------
e: an Event object
'''
self.initUI()
[docs] def plotBackground(self):
'''
Handles the drawing of the background onto
the QPixmap.
'''
## initialize a painter object and draw the frame
qp = QtGui.QPainter()
qp.begin(self.plotBitMap)
qp.setRenderHint(qp.Antialiasing)
qp.setRenderHint(qp.TextAntialiasing)
self.draw_frame(qp)
## draw the isobar ticks and the theta-e ticks
for p in [1000, 900, 800, 700, 600, 500]:
self.draw_isobar(p, qp)
for t in np.arange( 200, 400, 10):
self.draw_thetae(t, qp)
qp.end()
[docs] def clear(self):
'''
Clear the widget
'''
self.plotBitMap.fill(self.bg_color)
[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)
## 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)
qp.setFont(self.label_font)
## draw the plot name on the background
qp.drawText(35, 15, 50, 50,
QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter,
'Theta-E\nv.\nPres')
[docs] def draw_isobar(self, p, qp):
'''
Draw background isobar ticks.
Parameters
----------
p: pressure in hPa or mb
qp: QtGui.QPainter object
'''
## set a new pen with a white color and solid line of thickness 1
pen = QtGui.QPen(self.fg_color, 1, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.setFont(self.label_font)
## convert the pressure to pixel coordinates
y1 = self.pres_to_pix(p)
## length of line to draw
offset = 5
## draw the isobar line and text
qp.drawLine(self.lpad, y1, self.lpad+offset, y1)
qp.drawLine(self.brx+self.rpad-offset, y1,
self.brx+self.rpad, y1)
qp.drawText(0, y1-20, 20, 40,
QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight,
utils.INT2STR(p))
[docs] def draw_thetae(self, t, qp):
'''
Draw background Theta-E ticks.
Parameters
----------
t: Theta-E in degrees Kelvin
qp: QtGui.QPainter object
'''
## set a new pen with a white color, thickness one, solid line
pen = QtGui.QPen(self.fg_color, 1, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.setFont(self.label_font)
## convert theta-E to pixel values
x1 = self.theta_to_pix(t)
## length of tick to draw
offset = 5
## draw the tick and label it with a value
qp.drawLine(x1, 0, x1, 0+offset)
qp.drawLine(x1, self.bry+self.tpad-offset,
x1, self.bry+self.rpad)
qp.drawText(x1, self.bry-20, 15, 20,
QtCore.Qt.AlignTop | QtCore.Qt.AlignCenter, utils.INT2STR(t))
[docs] def pres_to_pix(self, p):
'''
Function to convert a pressure value (hPa) to a Y pixel.
Parameters
----------
p: pressure in hPa or mb
'''
scl1 = self.pmax - self.pmin
scl2 = self.pmax - p
return self.bry - (scl2 / scl1) * (self.bry - self.tpad)
[docs] def theta_to_pix(self, t):
'''
Function to convert a Theta-E value (K) to a X pixel.
Parameters
----------
t: temperature in Kelvin
'''
scl1 = self.tmax - self.tmin
scl2 = self.tmax - t
return self.bry - (scl2 / scl1) * (self.bry - self.rpad)
[docs]class plotThetae(backgroundThetae):
'''
Draws the theta-E window. Inherits from the backgroundThetae
class that handles plotting of the frame. Draws the contours
to the QPixmap inherited by the backgroundThetae class.
'''
def __init__(self):
'''
Initializes the data needed from the Profile object.
Parameters
----------
prof: a Profile object
'''
self.bg_color = QtGui.QColor('#000000')
self.fg_color = QtGui.QColor('#ffffff')
self.thte_color = QtGui.QColor('#ff0000')
super(plotThetae, self).__init__()
## set the varables for pressure and thetae
self.prof = None
[docs] def setProf(self, prof):
self.prof = prof
self.thetae = prof.thetae
self.pres = prof.pres
idx = np.where( self.pres > 400. )[0]
self.tmin = self.thetae[idx].min() - 10.
self.tmax = self.thetae[idx].max() + 10.
self.clear()
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.thte_color = QtGui.QColor(prefs['temp_color'])
if update_gui:
self.clear()
self.plotBackground()
self.plotData()
self.update()
[docs] def resizeEvent(self, e):
'''
Handles when the window is resized.
Parameters
----------
e: an Event object
'''
super(plotThetae, self).resizeEvent(e)
if self.prof is not None:
idx = np.where( self.pres > 400. )[0]
self.tmin = self.thetae[idx].min() - 10.
self.tmax = self.thetae[idx].max() + 10.
self.update()
self.plotData()
[docs] def paintEvent(self, e):
'''
Draws the QPixmap onto the QWidget.
Parameters
----------
e: an Event object
'''
super(plotThetae, self).paintEvent(e)
qp = QtGui.QPainter()
qp.begin(self)
qp.drawPixmap(0, 0, self.plotBitMap)
qp.end()
[docs] def plotData(self):
'''
Plots the data onto the QPixmap.
'''
if self.prof is None:
return
## this function handles painting the plot
## create a new painter obkect
qp = QtGui.QPainter()
qp.begin(self.plotBitMap)
qp.setRenderHint(qp.Antialiasing)
qp.setRenderHint(qp.TextAntialiasing)
## draw the theta-e profile
self.draw_profile(qp)
## end the painter
qp.end()
[docs] def draw_profile(self, qp):
'''
Draw the Theta-E v. Pres profile.
Parameters
----------
qp: QtGui.QPainter object
'''
pen = QtGui.QPen(self.thte_color, 2)
pen.setStyle(QtCore.Qt.SolidLine)
mask1 = self.thetae.mask
mask2 = self.pres.mask
mask = np.maximum(mask1, mask2)
pres = self.pres[~mask]
thetae = self.thetae[~mask]
for i in range( pres.shape[0] - 1 ):
## we really only want to plot the data in the lowest 500mb
if pres[i] > 400:
## get two pressure, temperature, and dewpoint values
p1 = pres[i]; p2 = pres[i+1]
## get two theta-e values from the above sounding profile data
thte1 = thetae[i]; thte2 = thetae[i+1]
## convert the theta-e values to x pixel coordinates
## and the pressure values to y pixel coordinates
x1 = self.theta_to_pix(thte1); x2 = self.theta_to_pix(thte2)
y1 = self.pres_to_pix(p1); y2 = self.pres_to_pix(p2)
## set the pen and draw a line between the two points
qp.setPen(pen)
qp.drawLine(x1, y1, x2, y2)
if __name__ == '__main__':
app_frame = QtGui.QApplication([])
tester = plotThetae()
tester.show()
app_frame.exec_()