# Author: Miguel Martinez Lopez
# Version: 0.3
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, borderwidth=1, padx=0, pady=0, background=None, foreground=None, font=None):
Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=borderwidth, 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, borderwidth=1, padx=None, pady=None, background=None, foreground=None, font=None, anchor=CENTER):
Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=borderwidth, 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", innerborder=True, stripped_rows=("#EEEEEE", "white"), cell_anchor=W, on_change_data=None):
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
if innerborder:
self._innerborder_width=1
else:
self._innerborder_width=0
self._data_vars = []
self._columns = columns
for j in range(len(columns)):
column_name = columns[j]
header_cell = Header_Cell(self, text=column_name, borderwidth=self._innerborder_width, 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._on_change_data = on_change_data
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, borderwidth=self._innerborder_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)
else:
cell = Data_Cell(self, borderwidth=self._innerborder_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.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])
if self._on_change_data is not None: self._on_change_data()
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])
if self._on_change_data is not None: self._on_change_data()
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])
if self._on_change_data is not None: self._on_change_data()
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("")
if self._on_change_data is not None: self._on_change_data()
def delete_row(self, index):
column_indices = range(0, self.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)
if self._on_change_data is not None: self._on_change_data()
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())
if self._on_change_data is not None: self._on_change_data()
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)
if self._on_change_data is not None: self._on_change_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")
def on_change_data(self, callback):
self._on_change_data = callback
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 7 2017-04-19 15:58:02
+++ revision 8 2017-04-20 09:02:07
@@ -1,5 +1,5 @@
# Author: Miguel Martinez Lopez
-# Version: 0.2
+# Version: 0.3
try:
from Tkinter import Frame, Label, Message, StringVar
@@ -12,8 +12,8 @@
"""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)
+ def __init__(self, master, variable, anchor=W, bordercolor=None, borderwidth=1, padx=0, pady=0, background=None, foreground=None, font=None):
+ Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=borderwidth, 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)
@@ -22,8 +22,8 @@
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)
+ def __init__(self, master, text, bordercolor=None, borderwidth=1, padx=None, pady=None, background=None, foreground=None, font=None, anchor=CENTER):
+ Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=borderwidth, 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)
@@ -32,7 +32,7 @@
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, on_change_data=None):
+ 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", innerborder=True, stripped_rows=("#EEEEEE", "white"), cell_anchor=W, on_change_data=None):
Frame.__init__(self,master, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=1, bd= 0)
self._cell_background = cell_background
@@ -41,11 +41,15 @@
self._cell_anchor = cell_anchor
self._stripped_rows = stripped_rows
-
+
self._padx = padx
self._pady = pady
self._bordercolor = bordercolor
+ if innerborder:
+ self._innerborder_width=1
+ else:
+ self._innerborder_width=0
self._data_vars = []
@@ -54,7 +58,7 @@
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 = Header_Cell(self, text=column_name, borderwidth=self._innerborder_width, 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:
@@ -99,9 +103,9 @@
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)
+ cell = Data_Cell(self, borderwidth=self._innerborder_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)
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 = Data_Cell(self, borderwidth=self._innerborder_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.grid(row=i, column=j, sticky=N+E+W+S)
self._data_vars.append(list_of_vars)