I have a table built from multiple Label widgets and laid out with the grid() method. I can group several columns in the header using rowspan and columnspan to organize the information in a certain way.
I can easily replicate most of it with a Treeview widget, but I don't know how to organize the header the same way as before. Is it possible to do so?
This picture shows both tables. In blue the header that I would like to replicate in the second table.

Code that displays both tables:
import tkinter as tk
from tkinter import ttk
from random import randint
COLUMNS = 10
ROWS = 5
class TableWithLabels:
def __init__(self, parent):
self.style = ttk.Style()
self.style.configure('Header.TLabel', borderwidth=1, background='#9EBDF0', relief=tk.GROOVE)
self.lbf = ttk.Labelframe(parent, text='Table with Label')
self.lbf.grid(row=0, column=0, sticky='NSWE')
self.lbf.rowconfigure(list(range(10)), weight=0)
self.lbf.columnconfigure(list(range(10)), weight=1)
# Headers definition
lbf_list_headers = [None] * (COLUMNS + 3)
lbf_list_headers[0] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 0")
lbf_list_headers[1] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 1")
lbf_list_headers[2] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 2")
lbf_list_headers[3] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 3")
lbf_list_headers[4] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 4")
lbf_list_headers[5] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 5")
lbf_list_headers[6] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 6")
lbf_list_headers[7] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 7")
lbf_list_headers[8] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 8")
lbf_list_headers[9] = ttk.Label(self.lbf, style='Header.TLabel', text="Column 9")
lbf_list_headers[10] = ttk.Label(self.lbf, style='Header.TLabel', text="Group 1", anchor=tk.CENTER)
lbf_list_headers[11] = ttk.Label(self.lbf, style='Header.TLabel', text="Group 2", anchor=tk.CENTER)
lbf_list_headers[12] = ttk.Label(self.lbf, style='Header.TLabel', text="Group 3", anchor=tk.CENTER)
# Headers gridding
lbf_list_headers[0].grid(row=0, rowspan=2, column=0, sticky='NSWE')
lbf_list_headers[1].grid(row=0, rowspan=2, column=1, sticky='NSWE')
lbf_list_headers[2].grid(row=0, rowspan=2, column=2, sticky='NSWE')
# Group 1
lbf_list_headers[3].grid(row=1, column=3, sticky='NSWE')
lbf_list_headers[4].grid(row=1, column=4, sticky='NSWE')
lbf_list_headers[5].grid(row=1, column=5, sticky='NSWE')
lbf_list_headers[10].grid(row=0, column=3, columnspan=3, sticky='NSWE')
# Group 2
lbf_list_headers[6].grid(row=1, column=6, sticky='NSWE')
lbf_list_headers[7].grid(row=1, column=7, sticky='NSWE')
lbf_list_headers[11].grid(row=0, column=6, columnspan=2, sticky='NSWE')
# Group 3
lbf_list_headers[8].grid(row=1, column=8, sticky='NSWE')
lbf_list_headers[9].grid(row=1, column=9, sticky='NSWE')
lbf_list_headers[12].grid(row=0, column=8, columnspan=2, sticky='NSWE')
# Display some random data
lbf_list_data = [None] * COLUMNS
for row in range(ROWS):
for column in range(COLUMNS):
lbf_list_data[column] = ttk.Label(self.lbf, relief=tk.GROOVE, text=f"{10000 + randint(0, 2000)}")
lbf_list_data[column].grid(row=row+2, column=column, sticky='NSWE')
class TableWithTreeview:
def __init__(self, parent):
self.lbf = ttk.Labelframe(parent, text='Table with Treeview')
self.lbf.grid(row=1, column=0, sticky='NSWE')
columns = (
'Column 0', 'Column 1', 'Column 2',
'Column 3', 'Column 4', 'Column 5',
'Column 6', 'Column 7', 'Column 8',
'Column 9'
)
self.tw = ttk.Treeview(self.lbf, selectmode='browse', columns=columns, style='Custom.Treeview')
self.tw.heading('Column 0', text='Column 0')
self.tw.heading('Column 1', text='Column 1')
self.tw.heading('Column 2', text='Column 2')
self.tw.heading('Column 3', text='Column 3')
self.tw.heading('Column 4', text='Column 4')
self.tw.heading('Column 5', text='Column 5')
self.tw.heading('Column 6', text='Column 6')
self.tw.heading('Column 7', text='Column 7')
self.tw.heading('Column 8', text='Column 8')
self.tw.heading('Column 9', text='Column 9')
self.tw['show'] = 'headings'
self.tw.column('Column 0', width=100)
self.tw.column('Column 1', width=100)
self.tw.column('Column 2', width=100)
self.tw.column('Column 3', width=100)
self.tw.column('Column 4', width=100)
self.tw.column('Column 5', width=100)
self.tw.column('Column 6', width=100)
self.tw.column('Column 7', width=100)
self.tw.column('Column 8', width=100)
self.tw.column('Column 9', width=100)
# Display some random data
lbf_list_data = [None] * COLUMNS
for row in range(ROWS):
values = []
for column in range(COLUMNS):
values.append(f"{10000 + randint(0, 2000)}")
lbf_list_data[column] = self.tw.insert('', 'end', values=values)
self.tw.grid(row=0, column=0)
window = tk.Tk()
table_1 = TableWithLabels(window)
table_2 = TableWithTreeview(window)
window.mainloop()
I am using Python 3.13.7, though that is probably not relevant.
ttkapi thoug (which I never bothered to learn). What makes you favor the Treeview ?Treeview. I don't know if implementing aTreeviewis more (memory or CPU) efficient than using multipleLabel+grid()or not.Treeviewis mostly a system specific widget that is just supplied with your data, configuration and is already managed by the tkinters geometry it is faster. You could also go outside of the intended usecase for the widget and a selfmade one can accomplish a specific task better. So generally speaking before you don't have a good reason to do this on your own, you should do it with the widgets you get by tkinter. What`s also possible to use more than one Treeview and combine them. That's why I ask for more information.