
This tutorial shows how to display Pandas plots and Matplotlib charts inside a Tkinter application. We begin with a simple DataFrame, embed a pie chart in Tkinter, display multiple charts, and then create a dynamic chart using records from a MySQL or SQLite database.
Matplotlib provides FigureCanvasTkAgg, which places a Matplotlib Figure inside a Tkinter widget.
Pandas DataFrame
|
v
Matplotlib Figure
|
v
FigureCanvasTkAgg
|
v
Tkinter window
Show Table of Contents
The main libraries are Pandas, Matplotlib and Tkinter.
import tkinter as tk
from tkinter import ttk
import pandas as pd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
Figure creates the Matplotlib chart container. FigureCanvasTkAgg connects that Figure to Tkinter.
Create a Pandas DataFrame:
data={
'NAME':[
'Infant',
'Child',
'Young',
'Old'
],
'Nos':[
30,
40,
50,
50
]
}
df=pd.DataFrame(data)
Create a Matplotlib Figure and Axes:
fig=Figure(
figsize=(4,4),
dpi=100
)
ax=fig.add_subplot(
111
)
Pandas can draw directly on that Axes:
df.plot.pie(
y='Nos',
labels=df['NAME'],
title='Population',
legend=False,
ax=ax
)
import tkinter as tk
import pandas as pd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
data={'NAME':['Infant','Child','Young','Old'],
'Nos':[30,40,50,50]}
df=pd.DataFrame(data)
root=tk.Tk()
root.title('Pandas Pie Chart - plus2net')
fig=Figure(figsize=(4,4),dpi=100)
ax=fig.add_subplot(111)
df.plot.pie(
y='Nos',
labels=df['NAME'],
title='Population',
legend=False,
ax=ax
)
canvas=FigureCanvasTkAgg(fig,master=root)
canvas.draw()
canvas.get_tk_widget().pack(
fill=tk.BOTH,
expand=True
)
root.mainloop()
A Matplotlib Figure is not automatically a Tkinter widget.
The bridge is:
canvas=FigureCanvasTkAgg(
fig,
master=root
)
Draw the Figure:
canvas.draw()
Then obtain the Tkinter widget:
canvas.get_tk_widget()
This widget can use normal Tkinter layout methods such as grid() or pack().

Create separate Figures when two independent charts are needed.
import tkinter as tk
import pandas as pd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
root=tk.Tk()
root.title('Multiple Charts - plus2net')
population=pd.DataFrame({
'NAME':['Infant','Child','Young','Old'],
'Nos':[30,40,50,50]
})
marks=pd.DataFrame({
'NAME':['Ravi','Raju','Alex','Ron','Geek','Kim'],
'MARK':[20,30,40,30,40,50]
})
fig1=Figure(figsize=(4,3),dpi=100)
ax1=fig1.add_subplot(111)
population.plot.pie(
y='Nos',
labels=population['NAME'],
legend=False,
ax=ax1
)
fig2=Figure(figsize=(4,3),dpi=100)
ax2=fig2.add_subplot(111)
marks.plot(
x='NAME',
y='MARK',
marker='o',
legend=False,
ax=ax2
)
canvas1=FigureCanvasTkAgg(fig1,master=root)
canvas1.draw()
canvas1.get_tk_widget().grid(row=0,column=0)
canvas2=FigureCanvasTkAgg(fig2,master=root)
canvas2.draw()
canvas2.get_tk_widget().grid(row=1,column=0)
root.mainloop()

