My problem is the following:
I create a Matplotlib figure including some widget sliders and a button to close the figure. This works. What can I do to use this code inside a function which returns e.g. the values of the slides AFTER clicking the "close figure" button?
Here is the code (im3d is a 3d image numpy array):
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
class IndexTracker(object):
def __init__(self, ax, data3d, title):
self.ax = ax
ax.set_title(title)
self.data3d = data3d
rows, cols, self.slices = data3d.shape
self.ind = self.slices//2
self.im = ax.imshow(self.data3d[:, :, self.ind])
self.update()
def update(self):
self.im.set_data(self.data3d[:, :, self.ind])
ax.set_ylabel('slice %s' % self.ind)
self.im.axes.figure.canvas.draw()
#
fig = plt.figure(figsize=(18, 8), dpi=80, facecolor='w', edgecolor='b')
ax = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)
tracker1 = IndexTracker(ax, im3d, 'Select First Image')
tracker2 = IndexTracker(ax2, im3d, 'Select Last Image')
def slider_changed(value, tracker):
numb = int(round(value))
tracker.ind = numb
tracker.update()
max0 = im3d.shape[2] -1
ax_start = fig.add_axes([0.1, 0.85, 0.35, 0.03])
sl_start = Slider(ax_start, 'START', 0, max0, valinit=0, valfmt="%i")
ax_end = fig.add_axes([0.6, 0.85, 0.35, 0.03])
sl_end = Slider(ax_end, 'END', 0, max0, valinit=0, valfmt="%i")
def sl_start_changed(val):
slider_changed(sl_start.val,tracker1)
def sl_end_changed(val):
slider_changed(sl_end.val,tracker2)
sl_start.on_changed(sl_start_changed)
sl_end.on_changed(sl_end_changed)
class Index(object):
def close_figure(self, event):
plt.close(fig)
callback = Index()
ax_button = fig.add_axes([0.7, 0.06, 0.15, 0.075])
button = Button(ax_button, 'DONE')
button.on_clicked(callback.close_figure)
fig.canvas.manager.window.raise_()
plt.plot()
My first idea was to run a while loop after plt.plot(), something like this:
while not_done:
time.sleep(0.5)
and change not_done to False inside the function close_figure. But in this case, the plot doesn't show.
The slider is still available after the figure is closed. Hence you can just access its val
attribute after closing the figure
fig, ax = plt.subplots()
slider = Slider(...)
# .. callbacks
plt.show() # you may use plt.show(block=True) when this is run in interactive mode
print(slider.val)
Edit after clarifications:
You are running spyder and use the IPython console within. This has several implications.
plt.ioff()
Then the following code runs fine and only prints the values after the figure is closed.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
plt.ioff()
im3d = np.random.rand(20,20,10)
class IndexTracker(object):
def __init__(self, ax, data3d, title):
self.ax = ax
ax.set_title(title)
self.data3d = data3d
rows, cols, self.slices = data3d.shape
self.ind = self.slices//2
self.im = ax.imshow(self.data3d[:, :, self.ind])
self.update()
def update(self):
self.im.set_data(self.data3d[:, :, self.ind])
ax.set_ylabel('slice %s' % self.ind)
self.im.axes.figure.canvas.draw()
#
fig = plt.figure(figsize=(18, 8), dpi=80, facecolor='w', edgecolor='b')
ax = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)
tracker1 = IndexTracker(ax, im3d, 'Select First Image')
tracker2 = IndexTracker(ax2, im3d, 'Select Last Image')
def slider_changed(value, tracker):
numb = int(round(value))
tracker.ind = numb
tracker.update()
max0 = im3d.shape[2] -1
ax_start = fig.add_axes([0.1, 0.85, 0.35, 0.03])
sl_start = Slider(ax_start, 'START', 0, max0, valinit=0, valfmt="%i")
ax_end = fig.add_axes([0.6, 0.85, 0.35, 0.03])
sl_end = Slider(ax_end, 'END', 0, max0, valinit=0, valfmt="%i")
def sl_start_changed(val):
slider_changed(sl_start.val,tracker1)
def sl_end_changed(val):
slider_changed(sl_end.val,tracker2)
sl_start.on_changed(sl_start_changed)
sl_end.on_changed(sl_end_changed)
class Index(object):
def close_figure(self, event):
plt.close(fig)
callback = Index()
ax_button = fig.add_axes([0.7, 0.06, 0.15, 0.075])
button = Button(ax_button, 'DONE')
button.on_clicked(callback.close_figure)
fig.canvas.manager.window.raise_()
plt.show()
print(sl_start.val, sl_end.val)
Alternatively, you may just run the complete code in external command line, without the need for plt.ioff()