Welcome, guest | Sign In | My Account | Store | Cart
# Author: Miguel Martinez Lopez
# Version: 0.1

try:
    from Tkinter import Frame, Label, Message, StringVar
    from Tkconstants import *
except ImportError:
    from tkinter import Frame, Label, Message, StringVar
    from tkinter.constants import *

class Cell(Frame):
    """Base class for cells"""

class Data_Cell(Cell):
    def __init__(self, master, variable, anchor=W, bordercolor=None, padx=0, pady=0, background=None, foreground=None, font=None):
        Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)

        self._message_widget = Message(self, textvariable=variable, font=font, background=background, foreground=foreground)
        self._message_widget.pack(expand=True, padx=padx, pady=pady, anchor=anchor)

    def configure_width(self, width):
        self._message_widget.configure(width=width)

class Header_Cell(Cell):
    def __init__(self, master, text, bordercolor=None, padx=None, pady=None, background=None, foreground=None, font=None, anchor=CENTER):
        Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)
        self._header_label = Label(self, text=text, background=background, foreground=foreground, font=font)
        self._header_label.pack(padx=padx, pady=pady, expand=True)
        
        if bordercolor is not None:
            separator = Frame(self, height=2, background=bordercolor, bd=0, highlightthickness=0, class_="Separator")
            separator.pack(fill=X, anchor=anchor)
        
class Table(Frame):
    def __init__(self, master, columns, column_weights=None, column_minwidths=None, height=None, minwidth=20,minheight=20, padx=5, pady=5, cell_font=None, cell_foreground="black", cell_background="white", header_font=None, header_background="white", header_foreground="black", header_anchor=CENTER,bordercolor = "#999999", stripped_rows=("#EEEEEE", "white"), cell_anchor=W):
        Frame.__init__(self,master, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)

        self._cell_background = cell_background
        self._cell_foreground = cell_foreground
        self._cell_font = cell_font
        self._cell_anchor = cell_anchor
        
        self._stripped_rows = stripped_rows
        
        self._padx = padx
        self._pady = pady
        
        self._bordercolor = bordercolor

        self._data_vars = []

        self._columns = columns

        for j in range(len(columns)):
            column_name = columns[j]

            header_cell = Header_Cell(self, text=column_name, font=header_font, background=header_background, foreground=header_foreground, padx=padx, pady=pady, bordercolor=bordercolor, anchor=header_anchor)
            header_cell.grid(row=0, column=j, sticky=N+E+W+S)

        if column_weights is None:
            column_weights =[1]*len(columns)

        for j, weight in enumerate(column_weights):
            self.grid_columnconfigure(j, weight=weight)
        
        self._column_weights = column_weights

        if column_minwidths is not None:
            self.update_idletasks()
            for j, minwidth in enumerate(column_minwidths):
                if minwidth is None:
                    header_cell = self.grid_slaves(row=0, column=j)[0]
                    minwidth = header_cell.winfo_reqwidth()
                self.grid_columnconfigure(j, minsize=minwidth)

        if height is not None:
            self._add_rows(height)

        self.bind("<Configure>", self._on_configure)
    
    def _on_configure(self, event):
        for j, weight in enumerate(self._column_weights):
            if weight > 0:
                header_cell = self.grid_slaves(row=0, column=j)[0]
                width = header_cell.winfo_width()

                for i in range(self.number_of_rows):
                    widget = self.grid_slaves(row=i+1, column=j)[0]
                    widget.configure_width(width)
        
    def _add_rows(self, n):
        number_of_rows = self.number_of_rows
        for i in range(number_of_rows+1, number_of_rows+n+1):
            list_of_vars = []
            for j in range(self.number_of_columns):
                var = StringVar()
                list_of_vars.append(var)

                if self._stripped_rows:
                    cell = Data_Cell(self, variable=var, bordercolor=self._bordercolor, padx=self._padx, pady=self._pady, background=self._stripped_rows[(i+1)%2], foreground=self._cell_foreground, font=self._cell_font, anchor=self._cell_anchor)
                else:
                    cell = Data_Cell(self, variable=var, bordercolor=self._bordercolor, padx=self._padx, pady=self._pady, background=self._cell_background, foreground=self._cell_foreground, font=self._cell_font, anchor=self._cell_anchor)
                cell.grid(row=i, column=j, sticky=N+E+W+S)

            self._data_vars.append(list_of_vars)

    def _delete_rows(self, n):
        number_of_rows = self.number_of_rows
        for i in range(number_of_rows-n+1, number_of_rows+1):
            for j in range(self.number_of_columns):
                self.grid_slaves(row=i, column=j)[0].destroy()
    
    def set_data(self, data):
        n = len(data)
        m = len(data[0])

        number_of_rows = self.number_of_rows

        if number_of_rows > n:
            self._delete_rows(number_of_rows-n)
        elif number_of_rows < n:
            self._add_rows(n-number_of_rows)

        for i in range(n):
            for j in range(m):
                self._data_vars[i][j].set(data[i][j])
    
    def get_data(self):
        number_of_rows = self.number_of_rows
        number_of_columns = self.number_of_columns
        
        data = []
        for i in range(number_of_rows):
            row = []
            row_of_vars = self._data_vars[i]
            for j in range(number_of_columns):
                cell_data = row_of_vars[j].get()
                row.append(cell_data)
            
            data.append(row)
        return data

    @property
    def number_of_rows(self):
        return len(self._data_vars)

    @property
    def number_of_columns(self):
        return len(self._columns)

    def row(self, index, data=None):
        if data is None:
            row = []
            row_of_vars = self._data_vars[index]

            for j in range(self.number_of_columns):
                row.append(row_of_vars[j].get())
                
            return row
        else:
            number_of_columns = self.number_of_columns
            
            if len(data) != number_of_columns:
                raise ValueError("data has no %d elements: %s"%(number_of_columns, data))

            row_of_vars = self._data_vars[index]
            for j in range(number_of_columns):
                row_of_vars[index][j].set(data[j])

    def column(self, index, data=None):
        if data is None:
            column= []

            for i in range(self.number_of_rows):
                column.append(self._data_vars[i][index].get())
                
            return column
        else:
            number_of_rows = self.number_of_rows
            
            if len(data) != number_of_rows:
                raise ValueError("data has no %d elements: %s"%(number_of_rows, data))

            for i in range(number_of_columns):
                self._data_vars[i][index].set(data[i])

    def clear(self):
        for i in range(self.number_of_rows):
            for j in range(self.number_of_columns):
                self._data_vars[row][column].set("")

    def delete_row(self, index):
        column_indices = range(0, number_of_columns)

        for i in range(index, self.number_of_rows-1):
            row_of_vars_1 = self._data_vars[i]
            row_of_vars_2 = self._data_vars[i+1]
            for j in column_indices:
                row_of_vars_1[j].set(row_of_vars_2[j])

        self._delete_rows(1)

    def insert_row(self, data, index=END):
        column_indices = range(0, number_of_columns)

        if index == END:
            index = self.number_of_rows - 1
        
        self._add_rows(1)
        for i in range(self.number_of_rows-1, index-1, -1):
            row_of_vars_1 = self._data_vars[i-1]
            row_of_vars_2 = self._data_vars[i]
            for j in column_indices:
                row_of_vars_2[j].set(row_of_vars_1[j])
             
        row_of_vars = self._data_vars[index]
        for j in row_of_vars:
            row_of_vars[j].set(data[j].get())

    def cell(self, row, column, data=None):
        """Get the value of a table cell"""
        if data is None:
            self._data_vars[row][column].get()
        else:
            self._data_vars[row][column].set(data)

    def __getitem__(self, index):
        if isinstance(index, tuple):
            row, column = index
            return self.cell(row, column)
        else:
            raise Exception("Row and column indices are required")
        
    def __setitem__(self, index, value):
        if isinstance(index, tuple):
            row, column = index
            self.cell(row, column, value)
        else:
            raise Exception("Row and column indices are required")


