Welcome, guest | Sign In | My Account | Store | Cart
from Tkinter import *
from collections import OrderedDict, defaultdict
import itertools


class PannedGrid(Frame):
	def __init__(self, master, orient=HORIZONTAL, minsize=20, columnWidths = {}, rowHeights={}, fixedPanes = []):
		Frame.__init__(self,master)

		self.clear_table()

		if orient in (HORIZONTAL, VERTICAL):
			self.orient = orient
		else:
			raise Exception("orient must be 'horizontal' or 'vertical, not '%s'." % orient)

		if type(minsize) is int:
			self.minsize = minsize
		else:
			raise Exception("Minsize must be an integer.")

		self.fixedPanes = set(fixedPanes)

		self.__packSideBetweenPanes = LEFT if self.orient == HORIZONTAL else TOP
		self.__packSideInsidePane = TOP if self.orient == HORIZONTAL else LEFT
		self.__cellFill = X if self.orient == HORIZONTAL else Y

		self.userDefinedWidths = columnWidths
		self.userDefinedHeights = rowHeights

		self.updated = False
		self.areDimensionsCalculated = False


	def clear_table(self):
		self.__gridOfWidgets = {}

		self.__rows = defaultdict(set)
		self.__columns = defaultdict(set)

		self.__panes = {}

		self.rowHeightList = {}
		self.columnWidthList = {}

	def clear_definedWidths(self):
		self.userDefinedWidths = {}

	def clear_definedHeights(self):
		self.userDefinedHeights = {}

	def rowIndices(self):
		return self.__rows.keys()

	def columnIndices(self):
		return self.__column.keys()

	def numberOfRows(self):
		return len(self.__rows)

	def numberOfColumns(self):
		return len(self.__columns)

	def update_cell(self, coordinates, widget):
		if not self.updated:
			raise Exception("First you must to update the pannedgrid.")

		if coordinates in self.__gridOfWidgets:
			master = self.__gridOfWidgets[coordinates].pack_info()['in']
			self.__gridOfWidgets[coordinates].destroy()
			self.__gridOfWidgets[coordinates] = widget
			widget.pack(in_=master, expand=YES, fill=BOTH)
		else:
			raise Exception("There is no widget with coordiantes: %s." % coordinates)


	def update(self):
		if not self.__gridOfWidgets: return

		self.panedWindows = []

		if not self.areDimensionsCalculated:
			self.calculate_list_of_cell_widths_and_heights()

		listOfCellCoordinates = list(itertools.product(self.__rows, self.__columns))

		if self.orient == HORIZONTAL:
			listOfCellCoordinates.sort(key = lambda item: item[1])
		else:
			listOfCellCoordinates.sort()

		pane_index = self.__columns[0] if self.orient == HORIZONTAL else self.__rows[0]

		position_of_outer_index = 1 if self.orient == HORIZONTAL else 0

		newPanedWindow = True

		for cell_coordinates in listOfCellCoordinates:

			outer_index = cell_coordinates[position_of_outer_index]
			if pane_index != outer_index:
				# Creating a new pane
				pane_index = outer_index

				if outer_index in self.fixedPanes:
					frameOfPane = Frame(self)
					frameOfPane.pack(side=self.__packSideBetweenPanes)
					newPanedWindow = True
				else:
					if newPanedWindow:
						paneMaster = PanedWindow(self,orient=self.orient )
						paneMaster.pack(side=self.__packSideBetweenPanes)

						self.panedWindows.append(paneMaster)
						newPanedWindow = False

					frameOfPane = Frame(paneMaster)
					frameOfPane.pack()
					paneMaster.add(frameOfPane)

					paneMaster.paneconfigure(frameOfPane, minsize=self.minsize)

				sizeOfPane = self.columnWidthList[outer_index] if self.orient == HORIZONTAL else self.rowHeightList[outer_index]

				if self.orient == HORIZONTAL:
					frameOfPane.configure(width=sizeOfPane, height=self.tableHeight)
				else:
					frameOfPane.configure(width=self.tableWidth, height=sizeOfPane)

				frameOfPane.pack_propagate(False)

				self.__panes[pane_index] = frameOfPane

			cellFrame = Frame(frameOfPane, bd=1, relief=SUNKEN)
			cellFrame.pack(side=self.__packSideInsidePane, expand=YES, fill=self.__cellFill)
			cellFrame.pack_propagate(False)

			widget = self.__gridOfWidgets[cell_coordinates]

			if widget:
				widget.lift()
				widget.pack(in_=cellFrame, expand=YES, fill=BOTH)

				if self.orient == HORIZONTAL:
					cellFrame.configure(
										height=self.rowHeightList[ cell_coordinates[0] ],
									)
				else:
					cellFrame.configure(
										width=self.columnWidthList[ cell_coordinates[1] ]
									)

		self.updated = True

	def calculate_list_of_cell_widths_and_heights(self):
		self.rowHeightList = self.userDefinedHeights.copy()
		self.columnWidthList = self.userDefinedWidths.copy()

		for coordinate, widget in self.__gridOfWidgets.items():
			i , j = coordinate
			if i not in self.userDefinedHeights:
				self.rowHeightList[i] = max(self.rowHeightList.get(i,0),widget.winfo_reqheight())

			if j not in self.userDefinedWidths:
				self.columnWidthList[j] = max(self.columnWidthList.get(j,0),widget.winfo_reqwidth())


		self.tableHeight = sum(self.rowHeightList.values())
		self.tableWidth = sum(self.columnWidthList.values())

		self.areDimensionsCalculated = True

	def rowconfigure(self,row, height=None, fixed = None):
		if row not in self.__rows:
			raise Exception("Row %d doesn't exists." % column)

		self.updated = False

		if height is not None:
			self.areDimensionsCalculated = False

			if type(height) is not int: raise Exception("The height must be an integer:%s"%size)

			self.userDefinedHeights[row] = height

		if fixed is not None:
			if self.orient == HORIZONTAL:
				raise Exception("You can't set fixed property on a row because 'orient' is VERTICAL.")

			self.fixPane(index, fixed)

	def columnconfigure(self,column, width=None, fixed = None):
		if column not in self.__columns:
			raise Exception("Column %d doesn't exists." % column)

		self.updated = False

		if width is not None:
			self.areDimensionsCalculated = False

			if type(width) is not int: raise Exception("The width must be an integer:%s"%size)

			self.userDefinedWidths[column] = width

		if fixed is not None:
			if self.orient == VERTICAL:
				raise Exception("You can't set fixed property on a column because 'orient' is HORIZONTAL.")

			self.fixPane(index, fixed)

	def fixPane(index, fixed=True):
		if fixed == True:
			self.fixedPanes.add(index)
		elif fixed == False:
			self.fixedPanes.discard(index)
		else:
			raise Exception("fixed must be 'True' or 'False'.")

	def fixAllPanes():
		fixedPanes_list = self.__columns.keys() if self.orient == HORIZONTAL else self.__rows.keys()
		self.fixedPanes = set(fixedPanes_list)

	def realeaseAllPanes():
		self.fixedPanes = set()

	def __getitem__(self, coordinates):
		return self.__gridOfWidgets.get(coordinates)

	def __setitem__(self,coordinates, widget):
		self.updated = False
		self.areDimensionsCalculated = False

		self.__gridOfWidgets[coordinates] = widget

		i,j = coordinates
		self.__rows[i].add(j)
		self.__columns[j].add(i)

	def __delitem__(self, coordinates ):
		self.updated = False
		self.areDimensionsCalculated = False

		del self.__gridOfWidgets[coordinates]

		i,j = coordinates

		self.__rows[i].discard(j)
		if not self.__rows[i]: del self.__rows[i]

		self.__columns[j].discard(i)
		if not self.__columns[j]: del self.__columns[j]


	def grid(self, *args, **kargs):
		if not self.updated: self.update()
		Frame.grid(self,*args, **kargs)

	def pack(self, *args, **kargs):
		if not self.updated: self.update()
		Frame.pack(self,*args, **kargs)

