Reputation: 3885
Is is possible to wrap text in row using tkinter treeview? I have tried to change row height but text wont wrap. basically i want to have multi line text in row.
This is the code:
from tkinter import*
from tkinter import ttk
myApp = Tk()
NewTree= ttk.Treeview(myApp, height=5)
NewTree['show'] = 'headings'
s = ttk.Style()
s.configure('Treeview', rowheight=40)
NewTree["columns"]=("1","2")
NewTree.column("1", width=60)
NewTree.column("2", width=60)
NewTree.heading("1", text="col a")
NewTree.heading("2", text="col b")
item = NewTree.insert("", "end", values=("i want to wrap this text","and this text"))
NewTree.grid(row=0,column=0)
myApp.mainloop()
This is the second version of code (when you can manually add items in treeview), It just have one big function before the code that I posted above. I tried to put different values on lenght
but the result is the same (It wont wrap):
from tkinter import*
from tkinter import ttk
from tkinter import messagebox
import textwrap
def wrap(string, lenght=15):
return '\n'.join(textwrap.wrap(string, lenght))
myApp = Tk()
def editact(event):
def double(event): # funkcija koja kreira celiju kada se klikne dupli klik
try:
if NewTree.identify_region(event.x, event.y) == 'cell':
# the user clicked on a cell
def ok(event):
"""Change item value."""
NewTree.set(item, column, entry.get())
entry.destroy()
column = NewTree.identify_column(event.x) # identify column
item = NewTree.identify_row(event.y) # identify item
x, y, width, height = NewTree.bbox(item, column)
value = NewTree.set(item, column)
elif NewTree.identify_region(event.x, event.y) == 'heading':
# the user clicked on a heading
def ok(event):
"""Change heading text."""
NewTree.heading(column, text=entry.get())
entry.destroy()
column = NewTree.identify_column(event.x) # identify column
# tree.bbox work only with items so we have to get the bbox of the heading differently
x, y, width, _ = NewTree.bbox(NewTree.get_children('')[0], column) # get x and width (same as the one of any cell in the column)
# get vertical coordinates (y1, y2)
y2 = y
# get bottom coordinate
while NewTree.identify_region(event.x, y2) != 'heading':
y2 -= 1
# get top coordinate
y1 = y2
while NewTree.identify_region(event.x, y1) == 'heading':
y1 -= 1
height = y2 - y1
y = y1
value = NewTree.heading(column, 'text')
elif NewTree.identify_region(event.x, event.y) == 'nothing':
column = NewTree.identify_column(event.x) # identify column
# check whether we are below the last row:
x, y, width, height = NewTree.bbox(NewTree.get_children('')[-1], column)
if event.y > y:
def ok(event):
"""Change item value."""
# create item
item = NewTree.insert("", "end", values=("", ""))
NewTree.set(item, column, entry.get())
entry.destroy()
y += height
value = ""
else:
return
else:
return
# display the Entry
entry = ttk.Entry(NewTree) # create edition entry
entry.place(x=x, y=y, width=width, height=height, anchor='nw') # display entry on top of cell
entry.insert(0, value) # put former value in entry
entry.bind('<FocusOut>', ok) #validate when you click on other cell
entry.focus_set()
except IndexError:
Error=messagebox.showinfo("Error!","You have 0 rows. Please add a new row.")
sys.exit() #za resavalje greske`
pass
NewTree.bind('<Double-Button-1>', double) #create new cell with double click
NewTree= ttk.Treeview(myApp, height=5)
NewTree['show'] = 'headings'
s = ttk.Style()
s.configure('Treeview', rowheight=60)
NewTree["columns"]=("1","2")
NewTree.column("1", width=100, anchor="center")
NewTree.column("2", width=100, anchor="w")
NewTree.heading("1", text="Col A")
NewTree.heading("2", text="Col B")
item = NewTree.insert("", "end", values=(wrap(""),wrap("")))
NewTree.item(item, tags=item)
NewTree.bind('<1>', editact)
NewTree.grid(row=0,column=0, columnspan=5, padx=5)
myApp.mainloop()
Upvotes: 5
Views: 10375
Reputation: 30200
The following code uses the <B1-Motion>
event to dynamically wrap the text (so resizing the columns is supported, and the text will "wrap" accordingly).
from tkinter import Tk, ttk
from tkinter.font import Font
from functools import partial
myApp = Tk()
NewTree= ttk.Treeview(myApp, height=5)
NewTree['show'] = 'headings'
s = ttk.Style()
s.configure('Treeview', rowheight=50)
NewTree["columns"]=("1","2")
NewTree.column("1", width=80, anchor="nw")
NewTree.column("2", width=80, anchor="nw")
NewTree.heading("1", text="col a")
NewTree.heading("2", text="col b")
item = NewTree.insert("", "end", values=("i want to wrap this text","and this text"))
print(item)
NewTree.grid(row=0,column=0)
def motion_handler(tree, event):
f = Font(font='TkDefaultFont')
# A helper function that will wrap a given value based on column width
def adjust_newlines(val, width, pad=10):
if not isinstance(val, str):
return val
else:
words = val.split()
lines = [[],]
for word in words:
line = lines[-1] + [word,]
if f.measure(' '.join(line)) < (width - pad):
lines[-1].append(word)
else:
lines[-1] = ' '.join(lines[-1])
lines.append([word,])
if isinstance(lines[-1], list):
lines[-1] = ' '.join(lines[-1])
return '\n'.join(lines)
if (event is None) or (tree.identify_region(event.x, event.y) == "separator"):
# You may be able to use this to only adjust the two columns that you care about
# print(tree.identify_column(event.x))
col_widths = [tree.column(cid)['width'] for cid in tree['columns']]
for iid in tree.get_children():
new_vals = []
for (v,w) in zip(tree.item(iid)['values'], col_widths):
new_vals.append(adjust_newlines(v, w))
tree.item(iid, values=new_vals)
NewTree.bind('<B1-Motion>', partial(motion_handler, NewTree))
motion_handler(NewTree, None) # Perform initial wrapping
myApp.mainloop()
Upvotes: 7
Reputation:
You can use the textwrap
module to define this wrap
function:
def wrap(string, lenght=8):
return '\n'.join(textwrap.wrap(string, lenght))
Now, you can replace the item
by:
item = NewTree.insert("", "end", values=(wrap("i want to wrap this text"),
wrap("and this text")))
from tkinter import *
from tkinter import ttk
import textwrap
def wrap(string, lenght=8):
return '\n'.join(textwrap.wrap(string, lenght))
myApp = Tk()
NewTree = ttk.Treeview(myApp, height=5)
NewTree['show'] = 'headings'
s = ttk.Style()
s.configure('Treeview', rowheight=40)
NewTree["columns"] = ("1", "2")
NewTree.column("1", width=60)
NewTree.column("2", width=60)
NewTree.heading("1", text="col a")
NewTree.heading("2", text="col b")
item = NewTree.insert("", "end", values=(wrap("i want to wrap this text"),
wrap("and this text")))
NewTree.grid(row=0, column=0)
myApp.mainloop()
Upvotes: 1