15
15
See the online ObjCryst++ documentation (http://vincefn.net/ObjCryst/).
16
16
17
17
Changes from ObjCryst::PowderPattern::
18
- In development !
18
+ Additional functions for plotting, basic QPA and profile fitting.
19
19
"""
20
20
21
21
from urllib .request import urlopen
@@ -65,10 +65,19 @@ def UpdateDisplay(self):
65
65
except :
66
66
pass
67
67
if self ._plot_fig is not None :
68
- import matplotlib .pyplot as plt
69
- if 'inline' not in plt .get_backend ():
70
- if plt .fignum_exists (self ._plot_fig .number ):
71
- self .plot ()
68
+ if self ._plot_fig is not None :
69
+ self .plot ()
70
+
71
+ def update_display (self , return_figure = False ):
72
+ """
73
+ Update the plotted figure (if it exists)
74
+
75
+ :param return_figure: if True, returns the figure
76
+ :return: the figure if return_figure is True
77
+ """
78
+ self .UpdateDisplay ()
79
+ if return_figure :
80
+ return self .figure
72
81
73
82
def disable_display_update (self ):
74
83
""" Disable display (useful for multiprocessing)"""
@@ -118,38 +127,32 @@ def plot(self, diff=None, hkl=None, figsize=(9, 4), fontsize_hkl=6, reset=False,
118
127
119
128
# TODO: handle other coordinates than angles (TOF)
120
129
x = np .rad2deg (self .GetPowderPatternX ())
121
- if 'inline' not in plt .get_backend ():
122
- if self ._plot_fig is None :
123
- self ._plot_fig = plt .figure (figsize = figsize )
124
- elif plt .fignum_exists (self ._plot_fig .number ) is False :
125
- # Somehow the figure disappeared, create a new one
126
- self ._plot_fig = plt .figure (figsize = figsize )
127
- else :
128
- self ._plot_fig = plt .figure (self ._plot_fig .number )
129
- plt .clf ()
130
+ if self ._plot_fig is None or 'inline' in plt .get_backend ():
131
+ self ._plot_fig = plt .figure (figsize = figsize )
130
132
else :
131
- plt .figure (figsize = figsize )
132
- plt .plot (x , obs , 'k' , label = 'obs' , linewidth = 1 )
133
- plt .plot (x , calc , 'r' , label = 'calc' , linewidth = 1 )
133
+ self ._plot_fig .clear ()
134
+ ax = self ._plot_fig .axes [0 ] if len (self ._plot_fig .axes ) else self ._plot_fig .subplots ()
135
+ ax .plot (x , obs , 'k' , label = 'obs' , linewidth = 1 )
136
+ ax .plot (x , calc , 'r' , label = 'calc' , linewidth = 1 )
134
137
m = min (1 , self .GetMaxSinThetaOvLambda () * self .GetWavelength ())
135
138
mtth = np .rad2deg (np .arcsin (m )) * 2
136
139
if plot_diff :
137
140
diff = calc - obs - obs .max () / 20
138
141
# Mask difference above max sin(theta)/lambda
139
142
diff = np .ma .masked_array (diff , x > mtth )
140
- plt .plot (x , diff , 'g' , label = 'calc-obs' ,
141
- linewidth = 0.5 )
143
+ ax .plot (x , diff , 'g' , label = 'calc-obs' ,
144
+ linewidth = 0.5 )
142
145
143
- plt .legend (loc = 'upper right' )
146
+ ax .legend (loc = 'upper right' )
144
147
if self .GetName () != "" :
145
- plt .title ("PowderPattern: %s" % self .GetName ())
148
+ self . _plot_fig .title ("PowderPattern: %s" % self .GetName ())
146
149
147
150
if self ._plot_ylim is not None :
148
- plt . ylim (self ._plot_ylim )
151
+ ax . set_ylim (self ._plot_ylim )
149
152
if self ._plot_xlim is not None :
150
- plt . xlim (self ._plot_xlim )
153
+ ax . set_xlim (self ._plot_xlim )
151
154
elif m < 1 :
152
- plt . xlim (x .min (), mtth )
155
+ ax . set_xlim (x .min (), mtth )
153
156
154
157
if plot_hkl :
155
158
self ._do_plot_hkl (nb_max = 100 , fontsize_hkl = fontsize_hkl )
@@ -173,15 +176,15 @@ def plot(self, diff=None, hkl=None, figsize=(9, 4), fontsize_hkl=6, reset=False,
173
176
if comp .GetExtractionMode ():
174
177
s += "[Le Bail mode]"
175
178
self ._plot_phase_labels .append (s )
176
- plt .text (0.005 , 0.995 , "\n " * iphase + s , horizontalalignment = "left" , verticalalignment = "top" ,
177
- transform = plt . gca () .transAxes , fontsize = 6 , color = self ._colour_phases [iphase ])
179
+ ax .text (0.005 , 0.995 , "\n " * iphase + s , horizontalalignment = "left" , verticalalignment = "top" ,
180
+ transform = ax .transAxes , fontsize = 6 , color = self ._colour_phases [iphase ])
178
181
iphase += 1
179
182
180
183
if 'inline' not in plt .get_backend ():
181
184
try :
182
185
# Force immediate display. Not supported on all backends (e.g. nbagg)
183
- plt .draw ()
184
- plt . gcf () .canvas .draw ()
186
+ ax .draw ()
187
+ self . _plot_fig .canvas .draw ()
185
188
if 'ipympl' not in plt .get_backend ():
186
189
plt .pause (.001 )
187
190
except :
@@ -198,24 +201,25 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
198
201
fontsize_hkl = self ._plot_hkl_fontsize
199
202
else :
200
203
self ._plot_hkl_fontsize = fontsize_hkl
204
+ ax = self ._plot_fig .axes [0 ]
201
205
# Plot up to nb_max hkl reflections
202
206
obs = self .GetPowderPatternObs ()
203
207
calc = self .GetPowderPatternCalc ()
204
208
x = np .rad2deg (self .GetPowderPatternX ())
205
209
# Clear previous text (assumes only hkl were printed)
206
210
if version_parse (mpl_version ) < version_parse ("3.7.0" ):
207
211
# This will fail for matplotlib>=(3,7,0)
208
- plt . gca () .texts .clear ()
212
+ ax .texts .clear ()
209
213
else :
210
- for t in plt . gca () .texts :
214
+ for t in ax .texts :
211
215
t .remove ()
212
216
iphase = 0
213
217
for ic in range (self .GetNbPowderPatternComponent ()):
214
218
c = self .GetPowderPatternComponent (ic )
215
219
if isinstance (c , PowderPatternDiffraction ) is False :
216
220
continue
217
221
# print("HKL for:", c.GetName())
218
- xmin , xmax = plt . xlim ()
222
+ xmin , xmax = ax . get_xlim ()
219
223
vh = np .round (c .GetH ()).astype (np .int16 )
220
224
vk = np .round (c .GetK ()).astype (np .int16 )
221
225
vl = np .round (c .GetL ()).astype (np .int16 )
@@ -228,12 +232,12 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
228
232
renderer = plt .gcf ().canvas .renderer
229
233
except :
230
234
# Force immediate display. Not supported on all backends (e.g. nbagg)
231
- plt .draw ()
232
- plt . gcf () .canvas .draw ()
235
+ ax .draw ()
236
+ self . _plot_fig .canvas .draw ()
233
237
if 'ipympl' not in plt .get_backend ():
234
238
plt .pause (.001 )
235
239
try :
236
- renderer = plt . gcf () .canvas .renderer
240
+ renderer = self . _plot_fig .canvas .renderer
237
241
except :
238
242
renderer = None
239
243
else :
@@ -242,7 +246,6 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
242
246
props = {'ha' : 'center' , 'va' : 'bottom' }
243
247
ct = 0
244
248
last_bbox = None
245
- ax = plt .gca ()
246
249
tdi = ax .transData .inverted ()
247
250
for i in range (len (vh )):
248
251
xhkl = np .rad2deg (self .X2XCorr (self .STOL2X (stol [i ])))
@@ -258,8 +261,8 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
258
261
259
262
ihkl = max (calc [idxhkl ], obs [idxhkl ])
260
263
s = " %d %d %d" % (vh [i ], vk [i ], vl [i ])
261
- t = plt .text (xhkl , ihkl , s , props , rotation = 90 , fontsize = fontsize_hkl ,
262
- fontweight = 'light' , color = self ._colour_phases [iphase ])
264
+ t = ax .text (xhkl , ihkl , s , props , rotation = 90 , fontsize = fontsize_hkl ,
265
+ fontweight = 'light' , color = self ._colour_phases [iphase ])
263
266
if renderer is not None :
264
267
# Check for overlap with previous
265
268
bbox = t .get_window_extent (renderer )
@@ -270,12 +273,22 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
270
273
t .set_y (ihkl + b .height * 1.2 )
271
274
last_bbox = t .get_window_extent (renderer )
272
275
iphase += 1
273
- self ._last_hkl_plot_xlim = plt . xlim ()
276
+ self ._last_hkl_plot_xlim = ax . get_xlim ()
274
277
if self ._plot_phase_labels is not None :
275
278
for iphase in range (len (self ._plot_phase_labels )):
276
279
s = self ._plot_phase_labels [iphase ]
277
- plt .text (0.005 , 0.995 , "\n " * iphase + s , horizontalalignment = "left" , verticalalignment = "top" ,
278
- transform = plt .gca ().transAxes , fontsize = 6 , color = self ._colour_phases [iphase ])
280
+ ax .text (0.005 , 0.995 , "\n " * iphase + s , horizontalalignment = "left" , verticalalignment = "top" ,
281
+ transform = ax .transAxes , fontsize = 6 , color = self ._colour_phases [iphase ])
282
+
283
+ @property
284
+ def figure (self ):
285
+ """
286
+ return: the figure used for plotting, or None. Note that
287
+ if you want to display it in a notebook using ipympl (aka
288
+ 'matplotlib widget'), you should 'figure.canvas' to display
289
+ also the toolbar (zoom, etc...).
290
+ """
291
+ return self ._plot_fig
279
292
280
293
def quick_fit_profile (self , pdiff = None , auto_background = True , init_profile = True , plot = True ,
281
294
zero = True , constant_width = True , width = True , eta = True , backgd = True , cell = True ,
@@ -434,11 +447,11 @@ def _on_mouse_event(self, event):
434
447
self .plot ()
435
448
436
449
def _on_draw_event (self , event ):
437
- if self ._plot_hkl and self ._last_hkl_plot_xlim is not None :
438
- import matplotlib . pyplot as plt
439
- self ._plot_xlim = plt . gca () .get_xlim ()
440
- dx1 = abs (self ._last_hkl_plot_xlim [0 ] - plt . xlim () [0 ])
441
- dx2 = abs (self ._last_hkl_plot_xlim [1 ] - plt . xlim () [1 ])
450
+ if self ._plot_hkl and self ._last_hkl_plot_xlim is not None and len ( self . _plot_fig . axes ) :
451
+ ax = self . _plot_fig . axes [ 0 ]
452
+ self ._plot_xlim = ax .get_xlim ()
453
+ dx1 = abs (self ._last_hkl_plot_xlim [0 ] - self . _plot_xlim [0 ])
454
+ dx2 = abs (self ._last_hkl_plot_xlim [1 ] - self . _plot_xlim [1 ])
442
455
if max (dx1 , dx2 ) > 0.1 * (self ._last_hkl_plot_xlim [1 ] - self ._last_hkl_plot_xlim [0 ]):
443
456
# Need to update the hkl list
444
457
self ._do_plot_hkl ()
0 commit comments