2121import numpy as np
2222
2323
24- __version__ = "1.7.1 "
24+ __version__ = "1.7.2 "
2525
2626
2727def string_from_ffi (s ):
@@ -45,7 +45,7 @@ def __init__(self):
4545 self .debug = os .environ .get ("METVIEW_PYTHON_DEBUG" , "0" ) == "1"
4646
4747 # check whether we're in a running Metview session
48- if "METVIEW_TITLE_PROD" in os .environ :
48+ if "METVIEW_TITLE_PROD" in os .environ : # pragma: no cover
4949 self .persistent_session = True
5050 self .info_section = {"METVIEW_LIB" : os .environ ["METVIEW_LIB" ]}
5151 return
@@ -54,7 +54,7 @@ def __init__(self):
5454 import time
5555 import subprocess
5656
57- if self .debug :
57+ if self .debug : # pragma: no cover
5858 print ("MetviewInvoker: Invoking Metview" )
5959 self .persistent_session = False
6060 self .metview_replied = False
@@ -77,14 +77,14 @@ def __init__(self):
7777 env_file .name ,
7878 str (pid ),
7979 ]
80- if self .debug :
80+ if self .debug : # pragma: no cover
8181 metview_flags .insert (2 , "-slog" )
8282 print ("Starting Metview using these command args:" )
8383 print (metview_flags )
8484
8585 try :
8686 subprocess .Popen (metview_flags )
87- except Exception as exp :
87+ except Exception as exp : # pragma: no cover
8888 print (
8989 "Could not run the Metview executable ('" + metview_startup_cmd + "'); "
9090 "check that the binaries for Metview (version 5 at least) are installed "
@@ -99,7 +99,7 @@ def __init__(self):
9999 ):
100100 time .sleep (0.001 )
101101
102- if not (self .metview_replied ):
102+ if not (self .metview_replied ): # pragma: no cover
103103 raise Exception (
104104 'Command "metview" did not respond within '
105105 + str (self .metview_startup_timeout )
@@ -121,7 +121,7 @@ def __init__(self):
121121 def destroy (self ):
122122 """Kills the Metview session. Raises an exception if it could not do it."""
123123
124- if self .persistent_session :
124+ if self .persistent_session : # pragma: no cover
125125 return
126126
127127 if self .metview_replied :
@@ -188,7 +188,7 @@ def restore_signal_handlers(self):
188188 # MacOS systems
189189 lib = ffi .dlopen (os .path .join (mv_lib , "libMvMacro" ))
190190
191- except Exception as exp :
191+ except Exception as exp : # pragma: no cover
192192 print (
193193 "Error loading Metview/libMvMacro. LD_LIBRARY_PATH="
194194 + os .environ .get ("LD_LIBRARY_PATH" , "" )
@@ -597,7 +597,7 @@ def to_dataset(self, **kwarg):
597597 # soft dependency on cfgrib
598598 try :
599599 import xarray as xr
600- except ImportError :
600+ except ImportError : # pragma: no cover
601601 print ("Package xarray not found. Try running 'pip install xarray'." )
602602 raise
603603 dataset = xr .open_dataset (self .url (), engine = "cfgrib" , backend_kwargs = kwarg )
@@ -639,7 +639,7 @@ def __init__(self, val_pointer):
639639 def to_dataframe (self ):
640640 try :
641641 import pandas as pd
642- except ImportError :
642+ except ImportError : # pragma: no cover
643643 print ("Package pandas not found. Try running 'pip install pandas'." )
644644 raise
645645
@@ -665,7 +665,7 @@ def to_dataset(self):
665665 # soft dependency on xarray
666666 try :
667667 import xarray as xr
668- except ImportError :
668+ except ImportError : # pragma: no cover
669669 print ("Package xarray not found. Try running 'pip install xarray'." )
670670 raise
671671 dataset = xr .open_dataset (self .url ())
@@ -679,7 +679,7 @@ def __init__(self, val_pointer):
679679 def to_dataframe (self ):
680680 try :
681681 import pandas as pd
682- except ImportError :
682+ except ImportError : # pragma: no cover
683683 print ("Package pandas not found. Try running 'pip install pandas'." )
684684 raise
685685
@@ -700,7 +700,7 @@ def __init__(self, val_pointer):
700700 def to_dataframe (self ):
701701 try :
702702 import pandas as pd
703- except ImportError :
703+ except ImportError : # pragma: no cover
704704 print ("Package pandas not found. Try running 'pip install pandas'." )
705705 raise
706706
@@ -867,7 +867,7 @@ def vector_from_metview(val):
867867 elif s == 8 :
868868 nptype = np .float64
869869 b = lib .p_vector_double_array (vec )
870- else :
870+ else : # pragma: no cover
871871 raise Exception ("Metview vector data type cannot be handled: " , s )
872872
873873 bsize = n * s
@@ -1080,28 +1080,15 @@ def merge(*args):
10801080class Plot :
10811081 def __init__ (self ):
10821082 self .plot_to_jupyter = False
1083+ self .plot_widget = True
10831084 self .jupyter_args = {}
10841085
10851086 def __call__ (self , * args , ** kwargs ):
1086- # if animate=True is supplied, then create a Jupyter animation
1087- if kwargs .get ("animate" , False ):
1088- return animate (args , kwargs )
1089-
1090- # otherwise create a single static plot
1091- if self .plot_to_jupyter :
1092- f , tmp = tempfile .mkstemp (".png" )
1093- os .close (f )
1094-
1095- base , ext = os .path .splitext (tmp )
1096-
1097- output_args = {"output_name" : base , "output_name_first_page_number" : "off" }
1098- output_args .update (self .jupyter_args )
1099- met_setoutput (png_output (output_args ))
1100- met_plot (* args )
1101-
1102- image = Image (tmp )
1103- os .unlink (tmp )
1104- return image
1087+ if self .plot_to_jupyter : # pragma: no cover
1088+ if self .plot_widget :
1089+ return plot_to_notebook (args , ** kwargs )
1090+ else :
1091+ return plot_to_notebook_return_image (args , ** kwargs )
11051092 else :
11061093 map_outputs = {
11071094 "png" : png_output ,
@@ -1122,14 +1109,9 @@ def __call__(self, *args, **kwargs):
11221109
11231110# animate - only usable within Jupyter notebooks
11241111# generates a widget allowing the user to select between plot frames
1125- def animate (* args , ** kwargs ):
1126-
1127- if not plot .plot_to_jupyter :
1128- raise EnvironmentError (
1129- "animate() can only be used after calling set_output('jupyter')"
1130- )
1112+ def plot_to_notebook (* args , ** kwargs ): # pragma: no cover
11311113
1132- import ipywidgets as widgets
1114+ animation_mode = kwargs . get ( "animate" , "auto" ) # True, False or "auto"
11331115
11341116 # create all the widgets first so that the 'waiting' label is at the bottom
11351117 image_widget = widgets .Image (
@@ -1138,47 +1120,9 @@ def animate(*args, **kwargs):
11381120 # height=400,
11391121 )
11401122
1141- frame_widget = widgets .IntSlider (
1142- value = 1 ,
1143- min = 1 ,
1144- max = 1 ,
1145- step = 1 ,
1146- description = "Frame:" ,
1147- disabled = False ,
1148- continuous_update = True ,
1149- readout = True ,
1150- )
1151-
1152- play_widget = widgets .Play (
1153- value = 1 ,
1154- min = 1 ,
1155- max = 1 ,
1156- step = 1 ,
1157- interval = 500 ,
1158- description = "Play animation" ,
1159- disabled = False ,
1160- )
1161-
1162- speed_widget = widgets .IntSlider (
1163- value = 3 ,
1164- min = 1 ,
1165- max = 20 ,
1166- step = 1 ,
1167- description = "Speed" ,
1168- disabled = False ,
1169- continuous_update = True ,
1170- readout = True ,
1171- )
1172-
1173- widgets .jslink ((play_widget , "value" ), (frame_widget , "value" ))
1174- play_and_speed_widget = widgets .HBox ([play_widget , speed_widget ])
1175- controls = widgets .VBox ([frame_widget , play_and_speed_widget ])
1176-
1177- controls .layout .visibility = "hidden"
11781123 image_widget .layout .visibility = "hidden"
11791124 waitl_widget = widgets .Label (value = "Generating plots...." )
1180- frame_widget .layout .width = "800px"
1181- display (image_widget , controls , waitl_widget )
1125+ display (image_widget , waitl_widget )
11821126
11831127 # plot all frames to a temporary directory owned by Metview to enure cleanup
11841128 tempdirpath = tempfile .mkdtemp (dir = os .environ .get ("METVIEW_TMPDIR" , None ))
@@ -1196,45 +1140,98 @@ def animate(*args, **kwargs):
11961140 return
11971141
11981142 files = [os .path .join (tempdirpath , f ) for f in sorted (filenames )]
1199- frame_widget .max = len (files )
1200- frame_widget .description = "Frame (" + str (len (files )) + ") :"
1201- play_widget .max = len (files )
1143+
1144+ if (animation_mode == True ) or (animation_mode == "auto" and len (filenames ) > 1 ):
1145+ frame_widget = widgets .IntSlider (
1146+ value = 1 ,
1147+ min = 1 ,
1148+ max = 1 ,
1149+ step = 1 ,
1150+ description = "Frame:" ,
1151+ disabled = False ,
1152+ continuous_update = True ,
1153+ readout = True ,
1154+ )
1155+
1156+ play_widget = widgets .Play (
1157+ value = 1 ,
1158+ min = 1 ,
1159+ max = 1 ,
1160+ step = 1 ,
1161+ interval = 500 ,
1162+ description = "Play animation" ,
1163+ disabled = False ,
1164+ )
1165+
1166+ speed_widget = widgets .IntSlider (
1167+ value = 3 ,
1168+ min = 1 ,
1169+ max = 20 ,
1170+ step = 1 ,
1171+ description = "Speed" ,
1172+ disabled = False ,
1173+ continuous_update = True ,
1174+ readout = True ,
1175+ )
1176+
1177+ widgets .jslink ((play_widget , "value" ), (frame_widget , "value" ))
1178+ play_and_speed_widget = widgets .HBox ([play_widget , speed_widget ])
1179+ controls = widgets .VBox ([frame_widget , play_and_speed_widget ])
1180+ controls .layout .visibility = "hidden"
1181+ frame_widget .layout .width = "800px"
1182+ display (controls )
1183+
1184+ frame_widget .max = len (files )
1185+ frame_widget .description = "Frame (" + str (len (files )) + ") :"
1186+ play_widget .max = len (files )
1187+
1188+ def on_frame_change (change ):
1189+ plot_frame (change ["new" ])
1190+
1191+ def on_speed_change (change ):
1192+ play_widget .interval = 1500 / change ["new" ]
1193+
1194+ frame_widget .observe (on_frame_change , names = "value" )
1195+ speed_widget .observe (on_speed_change , names = "value" )
1196+ controls .layout .visibility = "visible"
12021197
12031198 def plot_frame (frame_index ):
12041199 im_file = open (files [frame_index - 1 ], "rb" )
12051200 imf = im_file .read ()
12061201 im_file .close ()
12071202 image_widget .value = imf
12081203
1209- def on_frame_change (change ):
1210- plot_frame (change ["new" ])
1211-
1204+ # everything is ready now, so plot the first frame, hide the
1205+ # 'waiting' label and reveal the plot and the frame slider
12121206 plot_frame (1 )
1213- frame_widget .observe (on_frame_change , names = "value" )
1207+ waitl_widget .layout .visibility = "hidden"
1208+ image_widget .layout .visibility = "visible"
12141209
1215- def on_speed_change (change ):
1216- play_widget .interval = 1500 / change ["new" ]
12171210
1218- speed_widget . observe ( on_speed_change , names = "value" )
1211+ def plot_to_notebook_return_image ( * args , ** kwargs ): # pragma: no cover
12191212
1220- # everything is ready now, so hide the 'waiting' label
1221- # and reveal the plot and the frame slider
1222- waitl_widget .layout .visibility = "hidden"
1223- controls .layout .visibility = "visible"
1224- image_widget .layout .visibility = "visible"
1213+ from IPython .display import Image
1214+
1215+ f , tmp = tempfile .mkstemp (".png" )
1216+ os .close (f )
1217+ base , ext = os .path .splitext (tmp )
1218+ plot .jupyter_args .update (output_name = base , output_name_first_page_number = "off" )
1219+ met_setoutput (png_output (plot .jupyter_args ))
1220+ met_plot (* args )
1221+ image = Image (tmp )
1222+ os .unlink (tmp )
1223+ return image
12251224
12261225
12271226# On a test system, importing IPython took approx 0.5 seconds, so to avoid that hit
12281227# under most circumstances, we only import it when the user asks for Jupyter
12291228# functionality. Since this occurs within a function, we need a little trickery to
12301229# get the IPython functions into the global namespace so that the plot object can use them
12311230def setoutput (* args , ** kwargs ):
1232- if "jupyter" in args :
1231+ if "jupyter" in args : # pragma: no cover
12331232 try :
1234- global Image
1235- global get_ipython
1236- IPython = __import__ ("IPython" , globals (), locals ())
1237- Image = IPython .display .Image
1233+ import IPython
1234+
12381235 get_ipython = IPython .get_ipython
12391236 except ImportError as imperr :
12401237 print ("Could not import IPython module - plotting to Jupyter will not work" )
@@ -1243,12 +1240,25 @@ def setoutput(*args, **kwargs):
12431240 # test whether we're in the Jupyter environment
12441241 if get_ipython () is not None :
12451242 plot .plot_to_jupyter = True
1243+ plot .plot_widget = kwargs .get ("plot_widget" , True )
1244+ if "plot_widget" in kwargs :
1245+ del kwargs ["plot_widget" ]
12461246 plot .jupyter_args = kwargs
12471247 else :
12481248 print (
12491249 "ERROR: setoutput('jupyter') was set, but we are not in a Jupyter environment"
12501250 )
12511251 raise (Exception ("Could not set output to jupyter" ))
1252+
1253+ try :
1254+ global widgets
1255+ widgets = __import__ ("ipywidgets" , globals (), locals ())
1256+ except ImportError as imperr :
1257+ print (
1258+ "Could not import ipywidgets module - plotting to Jupyter will not work"
1259+ )
1260+ raise imperr
1261+
12521262 else :
12531263 plot .plot_to_jupyter = False
12541264 met_setoutput (* args )
0 commit comments