Welcome, guest | Sign In | My Account | Store | Cart
# Version 1.1
# Author: Miguel Martinez Lopez
# Uncomment the next line to see my email
# print "Email: ", "61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex")


from __future__ import print_function

from collections import defaultdict
import itertools


try:
	from tkinter import *
except ImportError:
	from Tkinter import *




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

		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.__panes = {}

		self.is_builded = False

		self.clear_table()

		self.is_displayed = False

	def clear_table(self):
		if not self.is_builded:
			self.__gridOfWidgets = {}

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

			self.__rowHeights = {}
			self.__columnWidths = {}

			self.userDefinedWidths = {}
			self.userDefinedHeights = {}
			
			self.calculate_dimensions = True
			
		else:
			raise Exception("You can't clear the table one this is is_builded.")

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

	@property
	def columnIndices(self):
		return self.__columns.keys()

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

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

	def update_cell(self, coordinates, widget):
		if not self.is_builded:
			raise Exception("First you must to build the panedgrid.")

		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 build(self):
		if self.is_builded:
			raise Exception("You have just is_builded the grid before.")

		if not self.__gridOfWidgets: return

		if self.calculate_dimensions:
			self.__calculate_list_of_cell_widths_and_heights()

		self.panedwindows = []

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

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

		# We set up the first pane index
		pane_index = self.__columns[0] if self.orient == HORIZONTAL else self.__rows[0]

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

		newPanedWindow = True

		for cell_coordinates in listOfCellCoordinates:

			main_index = cell_coordinates[position_of_main_index]
			if pane_index != main_index:
				# Creating a new pane
				pane_index = main_index

				if pane_index in self.fixedPanes:
					frameOfPane = Frame(self)
					frameOfPane.pack(side=self.__packSideBetweenPanes)
					newPanedWindow = True
				else:
					if newPanedWindow:
						paneMaster = PanedWindow(self,orient=self.orient, bd=0, sashwidth=3)
						paneMaster.index_range = (pane_index, pane_index + 1 )

						paneMaster.pack(side=self.__packSideBetweenPanes)

						self.panedwindows.append(paneMaster)
						newPanedWindow = False
					else:
						beginning_of_range = paneMaster.index_range[0]
						ending_of_range = pane_index + 1
						paneMaster.index_range = (beginning_of_range, ending_of_range)

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

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

				sizeOfPane = self.__columnWidths[pane_index] if self.orient == HORIZONTAL else self.__rowHeights[pane_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.__rowHeights[ cell_coordinates[0] ] )
				else:
					cellFrame.configure( width=self.__columnWidths[ cell_coordinates[1] ] )

		self.is_builded = True

	def __calculate_list_of_cell_widths_and_heights(self):
		self.__rowHeights = self.userDefinedHeights.copy()
		self.__columnWidths = self.userDefinedWidths.copy()

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

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

		self.calculate_dimensions = False

	@property
	def tableHeight(self):
		if self.calculate_dimensions:
			self.__calculate_list_of_cell_widths_and_heights()
			
		return sum(self.__rowHeights.values())

	@property
	def tableWidth(self):
		if self.calculate_dimensions:
			self.__calculate_list_of_cell_widths_and_heights()
		return sum(self.__columnWidths.values())


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

		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.fix_pane(index, fixed)
			
		if height is not None:

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

			self.userDefinedHeights[row] = height
			self.__rowHeights[row] = height
			self.calculate_dimensions = True
			
			if self.is_builded:
				if self.orient == VERTICAL:
					if self.is_a_fixed_pane(row):
						self.__panes[row].configure(height=height)
					else:
						self.__set_mobile_pane_size(row, height)
				else:
					for column in self.__rows[row]:
						cellFrame = self.__gridOfWidgets[row,column].pack_info()['in']
						cellFrame.configure(height=height)


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

		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.fix_pane(index, fixed)
					
		if width is not None:
			if type(width) is not int: raise Exception("The width must be an integer:%s"%width)

			self.userDefinedWidths[column] = width
			self.__columnWidths[column] = width
			self.calculate_dimensions = True
			
			if self.is_builded:
				if self.orient == HORIZONTAL:
					if self.is_a_fixed_pane(column):
						self.__panes[column].configure(width=width)
					else:
						self.__set_mobile_pane_size(column, width)
				else:
					for row in self.__columns[column]:
						cellFrame = self.__gridOfWidgets[row,column].pack_info()['in']
						cellFrame.configure(width=width)


	def fix_pane(index, fixed=True):
		if self.is_builded:
			raise Exception("You can't modify the fixed of the panes once you is_builded the grid.")

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

	def fix_all_panes(self):
		if self.is_builded:
			raise Exception("You can't modify the fixed of the panes once you is_builded the grid.")

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

	def realease_all_panes(self):
		if self.is_builded:
			raise Exception("You can't modify the fixed of the panes once you is_builded the grid.")

		self.fixedPanes = set()

	def is_a_fixed_pane(self, index ):
		paneWidget = self.__panes[index]

		parent = self.nametowidget( paneWidget.winfo_parent() )

		if parent.winfo_class() == 'Panedwindow':
			return False
		else:
			return True

	def __get_panedwindow_INFO(self, index):
		panedwindow = self.nametowidget( self.__panes[index].winfo_parent() )
		panedwindow_index = index - panedwindow.index_range[0]
		return panedwindow, panedwindow_index
				
	def __set_mobile_pane_size(self, index, size):
		panedwindow, panedwindow_index = self.__get_panedwindow_INFO(index)
		self.__set_pane_size_in_panedwindow(panedwindow, panedwindow_index, size)

	def __get_mobile_pane_size(self, index):
		panedwindow, panedwindow_index = self.__get_panedwindow_INFO(index)
		return self.__get_pane_size_in_panedwindow(panedwindow, panedwindow_index)

	def __set_pane_size_in_panedwindow(self,panedwindow, index, size):
		sash_coordinates = [0,0]
		last_index = len(panedwindow.panes()) - 1 
		
		if panedwindow['orient'] == HORIZONTAL:
			# Coordinate X
			position_coord = 0
				
			method_for_size_info = 'winfo_reqwidth'
		else:
			# Coordinate Y
			position_coord = 1
				
			method_for_size_info = 'winfo_reqheight'
					
					
		if index == 0:
			sash_index = 0			
			new_position = size			
		elif index == last_index:
			sash_index = last_index - 1
			new_position = getattr(panedwindow, method_for_size_info)() - 2*panedwindow['bd'] \
							- panedwindow['sashpad'] - panedwindow['sashwidth'] \
							- size			
		else:
			sash_index = index - 1
			new_position = panedwindow.sash_coord(index)[position_coord] \
							- panedwindow['sashpad'] - panedwindow['sashwidth'] \
							- size

		sash_coordinates[position_coord] = new_position
			
		panedwindow.sash_place(sash_index, *sash_coordinates)

	def __get_pane_size_in_panedwindow(self, panedwindow, index):
		if panedwindow['orient'] == HORIZONTAL:
			# Coordinate X
			position_coord = 0
				
			method_for_size_info = 'winfo_reqwidth'
		else:
			# Coordinate Y
			position_coord = 1
				
			method_for_size_info = 'winfo_reqwidth'
			
		last_index = len(panedwindow.panes()) - 1 
							
		if index == 0:
			pane_size = panedwindow.sash_coord(index)[position_coord]
		elif index == last_index:
			pane_size = getattr(panedwindow, method_for_size_info)() - 2*panedwindow['bd'] \
						- panedwindow.sash_coord(index-1)[position_coord] \
						- panedwindow['sashpad'] - panedwindow['sashwidth']
		else:
			pane_size = panedwindow.sash_coord(index)[position_coord] \
						- panedwindow.sash_coord(index-1)[position_coord] \
						- panedwindow['sashpad'] - panedwindow['sashwidth']
					
		return pane_size
		
	def get_row_height(self, row):
		if self.calculate_dimensions:
			self.__calculate_list_of_cell_widths_and_heights()

		if self.orient == VERTICAL:
			if self.is_builded:
				if self.is_a_fixed_pane(row):
					return self.__rowHeights[row]
				else:
					return self.__get_mobile_pane_size(row)
			else:
				return self.__rowHeights[row]
		else:
			return self.__rowHeights[row]

	def get_column_width(self, column):
		if self.calculate_dimensions:
			self.__calculate_list_of_cell_widths_and_heights()
		if self.orient == HORIZONTAL:
			if self.is_builded:
				if self.is_a_fixed_pane(column):
					return self.__columnWidths[column]
				else:
					return self.__get_mobile_pane_size(column)
			else:
				return self.__columnWidths[column]
		else:
			return self.__columnWidths[column]

	def check_coordinates(self, coordinates):
		if type(coordinates) is int:
			i = 0
			j = coordinates
		else:
			try:
				i, j = coordinates
				if type(i) is not int or type(j) is not int:
					raise Exception("Invalid coordinate")
			except:
				raise Exception("Invalid coordinate")

		return (i,j)

	def __getitem__(self, coordinates):
		coordinates = self.check_coordinates(coordinates)

		return self.__gridOfWidgets.get(coordinates)


	def __setitem__(self,coordinates, widget):
		if self.is_builded:
			raise Exception("The panedgrid was is_builded. Use cell_update() instead.")

		i , j = self.check_coordinates(coordinates)

		self.__gridOfWidgets[i,j] = widget

		self.__rows[i].add(j)
		self.__columns[j].add(i)

		self.calculate_dimensions = True

	def __delitem__(self, coordinates ):
		if self.is_builded:
			raise Exception("The panedgrid was is_builded. Use cell_update() instead.")

		i, j = self.check_coordinates(coordinates)

		del self.__gridOfWidgets[i,j]

		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]

		self.calculate_dimensions = True

	def grid(self, *args, **kargs):
		if not self.is_builded: self.build()
		Frame.grid(self,*args, **kargs)
		self.is_displayed = True
		
	def pack(self, *args, **kargs):
		if not self.is_builded: self.build()
		Frame.pack(self,*args, **kargs)
		self.is_displayed = True

	def place(self, *args, **kargs):
		if not self.is_builded: self.build()
		Frame.place(self,*args, **kargs)
		self.is_displayed = True

