10

I'm trying to write a python class to display data in a tabular format. I'm sure there are classes out there already to do the same thing, however, I'm using this exercise as a way to teach myself Python and tkinter. For the most part, I have the class working the way I want it to, however I cannot get the header and data cells to fill their entire cell, while being aligned left. Here is what my class currently generates for a table:

How the table currently looks

I went ahead and changed the sticky on the cells to be (W,E) rather than just W, in order to show how I want the table to look, except each cell left justified. Below is what I'm shooting for:

How I want the table to look

Based on the research I've done, it would seem I need to be using the weight attribute of grid_columnconfigure and grid_rowconfigure, however every way I have tried using them I cannot, get it to work.

Here is the code for my class (I am using Python 3.4):

from tkinter import *
from tkinter import ttk
from tkinter import font

class TableData:

    def __init__(self,parent,attributes,columns,data):
        self.parent = parent
        self.tableName = StringVar()
        self.tableName.set(attributes['tableName'])
        self.columns = columns
        self.columnCount = 0
        self.borderColor = attributes['borderColor']
        self.titleBG = attributes['titleBG']
        self.titleFG = attributes['titleFG']
        self.titleFontSize = attributes['titleFontSize']
        self.headerBG = attributes['headerBG']
        self.headerFG = attributes['headerFG']
        self.headerFontSize = attributes['headerFontSize']
        self.dataRowColor1 = attributes['dataRowColor1']
        self.dataRowColor2 = attributes['dataRowColor2']
        self.dataRowFontSize = attributes['dataRowFontSize']
        self.dataRowFG = attributes['dataRowFG']
        self.data = data
        self.tableDataFrame = ttk.Frame(self.parent)
        self.tableDataFrame.grid(row=0,column=0)
        self.initUI()

    def countColumns(self):
        cnt = 0
        for i in self.columns:
            cnt += 1

        self.columnCount = cnt

    def buildTableTitle(self):
        tableTitleFont = font.Font(size=self.titleFontSize)
        Label(self.tableDataFrame,textvariable=self.tableName,bg=self.titleBG,fg=self.titleFG,font=tableTitleFont, highlightbackground=self.borderColor,highlightthickness=2).grid(row=0,columnspan=self.columnCount,sticky=(W,E), ipady=3)

    def buildHeaderRow(self):
        colCount = 0
        tableHeaderFont = font.Font(size=self.headerFontSize)
        for col in self.columns:
            Label(self.tableDataFrame,text=col,font=tableHeaderFont,bg=self.headerBG,fg=self.headerFG,highlightbackground=self.borderColor,highlightthickness=1).grid(row=1,column=colCount,sticky=W, ipady=2, ipadx=5)
            colCount += 1

    def buildDataRow(self):
        tableDataFont = font.Font(size=self.dataRowFontSize)
        rowCount = 2
        for row in self.data:
            if rowCount % 2 == 0:
                rowColor = self.dataRowColor2
            else:
                 rowColor = self.dataRowColor1
            colCount = 0
            for col in row:
                Label(self.tableDataFrame,text=col,bg=rowColor,fg=self.dataRowFG,font=tableDataFont,highlightbackground=self.borderColor,highlightthickness=1).grid(row=rowCount,column=colCount,sticky=W,ipady=1, ipadx=5)
                colCount += 1
            rowCount += 1

    def initUI(self):
        self.countColumns()
        self.buildTableTitle()
        self.buildHeaderRow()
        self.buildDataRow()

Here is a test file referencing the TableData class:

from tkinter import *
from tkinter import ttk
from tableData import TableData
import sqlite3

root = Tk()
root.geometry('1000x400')

mainframe = ttk.Frame(root).grid(row=0,column=0)

attributes = {}
attributes['tableName'] = 'Title'
attributes['borderColor'] = 'black'
attributes['titleBG'] = '#1975D1'
attributes['titleFG'] = 'white'
attributes['titleFontSize'] = 16
attributes['headerBG'] = 'white'
attributes['headerFG'] = 'black'
attributes['headerFontSize'] = 12
attributes['dataRowColor1'] = 'white'
attributes['dataRowColor2'] = 'grey'
attributes['dataRowFontSize'] = 10
attributes['dataRowFG'] = 'black'

columns = ['Col 1', 'Column 2', 'Column 3','Column    4']

results = [('1','Key','Desc','Attribute'),('2','Key Column','Description Column','AttributeColumn')]

table = TableData(mainframe,attributes,columns,results)

root.mainloop()

Thanks in advance for any insight. Please, let me know if there is any other info that would be helpful.

Ryan
  • 617
  • 2
  • 7
  • 16
  • One thing I will add is this class will be used to dynamically create the table. Once in use, rather than sending a hardcoded dataset to the class like I did in the test file, it will be a result set from a query. Therefore, I cannot hardcode the width or weight of the columns. – Ryan May 30 '15 at 20:06
  • I am receiving this error whe I run your code: `UnboundLocalError: local variable 'rowCount' referenced before assignment`. You might want to fix this first and then update your question. Anyway, the solution to stretch your columns and rows is really to set the `weight` property of your columns and/or rows to `1`. I would help, but you need first to give us a runnable program... – nbro May 30 '15 at 22:31
  • @Xenomorph, thank you very much for taking the time to look at this. I have added the declaration for rowCount, and the program now runs successfully. Let me know if you need anything else from me. It's very much appreciated. – Ryan May 30 '15 at 23:26
  • FWIW, the `weight` option only affects how much extra space is given to a row or column. It doesn't in any way affect the alignment of a widget in its cell. – Bryan Oakley May 31 '15 at 03:33
  • 1
    @BryanOakley, do you know what I'm missing in order to make the cell fill the entire column, without making it center aligned? – Ryan Jun 02 '15 at 01:13

4 Answers4

34

For any grid of geometry, add option sticky="W", for example,

self.tableDataFrame.grid(sticky="W", row=0, column=0)
sheilak
  • 5,833
  • 7
  • 34
  • 43
xjfengck
  • 991
  • 1
  • 8
  • 9
11

If you want the text in a label to be left-aligned, use the anchor option. It takes a string representing a point on a compass (eg: "w" = "west", meaning the text is anchored to the left):

for col in row:
    Label(..., anchor="w").grid(...)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
0

You are not defining the width of your label so tkinter sets the width of Label equal to the width of the text. Thats why your labels do not occupy entire width of the cell. To make them occupy all the available width use stickey = E+W

    Label(self.tableDataFrame,text=col,font=tableHeaderFont,bg=self.headerBG,fg=self.headerFG,highlightbackground=self.borderColor,highlightthickness=1).grid(row=1,column=colCount,sticky=W+E, ipady=2, ipadx=5)
khan
  • 21
  • 3
-2

Try to set the columnspan attribute when griding:

self.tableDataFrame.grid(row=0, column=0, columnspan=2)

2 may not be the correct size, try 'til you get it like you want it.

nbro
  • 15,395
  • 32
  • 113
  • 196
WoodyP
  • 9