Help and optimization hints, anyone?

K

Kim Petersen

I've worked on this table object a bit too long - and seem to have
stared too long at the code. Can someone see where it goes wrong in
the insertrow function?

Also optimization hints and alternatives will be greatly appreciated.

<code>
#!env python
#
# created: "15:19 20/01-2004" by Kim Petersen <[email protected]>
#
# $Id$
#
import Tkinter

class UserDict:
defaults={}
def __init__(self,inherit=None):
if not inherit:
self.inherit=[]
else:
self.inherit=inherit
self.dict=self.defaults.copy()

def __setitem__(self,name,value): self.dict[name]=value

def __getitem__(self,name):
if not self.dict.has_key(name):
for dict in self.inherit:
if dict.has_key(name):
return dict[name]
raise KeyError,"%s not found" % (name,)
return self.dict[name]

def haskey(self,name): return self.dict.has_key(name)

class Cell(UserDict):
defaults={"width": 0,"height": 0,"text": '',"color": "black","background": "white",
"widgets": None}
def __init__(self,master,row,column,text):
UserDict.__init__(self,[row,column])
self.master=master
self["text"]=text
self.row=row # these are needed for us to find the actual row/col
self.column=column
self.create()

def create(self):
"""Create the widgets the first time (might inflict up to two resize's)"""
x,y=self.column["x"],self.row["y"]
w,h=self.column["width"],self.row["height"]
r=self.master.create_rectangle((x,y,x+w,y+h),fill=self["background"])
t=self.master.create_text((x+self.master.colpadding,y+h/2),
text=self["text"],
anchor="w",fill=self["color"])
self["widgets"]=[r,t]
bbox=self.master.bbox(t)
self["width"]=bbox[2]-bbox[0]
self["height"]=bbox[3]-bbox[1]
if self["width"]+self.master.colpadding*2>w:
self.column.resize(self["width"]+self.master.colpadding*2)
self.resize()
if self["height"]+self.master.rowpadding*2>h:
self.row.resize(self["height"]+self.master.rowpadding*2)
self.resize()

def resize(self):
"""Resize according to the width/height given by row,column"""
x,y=self.column["x"],self.row["y"]
w,h=self.column["width"],self.row["height"]
self.master.coords(self["widgets"][0],(x,y,x+w,y+h))
self.master.coords(self["widgets"][1],(x+self.master.colpadding,y+h/2))

def move(self,dx,dy):
"""Relatively move according to delta's"""
self.master.move(self["widgets"][0],dx,dy)
self.master.move(self["widgets"][1],dx,dy)

class Column(UserDict):
""" """
defaults={"x": 0,"width": 0,"label": '',"tag": ''}

def __init__(self,master,label='',before=None):
UserDict.__init__(self)
self.master=master
if master.columns:
if not before or before>0:
if not before:
after=(-1)
else:
after=before-1
self.dict["x"]=master.columns[after]["x"]+master.columns[after]["width"]
# since we have a width of 0 there is no need to move the rest of the columns.

def calcwidth(self):
"""Calculate the *actually* needed width of the column (*not* the current one)."""
width=0
for row in self.master.rows:
width=max(self.master.cells[(row,self)]["width"],width)
return width+self.master.colpadding*2

def resize(self,width):
# calc delta, set new width
dx=width-self["width"]
self["width"]=width
ci=self.master.columns.index(self)
# resize the cells
for row in self.master.rows:
try:
self.master.cells[(row,self)].resize()
except KeyError:
pass
# move columns to the right further to the right
for i in range(ci+1,len(self.master.columns)):
self.master.columns.move(dx)

def move(self,dx):
self["x"]=self["x"]+dx
# move the cells correspondingly
for row in self.master.rows:
try:
self.master.cells[(row,self)].move(dx,0)
except KeyError:
pass

class Row(UserDict):
defaults={"y": 0,"height": 0,"label": '',"tag": ''}
def __init__(self,master,label,before=None):
UserDict.__init__(self)
self["label"]=label
# now insert it.
self.master=master
if master.rows:
if not before or before>0:
if not before:
after=(-1)
else:
after=before-1
self.dict["y"]=master.rows[after]["y"]+master.rows[after]["height"]