The next example reads student records from MySQL or SQLite. A Combobox contains the available class names. When the user selects a class, Pandas loads the names and marks for that class and the existing chart is redrawn.
The sample table structure is the same student table used in several Plus2net database tutorials.
Create the SQLAlchemy Engine:
engine=create_engine(
'sqlite:///my_db.db'
)
For MySQL:
engine=create_engine(
'mysql+mysqldb://root:password@localhost/my_tutorial'
)
Use a SQLAlchemy 2.x connection to read the distinct class values:
class_sql=text(
'SELECT DISTINCT class '
'FROM student '
'WHERE class IS NOT NULL '
'ORDER BY class'
)
with engine.connect() as conn:
result=conn.execute(
class_sql
)
class_list=[
row[0]
for row in result
]
This replaces the older:
engine.execute(query)
pattern.
The old code constructed SQL like this:
query="SELECT name,mark FROM student WHERE class='"+sel.get()+"'"
Application values should not be joined directly into SQL text.
Use a parameter:
student_sql=text(
'SELECT name,mark '
'FROM student '
'WHERE class=:class_name '
'ORDER BY name'
)
Then pass the selected value separately:
with engine.connect() as conn:
df=pd.read_sql(
student_sql,
conn,
params={
'class_name':selected_class
}
)
See Pandas read_sql() with database records and SQLAlchemy database connections for related examples.
The older example tracked a StringVar using:
sel.trace(
'w',
my_upd
)
Because the interaction comes from a Combobox, the more direct event is:
class_combo.bind(
'<<ComboboxSelected>>',
update_plot
)
The Combobox is also set to:
state='readonly'
so the user selects only one of the database values.
The old my_upd() function created another Figure and another FigureCanvasTkAgg every time the class changed.
Repeated selections could therefore create multiple overlapping chart widgets.
The revised application creates these once:
fig=Figure(...)
ax=fig.add_subplot(111)
canvas=FigureCanvasTkAgg(
fig,
master=chart_frame
)
For each update:
ax.clear()
then draw the new DataFrame:
ax.plot(
df['name'],
df['mark'],
marker='o'
)
Finally:
canvas.draw_idle()
The same Tkinter canvas now displays the updated chart.
The following example uses SQLite by default. Change DATABASE_URL to the MySQL connection string when using the same student table in MySQL.
The application logic does not need to change. Replace:
DATABASE_URL='sqlite:///my_db.db'
with the required MySQL SQLAlchemy URL:
DATABASE_URL=(
'mysql+mysqldb://'
'root:password@localhost/my_tutorial'
)
The same parameterized queries and Pandas DataFrame plotting logic can then be used.
Download Sample SQLite Database Student Table SQL DumpThe old approach:
"WHERE class='"+selected_class+"'"
mixes application values with the SQL statement.
The revised approach keeps them separate:
SQL:
WHERE class=:class_name
Parameters:
{
'class_name':selected_class
}
This is clearer, handles values correctly and avoids constructing SQL by concatenating user-controlled text.
The chart has three separate objects:
Figure
|
v
Axes
|
v
FigureCanvasTkAgg
Changing the selected class does not require creating these objects again.
Only the plotted data changes:
Combobox change
|
v
query database
|
v
new DataFrame
|
v
ax.clear()
|
v
draw new values
|
v
canvas.draw_idle()
This prevents new chart widgets from accumulating in the window.
The same dynamic application can display a pie chart instead of a line chart.
After loading plot_df, replace the line plotting section with:
ax.pie(
plot_df['mark'],
labels=plot_df['name'],
autopct='%1.0f%%'
)
ax.set_title(
f'Marks - Class {selected_class}'
)
Whether a pie chart is meaningful depends on the data being represented. For comparing student marks, a line or bar chart is often easier to compare precisely.
Build more Pandas DataFrame applications:
Tkinter Pandas ProjectsAnalyze CSV records with GroupBy and Pivot Tables:
Pandas Data AnalysisSearch DataFrame rows through Tkinter:
Search DataFrameDisplay database records in Treeview:
MySQL Records in TreeviewCreate a Matplotlib Figure and pass it to FigureCanvasTkAgg. The canvas provides a Tkinter widget that can be placed with grid() or pack().
Yes. Pandas plotting methods accept the ax parameter, allowing the DataFrame plot to use an Axes created for the Tkinter application.
Creating another canvas after every user selection can produce overlapping widgets and unnecessary Figure objects. Clearing and redrawing the existing Axes is more efficient.
Bind <<ComboboxSelected>> to a function that loads the selected data, clears the Axes, plots the new records and calls canvas.draw_idle().
Parameterized SQL keeps application values separate from the SQL statement and avoids constructing database queries by joining user-controlled strings.
Yes. SQLAlchemy provides Engines for both databases. The plotting and Tkinter code can remain the same when the table structure and SQL query are compatible.
The application clears the existing chart, displays a message on the Figure or status Label, and waits for another Combobox selection.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.