21
21
import numpy as np
22
22
23
23
24
- __version__ = "1.7.1 "
24
+ __version__ = "1.7.2 "
25
25
26
26
27
27
def string_from_ffi (s ):
@@ -45,7 +45,7 @@ def __init__(self):
45
45
self .debug = os .environ .get ("METVIEW_PYTHON_DEBUG" , "0" ) == "1"
46
46
47
47
# 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
49
49
self .persistent_session = True
50
50
self .info_section = {"METVIEW_LIB" : os .environ ["METVIEW_LIB" ]}
51
51
return
@@ -54,7 +54,7 @@ def __init__(self):
54
54
import time
55
55
import subprocess
56
56
57
- if self .debug :
57
+ if self .debug : # pragma: no cover
58
58
print ("MetviewInvoker: Invoking Metview" )
59
59
self .persistent_session = False
60
60
self .metview_replied = False
@@ -77,14 +77,14 @@ def __init__(self):
77
77
env_file .name ,
78
78
str (pid ),
79
79
]
80
- if self .debug :
80
+ if self .debug : # pragma: no cover
81
81
metview_flags .insert (2 , "-slog" )
82
82
print ("Starting Metview using these command args:" )
83
83
print (metview_flags )
84
84
85
85
try :
86
86
subprocess .Popen (metview_flags )
87
- except Exception as exp :
87
+ except Exception as exp : # pragma: no cover
88
88
print (
89
89
"Could not run the Metview executable ('" + metview_startup_cmd + "'); "
90
90
"check that the binaries for Metview (version 5 at least) are installed "
@@ -99,7 +99,7 @@ def __init__(self):
99
99
):
100
100
time .sleep (0.001 )
101
101
102
- if not (self .metview_replied ):
102
+ if not (self .metview_replied ): # pragma: no cover
103
103
raise Exception (
104
104
'Command "metview" did not respond within '
105
105
+ str (self .metview_startup_timeout )
@@ -121,7 +121,7 @@ def __init__(self):
121
121
def destroy (self ):
122
122
"""Kills the Metview session. Raises an exception if it could not do it."""
123
123
124
- if self .persistent_session :
124
+ if self .persistent_session : # pragma: no cover
125
125
return
126
126
127
127
if self .metview_replied :
@@ -188,7 +188,7 @@ def restore_signal_handlers(self):
188
188
# MacOS systems
189
189
lib = ffi .dlopen (os .path .join (mv_lib , "libMvMacro" ))
190
190
191
- except Exception as exp :
191
+ except Exception as exp : # pragma: no cover
192
192
print (
193
193
"Error loading Metview/libMvMacro. LD_LIBRARY_PATH="
194
194
+ os .environ .get ("LD_LIBRARY_PATH" , "" )
@@ -597,7 +597,7 @@ def to_dataset(self, **kwarg):
597
597
# soft dependency on cfgrib
598
598
try :
599
599
import xarray as xr
600
- except ImportError :
600
+ except ImportError : # pragma: no cover
601
601
print ("Package xarray not found. Try running 'pip install xarray'." )
602
602
raise
603
603
dataset = xr .open_dataset (self .url (), engine = "cfgrib" , backend_kwargs = kwarg )
@@ -639,7 +639,7 @@ def __init__(self, val_pointer):
639
639
def to_dataframe (self ):
640
640
try :
641
641
import pandas as pd
642
- except ImportError :
642
+ except ImportError : # pragma: no cover
643
643
print ("Package pandas not found. Try running 'pip install pandas'." )
644
644
raise
645
645
@@ -665,7 +665,7 @@ def to_dataset(self):
665
665
# soft dependency on xarray
666
666
try :
667
667
import xarray as xr
668
- except ImportError :
668
+ except ImportError : # pragma: no cover
669
669
print ("Package xarray not found. Try running 'pip install xarray'." )
670
670
raise
671
671
dataset = xr .open_dataset (self .url ())
@@ -679,7 +679,7 @@ def __init__(self, val_pointer):
679
679
def to_dataframe (self ):
680
680
try :
681
681
import pandas as pd
682
- except ImportError :
682
+ except ImportError : # pragma: no cover
683
683
print ("Package pandas not found. Try running 'pip install pandas'." )
684
684
raise
685
685
@@ -700,7 +700,7 @@ def __init__(self, val_pointer):
700
700
def to_dataframe (self ):
701
701
try :
702
702
import pandas as pd
703
- except ImportError :
703
+ except ImportError : # pragma: no cover
704
704
print ("Package pandas not found. Try running 'pip install pandas'." )
705
705
raise
706
706
@@ -867,7 +867,7 @@ def vector_from_metview(val):
867
867
elif s == 8 :
868
868
nptype = np .float64
869
869
b = lib .p_vector_double_array (vec )
870
- else :
870
+ else : # pragma: no cover
871
871
raise Exception ("Metview vector data type cannot be handled: " , s )
872
872
873
873
bsize = n * s
@@ -1080,28 +1080,15 @@ def merge(*args):
1080
1080
class Plot :
1081
1081
def __init__ (self ):
1082
1082
self .plot_to_jupyter = False
1083
+ self .plot_widget = True
1083
1084
self .jupyter_args = {}
1084
1085
1085
1086
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 )
1105
1092
else :
1106
1093
map_outputs = {
1107
1094
"png" : png_output ,
@@ -1122,14 +1109,9 @@ def __call__(self, *args, **kwargs):
1122
1109
1123
1110
# animate - only usable within Jupyter notebooks
1124
1111
# 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
1131
1113
1132
- import ipywidgets as widgets
1114
+ animation_mode = kwargs . get ( "animate" , "auto" ) # True, False or "auto"
1133
1115
1134
1116
# create all the widgets first so that the 'waiting' label is at the bottom
1135
1117
image_widget = widgets .Image (
@@ -1138,47 +1120,9 @@ def animate(*args, **kwargs):
1138
1120
# height=400,
1139
1121
)
1140
1122
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"
1178
1123
image_widget .layout .visibility = "hidden"
1179
1124
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 )
1182
1126
1183
1127
# plot all frames to a temporary directory owned by Metview to enure cleanup
1184
1128
tempdirpath = tempfile .mkdtemp (dir = os .environ .get ("METVIEW_TMPDIR" , None ))
@@ -1196,45 +1140,98 @@ def animate(*args, **kwargs):
1196
1140
return
1197
1141
1198
1142
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"
1202
1197
1203
1198
def plot_frame (frame_index ):
1204
1199
im_file = open (files [frame_index - 1 ], "rb" )
1205
1200
imf = im_file .read ()
1206
1201
im_file .close ()
1207
1202
image_widget .value = imf
1208
1203
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
1212
1206
plot_frame (1 )
1213
- frame_widget .observe (on_frame_change , names = "value" )
1207
+ waitl_widget .layout .visibility = "hidden"
1208
+ image_widget .layout .visibility = "visible"
1214
1209
1215
- def on_speed_change (change ):
1216
- play_widget .interval = 1500 / change ["new" ]
1217
1210
1218
- speed_widget . observe ( on_speed_change , names = "value" )
1211
+ def plot_to_notebook_return_image ( * args , ** kwargs ): # pragma: no cover
1219
1212
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
1225
1224
1226
1225
1227
1226
# On a test system, importing IPython took approx 0.5 seconds, so to avoid that hit
1228
1227
# under most circumstances, we only import it when the user asks for Jupyter
1229
1228
# functionality. Since this occurs within a function, we need a little trickery to
1230
1229
# get the IPython functions into the global namespace so that the plot object can use them
1231
1230
def setoutput (* args , ** kwargs ):
1232
- if "jupyter" in args :
1231
+ if "jupyter" in args : # pragma: no cover
1233
1232
try :
1234
- global Image
1235
- global get_ipython
1236
- IPython = __import__ ("IPython" , globals (), locals ())
1237
- Image = IPython .display .Image
1233
+ import IPython
1234
+
1238
1235
get_ipython = IPython .get_ipython
1239
1236
except ImportError as imperr :
1240
1237
print ("Could not import IPython module - plotting to Jupyter will not work" )
@@ -1243,12 +1240,25 @@ def setoutput(*args, **kwargs):
1243
1240
# test whether we're in the Jupyter environment
1244
1241
if get_ipython () is not None :
1245
1242
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" ]
1246
1246
plot .jupyter_args = kwargs
1247
1247
else :
1248
1248
print (
1249
1249
"ERROR: setoutput('jupyter') was set, but we are not in a Jupyter environment"
1250
1250
)
1251
1251
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
+
1252
1262
else :
1253
1263
plot .plot_to_jupyter = False
1254
1264
met_setoutput (* args )
0 commit comments