Recipe11.13.Implementing a Tabbed Notebook for Tkinter


Recipe 11.13. Implementing a Tabbed Notebook for Tkinter

Credit: Iuri Wickert

Problem

You have some Tkinter applications, each with a single top-level window, and want to organize them as panels in a tabbed notebook with minimal changes to your original applications' source code.

Solution

A simple widget class can implement a notebook with all the features we need, including all possible orientations and the ability to add and switch frames (panels) at will:

from Tkinter import * class notebook(object):     def _ _init_ _(self, master, side=LEFT):         self.active_fr = None         self.count = 0         self.choice = IntVar(0)         if side in (TOP, BOTTOM): self.side = LEFT         else: self.side = TOP         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)     def _ _call_ _(self):         return self.screen_fr     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)         if not self.active_fr:             fr.pack(fill=BOTH, expand=1)             self.active_fr = fr         self.count += 1     def display(self, fr):         self.active_fr.forget( )         fr.pack(fill=BOTH, expand=1)         self.active_fr = fr

Just save this code as a notebook.py module, somewhere on your Python sys.path, and you can import and use it in your apps.

Discussion

The simplest way to show how this notebook class works is with a simple demonstration program:

from Tkinter import * from notebook import * # make a toplevel with a notebook in it, with tabs on the left: root = Tk( ) nb = notebook(root, LEFT) # make a few diverse frames (panels), each using the NB as 'master': f1 = Frame(nb( )) b1 = Button(f1, text="Button 1") e1 = Entry(f1) # pack your widgets in the frame before adding the frame to the # notebook, do NOT pack the frame itself! b1.pack(fill=BOTH, expand=1) e1.pack(fill=BOTH, expand=1) f2 = Frame(nb( )) b2 = Button(f2, text='Button 2') b3 = Button(f2, text='Beep 2', command=Tk.bell) b2.pack(fill=BOTH, expand=1) b3.pack(fill=BOTH, expand=1) f3 = Frame(nb( )) # add the frames as notebook 'screens' and run this GUI app nb.add_screen(f1, "Screen 1") nb.add_screen(f2, "Screen 2") nb.add_screen(f3, "dummy") root.mainloop( )

Tkinter is a simple GUI toolkit, easy to use but notoriously feature-poor when compared to more advanced toolkits. And yet, sometimes advanced features are not all that difficult to add! I wondered how I could use a tabbed appearance, also known as a notebook, to organize various pages of an application, or various related applications, simply and elegantly. I discovered that simulating a notebook widget by using standard Tkinter frames and radio buttons was not only possible, but also quite simple and effective.

Tk has some "odd", and somewhat unknown, corners, which make the whole task a snap. The indicatoron option on a radio button reverts the radio button default appearance back to the normal button looka rectangle, which may not be a perfect-looking tab but is plenty good enough for me. Each Tkinter frame has a forget method, which allows easy and fast swapping of "screens" (notebook panels, application frames) within the single "screen frame" of the notebook object.

To convert any existing Tkinter app, based on a single top-level window, to run inside a notebook panel, all you need to do is to change the application master frame's root, which is generally a top-level widget (an instance of Tkinter's Tk class), to the one provided by the notebook object when you call it. (The three occurrences of nb( ) in the example code show how to go about it.)

The notebook implementations in other toolkits often have advanced features such as the ability to exclude (remove) some frames as well as adding others. I have not found this kind of thing to be necessary, and so I have taken no trouble in this recipe to make it possible: all references to the external frames are kept implicitly in lambda closures, without any obvious way to remove them. If you think you need the ability to remove frames, you might consider an alternative architecture: keep the frames' references in a list, indexed by the binding variable of the radio buttons (i.e., the choice attribute of each radio button). Doing so lets you destroy a "frame" and its associated radio button in a reasonably clean way.

See Also

Information about Tkinter can be obtained from a variety of sources, such as Fredrik Lundh, An Introduction to Tkinter (PythonWare: http://www.pythonware.com/library), New Mexico Tech's Tkinter Reference (http://www.nmt.edu/tcc/help/lang/python/docs.html), Python in a Nutshell, and various other books.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net