2

I tried to display different data in a pyplot depending on the selection from a Ttk.checkbutton. For simplicity, in the example I will only try to change the title.

import tkinter as tk
from tkinter import ttk
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# create data to plot
xdata = np.linspace(0, 100, 100)
ydata = 0.1*xdata ** 2

# create window
root = tk.Tk()

# create figure
fig = plt.Figure(figsize = (10, 3), dpi = 100)
ax = fig.add_subplot()
ax.plot(xdata, ydata)
plt.show()
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack()

i = 0
def change_title():
    global i
    i += 1
    ax.set_title(f'Title number {i}')
    print(f'Title changed to {i}')
    
change_title()
change_title()

checkbutton = ttk.Checkbutton(root, command=change_title).pack()

root.mainloop()

So the number in the plot title should increase every time when the change_title() function is called.

This works when the function is called directly from the code. However, checking and unchecking the checkbutton does call the function (and produces the expected output in the console), but it doesn't change the title. Executing the code above and checking/unchecking the checkbox for example 4 times will give the following result:

  • The title of the plot will be 'Title number 2'
  • The console will output 'Title changed to 6'

I don't understand why that happens. Why does the title change when I call the change_title() function directly, but not when it is called from the Checkbutton?

Markus L
  • 23
  • 5

1 Answers1

2

You need to redraw the canvas. Next to that, but not part of the problem, the plt.show() and first canvas.draw() are not needed.

The following is the revised code:

import tkinter as tk
from tkinter import ttk
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# create data to plot
xdata = np.linspace(0, 100, 100)
ydata = 0.1*xdata ** 2

# create window
root = tk.Tk()

# create figure
fig = plt.Figure(figsize = (10, 3), dpi = 100)
ax = fig.add_subplot()
ax.plot(xdata, ydata)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack()

i = 0
def change_title():
    global i
    i += 1
    ax.set_title(f'Title number {i}')
    print(f'Title changed to {i}')
    canvas.draw()
    
change_title()

checkbutton = ttk.Checkbutton(root, command=change_title).pack()

root.mainloop()
RCTW
  • 117
  • 8
  • Thank you very much, that solves the problem. I still don't know why the function works when it is not called by the button but at least the code is running as intendet now. – Markus L Nov 02 '22 at 00:38
  • 1
    You are welcome! It is because the in-code change_title() gets called before entering the root.mainloop(). This means that the canvas.draw() (flushing the underlying matplotlib figure to tkinter) is still queued for execution but is not actually executed yet. In the meanwhile, you update your axis title, and when canvas.draw() is finally called upon reaching root.mainloop(), draw() can use the latest plot version. In your original version however the canvas.draw() is not issued again after that, so the tkinter front does not receive the changed underlying plot. – RCTW Nov 02 '22 at 10:54