""" 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')