";require "../templates/head_jq_bs4.php";echo "
";$img_path="..";require "top-link-tkinter.php";require "templates/top_bs4.php";echo "
A Tkinter Treeview can display data even when the number and names of columns are not known while writing the GUI. We can read the column names from CSV, Excel, Pandas, MySQL, SQLite or another source and create the Treeview columns dynamically.
The useful pattern is the same for every source:
headers=[...]rows=[...]configure_treeview(tree, headers, rows)The data-reading code changes, but the Treeview-building code remains the same.
Sample Student Data: Excel, CSV, MySQL, SQLite and PandasA database query can return a different number of columns depending on the selected fields.
SELECT id, name FROM studentSELECT id, name, class FROM studentSELECT * FROM studentThe first query returns two columns, the second returns three, and the third returns all columns in the table.
Instead of hard-coding five Treeview columns, Python can inspect the returned column names and build the interface automatically.

Regardless of whether the source is CSV, Excel, Pandas or a database, convert the result into:
headers=['id', 'Name', 'Class', 'Mark', 'Gender']rows=[ (1, 'Alex', 'Four', 75, 'Male'), (2, 'Ravi', 'Three', 65, 'Male')]The Treeview function does not need to know where those two objects came from.
headers and rows; one function handles the Treeview.import tkinter as tkfrom tkinter import ttkroot=tk.Tk()root.geometry('700x350')root.title('Dynamic Treeview - plus2net')tree=ttk.Treeview(root, show='headings', selectmode='browse')tree.grid(row=0, column=0, sticky='nsew')show='headings' is appropriate for flat tabular data because the special Treeview column #0 is not required.
selectmode='browse' means that one item can normally be selected at a time. It does not provide scrolling.
The function below clears existing Treeview rows, creates safe internal column identifiers, adds the visible headings and inserts the supplied rows.
def configure_treeview(tree, headers, rows): for item in tree.get_children(): tree.delete(item) column_ids=[f'col{i}' for i in range(len(headers))] tree['columns']=column_ids tree['show']='headings' for column_id, heading in zip(column_ids, headers): heading_text='' if heading is None else str(heading) tree.heading(column_id, text=heading_text) tree.column(column_id, width=110, minwidth=60, anchor='center') for row in rows: values=list(row) if len(values)<len(column_ids): values+= ['']*(len(column_ids)-len(values)) tree.insert('', tk.END, values=values[:len(column_ids)])A source can contain duplicate or blank headings:
ID | Name | Mark | Mark |Using those values directly as Treeview column identifiers can create problems. Instead:
col0col1col2col3col4are used internally, while the source headings remain the visible labels.
The old pattern:
tree.insert('', tk.END, iid=row[0], values=row)assumes that the first value is always unique and non-empty.
For a general-purpose dynamic-data tutorial, it is safer to omit iid and let Treeview generate its own item IDs.
Using SQLAlchemy, the result provides both column names and rows.
from sqlalchemy import create_engine, textengine=create_engine('mysql+mysqldb://userid:password@localhost/my_db')with engine.connect() as conn: result=conn.execute(text('SELECT * FROM student')) headers=list(result.keys()) rows=[tuple(row) for row in result]Use your actual database credentials and the database driver configured for your project.
MySQL DatabaseSELECT id, name FROM studentproduces two Treeview columns, while:
SELECT id, name, class, mark FROM studentproduces four. The rendering function does not change.
from sqlalchemy import create_engine, textengine=create_engine('sqlite:///my_db.db')with engine.connect() as conn: result=conn.execute(text('SELECT * FROM student')) headers=list(result.keys()) rows=[tuple(row) for row in result]SQLite Database import pandas as pddf=pd.read_excel('student.xlsx')headers=df.columns.tolist()rows=list(df.itertuples(index=False, name=None))df.columns.tolist() creates the header list. itertuples() creates the row data without adding the DataFrame index.
from openpyxl import load_workbookwb=load_workbook('student.xlsx', read_only=True, data_only=True)ws=wb.activerow_iter=ws.iter_rows(values_only=True)headers=next(row_iter)rows=list(row_iter)wb.close()The first worksheet row becomes the headings and the remaining rows become Treeview data.
OpenPyXL Excel Tutorials Excel Data in Treeviewimport csvwith open('student.csv', newline='', encoding='utf-8-sig') as file: reader=csv.reader(file) headers=next(reader) rows=list(reader)The with statement closes the CSV file automatically after reading.
Before connecting a real data source, the dynamic logic can be tested with ordinary Python lists.
headers=['id', 'Name', 'Class', 'Mark', 'Gender']rows=[ [1, 'Alex', 'Four', 75, 'Male'], [2, 'Ravi', 'Three', 65, 'Male']]configure_treeview(tree, headers, rows)The reusable function begins by deleting existing items:
for item in tree.get_children(): tree.delete(item)It then assigns a new column list and inserts the new rows.
This means the same Treeview can first show:
id | nameand later be rebuilt as:
id | name | class | mark | genderwithout creating another Treeview widget.
The Treeview height option controls the approximate number of visible rows:
tree['height']=8It does not limit how many items can be inserted.
For datasets containing more rows or columns, add scrollbars:
ys=ttk.Scrollbar(root, orient='vertical', command=tree.yview)xs=ttk.Scrollbar(root, orient='horizontal', command=tree.xview)tree.configure(yscrollcommand=ys.set, xscrollcommand=xs.set)selectmode='browse' controls row selection. It does not enable or configure scrolling.This complete example uses a simple list as the data source. Replace the headers and rows section with any of the MySQL, SQLite, Pandas, Excel or CSV examples above.
import tkinter as tkfrom tkinter import ttkdef configure_treeview(tree, headers, rows): for item in tree.get_children(): tree.delete(item) column_ids=[f'col{i}' for i in range(len(headers))] tree['columns']=column_ids tree['show']='headings' for column_id, heading in zip(column_ids, headers): heading_text='' if heading is None else str(heading) tree.heading(column_id, text=heading_text) tree.column(column_id, width=110, minwidth=60, anchor='center') for row in rows: values=list(row) if len(values)<len(column_ids): values+=['']*(len(column_ids)-len(values)) tree.insert('', tk.END, values=values[:len(column_ids)])headers=['id', 'Name', 'Class', 'Mark', 'Gender']rows=[ (1, 'Alex', 'Four', 75, 'Male'), (2, 'Ravi', 'Three', 65, 'Male'), (3, 'Ron', 'Five', 80, 'Male')]root=tk.Tk()root.geometry('700x350')root.title('Dynamic Treeview - plus2net')root.rowconfigure(0, weight=1)root.columnconfigure(0, weight=1)tree=ttk.Treeview(root, show='headings', selectmode='browse', height=8)tree.grid(row=0, column=0, sticky='nsew', padx=(10, 0), pady=(10, 0))ys=ttk.Scrollbar(root, orient='vertical', command=tree.yview)ys.grid(row=0, column=1, sticky='ns', pady=(10, 0))xs=ttk.Scrollbar(root, orient='horizontal', command=tree.xview)xs.grid(row=1, column=0, sticky='ew', padx=(10, 0))tree.configure(yscrollcommand=ys.set, xscrollcommand=xs.set)configure_treeview(tree, headers, rows)root.mainloop()The same technique is useful when the user changes a filter and a new database query returns another set of rows.
For example, a Combobox can select a month, a SQL query can retrieve matching records, and the existing Treeview can be cleared and filled with the new result.
See the related monthly Treeview report tutorial for the complete project.
Use the source metadata or first row to build the column list instead.
Duplicate or blank headings can cause problems. Use internal identifiers such as col0, col1 and col2.
The first value may be duplicated or empty. Let Treeview generate item IDs unless the source contains a reliable unique key.
Before displaying a replacement dataset:
for item in tree.get_children(): tree.delete(item)selectmode='browse' is a selection setting. Use Treeview scrollbars for scrolling.
Use a connection context manager:
with engine.connect() as conn: ...Use:
with open('student.csv') as file: ...Avoid:
D:\student.xlsxE:\testing\sqlite\my_db.dbUse project-relative paths where possible.
The examples use lists because the sample student data is small. For very large data sources, use pagination, SQL limits or incremental loading rather than placing every record in one Treeview.
See Treeview pagination with MySQL for one approach.
headers + rows structure.col0 prevent duplicate-header problems.iid.result.keys().df.columns.height controls the visible row count, not the total number of records.selectmode='browse' controls selection, not scrolling.