"""
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 types
import Tkinter
import Pmw
TRUE = 1
FALSE = 0
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)
# Get default colors.
# We don't quite capture the Windows look yet, which has
# "rounded" edges, using two different shades on each side.
self.FACE = interior.cget('background')
face_rgb = self.winfo_rgb(self.FACE)
bri = Pmw.Color.rgb2brightness(face_rgb) / 65536.0
hibri = min(1.0, bri + 0.2)
self.HILIGHT = Pmw.Color.changebrightness(self, self.FACE, hibri)
lobri = max(0.0, bri - 0.35)
self.SHADOW = Pmw.Color.changebrightness(self, '#000', lobri)
self._nbframe = self.createcomponent(
'nbframe',
(), None,
Tkinter.Canvas, (self.component('hull'),),
bd=0,
height=0,
width=0,
highlightcolor=self.FACE # hide the black border
)
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, w=1, h=1):
if e:
w,h = e.width, e.height
self._makereqsize(w,h)
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
# Clean up drawing the tab outlines.
# It will require more work to make the active tab
# a little taller than the others. We'll need to
# reserve additional space at the top of the widget.
l = x+d; li = x+2*d
r = x+3*d+w; ri = x+2*d+w-1
ti = 3*d
lightcoords = (
l, h,
l, ti,
li, d,
ri, d
)
shadowcoords = (
ri, d,
r, ti,
r, h-d+1
)
lightkw = {'fill': self.HILIGHT, 'width': d, 'tags': pagename+'_light'}
shadowkw = {'fill': self.SHADOW, 'width': d, 'tags': pagename+'_dark'}
apply( tabcanvas.create_line, shadowcoords, shadowkw )
apply( tabcanvas.create_line, lightcoords, lightkw )
tabcanvas.lower( pagename+'_topline' )
tabcanvas.create_line(
x+d, h,
x+4*d+w, h,
fill=self.HILIGHT,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=self.HILIGHT,
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=self.HILIGHT,
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=self.SHADOW,
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,w=1,h=1):
self.update_idletasks()
tabcanvas = self.component('nbframe')
ipadx = self['ipadx']
ipady = self['ipady']
manager = self.winfo_manager()
if manager == '':
return
mgr_info = getattr(self, manager + '_info')()
try:
mgr_ipadx = int(mgr_info['ipadx'])
mgr_ipady = int(mgr_info['ipady'])
except KeyError:
# NEED FIX for place mgr
raise ValueError, \
'Sorry, resizing works only with grid and pack mgrs'
highlightthickness = \
string.atoi(tabcanvas.cget('highlightthickness')) + \
string.atoi(self.cget('hull_highlightthickness'))
reqw = w - 2*(ipadx + mgr_ipadx + highlightthickness)
reqh = h - 2*(ipady + mgr_ipady + highlightthickness) - self._tabheight
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*ipadx])
self._nbheight = self._tabheight + 2*ipady + self._pageheight
self._nbcenterx = self._nbwidth/2
self._nbcentery = self._tabheight + 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) == types.StringType:
pagename = pagenameOrIndex
else:
if len(self._pages) <= pagenameOrIndex:
return
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=self.HILIGHT)
tabcanvas.lower( self._currentpage+'_topline' )
self._pagedict[self._currentpage]._lower()
tabcanvas.itemconfigure( pagename+'_bottom', fill=self.FACE )
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,
width=self._pagewidth,
height=self._pageheight,
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')