pythontkintertreeviewtabularrow-height

Table with variable row height in tkinter


I am making a GUI in which I want to display some data in a Excel-like table. For some rows there will only be one line of text and for others there will be a number of lines. Hence I want a table where the height of the rows are not the same.

I found this post describing how to make a table with wrapped text using treeview: Wrap text inside row in tkinter treeview

However, I am not able to change the height of a specific row.

Anyone knows how to do that or know of a better way to make a table like the one described above.


Solution

  • Okay, so I found some kind of solution to my problem.

    Inspired by this post: tkinter Canvas Scrollbar with Grid?. I came up with the following solution. Python is new to me so it could probably be done more elegant:-)

    The problem that I am facing now is that the label width is given in text units and wraplength in screen units. Is there a method to find the conversion factor between the two?

    from tkinter import *
    import math
    class MyApp(Tk):
        def __init__(self):
            Tk.__init__(self)
    
            #self.columnconfigure(0, weight=1)
            self.rowconfigure(0, weight=1)
            self.geometry('950x500+100+100')
    
            label = Label(self,text='Table with some data', font=("Arial Bold", 25))
            label.pack()
    
            master_frame = Frame(self, bd=3, relief=RIDGE)
            master_frame.pack(side=BOTTOM)
    
            # Create a frame for the canvas and scrollbar(s).
            frame2 = Frame(master_frame)
            frame2.pack(side = BOTTOM)
    
            # Add a canvas in that frame.
            canvas = Canvas(frame2)
            canvas.grid(row=0, column=0)
    
            # Create a vertical scrollbar linked to the canvas.
            vsbar = Scrollbar(frame2, orient=VERTICAL, command=canvas.yview)
            vsbar.grid(row=0, column=1, sticky=NS)
            canvas.configure(yscrollcommand=vsbar.set)
    
            # Create a frame on the canvas to contain the buttons.
            self.table_frame = Frame(canvas)
    
            #Add some info to table
            newLine = ["Step#","Process Type","Tool Info","Process details","Cost","Comments"]
            self.newRow(0,newLine)
            newLine = ["Step# Step#","Process Type","Tool Info","Process details","Cost","Comments"]
            self.newRow(1,newLine)
            newLine = ["Step# Step# Step#","Process Type","Tool Info","Process details","Cost","Comments"]
            self.newRow(2,newLine)
            newLine = ["Step# Step# Step# Step#","Process Type","Tool Info","Process details","Cost","Comments"]
            self.newRow(3,newLine)
            newLine = ["Step# Step# Step# Step# Step#","Process Type","Tool Info","Process details","Cost","Comments"]
            self.newRow(4,newLine)
    
            # Create canvas window to hold the buttons_frame.
            canvas.create_window((0,0), window=self.table_frame, anchor=NW)
    
            self.table_frame.update_idletasks()  # Needed to make bbox info available.
            bbox = canvas.bbox(ALL)  # Get bounding box of canvas with Buttons.
            #print('canvas.bbox(tk.ALL): {}'.format(bbox))
    
            # Define the scrollable region as entire canvas with only the desired
            # number of rows and columns displayed.
            canvas.configure(scrollregion=bbox, width=925, height=200)
    
        #Add a new line to the table     
        def newRow(self, rowNumber, info2Add):
    
            numberOfLines = []                  #List to store number of lines needed
            columnWidths = [7,20,20,40,10,30]   #Width of the different columns in the table - unit: text
            stringLength = []                   #Lengt of the strings in the info2Add list
    
            #Find the length of each element in the info2Add list 
            for item in info2Add:
                stringLength.append(len(item))
    
            #Find the number of lines needed for each column
            for index, item in enumerate(stringLength):
                #print(math.ceil(item/columnWidths[index]))
                numberOfLines.append(math.ceil(item/columnWidths[index]))
    
            #Find the maximum number of lines needed
            lineNumber = max(numberOfLines)
    
            #Define the 6 labels (columns) in each row consist of.                
            col1 = Label(self.table_frame, relief=RIDGE,text=info2Add[0], wraplength=60,bg='white')
            col1.grid(row=rowNumber, column=1, sticky='news')
            col1.config(width=7, height=lineNumber)
            col2 = Label(self.table_frame, relief=RIDGE,text=info2Add[1], wraplength=140,bg='white')
            col2.grid(row=rowNumber, column=2, sticky='news')
            col2.config(width=20, height=lineNumber)
            col3 = Label(self.table_frame, relief=RIDGE,text=info2Add[2], wraplength=140,bg='white')
            col3.grid(row=rowNumber, column=3, sticky='news')
            col3.config(width=20, height=lineNumber)
            col4 = Label(self.table_frame, relief=RIDGE,text=info2Add[3], wraplength=280,bg='white')
            col4.grid(row=rowNumber, column=4, sticky='news')
            col4.config(width=40, height=lineNumber)
            col5 = Label(self.table_frame, relief=RIDGE,text=info2Add[4], wraplength=70,bg='white')
            col5.grid(row=rowNumber, column=5, sticky='news')
            col5.config(width=10, height=lineNumber)
            col6 = Label(self.table_frame, relief=RIDGE,text=info2Add[5], wraplength=210,bg='white')
            col6.grid(row=rowNumber, column=6, sticky='news')
            col6.config(width=30, height=lineNumber)
    
    
    app = MyApp()
    app.mainloop()