Embedding matplotlib canvas into tkinter GUI - plot is not showing up, but no error is thrown

2024/10/15 21:23:46

Running the python python script below does not show the embedded matplotlib plot. However it also throws no error message. Upon running the script, it is supposed to display a GUI displaying 4 buttons on the left hand side and a realtime graph on the right hand side. The graph receives its input from a text file 'sample_graph_data.txt', which is in the same directory as the script. What's wrong in the script and how do I make it work?

#Script begins here
from tkinter import * 
from tkinter import messagebox
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import animation
from matplotlib import style
from matplotlib.figure import Figure
PROGRAM_NAME = 'Smart Farm Controller'
style.use('ggplot')fig = Figure(figsize=(5, 30), dpi=100)
a = fig.add_subplot(111)class Controller:def __init__(self, root):self.root = rootself.root.title(PROGRAM_NAME)self.root.protocol('WM_DELETE_WINDOW', self.exit_app)self.init_gui()def create_right_graphs(self):right_frame = Frame(self.root)right_frame.grid(row=2, column=6, sticky=N+E+W+S,padx=2, pady=2)anim = animation.FuncAnimation(fig, self.animate_graph(right_frame),interval=1000)def create_left_switches(self):left_frame = Frame(self.root)left_frame.grid(row=2, column=1, columnspan=6, sticky=N+E+W+S,padx=2, pady=2)led_button = Button(left_frame, text='LED') #command=self.on_led_button_clicked)led_button.config(height=2, width=30)led_button.grid(row=2, column=0, padx=4, pady=8)apump_button = Button(left_frame, text='Air Pump') #command=self.on_apump_button_clicked)apump_button.config(height=2, width=30)apump_button.grid(row=3, column=0, padx=4, pady=8)wpump_res_button = Button(left_frame, text='Reservoir Water Pump')#command=self.on_wpump_res_button_clicked)wpump_res_button.config(height=2, width=30)wpump_res_button.grid(row=4, column=0, padx=4, pady=8)wpump_grow_button = Button(left_frame, text='Grow Bucket Water Pump')#command=self.on_wpump_grow_button_clicked)wpump_grow_button.config(height=2, width=30)wpump_grow_button.grid(row=5, column=0, padx=4, pady=8)def animate_graph(self, right_frame):pullData = open("sample_graph_data.txt","r").read()dataList = pullData.split('\n')xList = []yList = []for eachLine in dataList:if len(eachLine)>1:x, y = eachLine.split(',')xList.append(int(x))yList.append(int(x))a.clear()a.plot(xList, yList)canvas = FigureCanvasTkAgg(fig, right_frame)canvas.show()canvas.get_tk_widget().pack(side=RIGHT, fill=BOTH, expand=True)def init_gui(self):self.create_right_graphs()self.create_left_switches()def exit_app(self):if messagebox.askokcancel("Quit", "Really quit?"):self.root.destroy()if __name__ == '__main__':root = Tk()Controller(root)root.mainloop()
Answer

There are indeed no errors triggered in the code from the question when being run, but it's also not running as expected. Some errors simply don't get triggered, because the respective part of the code is never called.
There are several issues:

  • You need to actually add the FigureCanvas to the Frame outside the animation.
  • Always keep a reference to the objects you want to work on. Especially the animation must stay alive. This is best be done by making it a class attribute using self.
  • You need to actually place the frames to the root window. This can be done using pack.
  • Inside the FuncAnimation you must not call the function to animate but simply provide it as argument. Also, you need to provide some number of frames to animate for the animation to start. FuncAnimation(fig, self.animate_graph, frames=12) instead of FuncAnimation(fig, self.animate_graph(someargument))
  • The animating function needs an argument which is the framenumber (or the list entry from a list that is given by the frames argument). You may provide further arguments if needed (in that case refer to the documentation).

I'm sure I forgot to mention some other things as well. But here is a running code.

from tkinter import * 
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import animation
from matplotlib import style
from matplotlib.figure import Figure
style.use('ggplot')fig = Figure(figsize=(5, 4), dpi=100)
a = fig.add_subplot(111)class Controller:def __init__(self, root):self.root = rootself.create_left_switches()self.create_right_graphs()    def create_right_graphs(self):right_frame = Frame(self.root)right_frame.grid(row=2, column=6, sticky=N+E+W+S,padx=2, pady=2)right_frame.pack(fill=X, padx=5, pady=5)self.canvas = FigureCanvasTkAgg(fig,right_frame ) self.canvas.get_tk_widget().pack(side=RIGHT, fill=BOTH, expand=True)self.anim = animation.FuncAnimation(fig, self.animate_graph, frames=12,interval=500, repeat=True)self.canvas.show()def create_left_switches(self):left_frame = Frame(self.root)left_frame.grid(row=2, column=1, columnspan=6, sticky=N+E+W+S,padx=2, pady=2)left_frame.pack(fill=X, padx=5, pady=5)led_button = Button(left_frame, text='LED') #command=self.on_led_button_clicked)led_button.config(height=2, width=30)led_button.grid(row=2, column=0, padx=4, pady=8)def animate_graph(self, i):pullData = open("sample_graph_data.txt","r").read()dataList = pullData.split('\n')xList = []yList = []for eachLine in dataList:if len(eachLine)>1:x, y = eachLine.split(',')xList.append(int(x))yList.append(int(y)**(1+i/12.))a.clear()a.plot(xList, yList)if __name__ == '__main__':root = Tk()Controller(root)root.mainloop()
https://en.xdnf.cn/q/117789.html

Related Q&A

Can I get rid of this b character in my print statement? [duplicate]

This question already has answers here:What does a b prefix before a python string mean?(2 answers)Closed 7 years ago.Im wondering what this b charcter is and why its appearing. Im also wondering if I…

Python tkinter: configure multiple labels with a loop

I have a window with multiple labels. Instead of configuring each label individually, I want to use a for loop to configure them.Basically, what I get from the below code is all labels are showing the …

How do you create a sitemap index in Django?

The Django documentation is very minimal and I cant seem to get this to work.Currently I have 3 individual sitemaps, and I would like to create a sitemap index for them: (r^sitemap1\.xml$, django.contr…

Numpy Append to Array

I’m building a project for the Raspberry Pi that turns a relay on and off random times in a specific time window. To manage the time slots, I want to use a two-dimensional array that’s generated dail…

Output of numpy.diff is wrong

heres my problem: I am trying to use numpy to compute (numerical) derivatives, but I am finding some problems in the value the function numpy.diff returns (and numpy.gradient as well). What I find is t…

Calculate Fibonacci numbers up to at least n

I am trying to make a function that allows a user to input a number and the result will be a list containing Fibonacci numbers up to the input and one above if the input is not in the series. For examp…

IEC 62056-21, implement the protocol over a gsm connection

The protocol IEC 62056:21 tells us how to deal with enegy meters, its quite easy!The part where I am stuck is the implementation over a GSM data channel. Normally I would set things like:300 baudrate …

Cannot install plyfile in Anaconda

When u=i try to run the commandconda install plyfilein windows command promptFetching package metadata ...........PackageNotFoundError: Package not found: Package missing in current win-64 channels: -…

Expected singleton: stock.move - Odoo v9 community

Im creating a stock.picking from fleet_vehicle_log_services with this method:@api.multi def create_picking(self):self.ensure_one()vals = {move_lines: self.move_lines.id,origin: self.name}picking = self…

Python Kivy screen manager wiget scope

I am trying to control a screen manager from buttons in a separate class, but I cannot figure out what to set on the button on_press: statements.Kivy file:<HeaderSection>:anchor_x: centeranchor_y…