def test():
	try:
		from tkinter import ttk
	except ImportError:
		import ttk
			
	root = Tk()

	def table_of_random_widgets(master, numRows,numColumns, **kwargs):
		import random
		demo = PanedGrid(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')
	table1 = table_of_random_widgets(root, numRows=6,numColumns=5, orient=VERTICAL, minsize=20, fixedPanes=[2,5])
	table1.pack(anchor=NW,pady=2, padx='2m')

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


	Label(root, text='''orient=HORIZONTAL, minsize = 30, fixed columns = [2], 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')

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

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

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

	def show_table_data():
		tableName = TableVar.get()
		if tableName=='top table':
			table = table1
		elif tableName=='bottom table':
			table = table2
		else:
			return
		
		StringData = ""
		
		StringData += "On the last table:\n"
		StringData += "\tnumber of rows: %d\n" % table.numberOfRows
		StringData +="\tnumber of columns: %d\n" % table.numberOfColumns
		StringData +="\tTable width: %d\n" % table.tableWidth
		StringData +="\tTable height: %d\n" % table.tableHeight
	
		StringData +="\n\tHeights:\n"
	
		for row in table.rowIndices:
			StringData += "\t\trow %s: %s\n" % (row, table.get_row_height(row) ) 
	
		StringData +="\n\tWidths:\n"
	
		for column in table.columnIndices:
			StringData += "\t\tcolumn %s: %s\n" % (column, table.get_column_width(column) )

		print( StringData)

	def change_pane_size():
		tableName = TableVar.get()
		
		if tableName=='top table':
			table = table1
		elif tableName=='bottom table':
			table = table2
		else:
			return

		if table.orient == HORIZONTAL:
			table.columnconfigure(PaneVar.get(),SizeVar.get() ) 
		else:
			table.rowconfigure(PaneVar.get(),SizeVar.get() ) 

	emptySpace = Frame(root, height =40)
	emptySpace.pack()
	
	ControlArea = LabelFrame(root, text ="Control area", labelanchor=N)
	ControlArea.pack(expand=YES, fill=X, padx=20)

	ControlArea1 = Frame(ControlArea)
	ControlArea1.pack(expand=YES, fill=X, padx = 13)
		
	Button(ControlArea1, text="Show Data Table", command= lambda : show_table_data() ).pack(side=LEFT)
	
	ControlArea2 = Frame(ControlArea)
	ControlArea2.pack(expand=YES, fill=X, padx = 13)

	Label(ControlArea2, text="Table: ").pack(side=LEFT)
	TableVar = StringVar()
	TableVar.set('top table')
	OptionMenu(ControlArea2, TableVar,'top table', 'bottom table').pack(side=LEFT)

	
	Label(ControlArea2, text="Pane index: ").pack(side=LEFT)
	PaneVar = IntVar()
	Pane_entry = Entry(ControlArea2, textvariable = PaneVar, width=2)
	Pane_entry.pack(side=LEFT)
	Pane_entry.bind('<Return>',  lambda event: change_pane_size() )
	Pane_entry.focus()
	
	
	Label(ControlArea2, text="Size: ").pack(side=LEFT)
	SizeVar = IntVar()
	Size_entry = Entry(ControlArea2, textvariable = SizeVar, width=4)
	Size_entry.pack(side=LEFT)
	Size_entry.bind('<Return>',  lambda event: change_pane_size() )
	
	
	Button(ControlArea2, text="Change pane size", command = lambda: change_pane_size() ).pack(side=LEFT)
	
	root.mainloop()


if __name__ == '__main__':
	test()

Diff to Previous Revision

--- revision 15 2014-06-18 13:39:43
+++ revision 16 2014-09-17 16:20:21
@@ -1,11 +1,22 @@
-# Version 0.6
+# Version 1.1
 # Author: Miguel Martinez Lopez
 # Uncomment the next line to see my email
 # print "Email: ", "61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex")
 
-from Tkinter import *
+
+from __future__ import print_function
+
 from collections import defaultdict
 import itertools
+
+
+try:
+	from tkinter import *
+except ImportError:
+	from Tkinter import *
+
+
+
 
 class PanedGrid(Frame):
 	def __init__(self, master, orient=HORIZONTAL, minsize=20, columnWidths = {}, rowHeights={}, fixedPanes = []):
@@ -32,13 +43,14 @@
 
 		self.__panes = {}
 
-		self.builded = False
+		self.is_builded = False
 
 		self.clear_table()
 
+		self.is_displayed = False
 
 	def clear_table(self):
-		if not self.builded:
+		if not self.is_builded:
 			self.__gridOfWidgets = {}
 
 			self.__rows = defaultdict(set)
@@ -49,24 +61,30 @@
 
 			self.userDefinedWidths = {}
 			self.userDefinedHeights = {}
-
-		else:
-			raise Exception("You can't clear the table one this is builded.")
-
+			
+			self.calculate_dimensions = True
+			
+		else:
+			raise Exception("You can't clear the table one this is is_builded.")
+
+	@property
 	def rowIndices(self):
 		return self.__rows.keys()
 
+	@property
 	def columnIndices(self):
 		return self.__columns.keys()
 
+	@property
 	def numberOfRows(self):
 		return len(self.__rows)
 
+	@property
 	def numberOfColumns(self):
 		return len(self.__columns)
 
 	def update_cell(self, coordinates, widget):
-		if not self.builded:
+		if not self.is_builded:
 			raise Exception("First you must to build the panedgrid.")
 
 		if coordinates in self.__gridOfWidgets:
@@ -79,8 +97,8 @@
 
 
 	def build(self):
-		if self.builded:
-			raise Exception("You have just builded the grid before.")
+		if self.is_builded:
+			raise Exception("You have just is_builded the grid before.")
 
 		if not self.__gridOfWidgets: return
 
@@ -160,7 +178,7 @@
 				else:
 					cellFrame.configure( width=self.__columnWidths[ cell_coordinates[1] ] )
 
-		self.builded = True
+		self.is_builded = True
 
 	def __calculate_list_of_cell_widths_and_heights(self):
 		self.__rowHeights = self.userDefinedHeights.copy()
@@ -178,61 +196,84 @@
 
 	@property
 	def tableHeight(self):
+		if self.calculate_dimensions:
+			self.__calculate_list_of_cell_widths_and_heights()
+			
 		return sum(self.__rowHeights.values())
 
 	@property
 	def tableWidth(self):
+		if self.calculate_dimensions:
+			self.__calculate_list_of_cell_widths_and_heights()
 		return sum(self.__columnWidths.values())
 
 
 	def rowconfigure(self,row, height=None, fixed = None):
+		if type(row) is str: row = int(row)
+		
 		if row not in self.__rows:
-			raise Exception("Row %d doesn't exists." % column)
-
-		if height is not None:
-
-			if type(height) is not int: raise Exception("The height must be an integer:%s"%height)
-
-			self.userDefinedHeights[row] = height
-
-			if self.orient == VERTICAL:
-				self.__panes[row].configure(height=height)
-			else:
-				for column in self.__rows[row]:
-					cellFrame = self.__gridOfWidgets[row,column].pack_info()['in']
-					cellFrame.configure(height=height)
+			raise Exception("Row %d doesn't exists." % row)
 
 		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.fix_pane(index, fixed)
+			
+		if height is not None:
+
+			if type(height) is not int: raise Exception("The height must be an integer:%s"%height)
+
+			self.userDefinedHeights[row] = height
+			self.__rowHeights[row] = height
+			self.calculate_dimensions = True
+			
+			if self.is_builded:
+				if self.orient == VERTICAL:
+					if self.is_a_fixed_pane(row):
+						self.__panes[row].configure(height=height)
+					else:
+						self.__set_mobile_pane_size(row, height)
+				else:
+					for column in self.__rows[row]:
+						cellFrame = self.__gridOfWidgets[row,column].pack_info()['in']
+						cellFrame.configure(height=height)
+
 
 	def columnconfigure(self,column, width=None, fixed = None):
+		if type(column) is str: column = int(column)
+		
 		if column not in self.__columns:
 			raise Exception("Column %d doesn't exists." % column)
-
-		if width is not None:
-			if type(width) is not int: raise Exception("The width must be an integer:%s"%width)
-
-			self.userDefinedWidths[column] = width
-
-			if self.orient == HORIZONTAL:
-				self.__panes[column].configure(width=width)
-			else:
-				for row in self.__columns[column]:
-					cellFrame = self.__gridOfWidgets[row,column].pack_info()['in']
-					cellFrame.configure(width=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.fix_pane(index, fixed)
+					
+		if width is not None:
+			if type(width) is not int: raise Exception("The width must be an integer:%s"%width)
+
+			self.userDefinedWidths[column] = width
+			self.__columnWidths[column] = width
+			self.calculate_dimensions = True
+			
+			if self.is_builded:
+				if self.orient == HORIZONTAL:
+					if self.is_a_fixed_pane(column):
+						self.__panes[column].configure(width=width)
+					else:
+						self.__set_mobile_pane_size(column, width)
+				else:
+					for row in self.__columns[column]:
+						cellFrame = self.__gridOfWidgets[row,column].pack_info()['in']
+						cellFrame.configure(width=width)
+
 
 	def fix_pane(index, fixed=True):
-		if self.builded:
-			raise Exception("You can't modify the fixed of the panes once you builded the grid.")
+		if self.is_builded:
+			raise Exception("You can't modify the fixed of the panes once you is_builded the grid.")
 
 		if fixed == True:
 			self.fixedPanes.add(index)
@@ -242,35 +283,112 @@
 			raise Exception("fixed must be 'True' or 'False'.")
 
 	def fix_all_panes(self):
-		if self.builded:
-			raise Exception("You can't modify the fixed of the panes once you builded the grid.")
+		if self.is_builded:
+			raise Exception("You can't modify the fixed of the panes once you is_builded the grid.")
 
 		fixedPanes_list = self.__columns.keys() if self.orient == HORIZONTAL else self.__rows.keys()
 		self.fixedPanes = set(fixedPanes_list)
 
 	def realease_all_panes(self):
-		if self.builded:
-			raise Exception("You can't modify the fixed of the panes once you builded the grid.")
+		if self.is_builded:
+			raise Exception("You can't modify the fixed of the panes once you is_builded the grid.")
 
 		self.fixedPanes = set()
 
 	def is_a_fixed_pane(self, index ):
 		paneWidget = self.__panes[index]
 
-		parent = self.nametowidget( paneWidgetid.winfo_parent() )
-
-		if parent.__class__.name == 'PanedWindow':
+		parent = self.nametowidget( paneWidget.winfo_parent() )
+
+		if parent.winfo_class() == 'Panedwindow':
+			return False
+		else:
 			return True
-		else:
-			return False
-
+
+	def __get_panedwindow_INFO(self, index):
+		panedwindow = self.nametowidget( self.__panes[index].winfo_parent() )
+		panedwindow_index = index - panedwindow.index_range[0]
+		return panedwindow, panedwindow_index
+				
+	def __set_mobile_pane_size(self, index, size):
+		panedwindow, panedwindow_index = self.__get_panedwindow_INFO(index)
+		self.__set_pane_size_in_panedwindow(panedwindow, panedwindow_index, size)
+
+	def __get_mobile_pane_size(self, index):
+		panedwindow, panedwindow_index = self.__get_panedwindow_INFO(index)
+		return self.__get_pane_size_in_panedwindow(panedwindow, panedwindow_index)
+
+	def __set_pane_size_in_panedwindow(self,panedwindow, index, size):
+		sash_coordinates = [0,0]
+		last_index = len(panedwindow.panes()) - 1 
+		
+		if panedwindow['orient'] == HORIZONTAL:
+			# Coordinate X
+			position_coord = 0
+				
+			method_for_size_info = 'winfo_reqwidth'
+		else:
+			# Coordinate Y
+			position_coord = 1
+				
+			method_for_size_info = 'winfo_reqheight'
+					
+					
+		if index == 0:
+			sash_index = 0			
+			new_position = size			
+		elif index == last_index:
+			sash_index = last_index - 1
+			new_position = getattr(panedwindow, method_for_size_info)() - 2*panedwindow['bd'] \
+							- panedwindow['sashpad'] - panedwindow['sashwidth'] \
+							- size			
+		else:
+			sash_index = index - 1
+			new_position = panedwindow.sash_coord(index)[position_coord] \
+							- panedwindow['sashpad'] - panedwindow['sashwidth'] \
+							- size
+
+		sash_coordinates[position_coord] = new_position
+			
+		panedwindow.sash_place(sash_index, *sash_coordinates)
+
+	def __get_pane_size_in_panedwindow(self, panedwindow, index):
+		if panedwindow['orient'] == HORIZONTAL:
+			# Coordinate X
+			position_coord = 0
+				
+			method_for_size_info = 'winfo_reqwidth'
+		else:
+			# Coordinate Y
+			position_coord = 1
+				
+			method_for_size_info = 'winfo_reqwidth'
+			
+		last_index = len(panedwindow.panes()) - 1 
+							
+		if index == 0:
+			pane_size = panedwindow.sash_coord(index)[position_coord]
+		elif index == last_index:
+			pane_size = getattr(panedwindow, method_for_size_info)() - 2*panedwindow['bd'] \
+						- panedwindow.sash_coord(index-1)[position_coord] \
+						- panedwindow['sashpad'] - panedwindow['sashwidth']
+		else:
+			pane_size = panedwindow.sash_coord(index)[position_coord] \
+						- panedwindow.sash_coord(index-1)[position_coord] \
+						- panedwindow['sashpad'] - panedwindow['sashwidth']
+					
+		return pane_size
+		
 	def get_row_height(self, row):
 		if self.calculate_dimensions:
 			self.__calculate_list_of_cell_widths_and_heights()
 
-		if self.orient == HORIZONTAL:
-			if self.builded:
-				return self.__panes[row].winfo_height()
+		if self.orient == VERTICAL:
+			if self.is_builded:
+				if self.is_a_fixed_pane(row):
+					return self.__rowHeights[row]
+				else:
+					return self.__get_mobile_pane_size(row)
 			else:
 				return self.__rowHeights[row]
 		else:
@@ -279,20 +397,22 @@
 	def get_column_width(self, column):
 		if self.calculate_dimensions:
 			self.__calculate_list_of_cell_widths_and_heights()
-
 		if self.orient == HORIZONTAL:
-			return self.__columnWidths[column]
-		else:
-			if self.builded:
-				return self.__panes[column].winfo_width()
+			if self.is_builded:
+				if self.is_a_fixed_pane(column):
+					return self.__columnWidths[column]
+				else:
+					return self.__get_mobile_pane_size(column)
 			else:
 				return self.__columnWidths[column]
+		else:
+			return self.__columnWidths[column]
 
 	def check_coordinates(self, coordinates):
 		if type(coordinates) is int:
 			i = 0
 			j = coordinates
- 		else:
+		else:
 			try:
 				i, j = coordinates
 				if type(i) is not int or type(j) is not int:
@@ -309,8 +429,8 @@
 
 
 	def __setitem__(self,coordinates, widget):
-		if self.builded:
-			raise Exception("The panedgrid was builded. Use cell_update() instead.")
+		if self.is_builded:
+			raise Exception("The panedgrid was is_builded. Use cell_update() instead.")
 
 		i , j = self.check_coordinates(coordinates)
 
@@ -322,8 +442,8 @@
 		self.calculate_dimensions = True
 
 	def __delitem__(self, coordinates ):
-		if self.builded:
-			raise Exception("The panedgrid was builded. Use cell_update() instead.")
+		if self.is_builded:
+			raise Exception("The panedgrid was is_builded. Use cell_update() instead.")
 
 		i, j = self.check_coordinates(coordinates)
 
@@ -338,23 +458,30 @@
 		self.calculate_dimensions = True
 
 	def grid(self, *args, **kargs):
-		if not self.builded: self.build()
+		if not self.is_builded: self.build()
 		Frame.grid(self,*args, **kargs)
-
+		self.is_displayed = True
+		
 	def pack(self, *args, **kargs):
-		if not self.builded: self.build()
+		if not self.is_builded: self.build()
 		Frame.pack(self,*args, **kargs)
-
-
+		self.is_displayed = True
+
+	def place(self, *args, **kargs):
+		if not self.is_builded: self.build()
+		Frame.place(self,*args, **kargs)
+		self.is_displayed = True
 
 def test():
-	import random
-
+	try:
+		from tkinter import ttk
+	except ImportError:
+		import ttk
+			
 	root = Tk()
 
 	def table_of_random_widgets(master, numRows,numColumns, **kwargs):
-		import ttk
-
+		import random
 		demo = PanedGrid(master, **kwargs)
 
 		tkinterWidgets =  (
@@ -375,26 +502,104 @@
 		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')
+	table1 = table_of_random_widgets(root, numRows=6,numColumns=5, orient=VERTICAL, minsize=20, fixedPanes=[2,5])
+	table1.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") )
+	Label(root, text='''orient=HORIZONTAL, minsize = 30, fixed columns = [2], 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')
+
+	table2 = table_of_random_widgets(root, numRows=3,numColumns=6, minsize=30, fixedPanes=[2], columnWidths={0:200, 2: 150, 3:250}, rowHeights={0:90})
+	table2.pack(anchor=NW,pady=2, padx='2m')
+
+	table2.update_cell((1,2), Label(table2,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()
+	def show_table_data():
+		tableName = TableVar.get()
+		if tableName=='top table':
+			table = table1
+		elif tableName=='bottom table':
+			table = table2
+		else:
+			return
+		
+		StringData = ""
+		
+		StringData += "On the last table:\n"
+		StringData += "\tnumber of rows: %d\n" % table.numberOfRows
+		StringData +="\tnumber of columns: %d\n" % table.numberOfColumns
+		StringData +="\tTable width: %d\n" % table.tableWidth
+		StringData +="\tTable height: %d\n" % table.tableHeight
+	
+		StringData +="\n\tHeights:\n"
+	
+		for row in table.rowIndices:
+			StringData += "\t\trow %s: %s\n" % (row, table.get_row_height(row) ) 
+	
+		StringData +="\n\tWidths:\n"
+	
+		for column in table.columnIndices:
+			StringData += "\t\tcolumn %s: %s\n" % (column, table.get_column_width(column) )
+
+		print( StringData)
+
+	def change_pane_size():
+		tableName = TableVar.get()
+		
+		if tableName=='top table':
+			table = table1
+		elif tableName=='bottom table':
+			table = table2
+		else:
+			return
+
+		if table.orient == HORIZONTAL:
+			table.columnconfigure(PaneVar.get(),SizeVar.get() ) 
+		else:
+			table.rowconfigure(PaneVar.get(),SizeVar.get() ) 
+
+	emptySpace = Frame(root, height =40)
+	emptySpace.pack()
+	
+	ControlArea = LabelFrame(root, text ="Control area", labelanchor=N)
+	ControlArea.pack(expand=YES, fill=X, padx=20)
+
+	ControlArea1 = Frame(ControlArea)
+	ControlArea1.pack(expand=YES, fill=X, padx = 13)
+		
+	Button(ControlArea1, text="Show Data Table", command= lambda : show_table_data() ).pack(side=LEFT)
+	
+	ControlArea2 = Frame(ControlArea)
+	ControlArea2.pack(expand=YES, fill=X, padx = 13)
+
+	Label(ControlArea2, text="Table: ").pack(side=LEFT)
+	TableVar = StringVar()
+	TableVar.set('top table')
+	OptionMenu(ControlArea2, TableVar,'top table', 'bottom table').pack(side=LEFT)
+
+	
+	Label(ControlArea2, text="Pane index: ").pack(side=LEFT)
+	PaneVar = IntVar()
+	Pane_entry = Entry(ControlArea2, textvariable = PaneVar, width=2)
+	Pane_entry.pack(side=LEFT)
+	Pane_entry.bind('<Return>',  lambda event: change_pane_size() )
+	Pane_entry.focus()
+	
+	
+	Label(ControlArea2, text="Size: ").pack(side=LEFT)
+	SizeVar = IntVar()
+	Size_entry = Entry(ControlArea2, textvariable = SizeVar, width=4)
+	Size_entry.pack(side=LEFT)
+	Size_entry.bind('<Return>',  lambda event: change_pane_size() )
+	
+	
+	Button(ControlArea2, text="Change pane size", command = lambda: change_pane_size() ).pack(side=LEFT)
+	
 	root.mainloop()
 
 

History