1

I've come across a really annoying difference between how windows and mac handles a python tk window and matplotlib figure close_event.

My problem is thus,

  1. I am trying to load a matplotlib figure from a tk button event.
  2. I want the figure to show, and block the tk UI program flow while the plot is active, and capturing user events until the plot is closed.
  3. After the plot is closed the tk app should continue.

Minimal example app showing issue.

from Tkinter import *
from matplotlib import pyplot as plt


class Plotter:
    def __init__(self):
        self.fig = plt.figure()
        self.fig.canvas.mpl_connect('close_event', self.dispose)

        plt.plot(1, 2, 'r*')
        plt.show()
        print "done with plotter"

    def dispose(self, event):
        plt.close('all')
        print "disposed"


if __name__ == '__main__':
    def pressed():
        print 'button pressed'
        Plotter()
        print 'YAY'

    root = Tk()
    button = Button(root, text='Press', command=pressed)
    button.pack(pady=20, padx=20)

    root.mainloop()

Sadly, I found this works as expected in windows but not on mac using the same versions of python2.7, matplotlib (1.5.2). Apart from the fact that this is not good UI practice, it bothers me that there is a difference on Mac and Windows for this piece of code. I would appreciate any feedback that would help with this issue, in the mean time i'll start work on implementing the plotter on a thread which is non-blocking and passing the result back to the main app when closed.

1 Answers1

1

You can use plt.ion() to turn on Matplotlib's interactive mode, but this by itself will cause the program to continue without blocking the flow. To manually block the flow, use self.fig.canvas.start_event_loop_default() and self.fig.canvas.stop_event_loop() to pause the program flow until events are captured.

Implemented in your minimal example:

from Tkinter import *
from matplotlib import pyplot as plt

class Plotter:
    def __init__(self):
        plt.ion()
        self.fig = plt.figure()
        self.fig.canvas.mpl_connect('close_event', self.dispose)
        self.fig.canvas.mpl_connect('button_press_event', self.on_mouse_click)

        plt.plot(1, 2, 'r*')
        plt.show()
        self.fig.canvas.start_event_loop_default()
        print "done with plotter"

    def dispose(self, event):
        self.fig.canvas.stop_event_loop()
        print "disposed"

    def on_mouse_click(self, event):
        print 'mouse clicked!'


if __name__ == '__main__':
    def pressed():
        print 'button pressed'
        Plotter()
        print 'YAY'

root = Tk()
button = Button(root, text='Press', command=pressed)
button.pack(pady=20, padx=20)

root.mainloop()
Nemo
  • 50
  • 1
  • 8