def calcheight(self):
"""Calculate the *actually* needed width of the column (*not* the current one)."""
height=0
for row in self.master.columns:
height=max(self.master.cells[(self,column)]["height"],height)
return height+self.master.rowpadding*2

def resize(self,height):
dy=height-self.dict["height"]
self.dict["height"]=height
ri=self.master.rows.index(self)
for column in self.master.columns:
if self.master.cells.has_key((self,column)):
self.master.cells[(self,column)].resize()
for i in range(ri+1,len(self.master.rows)):
self.master.rows.move(dy)

def move(self,dy):
self.dict["y"]=self.dict["y"]+dy
for col in self.master.columns:
try:
self.master.cells[(self,col)].move(0,dy)
except KeyError:
pass
pass

def moveto(self,y):
self.move(y-self.dict["y"])

def __setitem__(self,name,value):
if name=="height":
self.resize(value)
elif name=="y":
self.move(value-self["y"])
else:
self.dict[name]=value

class Table(Tkinter.Canvas):
"""A table object - it consists of a number of cells layed out in rows and columns

A row has a specific height
A row can have a label
A column has a specific width
A column can have a label
"""

def __init__(self,master,**args):
Tkinter.Canvas.__init__(self,master,**args)
self.colpadding=2
self.rowpadding=2
self.columns=[] # each item contains data about the column
self.rows=[] # each item contains data about the row
self.cells={} # index: (row,col)

def insertrow(self,pos,values):
self.rows[pos:pos]=[Row(self,'',pos)]
row=self.rows[pos]
for i in range(len(values)):
if i<len(self.columns):
col=self.columns
else:
self.columns.append(Column(self,''))
col=self.columns[-1]
self.cells[(row,col)]=Cell(self,row,col,values)

def row_add(self,values):
self.rows.append(Row(self,''))
row=self.rows[-1]
for i in range(len(values)):
if i<len(self.columns):
col=self.columns
else:
self.columns.append(Column(self,''))
col=self.columns[-1]
self.cells[(row,col)]=Cell(self,row,col,values)

if __name__=="__main__":
tk=Tkinter.Tk()
tk.grid_rowconfigure(1,weight=1)
tk.grid_columnconfigure(1,weight=1)
tk.wm_geometry("800x1150+0+0")

table=Table(tk)
table.grid(row=1,column=1,sticky="nsew")
for line in open("/etc/passwd","r"):
values=unicode(line.strip(),"iso8859-1").split(":")
#tk.update()
#table.insertrow(0,values)
table.row_add(values)
for row in table.rows:
print row["y"],row["height"]
tk.mainloop()

# Local Variables:
# tab-width: 3
# py-indent-offset: 3
# End:
</code>
 
J

John J. Lee

Kim Petersen said:
I've worked on this table object a bit too long - and seem to have
stared too long at the code. Can someone see where it goes wrong in
the insertrow function?

I made a few comments before I realised it was a Tkinter question, so
no answer, sorry! Just some style hints.

BTW, it's very helpful to mention in the subject line when a
particular GUI toolkit is involved (or when any other large library is
a central part of the question, for that matter).

Also optimization hints and alternatives will be greatly appreciated.

<code>
#!env python
#
# created: "15:19 20/01-2004" by Kim Petersen <[email protected]>
#
# $Id$
#
import Tkinter

class UserDict:

Bad name -- UserDict is a standard library module. In 2.2 or newer,
you can subclass dict. In 2.3, you also have the option of
subclassing UserDict.DictMixin. If you just want to implement part of
the maping interface, just call your class something other than
UserDict -- say SimpleDefaultDictBase.

defaults={}
def __init__(self,inherit=None):
if not inherit:
self.inherit=[]
else:
self.inherit=inherit
self.dict=self.defaults.copy()

def __setitem__(self,name,value): self.dict[name]=value

def __getitem__(self,name):
if not self.dict.has_key(name):
for dict in self.inherit:

dict is also a bad name -- this is the name of the builtin dictionary
type, which you've just clobbered (in the local scope).

