Welcome, guest | Sign In | My Account | Store | Cart

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).

Python, 106 lines
  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

1 comment

Stacy Staples 9 years, 5 months ago  # | flag

Habatchii says;

very well...

Created by Iuri Wickert on Sat, 8 Mar 2003 (PSF)
Python recipes (4591)
Iuri Wickert's recipes (2)

Required Modules

Other Information and Tasks