def test():
	import random

	root = Tk()

	def table_of_random_widgets(master, numRows,numColumns, **kwargs):
		import ttk
		# Falta pensar un nombre para estos atributos: columnWidthList = None, rowHeightList = None

		demo = PannedGrid(master, **kwargs)

		tkinterWidgets =  (
					(Label, {'text':'This is a label'}),
					(Label, {'text':'This is another label', 'bg':'yellow'}),
					(Checkbutton,{}),
					(Button, {'text':'Click me'}),
					(ttk.Combobox, {'values':('item1', 'item2','item3','item4')})
					)

		for i in range(numRows):
			for j in range(numColumns):
				widgetClass, args = random.choice(tkinterWidgets)
				widget = widgetClass(demo,**args)

				demo[i,j] =  widget

		return demo

	Label(root, text="orient=VERTICAL, minsize = 20, fixed rows = [2,5]").pack(anchor=NW, pady=6, padx='2m')
	table = table_of_random_widgets(root, numRows=6,numColumns=5, orient=VERTICAL, minsize=20, fixedPanes=[2,5])
	table.pack(anchor=NW,pady=2, padx='2m')

	emptySpace = Frame(root, height =20)
	emptySpace.pack()


	Label(root, text='''orient=HORIZONTAL, minsize = 30, fixed columns = [2,5], columnWidths={0:200, 2: 150, 3:250}, rowHeights={0:150}\nOn this table we will update later the cell (1,2)''', justify=LEFT).pack(anchor=NW, pady=6, padx='2m')

	table = table_of_random_widgets(root, numRows=3,numColumns=6, minsize=30, fixedPanes=[2,5], columnWidths={0:200, 2: 150, 3:250}, rowHeights={0:90})
	table.pack(anchor=NW,pady=2, padx='2m')

	table.update_cell((1,2), Label(table,text="Cell updated",bg="green") )

	emptySpace = Frame(root, height =20)
	emptySpace.pack()

	print "On the last table:"
	print "\tnumber of rows: ", table.numberOfRows()
	print "\tnumber of columns: ", table.numberOfColumns()
	root.mainloop()


