# Version 0.3
# Author: Miguel Martinez Lopez
# Email: Uncomment the next line to see my email
# print "Author's email: ", "61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex")
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)
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.builded = False
self.clear_table()
def clear_table(self):
if not self.builded:
self.__gridOfWidgets = {}
self.__rows = defaultdict(set)
self.__columns = defaultdict(set)
self.rowHeightList = {}
self.columnWidthList = {}
self.userDefinedWidths = {}
self.userDefinedHeights = {}
else:
raise Exception("You can't clear the table one this is builded.")
def clear_definedWidths(self):
self.userDefinedWidths = {}
def clear_definedHeights(self):
self.userDefinedHeights = {}
def rowIndices(self):
return self.__rows.keys()
def columnIndices(self):
return self.__columns.keys()
def numberOfRows(self):
return len(self.__rows)
def numberOfColumns(self):
return len(self.__columns)
def update_cell(self, coordinates, widget):
if not self.builded:
raise Exception("First you must to build 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 build(self):
if self.builded:
raise Exception("You have just builded the grid before.")
if not self.__gridOfWidgets: return
self.pannedwindows = []
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.pannedwindows.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.builded = 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())
@property
def tableHeight(self):
return sum(self.rowHeightList.values())
@property
def tableWidth(self):
return sum(self.columnWidthList.values())
def rowconfigure(self,row, height=None, fixed = None):
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)
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)
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.fixPane(index, fixed)
def fixPane(index, fixed=True):
if self.builded:
raise Exception("You can't modify the fixed of the panes once you 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 fixAllPanes():
if self.builded:
raise Exception("You can't modify the fixed of the panes once you builded the grid.")
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.__gridOfWidgets[coordinates] = widget
i,j = coordinates
self.__rows[i].add(j)
self.__columns[j].add(i)
def __delitem__(self, coordinates ):
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.builded: self.build()
Frame.grid(self,*args, **kargs)
def pack(self, *args, **kargs):
if not self.builded: self.build()
Frame.pack(self,*args, **kargs)
def test():
import random
root = Tk()
def table_of_random_widgets(master, numRows,numColumns, **kwargs):
import ttk
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 9 2014-06-03 21:08:11
+++ revision 10 2014-06-04 15:20:48
@@ -1,7 +1,7 @@
-# Version 0.2
+# Version 0.3
# Author: Miguel Martinez Lopez
-# Email: To see my email uncomment the next line
-# print '61706c69636163696f6e616d656469646140676d61696c2e636f6d'.decode('hex')
+# Email: Uncomment the next line to see my email
+# print "Author's email: ", "61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex")
from Tkinter import *
from collections import OrderedDict, defaultdict
@@ -12,8 +12,6 @@
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:
@@ -33,20 +31,28 @@
self.userDefinedWidths = columnWidths
self.userDefinedHeights = rowHeights
- self.updated = False
- self.areDimensionsCalculated = False
+ self.__panes = {}
+
+ self.builded = False
+
+ self.clear_table()
def clear_table(self):
- self.__gridOfWidgets = {}
-
- self.__rows = defaultdict(set)
- self.__columns = defaultdict(set)
-
- self.__panes = {}
-
- self.rowHeightList = {}
- self.columnWidthList = {}
+ if not self.builded:
+ self.__gridOfWidgets = {}
+
+ self.__rows = defaultdict(set)
+ self.__columns = defaultdict(set)
+
+ self.rowHeightList = {}
+ self.columnWidthList = {}
+
+ self.userDefinedWidths = {}
+ self.userDefinedHeights = {}
+
+ else:
+ raise Exception("You can't clear the table one this is builded.")
def clear_definedWidths(self):
self.userDefinedWidths = {}
@@ -58,7 +64,7 @@
return self.__rows.keys()
def columnIndices(self):
- return self.__column.keys()
+ return self.__columns.keys()
def numberOfRows(self):
return len(self.__rows)
@@ -67,8 +73,8 @@
return len(self.__columns)
def update_cell(self, coordinates, widget):
- if not self.updated:
- raise Exception("First you must to update the pannedgrid.")
+ if not self.builded:
+ raise Exception("First you must to build the pannedgrid.")
if coordinates in self.__gridOfWidgets:
master = self.__gridOfWidgets[coordinates].pack_info()['in']
@@ -79,13 +85,15 @@
raise Exception("There is no widget with coordiantes: %s." % coordinates)
- def update(self):
+ def build(self):
+ if self.builded:
+ raise Exception("You have just builded the grid before.")
+
if not self.__gridOfWidgets: return
- self.panedWindows = []
-
- if not self.areDimensionsCalculated:
- self.calculate_list_of_cell_widths_and_heights()
+ self.pannedwindows = []
+
+ self.__calculate_list_of_cell_widths_and_heights()
listOfCellCoordinates = list(itertools.product(self.__rows, self.__columns))
@@ -116,7 +124,7 @@
paneMaster = PanedWindow(self,orient=self.orient )
paneMaster.pack(side=self.__packSideBetweenPanes)
- self.panedWindows.append(paneMaster)
+ self.pannedwindows.append(paneMaster)
newPanedWindow = False
frameOfPane = Frame(paneMaster)
@@ -155,9 +163,9 @@
width=self.columnWidthList[ cell_coordinates[1] ]
)
- self.updated = True
-
- def calculate_list_of_cell_widths_and_heights(self):
+ self.builded = True
+
+ def __calculate_list_of_cell_widths_and_heights(self):
self.rowHeightList = self.userDefinedHeights.copy()
self.columnWidthList = self.userDefinedWidths.copy()
@@ -169,24 +177,31 @@
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
+ @property
+ def tableHeight(self):
+ return sum(self.rowHeightList.values())
+
+ @property
+ def tableWidth(self):
+ return sum(self.columnWidthList.values())
+
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)
+
+ 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)
if fixed is not None:
if self.orient == HORIZONTAL:
@@ -198,14 +213,17 @@
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)
+ 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:
@@ -214,6 +232,9 @@
self.fixPane(index, fixed)
def fixPane(index, fixed=True):
+ if self.builded:
+ raise Exception("You can't modify the fixed of the panes once you builded the grid.")
+
if fixed == True:
self.fixedPanes.add(index)
elif fixed == False:
@@ -222,6 +243,9 @@
raise Exception("fixed must be 'True' or 'False'.")
def fixAllPanes():
+ if self.builded:
+ raise Exception("You can't modify the fixed of the panes once you builded the grid.")
+
fixedPanes_list = self.__columns.keys() if self.orient == HORIZONTAL else self.__rows.keys()
self.fixedPanes = set(fixedPanes_list)
@@ -232,9 +256,6 @@
return self.__gridOfWidgets.get(coordinates)
def __setitem__(self,coordinates, widget):
- self.updated = False
- self.areDimensionsCalculated = False
-
self.__gridOfWidgets[coordinates] = widget
i,j = coordinates
@@ -242,9 +263,6 @@
self.__columns[j].add(i)
def __delitem__(self, coordinates ):
- self.updated = False
- self.areDimensionsCalculated = False
-
del self.__gridOfWidgets[coordinates]
i,j = coordinates
@@ -257,12 +275,14 @@
def grid(self, *args, **kargs):
- if not self.updated: self.update()
+ if not self.builded: self.build()
Frame.grid(self,*args, **kargs)
def pack(self, *args, **kargs):
- if not self.updated: self.update()
+ if not self.builded: self.build()
Frame.pack(self,*args, **kargs)
+
+
def test():
import random