if __name__ == "__main__":
    try:
        from Tkinter import Tk
    except ImportError:
        from tkinter import Tk

    root = Tk()

    table = Table(root, ["columna A", "columna B", "columna C"], column_minwidths=[None, None, None])
    table.pack(expand=True, fill=X, padx=10,pady=10)

    table.set_data([[1,2,3],[4,5,6], [7,8,9], [10,11,12]])
    table.cell(0,0, " a fdas fasd fasdf asdf asdfasdf asdf asdfa sdfas asd sadf ")
    root.mainloop()

Diff to Previous Revision

--- revision 3 2017-04-17 19:00:33
+++ revision 4 2017-04-19 13:39:43
@@ -1,5 +1,5 @@
 # Author: Miguel Martinez Lopez
-
+# Version: 0.1
 
 try:
     from Tkinter import Frame, Label, Message, StringVar
@@ -12,12 +12,15 @@
     """Base class for cells"""
 
 class Data_Cell(Cell):
-    def __init__(self, master, width, variable, anchor=W, bordercolor=None, padx=0, pady=0, background=None, foreground=None, font=None):
-        Cell.__init__(self, master, width=width, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)
-
-        self._message_widget = Message(self, width=width, textvariable=variable, font=font, background=background, foreground=foreground)
+    def __init__(self, master, variable, anchor=W, bordercolor=None, padx=0, pady=0, background=None, foreground=None, font=None):
+        Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)
+
+        self._message_widget = Message(self, textvariable=variable, font=font, background=background, foreground=foreground)
         self._message_widget.pack(expand=True, padx=padx, pady=pady, anchor=anchor)