if __name__ == '__main__':
	test()

Diff to Previous Revision

--- revision 4 2014-05-07 15:52:22
+++ revision 5 2014-06-03 20:45:59
@@ -1,125 +1,314 @@
 from Tkinter import *
+from collections import OrderedDict, defaultdict
+import itertools
+
 
 class PannedGrid(Frame):
-	def __init__(self, master, tableOfWidgets,  minsize='auto', columnWidthList= None, fixedColumns=[]):
+	def __init__(self, master, orient=HORIZONTAL, minsize=20, columnWidths = {}, rowHeights={}, fixedPanes = []):
 		Frame.__init__(self,master)
 
-		self.numberOfRows = len(tableOfWidgets)
-		self.numberOfColumns = len(tableOfWidgets[0])
-		self.rowHeightList = [0]*self.numberOfRows
-		
-		self.columnWidthList = [0]*self.numberOfColumns
-
-		self.listOfColumnsFrames = []
-
-		self.gridOfCellWidgets = \
-			[[None for j in range(self.numberOfColumns)] for i in range(self.numberOfRows)]
-
-
-		typeOfColumn = 'fixed'
-		
-		for j in range(self.numberOfColumns):
-			if j in fixedColumns:
-				masterFrame = Frame(self)
-				typeOfColumn = 'fixed'
-			elif typeOfColumn == 'fixed':
-				masterFrame = PanedWindow(self,orient=HORIZONTAL   )
-				typeOfColumn = 'mobile'
-			
-			masterFrame.pack(side=LEFT)
-			
-			columnFrame = Frame(masterFrame)
-			columnFrame.pack()
-			columnFrame.pack_propagate(False)
-			
-			if typeOfColumn == 'mobile':
-				masterFrame.add(columnFrame)
-
-			self.listOfColumnsFrames.append((typeOfColumn,masterFrame,columnFrame))
-			
-			for i in range(self.numberOfRows):
-				cellFrame = Frame(columnFrame, bd=1, relief=SUNKEN)
-				cellFrame.pack(expand=YES, fill=X)
-				cellFrame.pack_propagate(False)
-				
-				widgetDefinition = tableOfWidgets[i][j]
-
-				classObject, kwargs = widgetDefinition
-
-				widget = classObject(cellFrame, **kwargs)
-				widget.pack(expand=YES, fill=BOTH)
-
-				self.rowHeightList[i] = max(self.rowHeightList[i],widget.winfo_reqheight())
-				
-				self.columnWidthList[j] = max(self.columnWidthList[j],widget.winfo_reqwidth())
-
-				self.gridOfCellWidgets[i][j]= cellFrame
-
-
-		tableHeight = sum(self.rowHeightList)
-
-
-		for indexColumn, (typeOfColumn,masterFrame,columnFrame) in enumerate(self.listOfColumnsFrames):
-			if columnWidthList:
-				if columnWidthList[indexColumn] == 'auto':
-					columnWidth = self.columnWidthList[indexColumn]
+		self.clear_table()
+
+		if orient in (HORIZONTAL, VERTICAL):
+			self.orient = orient
+		else:
+			raise Exception("orient must be 'horizontal' or 'vertical, not '%s'." % orient)
+
+		if type(minsize) is int:
+			self.minsize = minsize
+		else:
+			raise Exception("Minsize must be an integer.")
+
+		self.fixedPanes = set(fixedPanes)
+
+		self.__packSideBetweenPanes = LEFT if self.orient == HORIZONTAL else TOP
+		self.__packSideInsidePane = TOP if self.orient == HORIZONTAL else LEFT
+		self.__cellFill = X if self.orient == HORIZONTAL else Y
+
+		self.userDefinedWidths = columnWidths
+		self.userDefinedHeights = rowHeights
+
+		self.updated = False
+		self.areDimensionsCalculated = False
+
+
+	def clear_table(self):
+		self.__gridOfWidgets = {}
+
+		self.__rows = defaultdict(set)
+		self.__columns = defaultdict(set)
+
+		self.__panes = {}
+
+		self.rowHeightList = {}
+		self.columnWidthList = {}
+
+	def clear_definedWidths(self):
+		self.userDefinedWidths = {}
+
+	def clear_definedHeights(self):
+		self.userDefinedHeights = {}
+
+	def rowIndices(self):
+		return self.__rows.keys()
+
+	def columnIndices(self):
+		return self.__column.keys()
+
+	def numberOfRows(self):
+		return len(self.__rows)
+
+	def numberOfColumns(self):
+		return len(self.__columns)
+
+	def update_cell(self, coordinates, widget):
+		if not self.updated:
+			raise Exception("First you must to update the pannedgrid.")
+
+		if coordinates in self.__gridOfWidgets:
+			master = self.__gridOfWidgets[coordinates].pack_info()['in']
+			self.__gridOfWidgets[coordinates].destroy()
+			self.__gridOfWidgets[coordinates] = widget
+			widget.pack(in_=master, expand=YES, fill=BOTH)
+		else:
+			raise Exception("There is no widget with coordiantes: %s." % coordinates)
+
+
+	def update(self):
+		if not self.__gridOfWidgets: return
+
+		self.panedWindows = []
+
+		if not self.areDimensionsCalculated:
+			self.calculate_list_of_cell_widths_and_heights()
+
+		listOfCellCoordinates = list(itertools.product(self.__rows, self.__columns))
+
+		if self.orient == HORIZONTAL:
+			listOfCellCoordinates.sort(key = lambda item: item[1])
+		else:
+			listOfCellCoordinates.sort()
+
+		pane_index = self.__columns[0] if self.orient == HORIZONTAL else self.__rows[0]
+
+		position_of_outer_index = 1 if self.orient == HORIZONTAL else 0
+
+		newPanedWindow = True
+
+		for cell_coordinates in listOfCellCoordinates:
+
+			outer_index = cell_coordinates[position_of_outer_index]
+			if pane_index != outer_index:
+				# Creating a new pane
+				pane_index = outer_index
+
+				if outer_index in self.fixedPanes:
+					frameOfPane = Frame(self)
+					frameOfPane.pack(side=self.__packSideBetweenPanes)
+					newPanedWindow = True
 				else:
