0

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.

Mr. T
  • 11,960
  • 10
  • 32
  • 54
gruenkohl
  • 103
  • 1
  • 8

1 Answers1

1

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.

  • You need to turn interactive mode off, plt.ioff()
  • You need to deactivate "support" for matplotlib

enter image description here

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()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thanks, this I know. But how do I get the program to halt after plt.show(), let the user interact with the figure and proceed with print(slider.val) only after the figure closing? – gruenkohl Jul 27 '18 at 20:30
  • The value is only printed after the figure is closed. – ImportanceOfBeingErnest Jul 27 '18 at 22:02
  • Not on my machine. If I wrap the above code in def my_function(im3d): ....... return sl_start.val,sl_end.val The function returns before I close the figure. – gruenkohl Jul 29 '18 at 08:02
  • What happens if you put `plt.ioff()` in the beginning of the code? In general the more we know about where and how you run the code, which versions are is use etc the higher the probability for a solution that works for you. – ImportanceOfBeingErnest Jul 29 '18 at 10:19
  • With plt.ioff() the plot doesn't show at all. I use python 3.6.3 and IPython 6.1.0. – gruenkohl Jul 30 '18 at 07:53
  • It might have to do with my Graphics backend, (I use Qt5 in Spyder). This example https://matplotlib.org/examples/widgets/lasso_selector_demo.html should do something similar to what I want. If I run this code, the matplotlib figure window opens, but doesn't render and the coder after plt.draw() executes. – gruenkohl Jul 30 '18 at 09:32
  • Thank you very much! That worked. The example in my last comment is still not running but nevermind... – gruenkohl Jul 30 '18 at 11:15
  • The example should at least work as it is with the "Execute in external system terminal" option. If it isn't that may be a different issue. – ImportanceOfBeingErnest Jul 30 '18 at 11:28