4

The text is "This is too long text". As you can see the view provides an option to expand horizontally but there is not indication that there are more characters.

I need some indication for the user that there are more characters in the cell. i.e showing "...".

enter image description here

This is the code:

from tkinter import ttk
import tkinter as tk

win = tk.Tk()
win.resizable(width=0, height=0)

tree = ttk.Treeview(win, selectmode='browse')
tree.grid(row=0, column=0)

vsb = ttk.Scrollbar(win, orient="horizontal", command=tree.xview)
vsb.grid(row=1, column=0, sticky=tk.W + tk.E + tk.N + tk.S)
tree.configure(xscrollcommand=vsb.set)

vsb = ttk.Scrollbar(win, orient="vertical", command=tree.yview)
vsb.grid(row=0, column=1, sticky=tk.W + tk.E + tk.N + tk.S)
tree.configure(yscrollcommand=vsb.set)

tree["columns"] = ("1", "2")
tree['show'] = 'headings'
tree.column("1", anchor='c')
tree.column("2", width=100, anchor='c')
tree.heading("1", text="Col 1")
tree.heading("2", text="Col 2")
tree.insert("", 'end', text="L4", values=("This text is too long","Short text"))

win.mainloop()
TheLogicGuy
  • 682
  • 8
  • 19
  • @stovfl I didn't find anything in the documentation https://docs.python.org/3/library/tkinter.ttk.html#treeview do you know how? – TheLogicGuy Oct 10 '19 at 21:31
  • Far as i know, **not implemented**. You have to do it yourself. Basicly catch `.bind(, ...` column `width` change, compute the `width` of the column data based on the used `font`, replace `column` data? – stovfl Oct 10 '19 at 21:46
  • @stovfl ya I think that is the way to go. Unfortunately its hard to calculate with unless you are using monospace font. – Mike - SMT Oct 16 '19 at 13:13
  • @stovfl I am not sure that bind will work here. Configure only triggers if you change the size of the treeview but not when changing the size of the columns. – Mike - SMT Oct 16 '19 at 13:33
  • @Mike-SMT: ***"not sure that bind will work here"***: Relevant [forcing-a-tkinter-ttk-treeview-widget-to-resize-after-shrinking-its-column-width](https://stackoverflow.com/questions/49715456/forcing-a-tkinter-ttk-treeview-widget-to-resize-after-shrinking-its-column-width) – stovfl Oct 16 '19 at 14:04
  • @stovfl Not able to get `bind()` to work when column is changed with that info. I think the poster also mentioned that was the issue. – Mike - SMT Oct 16 '19 at 16:32
  • I had a similar case when I wanted to display some amount of short and long title names. I ended up just calculating the length of each title, shorten it to "title..." when its over a wrap limit, and use a tooltip to display the full title upon mouse hover. – Henry Yik Oct 17 '19 at 09:22

1 Answers1

3

Update:

Turns out we can bind '<ButtonRelease-1>' to the treeview as noted on this post: how-to-detect-resizing-of-ttk-treeview-column that @stovfl has pointed out in the comments. So I have updated my answer with less overhead :D.

I created a method that will check to see of the column width has changed and then if it has then calculate each row to see if we need to add ... to the end.

This is accomplished with ImageFont from PIL thought for some reason there seams to be a size difference between Treeview font and PIL font. After some testing it appears that any font size you set in the style for the Treeview just add 5 to the PIL font and it should calculate correctly.

import tkinter as tk
import tkinter.ttk as ttk
from PIL import ImageFont


track_data = [[('This text is too long', 'Short text'), 'This text is too long'],
              [('This text is toooooooo long', 'Short text'), 'This text is toooooooo long'],
              [('This text is longer than most others', 'Short text'), 'This text is longer than most others'],
              [('This text is short', 'Short text'), 'This text is short']]


def add_to_tree_list(long_text, short_text, top):
    track_data.append([(long_text.get(), short_text.get()), long_text.get()])
    top.destroy()
    update_text()


def pop_list(ndex):
    track_data.pop(ndex)


def remove_row_from_list():
    top = tk.Toplevel(win)
    build_top_frame(top)
    update_text()


def build_top_frame(top):
    row_button_list = []
    for child in top.winfo_children():
        child.destroy()
    frame = tk.Frame(top)
    frame.pack()
    for ndex, sub_list in enumerate(track_data):
        row_button_list.append([tk.Button(frame, text='Remove',
                                          command=lambda n=ndex: (pop_list(n), update_text(), build_top_frame(top))),
                                tk.Label(frame, text='Row {}:  {}'.format(ndex + 1, sub_list[1]))])
        row_button_list[-1][0].grid(row=ndex, column=0)
        row_button_list[-1][1].grid(row=ndex, column=1, sticky='w')


def top_for_new_row():
    top = tk.Toplevel(win)
    tk.Label(top, text='Add long text: ').grid(row=0, column=0)
    tk.Label(top, text='Add short text: ').grid(row=1, column=0)
    e1 = tk.Entry(top)
    e2 = tk.Entry(top)
    e1.grid(row=0, column=1)
    e2.grid(row=1, column=1)
    tk.Button(top, text='Submit', command=lambda: add_to_tree_list(e1, e2, top)).grid(row=2, column=0)
    tk.Button(top, text='Cancel', command=top.destroy).grid(row=2, column=1)


def clear_and_load(data):
    tree.delete(*tree.get_children())
    for sub_list in data:
        tree.insert('''''', 'end', text='L4', values=sub_list[0])


def update_text(_=None):
    global old_col_width, old_track_data_len
    col_width = tree.column('1')['width']
    if col_width != old_col_width or old_track_data_len != len(track_data):
        old_col_width = col_width
        new_track_data = []
        for text in track_data:
            font = ImageFont.truetype("arial.ttf", 17)
            size = font.getsize(text[0][0])
            previous_long_text = ''
            new_long_text = ''
            if size[0] > col_width:
                for char in text[0][0]:
                    new_long_text = '{}{}'.format(new_long_text, char)
                    new_size = font.getsize('{}{}'.format(new_long_text, '...'))[0]
                    if new_size < col_width:
                        previous_long_text = new_long_text
                    else:
                        new_track_data.append([('{}{}'.format(previous_long_text, '...'), text[0][1]), ''])
                        break
            else:
                new_track_data.append(text)
        clear_and_load(new_track_data)
    old_track_data_len = len(track_data)


win = tk.Tk()
win.columnconfigure(0, weight=1)
old_col_width = 200
old_track_data_len = 0
win.resizable(width=0, height=0)
style = ttk.Style()
style.configure('Treeview', font=('arial', 12))
tree = ttk.Treeview(win)
tree.grid(row=0, column=0)
tree.bind('<ButtonRelease-1>', update_text)

vsb = ttk.Scrollbar(win, orient='horizontal', command=tree.xview)
vsb.grid(row=1, column=0, sticky='nsew')
tree.configure(xscrollcommand=vsb.set)

vsb = ttk.Scrollbar(win, orient='vertical', command=tree.yview)
vsb.grid(row=0, column=1, sticky='nsew')
tree.configure(yscrollcommand=vsb.set)
btn_frame = tk.Frame(win)
btn_frame.grid(row=2, column=0, columnspan=2, sticky='ew')
btn_frame.columnconfigure(0, weight=1)
btn_frame.columnconfigure(1, weight=1)
tk.Button(btn_frame, text='Add Row!', command=top_for_new_row).grid(row=0, column=0, sticky='ew')
tk.Button(btn_frame, text='Remove Row!', command=remove_row_from_list).grid(row=0, column=1, sticky='ew')

columns = ('1', '2')
tree['columns'] = columns
tree['show'] = 'headings'
tree.column('1', width=200, stretch=True, anchor='w')
tree.column('2', width=100, anchor='w')
tree.heading('1', text='Col 1')
tree.heading('2', text='Col 2')

clear_and_load(track_data)
update_text()
win.mainloop()

Results:

enter image description here

Adding new line:

enter image description here

Removing lines:

enter image description here

Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
  • 1
    A **one time** Event, see the comments at [how-to-detect-resizing-of-ttk-treeview-column](https://stackoverflow.com/questions/47695391/how-to-detect-resizing-of-ttk-treeview-column) – stovfl Oct 21 '19 at 18:50
  • @stovfl well look at that. Got rid of 30 lines of code and now can simply run my check at button release. So much for your custom treeview and my after loop lol. Thanks for the link I have updated my answer. – Mike - SMT Oct 22 '19 at 13:27