-					columnWidth = columnWidthList[indexColumn]
-			else:
-				columnWidth = self.columnWidthList[indexColumn]
-			
-			columnFrame.configure(width=columnWidth, height=tableHeight)
-			
-			if typeOfColumn == 'mobile':
-				if minsize == 'auto':
-					masterFrame.paneconfigure(columnFrame, minsize=self.columnWidthList[indexColumn])
-				elif type(minsize) is int:
-					masterFrame.paneconfigure(columnFrame, minsize=minsize)
+					if newPanedWindow:
+						paneMaster = PanedWindow(self,orient=self.orient )
+						paneMaster.pack(side=self.__packSideBetweenPanes)
+
+						self.panedWindows.append(paneMaster)
+						newPanedWindow = False
+
+					frameOfPane = Frame(paneMaster)
+					frameOfPane.pack()
+					paneMaster.add(frameOfPane)
+
+					paneMaster.paneconfigure(frameOfPane, minsize=self.minsize)
+
+				sizeOfPane = self.columnWidthList[outer_index] if self.orient == HORIZONTAL else self.rowHeightList[outer_index]
+
+				if self.orient == HORIZONTAL:
+					frameOfPane.configure(width=sizeOfPane, height=self.tableHeight)
 				else:
-					raise Exception("Minsize must be 'auto' or an integer.")
-
-		
-		for i in range(self.numberOfRows):
-			for j in range(self.numberOfColumns):
-				self.gridOfCellWidgets[i][j].configure(height=self.rowHeightList[i])
-
+					frameOfPane.configure(width=self.tableWidth, height=sizeOfPane)
+
+				frameOfPane.pack_propagate(False)
+
+				self.__panes[pane_index] = frameOfPane
+
+			cellFrame = Frame(frameOfPane, bd=1, relief=SUNKEN)
+			cellFrame.pack(side=self.__packSideInsidePane, expand=YES, fill=self.__cellFill)
+			cellFrame.pack_propagate(False)
+
+			widget = self.__gridOfWidgets[cell_coordinates]
+
+			if widget:
+				widget.lift()
+				widget.pack(in_=cellFrame, expand=YES, fill=BOTH)
+
+				if self.orient == HORIZONTAL:
+					cellFrame.configure(
+										height=self.rowHeightList[ cell_coordinates[0] ],
+									)
+				else:
+					cellFrame.configure(
+										width=self.columnWidthList[ cell_coordinates[1] ]
+									)
+
+		self.updated = True
+
+	def calculate_list_of_cell_widths_and_heights(self):
+		self.rowHeightList = self.userDefinedHeights.copy()
+		self.columnWidthList = self.userDefinedWidths.copy()
+
+		for coordinate, widget in self.__gridOfWidgets.items():
+			i , j = coordinate
+			if i not in self.userDefinedHeights:
+				self.rowHeightList[i] = max(self.rowHeightList.get(i,0),widget.winfo_reqheight())
+
+			if j not in self.userDefinedWidths:
+				self.columnWidthList[j] = max(self.columnWidthList.get(j,0),widget.winfo_reqwidth())
+
+
+		self.tableHeight = sum(self.rowHeightList.values())
+		self.tableWidth = sum(self.columnWidthList.values())
+
+		self.areDimensionsCalculated = True
+
+	def rowconfigure(self,row, height=None, fixed = None):
+		if row not in self.__rows:
+			raise Exception("Row %d doesn't exists." % column)
+
+		self.updated = False
+
+		if height is not None:
+			self.areDimensionsCalculated = False
+
+			if type(height) is not int: raise Exception("The height must be an integer:%s"%size)
+
+			self.userDefinedHeights[row] = height
+
+		if fixed is not None:
+			if self.orient == HORIZONTAL:
+				raise Exception("You can't set fixed property on a row because 'orient' is VERTICAL.")
+
+			self.fixPane(index, fixed)
+
+	def columnconfigure(self,column, width=None, fixed = None):
+		if column not in self.__columns:
+			raise Exception("Column %d doesn't exists." % column)
+
+		self.updated = False
+
+		if width is not None:
+			self.areDimensionsCalculated = False
+
+			if type(width) is not int: raise Exception("The width must be an integer:%s"%size)
+
+			self.userDefinedWidths[column] = width
+
+		if fixed is not None:
+			if self.orient == VERTICAL:
+				raise Exception("You can't set fixed property on a column because 'orient' is HORIZONTAL.")
+
+			self.fixPane(index, fixed)
+
+	def fixPane(index, fixed=True):
+		if fixed == True:
+			self.fixedPanes.add(index)
+		elif fixed == False:
+			self.fixedPanes.discard(index)
+		else:
+			raise Exception("fixed must be 'True' or 'False'.")
+
+	def fixAllPanes():
+		fixedPanes_list = self.__columns.keys() if self.orient == HORIZONTAL else self.__rows.keys()
+		self.fixedPanes = set(fixedPanes_list)
+
+	def realeaseAllPanes():
+		self.fixedPanes = set()
+
+	def __getitem__(self, coordinates):
+		return self.__gridOfWidgets.get(coordinates)
+
+	def __setitem__(self,coordinates, widget):
+		self.updated = False
+		self.areDimensionsCalculated = False
+
+		self.__gridOfWidgets[coordinates] = widget
+
+		i,j = coordinates
+		self.__rows[i].add(j)
+		self.__columns[j].add(i)
+
+	def __delitem__(self, coordinates ):
+		self.updated = False
+		self.areDimensionsCalculated = False
+
+		del self.__gridOfWidgets[coordinates]
+
+		i,j = coordinates
+
+		self.__rows[i].discard(j)
+		if not self.__rows[i]: del self.__rows[i]
+
+		self.__columns[j].discard(i)
+		if not self.__columns[j]: del self.__columns[j]
+
+
+	def grid(self, *args, **kargs):
+		if not self.updated: self.update()
+		Frame.grid(self,*args, **kargs)
+
+	def pack(self, *args, **kargs):
+		if not self.updated: self.update()
+		Frame.pack(self,*args, **kargs)
 
 def test():
 	import random
 
 	root = Tk()
 
