"""
I really should define a NBFrame widget according to the Tix specifications.
Thus, I could refer to the Tix documentation.
"""
import sys
import string
import Tkinter
import Pmw
TRUE = 1
FALSE = 0
DARKGRAY = 'gray60'
LIGHTGRAY = 'gray82'
class NoteBookPage( Pmw.MegaWidget ):
def __init__(self, notebook, pagename, **kw):
self.notebook = notebook
self.pagename = pagename
# Define the megawidget options.
INITOPT = Pmw.INITOPT
optiondefs = (
('anchor', 'c', INITOPT),
#('bitmap', None, INITOPT),
('balloonHelp', '', None),
('statusHelp', None, None),
('createcmd', None, None),
#('image', None, INITOPT),
('justify', None, INITOPT),
('label', "", INITOPT),
('raisecmd', None, None),
('lowercmd', None, None),
('state', 'normal', self._setstate),
('underline', 0, INITOPT),
#('wraplength', 0, INITOPT)
)
self.defineoptions(kw, optiondefs)
# Initialise the base class (after defining the options).
Pmw.MegaWidget.__init__(self, notebook.interior())
# Create the components.
interior = Pmw.MegaWidget.interior(self)
l = Tkinter.Label(self.notebook.component('nbframe'))
font = l.cget('font')
l.destroy()
del l
self._button = self.createcomponent(
'button',
(), None,
Tkinter.Button, (self.notebook.interior(),),
bd=0, relief='flat',
text=self['label'],
font=font,
underline=self['underline'],
command=self.lift
)
if not self.notebook['balloon'] is None:
self.notebook['balloon'].bind(
self.component('button'),
self['balloonHelp'],
self['statusHelp']
)
# Check keywords and initialise options.
self.initialiseoptions(NoteBookPage)
self._iscreated = FALSE
def _setstate(self):
self.component('button').configure(state=self['state'])
def lift(self):
if not self._iscreated:
if not self['createcmd'] is None: self['createcmd']()
self._iscreated = TRUE
if not self['raisecmd'] is None: self['raisecmd']()
if self.cget('state') == 'normal':
self.notebook.lift(self.pagename)
def _lower(self):
"""
Should no be called directly: top is lowered when other is raised !
Whereas the page.lift() calls the notebook.lift(), page._lower() is
called *from* notebook.lift(). Asymmetric indeed, but don't forget
that this is caused by the restriction that only one page may be
raised.
The reason for having this page._lower() method is to call the
lowercmd if defined, just as the page.lift() method calls the
raisecmd.
"""
if not self['lowercmd'] is None: self['lowercmd']
def req_size(self):
self.update_idletasks()
return self.winfo_reqwidth(), self.winfo_reqheight()
Pmw.forwardmethods(NoteBookPage, Tkinter.Frame, '_hull')
class NoteBookR( Pmw.MegaWidget ):
def __init__(self, parent = None, **kw):
# Define the megawidget options.
INITOPT = Pmw.INITOPT
optiondefs = (
#('dynamicgeometry', FALSE, INITOPT ),# deplorable option!
('balloon', None, None),
('ipadx', 4, INITOPT),
('ipady', 4, INITOPT)
)
self.defineoptions(kw, optiondefs)
# Initialise the base class (after defining the options).
Pmw.MegaWidget.__init__(self, parent)
# Create the components.
interior = Pmw.MegaWidget.interior(self)
self._nbframe = self.createcomponent(
'nbframe',
(), None,
Tkinter.Canvas, (self.component('hull'),),
bd=0,
height=0,
width=0
)
self._nbframe.pack(side='top',padx=0,pady=0)
# Check keywords and initialise options.
self.initialiseoptions(NoteBookR)
self._pages = [] # [name1, name2, ...]
self._pagedict = {} # { name: NoteBookPageObject}
self._currentpage = None # name (!) of currently raised page
self._indexpage = -1 # index of currently raised page
# Variables used for sizing the Canvas and painting the tabs.
self._tabheight = 0
self._tabwidth = 0
self._borderwidth = 2
self._pagewidth = 0
self._pageheight = 0
self._nbwidth = 2*self._borderwidth
self._nbheight = 2*self._borderwidth
self._nbcenterx = 0
self._nbcentery = 0
self._tablineitems = ['_light','_dark', '_topline','_button','_bottom']
# XXX Necessary, but clumsy to look at: really should be the
# Toplevel configure at which the method should be called, that is,
# before other widgets are already displayed.
self.bind('<Configure>', self.initialise)
def initialise(self,e=None):
self._makereqsize()
self._drawborder()
self.lift(0)
def interior(self):
return self._nbframe
def _drawtab(self,newpage):
tabcanvas = self.component('nbframe')
d = self._borderwidth
b = newpage.component('button')
w = b.winfo_reqwidth()
if self._tabwidth == 0:
x = self._tabwidth
self._tabwidth = 0
else:
x = self._tabwidth
pagename = newpage.pagename
tabcanvas.create_window(
x+2*d,d+1,window=b,anchor='nw',tags=pagename+'_button')
self._tabwidth = self._tabwidth + 3*d + w
oldwidth = string.atoi(tabcanvas.cget('width'))
if oldwidth < self._tabwidth:
tabcanvas.configure(width=self._tabwidth)
h = self._tabheight
lightcoords = (
x+d, h,
x+d, 3*d,
x+2*d-1, d,
x+2*d+w-1, d,
x+2*d+w+-2+2*d, d+2*d
)
shadowcoords = (
x+3*d+w, d+2*d+1,
x+3*d+w, h-d+1
)
toplinecoords = (
x, h-2*d,
x, d+2*d-1,
x+2*d-1, d-1,
x+d+w+1, d-1,
x+3*d+w, d+2*d,
x+3*d+w, h-d+1
)
lightkw = {'fill': 'white', 'width': d, 'tags': pagename+'_light'}
shadowkw = {'fill': DARKGRAY, 'width': d, 'tags': pagename+'_dark'}
toplinekw = {'fill': DARKGRAY, 'width': 1,'tags': pagename+'_topline'}
apply( tabcanvas.create_line, lightcoords, lightkw )
apply( tabcanvas.create_line, shadowcoords, shadowkw )
apply( tabcanvas.create_line, toplinecoords, toplinekw )
tabcanvas.lower( pagename+'_topline' )
tabcanvas.create_line(
x+d, h,
x+4*d+w, h,
fill='white',width=d,tags=pagename+'_bottom'
)
def _drawborder(self):
tabcanvas = self.component('nbframe')
tw = string.atoi(tabcanvas.cget('width'))
th = string.atoi(tabcanvas.cget('height'))
d = self._borderwidth
tabcanvas.delete('border')
tabcanvas.create_line(
d,self._tabheight-2*d,
d,self._tabheight+2*self['ipady']+self._pageheight,
fill='white',
width=self._borderwidth,
tags='border borderlight'
)
#print self._nbwidth - self._tabwidth
#print self._nbwidth - self._pagewidth
tabcanvas.create_line(
self._tabwidth, self._tabheight,
self._nbwidth, self._tabheight,
fill='white',
width=self._borderwidth,
tags='border borderlight'
)
tabcanvas.create_line(
d, self._tabheight+2*self['ipady']+self._pageheight,
self._nbwidth, self._tabheight+2*self['ipady']+self._pageheight,
self._nbwidth, self._tabheight-d,
fill=DARKGRAY,
width=self._borderwidth,
tags='border bordershadow'
)
def add(self,pagename,**kw):
if self._pagedict.has_key(pagename):
msg = "Attempt to create a second tab with name '%s'." % pagename
raise ValueError, msg
newpage = apply( NoteBookPage, (self, pagename), kw )
setattr(self,pagename,newpage)
tabcanvas = self.component('nbframe')
d = self._borderwidth
# Initialization:
if not self._tabheight:
b = newpage.component('button')
self._tabheight = b.winfo_reqheight() + 2*d
tabcanvas.configure(height=self._tabheight)
INITIALIZE = TRUE
else:
INITIALIZE = FALSE
self._drawtab(newpage)
self._pages.append( (pagename,newpage) )
self._pagedict[pagename] = newpage
if INITIALIZE:
self.lift(pagename)
def _makereqsize(self):
self.update_idletasks()
tabcanvas = self.component('nbframe')
reqw = 1
reqh = 1
for page in self._pagedict.values():
w, h = page.req_size()
reqw = max([reqw,w])
reqh = max([reqh,h])
self._pagewidth = reqw
self._pageheight = reqh
self._nbwidth = max([self._tabwidth,self._pagewidth+2*self['ipadx']])
self._nbheight = self._tabheight + 2*self['ipady'] + self._pageheight
self._nbcenterx = self._nbwidth/2
self._nbcentery = self._tabheight + self['ipady'] + self._pageheight/2
tabcanvas.configure( width=self._nbwidth, height=self._nbheight )
def _undrawtab(self,delpage):
tabcanvas = self.component('nbframe')
d = self._borderwidth
b = delpage.component('button')
w = b.winfo_reqwidth()
x = self._tabwidth
h = self._tabheight
pagename = delpage.pagename
for item in self._tablineitems:
tabcanvas.delete(pagename+item)
i = 0
for name in map( lambda x: x[0], self._pages):
i = i+1
if name == pagename: break
for name in map( lambda x: x[0], self._pages[i:]):
for item in self._tablineitems:
tabcanvas.move(name+item,-(w+4*d),0)
if self._pagewidth < self._tabwidth:
tabcanvas.configure(width=self._tabwidth)
def tkdelete(self,pagename):
delpage = self._pagedict[pagename]
ip = self._indexpage
if self.raised() == pagename:
if self._indexpage < len(self._pages) - 1:
self.lift( self._pages[self._indexpage + 1][0] )
elif self._indexpage > 0:
self.lift( self._pages[self._indexpage - 1][0] )
else:
self._indexpage = -1
self._currentpage = None
b = delpage.component('button')
w = b.winfo_reqwidth()
d = self._borderwidth
tabcanvas = self.component('nbframe')
self._tabwidth = self._tabwidth - w - 4*d
self._undrawtab(delpage)
delpage.destroy()
delattr(self,pagename)
del self._pagedict[pagename]
self._pages = self._pages[:ip] + self._pages[ip+1:]
def pagecget(self,pagename,option):
return self._pagedict[pagename].cget(option)
def pageconfigure(self,pagename,**kw):
return apply( self._pagedict[pagename].configure, (), kw )
def pages(self):
return self._pagedict.keys()
def lift(self,pagenameOrIndex):
if type(pagenameOrIndex) == type(''):
pagename = pagenameOrIndex
else:
pagename = self._pages[pagenameOrIndex][0]
tabcanvas = self.component('nbframe')
# deal with the present top page
if not self._currentpage is None:
tabcanvas.itemconfigure( self._currentpage+'_bottom', fill='white')
tabcanvas.lower( self._currentpage+'_topline' )
self._pagedict[self._currentpage]._lower()
tabcanvas.itemconfigure( pagename+'_bottom', fill=LIGHTGRAY )
tabcanvas.lift( pagename+'_topline' )
self._currentpage = pagename
self._indexpage = map( lambda x: x[0], self._pages ).index(pagename)
p = self._pagedict[pagename]
tabcanvas.delete('pageframe')
tabcanvas.create_window(
self._nbcenterx,
self._nbcentery,
window=p,
anchor='c',
tags='pageframe'
)
tkraise = lift
def raised(self):
return self._currentpage
def page(self,pagename):
return self._pagedict[pagename]
Pmw.forwardmethods(NoteBookR, Tkinter.Frame, '_hull')