Search code examples
pythonmatplotlibtkinterinteractivemplcursors

mplcursors not getting generated when used with Tkinter. What am I doing wrong?


Using mplcursors, I'm not able to add cursors to the plot when plot matplotlib is embedded in tkinter gui. It appears that mouse clicks are not registering. However zoom, pan functions work fine. What am I missing?

I use Anaconda Python 3.7.3, matplotlib 3.0.3, and mplcursors library. https://mplcursors.readthedocs.io/en/stable/#

I am able replicate simple example without tkinter and add interactive cursors as needed. I am also able to embed matplotlib plot in tkinter application. However when both are combined - even though plot is shown without errors, interactive cursors are not working.

import tkinter

from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
import matplotlib.pyplot as plt

import numpy as np
import mplcursors


root = tkinter.Tk()
root.wm_title("Embedding in Tk")


t = np.arange(0, 3, .01)

fig, ax = plt.subplots(1, 1)
line = ax.plot(t, 2 * np.sin(2 * np.pi * t))

mplcursors.cursor(line)

canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)

toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)


def on_key_press(event):
    print("you pressed {}".format(event.key))
    key_press_handler(event, canvas, toolbar)


canvas.mpl_connect("key_press_event", on_key_press)


def _quit():
    root.quit()
    root.destroy()  

button = tkinter.Button(master=root, text="Quit", command=_quit)
button.pack(side=tkinter.BOTTOM)

tkinter.mainloop()

I should be able to add and manipulate cursors like in example without tkinter.


Solution

  • Two problems:

    1. You should not use pyplot when embedding matplotlib in GUIs. Refer to the embedding in tk example.
    2. mplcursors needs to work with the figure canvas. So it can only be used once the canvas is defined. So that codeline needs to come after defining FigureCanvasTkAgg.

    Complete code:

    import tkinter
    
    from matplotlib.backends.backend_tkagg import (
        FigureCanvasTkAgg, NavigationToolbar2Tk)
    # Implement the default Matplotlib key bindings.
    from matplotlib.backend_bases import key_press_handler
    from matplotlib.figure import Figure
    
    import numpy as np
    import mplcursors
    
    
    root = tkinter.Tk()
    root.wm_title("Embedding in Tk")
    
    
    t = np.arange(0, 3, .01)
    
    fig = Figure()
    ax = fig.add_subplot(111)
    line = ax.plot(t, 2 * np.sin(2 * np.pi * t))
    
    canvas = FigureCanvasTkAgg(fig, master=root)
    
    mplcursors.cursor(line)
    
    canvas.draw()
    canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
    
    toolbar = NavigationToolbar2Tk(canvas, root)
    toolbar.update()
    canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
    
    
    def on_key_press(event):
        print("you pressed {}".format(event.key))
        key_press_handler(event, canvas, toolbar)
    
    
    canvas.mpl_connect("key_press_event", on_key_press)
    
    
    def _quit():
        root.quit()
        root.destroy()  
    
    button = tkinter.Button(master=root, text="Quit", command=_quit)
    button.pack(side=tkinter.BOTTOM)
    
    tkinter.mainloop()