StevenWang
StevenWang

Reputation: 11

tkinter ttk treeview how to set fixed width? why it change with number of column?

I am trying to use python tkinter to build a interface to show database table information with Treeview. The heading is the table schema, and insert data row to the treeview table. Since the number of column in different tables may not be the same, I want to auto adjust the width of column so that the total width of the treeview table is fixed. I wrote the python3 code in Pycharm 2018.3-community, and used a function(show table) to insert the data rows and used "treeview.column(col, width = int( total_width / len(cols)), minwidth=0, anchor='center',stretch=0 )" to set the column width. the function is called when showing the page(I put comment for the line when its called). When the code was run the first time, the column width was adjusted by the width normally and the total table width is well fit and correct, as shown below. normal width

However, if I click a button to call the same function to insert the same data row, then the treeview table width will change. and I found it change with the number of column, as shown below. I do not know why it happened like this. Please advise. Thanks a lot ! my code is attached at the bottom. changed width

class ListPage(tk.Tk):
table_list = sample.scan_result.table_list
table_index = 0

def __init__(self, parent, controller):
    tk.Frame.__init__(self, parent, bg='#C6E2FF')


    self.tbTitlelabel = tk.Button(self, text="Table", font=("Verdana", 18, "bold"), bg='#C6E2FF', fg='darkblue', command=lambda: controller.show_frame(start_page.StartPage))
    self.tbTitlelabel.grid(row=1, column=0, columnspan=1, padx=(150, 0), sticky='w')

    self.nextLabel = tk.Button(self, text="Next", font=("Verdana", 8, "bold"), bg='#C6E2FF', fg='darkblue',command=lambda:self.nextTable())
    self.nextLabel.grid(row=1, column=0, columnspan=1, padx=(280, 0), sticky='w')

    **# if call the function by the button, the table width change.** 
    self.preLabel = tk.Button(self, text="Previous", font=("Verdana", 18, "bold"), bg='#C6E2FF', fg='darkblue', command=lambda:self.showTable() )   
    self.preLabel.grid(row=1, column=0, columnspan=1, padx=(380, 0), sticky='w')

    style = ttk.Style()
    style.configure("Treeview.Heading", font=("Verdana", 15))
    self.tree = ttk.Treeview(self, height=10, show = 'headings', selectmode='browse')
    self.tree.grid(row=2, column=0, columnspan=1, padx=(120, 0), sticky='w')

    ysb = ttk.Scrollbar(self, orient='vertical', command=self.tree.yview)
    self.tree['yscroll'] = ysb.set
    ysb.grid(row=2, column=0, sticky='ens')

    **# if call the function during the page intial, the table width is good.** 
    self.showTable()  


def nextTable(self):
    if self.table_index < len(self.table_list)-1:
        self.table_index += 1
    self.showTable()

def previousTable(self):
    if self.table_index > 0:
        self.table_index -= 1
    self.showTable()

def showTable(self):
    print('run show table')
    col_start_pos = 0

    for i in self.tree.get_children():
        self.tree.delete(i)

    if len(self.table_list) > self.table_index:
        print(  self.table_index,  self.table_list)

        table_item = self.table_list[self.table_index]
        print(self.table_list[1], self.table_index)
        self.tbTitlelabel['text'] = table_item.db_name
        col_name = table_item.col_name[col_start_pos:]
        self.tree['columns'] = col_name
        data_list = table_item.data_list

        for col in col_name:
            self.tree.heading(col, text=col)
            **self.tree.column(col, width= int(600 / len(col_name)), minwidth=0, anchor='center',stretch=0) # auto adjust column width**

        x=0
        while x < 7:
            for list in data_list:
                self.tree.insert('', 'end', values = list[col_start_pos:] )
            x+=1

Upvotes: 1

Views: 9031

Answers (5)

Due to my reputation points, I cannot post a simple comment. Therefore, I will expand a bit on this.

I had the same issue of the tree resizing while refreshing the data.

For me, a variation of the answer by user16129445 (see above) worked perfectly. I defined the self.tree['show'] option to keep the columns widths fixed after refreshing the tree.

In my case, I also have a tree column (i.e. column #0), in which case you want to use the option:

self.tree['show'] = 'tree'

instead of

self.tree['show'] = 'headings'

which works fine, but hides the tree column; or you can use also the following combination, if you want both the column #0 and the headings to display:

self.tree['show'] = ['tree', 'headings']

Perhaps worth mentioning that I also have a hidden column which gets displayed after the automatic adjustment of the columns:

widths = (200, 300, 300)
self.tree.column("#0", width=widths[0], minwidth=widths[0])  # tree column
self.tree.column("key", width=0, minwidth=0)  # hidden
self.tree.column("name", anchor=tk.W, width=widths[1])
self.tree.column("value", anchor=tk.W, width=widths[2])

where I can force the column "key" to stay hidden by instructing which columns I want to display by using the option:

self.tree["displaycolumns"] = ('name', 'value')

I use the variable widths to define the columns widths at initialisation of the tree and these remain fixed after refresh.

Upvotes: 0

user16129445
user16129445

Reputation: 21

I know this is an old thread but I had a similar issue recently and the update options were not working and the destroy was not an option. The resolution was quite simple for me. Once I moved the show = 'headings' to after I had made all final adjustments to the column widths everything displays correctly. I have not been able run the OP's code I expect if the line in the init method:

self.tree = ttk.Treeview(self, height=10, show = 'headings', selectmode='browse')

is replaced with:

self.tree = ttk.Treeview(self, height=10, selectmode='browse')

and the following is added to the end of the showTable method:

self.tree['show'] = 'headings'

then it should render fine.

Upvotes: 2

StevenWang
StevenWang

Reputation: 11

After several tries, I found the solution myself. I put it below in case someone meet the same problem. Like I said earlier, the first time when it generate the treeview table, the table width is correct. and so, every time when we need to show a table, we need to destroy the old one treeview.destory() , then create a new treeview, and then insert the data.

Upvotes: 0

fung
fung

Reputation: 461

I do this when I refresh the tree:

self.geometry(self.winfo_geometry())

which the 'self' is the master of the tree. The tree will resize itself when I add a new column on it by tree.config(columns=new_columns), but it won't resize with the above command. The command can be placed before or after the config. But sorry that I don't know the reason.

Upvotes: 0

Tom Rivera
Tom Rivera

Reputation: 1

Had the same issue myself, after trying everything like root.update(), treeview.update() and treeview.destroy() which messed up all my gui. Finally fixed it by just saving the width of the columns in a list before clearing the treeview, and then using those width to compare with new ones and using the larger...

columns = []
wcols = []
row = 1
for each in self.table_tree.get_children():
    if row > 1: break
    columns = list(self.table_tree.item(each, option='values'))
    print(columns)
    for i in range(len(columns)):
        wcols.append(self.table_tree.column(i, width=None))
    row = row + 1

and then later on

self.table_tree["columns"]=column_names
indx = 0
h_col_w = []
new_col_w = []
for i in column_names:
    h_col_w.append(tkFont.Font().measure(i))
    if not(wcols == []):
        if h_col_w[indx] < wcols[indx]:
            h_col_w[indx] = wcols[indx]
    self.table_tree.column(i, width=h_col_w[indx], stretch=True) ;"set the width of the column to the width of the column name"
        self.table_tree.heading(i, text=i, command= lambda _col=i: \
                                self.treeview_sort_column(self.table_tree, _col, True))
        indx = indx + 1

Upvotes: 0

Related Questions