-	def build_random_table(master, numRows,numColumns, minsize, fixedColumns, columnWidthList = None):
+	def table_of_random_widgets(master, numRows,numColumns, **kwargs):
 		import ttk
-		
+		# Falta pensar un nombre para estos atributos: columnWidthList = None, rowHeightList = None
+
+		demo = PannedGrid(master, **kwargs)
+
 		tkinterWidgets =  (
-					(Label, {'text':'This is a label'}), 
+					(Label, {'text':'This is a label'}),
 					(Label, {'text':'This is another label', 'bg':'yellow'}),
 					(Checkbutton,{}),
 					(Button, {'text':'Click me'}),
 					(ttk.Combobox, {'values':('item1', 'item2','item3','item4')})
 					)
-		tableGrid = []
-
 
 		for i in range(numRows):
-			row = [random.choice(tkinterWidgets) for j in range(numColumns) ]
-
-			tableGrid.append(row)
-
-		PannedGrid(master, tableGrid,minsize=minsize,fixedColumns=fixedColumns, columnWidthList=columnWidthList).pack(anchor=NW,pady=2, padx='2m')
-
-	Label(root, text="minsize = 20, fixed columns = [2,5]").pack(anchor=NW, pady=6, padx='2m') 
-	build_random_table(root, numRows=4,numColumns=6, minsize=20, fixedColumns=[2,5])	
-
-	emptySpace = Frame(root, height =30)
+			for j in range(numColumns):
+				widgetClass, args = random.choice(tkinterWidgets)
+				widget = widgetClass(demo,**args)
+
+				demo[i,j] =  widget
+
+		return demo
+
+	Label(root, text="orient=VERTICAL, minsize = 20, fixed rows = [2,5]").pack(anchor=NW, pady=6, padx='2m')
+	table = table_of_random_widgets(root, numRows=6,numColumns=5, orient=VERTICAL, minsize=20, fixedPanes=[2,5])
+	table.pack(anchor=NW,pady=2, padx='2m')
+
+	emptySpace = Frame(root, height =20)
 	emptySpace.pack()
