0

I have the following code that simply embeds a matplotlib plot inside a tab in tkinter, then connects an onclick event for any mouse click to print some data:

import tkinter as tk
from tkinter import ttk

from matplotlib.figure import Figure
import matplotlib.pyplot as plt

from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)

import numpy as np

def onclick(event):
        print('%s click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
              ('double' if event.dblclick else 'single', event.button,
               event.x, event.y, event.xdata, event.ydata))

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

tabs = ttk.Notebook(root)

tab = ttk.Frame(tabs)

fig = Figure(figsize=(5, 4), dpi=100)
t = np.arange(0, 3, .01)
ax = fig.add_subplot(111)
ax.plot(t, 2 * np.sin(2 * np.pi * t))

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

toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
tabs.add(tab, text="Tab one")
tabs.pack(expand=1, fill='both')

fig.canvas.mpl_connect("button_press_event", onclick)


root.mainloop()

Which all works fine, however if the line fig.canvas.mpl_connect("button_press_event", onclick) is placed after the root.mainloop() line, the clicking function no longer works on the embedded tab. I wanted to know why this is. Below is the code that reproduces the issue:

import tkinter as tk
from tkinter import ttk

from matplotlib.figure import Figure
import matplotlib.pyplot as plt

from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)

import numpy as np

def onclick(event):
        print('%s click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
              ('double' if event.dblclick else 'single', event.button,
               event.x, event.y, event.xdata, event.ydata))
root = tk.Tk()
root.wm_title("Embedding in Tk")

tabs = ttk.Notebook(root)

tab = ttk.Frame(tabs)

fig = Figure(figsize=(5, 4), dpi=100)
t = np.arange(0, 3, .01)
ax = fig.add_subplot(111)
ax.plot(t, 2 * np.sin(2 * np.pi * t))

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

toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
tabs.add(tab, text="Tab one")
tabs.pack(expand=1, fill='both')

root.mainloop()

fig.canvas.mpl_connect("button_press_event", onclick)

The part that confuses me is I went out of my way to create a realistic example of when the fig.canvas.mpl_connect("button_press_event", onclick) might be placed after the root.mainloop() , and wrote this:

import tkinter as tk
from tkinter import ttk

from matplotlib.figure import Figure
import matplotlib.pyplot as plt

from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)

import numpy as np

def onclick(event):
        print('%s click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
              ('double' if event.dblclick else 'single', event.button,
               event.x, event.y, event.xdata, event.ydata))

def open_plot():

    tab = ttk.Frame(tabs)

    fig = Figure(figsize=(5, 4), dpi=100)
    t = np.arange(0, 3, .01)
    ax = fig.add_subplot(111)
    ax.plot(t, 2 * np.sin(2 * np.pi * t))

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

    toolbar = NavigationToolbar2Tk(canvas, root)
    toolbar.update()
    canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
    tabs.add(tab, text="Tab one")
    tabs.pack(expand=1, fill='both')

    fig.canvas.mpl_connect("button_press_event", onclick)


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

tabs = ttk.Notebook(root)

menubar = tk.Menu(root)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="Open", command=open_plot)
menubar.add_cascade(label="File", menu=filemenu)

root.config(menu=menubar)

root.mainloop()

But in this example, the onclick function now works again as expected. Would anyone be able to explain why this is?

1 Answer 1

1

if the line fig.canvas.mpl_connect("button_press_event", onclick) is placed after the root.mainloop() line, the clicking function no longer works on the embedded tab. I wanted to know why this is.

The "why" is because tkinter is single-threaded, and because mainloop will not return until the root window is destroyed or you call quit() on the root window. This is by design - mainloop is an infinite loop that is designed to continuously wait for and then respond to events.

Sign up to request clarification or add additional context in comments.

1 Comment

Ah, seems obvious now to be honest. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.