if dict.has_key(name):
return dict[name]
raise KeyError,"%s not found" % (name,)
return self.dict[name]

def haskey(self,name): return self.dict.has_key(name)

Did you really mean to call it that? The dict interface has a method
..has_key(), not .haskey().

class Cell(UserDict):
defaults={"width": 0,"height": 0,"text": '',"color": "black","background": "white",
"widgets": None}
[...]

Haven't read much further, but it looks like you might be better off
using attribute access rather than indexing. In 2.2, use properties.
Earlier, use __setattr__ / __getattr__ (make sure you read the docs).


John
 
K

Kim Petersen

Den Fri, 23 Jan 2004 12:32:04 +0000. skrev John J. Lee:
I made a few comments before I realised it was a Tkinter question, so
no answer, sorry! Just some style hints.

thx. no problem tho - i should've remembered.
Bad name -- UserDict is a standard library module. In 2.2 or newer,
you can subclass dict. In 2.3, you also have the option of
subclassing UserDict.DictMixin. If you just want to implement part of
the maping interface, just call your class something other than
UserDict -- say SimpleDefaultDictBase.

Actually i was planning to use the standard UserDict
(basically because a class is hashable and dict isn't) - i
got carried away and put some default handling in it as well - but
didn't rename ;-)
dict is also a bad name -- this is the name of the builtin dictionary
type, which you've just clobbered (in the local scope).

Hmmm - isn't it called __dict__ ?
Did you really mean to call it that? The dict interface has a method
.has_key(), not .haskey().

yup - oversight
class Cell(UserDict):
defaults={"width": 0,"height": 0,"text": '',"color": "black","background": "white",
"widgets": None}
[...]

Haven't read much further, but it looks like you might be better off
using attribute access rather than indexing. In 2.2, use properties.
Earlier, use __setattr__ / __getattr__ (make sure you read the docs).

this migrated from a regular dict which i had to drop because its
not hashable as mentioned earlier - next step __(get|set)attr__.
 
J

John J. Lee

Kim Petersen said:
Den Fri, 23 Jan 2004 12:32:04 +0000. skrev John J. Lee: [...]
dict is also a bad name -- this is the name of the builtin dictionary
type, which you've just clobbered (in the local scope).

Hmmm - isn't it called __dict__ ?

No, __dict__ is an attribute of Python objects. It's the dictionary
in which most Python objects keep their data:
.... def __init__(self):
.... self.blah = "stuff"
....{'blah': 'stuff'}


dict is the builtin name for the dictionary type:
dict({"foo": "bar", "spam": "eggs"}) {'foo': 'bar', 'spam': 'eggs'}
dict([("foo", "bar"), ("spam", "eggs")])
{'foo': 'bar', 'spam': 'eggs'}

[...]
this migrated from a regular dict which i had to drop because its
not hashable as mentioned earlier - next step __(get|set)attr__.

dicts are deliberately not hashable, because they're mutable. Are you
sure you want a dict as a dictionary key (assuming that's what you're
doing)?


John
 
K

Kim Petersen

Den Fri, 23 Jan 2004 16:20:37 +0000. skrev John J. Lee:
Kim Petersen said:
Den Fri, 23 Jan 2004 12:32:04 +0000. skrev John J. Lee: [...]
dict is also a bad name -- this is the name of the builtin dictionary
type, which you've just clobbered (in the local scope).

Hmmm - isn't it called __dict__ ?

No, __dict__ is an attribute of Python objects. It's the dictionary
in which most Python objects keep their data:

ah - the class dict() - yeah i can see that (i didn't notice since
i hardly ever call dict() directly but do it indirectly via {}. Sure
i've clobbered that internally in this class and derived (will change)
dicts are deliberately not hashable, because they're mutable. Are you
sure you want a dict as a dictionary key (assuming that's what you're
doing)?

I had the columns,rows and cells as simply dicts in the beginning,
but that made self.cells[(row,column)] impossible - so i converted
to classes instead (which works just as well). And migrated func-
tionality to the classes. [this is also the reason for the attributes
being access as dict].
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top