I'm trying to update a ttk.TreeView's columns, but the TreeView always expands beyond the column widths. The issue is shown in the screenshots below.
Here is a minimum reproducible example:
import tkinter as tk
from tkinter import ttk
class DataView(ttk.Labelframe):
"""Widget that displays tabular data in a console-like view."""
def __init__(self, master, max_lines=1000, **kwargs):
super().__init__(master, **kwargs)
self.rowconfigure(0, weight=1)
self._column_headers = ()
self._max_lines = max_lines
self._tv = ttk.Treeview(self, show="headings", selectmode="none", columns=[])
self._tv.grid(row=0, column=0, padx=2, pady=2, sticky=tk.NS)
def set_column_headers(self, headers: tuple[str, ...]):
self._column_headers = headers
self._tv.config(columns=headers)
for header in headers:
self._tv.column(header, width=70, stretch=False)
self._tv.heading(header, text=header)
def main():
root = tk.Tk()
root.title("Data View Test")
root.geometry("800x600")
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
data_view = DataView(root, text="Data View", max_lines=100)
data_view.grid(row=0, column=0, sticky=tk.NSEW)
data_view.set_column_headers(("Time", "Voltage", "Current"))
data_view.after(3000, data_view.set_column_headers, ("Time", "Voltage", "Current", "Power"))
root.mainloop()
if __name__ == "__main__":
main()
The first call to set_column_headers
works as expected:
However, the second call (and any subsequent calls) makes the treeview expand beyong the width of the columns:
Stepping through the code, this seems to happen on self._tv.config(columns=headers)
I would expect a result like the first screenshot, but with the additional "Power" column. Is there any way I can achieve this? This seems like a tkinter bug but I may be doing something wrong.
Python version: 3.11.4
OS: Windows
tkinter.TkVersion
: 8.6
tkinter.Tcl().call('info', 'patchlevel'))
: 8.6.12
When analyzing the execution of the program, I noticed that when setting self._tv.column(header, width=70, stretch=False)
, the type of the show
option changes. This is what causes the “strange” behavior of the program.
def set_column_headers(self, headers: tuple[str, ...]):
self._column_headers = headers
self._tv.config(columns=headers)
print(self._tv["show"], type(self._tv["show"][0]))
for i, header in enumerate(headers):
self._tv.column(header, width=70, stretch=False)
print(self._tv["show"], type(self._tv["show"][0]))
self._tv.heading(header, text=header + str(i))
----------------------
(<string object: 'headings'>,) <class '_tkinter.Tcl_Obj'>
('headings',) <class 'str'>
...........................
In the source code on GitHub, the column method is implemented through the _val_or_dict function. Several other methods use this function and they also changed the type of the show
option. That is, this is a flaw, at least ttk tkinter
. The workaround is to assign self._tv["show"] = "headings"
again, after applying the heading
and column
methods.