-        
+
+    def configure_width(self, width):
+        self._message_widget.configure(width=width)
+
 class Header_Cell(Cell):
     def __init__(self, master, text, bordercolor=None, padx=None, pady=None, background=None, foreground=None, font=None, anchor=CENTER):
         Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)
@@ -29,7 +32,7 @@
             separator.pack(fill=X, anchor=anchor)
         
 class Table(Frame):
-    def __init__(self, master, columns, column_widths, minwidth=20,minheight=20, padx=5, pady=5, cell_font=None, cell_foreground="black", cell_background="white", header_font=None, header_background="white", header_foreground="black", header_anchor=CENTER,bordercolor = "#999999", stripped_rows=("#EEEEEE", "white"), cell_anchor=W):
+    def __init__(self, master, columns, column_weights=None, column_minwidths=None, height=None, minwidth=20,minheight=20, padx=5, pady=5, cell_font=None, cell_foreground="black", cell_background="white", header_font=None, header_background="white", header_foreground="black", header_anchor=CENTER,bordercolor = "#999999", stripped_rows=("#EEEEEE", "white"), cell_anchor=W):
         Frame.__init__(self,master, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)
 
         self._cell_background = cell_background
@@ -45,16 +48,46 @@
         self._bordercolor = bordercolor
 
         self._data_vars = []
+
         self._columns = columns
-        self._column_widths = column_widths
 
         for j in range(len(columns)):
             column_name = columns[j]
-            column_width = column_widths[j]
 
             header_cell = Header_Cell(self, text=column_name, font=header_font, background=header_background, foreground=header_foreground, padx=padx, pady=pady, bordercolor=bordercolor, anchor=header_anchor)
             header_cell.grid(row=0, column=j, sticky=N+E+W+S)
 
+        if column_weights is None:
+            column_weights =[1]*len(columns)
+
+        for j, weight in enumerate(column_weights):
+            self.grid_columnconfigure(j, weight=weight)
+        
+        self._column_weights = column_weights
+
+        if column_minwidths is not None:
+            self.update_idletasks()
+            for j, minwidth in enumerate(column_minwidths):
+                if minwidth is None:
+                    header_cell = self.grid_slaves(row=0, column=j)[0]
+                    minwidth = header_cell.winfo_reqwidth()
+                self.grid_columnconfigure(j, minsize=minwidth)
+
+        if height is not None:
+            self._add_rows(height)
+
+        self.bind("<Configure>", self._on_configure)
+    
+    def _on_configure(self, event):
+        for j, weight in enumerate(self._column_weights):
+            if weight > 0:
+                header_cell = self.grid_slaves(row=0, column=j)[0]
+                width = header_cell.winfo_width()
+
+                for i in range(self.number_of_rows):
+                    widget = self.grid_slaves(row=i+1, column=j)[0]
+                    widget.configure_width(width)
+        
     def _add_rows(self, n):
         number_of_rows = self.number_of_rows
         for i in range(number_of_rows+1, number_of_rows+n+1):
@@ -62,12 +95,11 @@
             for j in range(self.number_of_columns):
                 var = StringVar()
                 list_of_vars.append(var)
-                
-                width = self._column_widths[j]
+
                 if self._stripped_rows:
-                    cell = Data_Cell(self, width, variable=var, bordercolor=self._bordercolor, padx=self._padx, pady=self._pady, background=self._stripped_rows[(i+1)%2], foreground=self._cell_foreground, font=self._cell_font, anchor=self._cell_anchor)
+                    cell = Data_Cell(self, variable=var, bordercolor=self._bordercolor, padx=self._padx, pady=self._pady, background=self._stripped_rows[(i+1)%2], foreground=self._cell_foreground, font=self._cell_font, anchor=self._cell_anchor)
                 else:
-                    cell = Data_Cell(self, width, variable=var, bordercolor=self._bordercolor, padx=self._padx, pady=self._pady, background=self._cell_background, foreground=self._cell_foreground, font=self._cell_font, anchor=self._cell_anchor)
+                    cell = Data_Cell(self, variable=var, bordercolor=self._bordercolor, padx=self._padx, pady=self._pady, background=self._cell_background, foreground=self._cell_foreground, font=self._cell_font, anchor=self._cell_anchor)
                 cell.grid(row=i, column=j, sticky=N+E+W+S)
 
             self._data_vars.append(list_of_vars)
@@ -215,8 +247,8 @@
 
     root = Tk()
 
-    table = Table(root, ["columna A", "columna B", "columna C"], [100, 200, 100])
-    table.pack(padx=10,pady=10)
+    table = Table(root, ["columna A", "columna B", "columna C"], column_minwidths=[None, None, None])
+    table.pack(expand=True, fill=X, padx=10,pady=10)
 
     table.set_data([[1,2,3],[4,5,6], [7,8,9], [10,11,12]])
     table.cell(0,0, " a fdas fasd fasdf asdf asdfasdf asdf asdfa sdfas asd sadf ")

History