-	
-	Label(root, text="minsize = auto, fixed columns = [2,5], columnWidthList=[200, 'auto', 200, 'auto', 250, 'auto']").pack(anchor=NW, pady=6, padx='2m') 
-	build_random_table(root, numRows=4,numColumns=6, minsize='auto', columnWidthList=[200, 'auto', 200, 'auto', 250, 'auto'], fixedColumns=[2,5])
-	
+
+
+	Label(root, text='''orient=HORIZONTAL, minsize = 30, fixed columns = [2,5], columnWidths={0:200, 2: 150, 3:250}, rowHeights={0:150}\nOn this table we will update later the cell (1,2)''', justify=LEFT).pack(anchor=NW, pady=6, padx='2m')
+
+	table = table_of_random_widgets(root, numRows=3,numColumns=6, minsize=30, fixedPanes=[2,5], columnWidths={0:200, 2: 150, 3:250}, rowHeights={0:90})
+	table.pack(anchor=NW,pady=2, padx='2m')
+
+	table.update_cell((1,2), Label(table,text="Cell updated",bg="green") )
+
+	emptySpace = Frame(root, height =20)
+	emptySpace.pack()
+
+	print "On the last table:"
+	print "\tnumber of rows: ", table.numberOfRows()
+	print "\tnumber of columns: ", table.numberOfColumns()
 	root.mainloop()
-
 
 
 if __name__ == '__main__':

History