Skip to content

Commit e1d145a

Browse files
committed
PowderPattern: fix re-using a matplotlib figure when plotting, add 'figure' property.
1 parent 3cc4884 commit e1d145a

File tree

1 file changed

+58
-45
lines changed

1 file changed

+58
-45
lines changed

src/pyobjcryst/powderpattern.py

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
See the online ObjCryst++ documentation (http://vincefn.net/ObjCryst/).
1616
1717
Changes from ObjCryst::PowderPattern::
18-
In development !
18+
Additional functions for plotting, basic QPA and profile fitting.
1919
"""
2020

2121
from urllib.request import urlopen
@@ -65,10 +65,19 @@ def UpdateDisplay(self):
6565
except:
6666
pass
6767
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
7281

7382
def disable_display_update(self):
7483
""" Disable display (useful for multiprocessing)"""
@@ -118,38 +127,32 @@ def plot(self, diff=None, hkl=None, figsize=(9, 4), fontsize_hkl=6, reset=False,
118127

119128
# TODO: handle other coordinates than angles (TOF)
120129
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)
130132
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)
134137
m = min(1, self.GetMaxSinThetaOvLambda() * self.GetWavelength())
135138
mtth = np.rad2deg(np.arcsin(m)) * 2
136139
if plot_diff:
137140
diff = calc - obs - obs.max() / 20
138141
# Mask difference above max sin(theta)/lambda
139142
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)
142145

143-
plt.legend(loc='upper right')
146+
ax.legend(loc='upper right')
144147
if self.GetName() != "":
145-
plt.title("PowderPattern: %s" % self.GetName())
148+
self._plot_fig.title("PowderPattern: %s" % self.GetName())
146149

147150
if self._plot_ylim is not None:
148-
plt.ylim(self._plot_ylim)
151+
ax.set_ylim(self._plot_ylim)
149152
if self._plot_xlim is not None:
150-
plt.xlim(self._plot_xlim)
153+
ax.set_xlim(self._plot_xlim)
151154
elif m < 1:
152-
plt.xlim(x.min(), mtth)
155+
ax.set_xlim(x.min(), mtth)
153156

154157
if plot_hkl:
155158
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,
173176
if comp.GetExtractionMode():
174177
s += "[Le Bail mode]"
175178
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])
178181
iphase += 1
179182

180183
if 'inline' not in plt.get_backend():
181184
try:
182185
# 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()
185188
if 'ipympl' not in plt.get_backend():
186189
plt.pause(.001)
187190
except:
@@ -198,24 +201,25 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
198201
fontsize_hkl = self._plot_hkl_fontsize
199202
else:
200203
self._plot_hkl_fontsize = fontsize_hkl
204+
ax = self._plot_fig.axes[0]
201205
# Plot up to nb_max hkl reflections
202206
obs = self.GetPowderPatternObs()
203207
calc = self.GetPowderPatternCalc()
204208
x = np.rad2deg(self.GetPowderPatternX())
205209
# Clear previous text (assumes only hkl were printed)
206210
if version_parse(mpl_version) < version_parse("3.7.0"):
207211
# This will fail for matplotlib>=(3,7,0)
208-
plt.gca().texts.clear()
212+
ax.texts.clear()
209213
else:
210-
for t in plt.gca().texts:
214+
for t in ax.texts:
211215
t.remove()
212216
iphase = 0
213217
for ic in range(self.GetNbPowderPatternComponent()):
214218
c = self.GetPowderPatternComponent(ic)
215219
if isinstance(c, PowderPatternDiffraction) is False:
216220
continue
217221
# print("HKL for:", c.GetName())
218-
xmin, xmax = plt.xlim()
222+
xmin, xmax = ax.get_xlim()
219223
vh = np.round(c.GetH()).astype(np.int16)
220224
vk = np.round(c.GetK()).astype(np.int16)
221225
vl = np.round(c.GetL()).astype(np.int16)
@@ -228,12 +232,12 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
228232
renderer = plt.gcf().canvas.renderer
229233
except:
230234
# 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()
233237
if 'ipympl' not in plt.get_backend():
234238
plt.pause(.001)
235239
try:
236-
renderer = plt.gcf().canvas.renderer
240+
renderer = self._plot_fig.canvas.renderer
237241
except:
238242
renderer = None
239243
else:
@@ -242,7 +246,6 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
242246
props = {'ha': 'center', 'va': 'bottom'}
243247
ct = 0
244248
last_bbox = None
245-
ax = plt.gca()
246249
tdi = ax.transData.inverted()
247250
for i in range(len(vh)):
248251
xhkl = np.rad2deg(self.X2XCorr(self.STOL2X(stol[i])))
@@ -258,8 +261,8 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
258261

259262
ihkl = max(calc[idxhkl], obs[idxhkl])
260263
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])
263266
if renderer is not None:
264267
# Check for overlap with previous
265268
bbox = t.get_window_extent(renderer)
@@ -270,12 +273,22 @@ def _do_plot_hkl(self, nb_max=100, fontsize_hkl=None):
270273
t.set_y(ihkl + b.height * 1.2)
271274
last_bbox = t.get_window_extent(renderer)
272275
iphase += 1
273-
self._last_hkl_plot_xlim = plt.xlim()
276+
self._last_hkl_plot_xlim = ax.get_xlim()
274277
if self._plot_phase_labels is not None:
275278
for iphase in range(len(self._plot_phase_labels)):
276279
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
279292

280293
def quick_fit_profile(self, pdiff=None, auto_background=True, init_profile=True, plot=True,
281294
zero=True, constant_width=True, width=True, eta=True, backgd=True, cell=True,
@@ -434,11 +447,11 @@ def _on_mouse_event(self, event):
434447
self.plot()
435448

436449
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])
442455
if max(dx1, dx2) > 0.1 * (self._last_hkl_plot_xlim[1] - self._last_hkl_plot_xlim[0]):
443456
# Need to update the hkl list
444457
self._do_plot_hkl()

0 commit comments

Comments
 (0)