You have some single-toplevel Tkinter apps that you want to organize in a notebook-like fashion, associating each tab to an app, in a way which requires minimal changes in your original apps. This simple notebook class allows that, also supporting different tab orientations (TOP, BOTTOM, LEFT & RIGHT).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | # file: notebook.py
# A simple notebook-like Tkinter widget.
# Copyright 2003, Iuri Wickert (iwickert yahoo.com)
from Tkinter import *
class notebook:
# initialization. receives the master widget
# reference and the notebook orientation
def __init__(self, master, side=LEFT):
self.active_fr = None
self.count = 0
self.choice = IntVar(0)
# allows the TOP and BOTTOM
# radiobuttons' positioning.
if side in (TOP, BOTTOM):
self.side = LEFT
else:
self.side = TOP
# creates notebook's frames structure
self.rb_fr = Frame(master, borderwidth=2, relief=RIDGE)
self.rb_fr.pack(side=side, fill=BOTH)
self.screen_fr = Frame(master, borderwidth=2, relief=RIDGE)
self.screen_fr.pack(fill=BOTH)
# return a master frame reference for the external frames (screens)
def __call__(self):
return self.screen_fr
# add a new frame (screen) to the (bottom/left of the) notebook
def add_screen(self, fr, title):
b = Radiobutton(self.rb_fr, text=title, indicatoron=0, \
variable=self.choice, value=self.count, \
command=lambda: self.display(fr))
b.pack(fill=BOTH, side=self.side)
# ensures the first frame will be
# the first selected/enabled
if not self.active_fr:
fr.pack(fill=BOTH, expand=1)
self.active_fr = fr
self.count += 1
# returns a reference to the newly created
# radiobutton (allowing its configuration/destruction)
return b
# hides the former active frame and shows
# another one, keeping its reference
def display(self, fr):
self.active_fr.forget()
fr.pack(fill=BOTH, expand=1)
self.active_fr = fr
# END
###-------------------------------
# file: test.py
# simple demonstration of the Tkinter notebook
from Tkinter import *
from notebook import *
a = Tk()
n = notebook(a, LEFT)
# uses the notebook's frame
f1 = Frame(n())
b1 = Button(f1, text="Button 1")
e1 = Entry(f1)
# pack your widgets before adding the frame
# to the notebook (but not the frame itself)!
b1.pack(fill=BOTH, expand=1)
e1.pack(fill=BOTH, expand=1)
f2 = Frame(n())
# this button destroys the 1st screen radiobutton
b2 = Button(f2, text='Button 2', command=lambda:x1.destroy())
b3 = Button(f2, text='Beep 2', command=lambda:Tk.bell(a))
b2.pack(fill=BOTH, expand=1)
b3.pack(fill=BOTH, expand=1)
f3 = Frame(n())
# keeps the reference to the radiobutton (optional)
x1 = n.add_screen(f1, "Screen 1")
n.add_screen(f2, "Screen 2")
n.add_screen(f3, "dummy")
if __name__ == "__main__":
a.mainloop()
# END
|
Out of envy of more advanced GUI toolkits, I wondered if it isn't possible to simulate a notebook widget using standard Tkinter frames and radiobuttons. It turns out that it is indeed, by the employment of some odd Tk corners, like the "indicatoron" radiobutton option (which reverts the radiobutton default appearance to the normal button one) and the forget() frame method, which allows easily swapping of "screens" (app frames) back and forth from the notebook "screen" area.
To convert an existing single-toplevel Tkinter app to a notebook-based one, it is necessary to change only the master frame root (usually a toplevel) to the one provided by the notebook (the 'n()' on the example code).
By request, now add_screen() returns a reference to the user frame's radiobutton, allowing its configuration (background/foreground color, etc) and destruction. Since all references to the external frames are kept implicitly in closures (lambdas) and in the active_fr member, destroying a radiobutton will destroy its respective frame, unless it's active (i.e., pointed by active_fr). In this case, pressing any remaining radiobutton will update active_fr and release the old frame.
References: "An Introduction to Tkinter", Fredrik Lundh "Practical programming in Tcl and Tk", Brent Welsh
Habatchii says;
very well...