IDLE_tabbedpages.071101.patch Tal Einat

Cosmetic changes, one bug.  Remove tabpage.py, replaced by tabbedpages.py
This commit is contained in:
Kurt B. Kaiser 2007-12-11 19:35:12 +00:00
parent 2382832bf3
commit 7a634e6027
2 changed files with 47 additions and 148 deletions

View File

@ -4,7 +4,7 @@ Originally developed for use in IDLE. Based on tabpage.py.
Classes exported:
TabbedPageSet -- A Tkinter implementation of a tabbed-page widget.
TabBarSet -- A widget containing tabs (buttons) in one or more rows.
TabSet -- A widget containing tabs (buttons) in one or more rows.
"""
from Tkinter import *
@ -13,7 +13,7 @@ class InvalidNameError(Exception): pass
class AlreadyExistsError(Exception): pass
class TabBarSet(Frame):
class TabSet(Frame):
"""A widget containing tabs (buttons) in one or more rows.
Only one tab may be selected at a time.
@ -30,11 +30,11 @@ class TabBarSet(Frame):
tabs -- A list of strings, the names of the tabs. Should be specified in
the desired tab order. The first tab will be the default and first
active tab. If tabs is None or empty, the TabBarSet will be initialized
active tab. If tabs is None or empty, the TabSet will be initialized
empty.
n_rows -- Number of rows of tabs to be shown. If n_rows <= 0 or is
None, then the number of rows will be decided by TabBarSet. See
None, then the number of rows will be decided by TabSet. See
_arrange_tabs() for details.
max_tabs_per_row -- Used for deciding how many rows of tabs are needed,
@ -76,15 +76,15 @@ class TabBarSet(Frame):
self._arrange_tabs()
def remove_tab(self, tab_name):
"""Remove the tab with the name given in tab_name."""
"""Remove the tab named <tab_name>"""
if not tab_name in self._tab_names:
raise KeyError("No such Tab: '%s" % page_name)
self._tab_names.remove(tab_name)
self._arrange_tabs()
def select_tab(self, tab_name):
"""Select the tab with the name given in tab_name."""
def set_selected_tab(self, tab_name):
"""Show the tab named <tab_name> as the selected one"""
if tab_name == self._selected_tab:
return
if tab_name is not None and tab_name not in self._tabs:
@ -111,14 +111,11 @@ class TabBarSet(Frame):
tab_row = Frame(self)
tab_row.pack(side=TOP, fill=X, expand=0)
tab_row.tab_set = self
self._tab_rows.append(tab_row)
for tab_name in tab_names:
def tab_command(select_command=self.select_command,
tab_name=tab_name):
return select_command(tab_name)
tab = TabBarSet.TabButton(tab_row, tab_name, tab_command)
tab = TabSet.TabButton(tab_name, self.select_command,
tab_row, self)
if expand_tabs:
tab.pack(side=LEFT, fill=X, expand=True)
else:
@ -126,6 +123,7 @@ class TabBarSet(Frame):
self._tabs[tab_name] = tab
self._tab2row[tab] = tab_row
# tab is the last one created in the above loop
tab.is_last_in_row = True
def _reset_tab_rows(self):
@ -158,8 +156,9 @@ class TabBarSet(Frame):
# calculate the required number of rows
n_rows = (len(self._tab_names) - 1) // self.max_tabs_per_row + 1
i = 0
# not expanding the tabs with more than one row is very ugly
expand_tabs = self.expand_tabs or n_rows > 1
i = 0 # index in self._tab_names
for row_index in xrange(n_rows):
# calculate required number of tabs in this row
n_tabs = (len(self._tab_names) - i - 1) // (n_rows - row_index) + 1
@ -169,47 +168,60 @@ class TabBarSet(Frame):
# re-select selected tab so it is properly displayed
selected = self._selected_tab
self.select_tab(None)
self.set_selected_tab(None)
if selected in self._tab_names:
self.select_tab(selected)
self.set_selected_tab(selected)
class TabButton(Frame):
"""A simple tab-like widget."""
bw = 2 # borderwidth
def __init__(self, tab_row, name, command):
def __init__(self, name, select_command, tab_row, tab_set):
"""Constructor arguments:
name -- The tab's name, which will appear in its button.
command -- The command to be called upon selection of the tab. It
is called with the tab's name as an argument.
select_command -- The command to be called upon selection of the
tab. It is called with the tab's name as an argument.
"""
Frame.__init__(self, tab_row, borderwidth=self.bw)
self.button = Radiobutton(self, text=name, command=command,
Frame.__init__(self, tab_row, borderwidth=self.bw, relief=RAISED)
self.name = name
self.select_command = select_command
self.tab_set = tab_set
self.is_last_in_row = False
self.button = Radiobutton(
self, text=name, command=self._select_event,
padx=5, pady=1, takefocus=FALSE, indicatoron=FALSE,
highlightthickness=0, selectcolor='', borderwidth=0)
self.button.pack(side=LEFT, fill=X, expand=True)
self.tab_set = tab_row.tab_set
self.is_last_in_row = False
self._init_masks()
self.set_normal()
def _select_event(self, *args):
"""Event handler for tab selection.
With TabbedPageSet, this calls TabbedPageSet.change_page, so that
selecting a tab changes the page.
Note that this does -not- call set_selected -- it will be called by
TabSet.set_selected_tab, which should be called when whatever the
tabs are related to changes.
"""
self.select_command(self.name)
return
def set_selected(self):
"""Assume selected look"""
for widget in self, self.mskl.ml, self.mskr.mr:
widget.config(relief=RAISED)
self._place_masks(selected=True)
def set_normal(self):
"""Assume normal look"""
for widget in self, self.mskl.ml, self.mskr.mr:
widget.config(relief=RAISED)
self._place_masks(selected=False)
def _init_masks(self):
@ -351,8 +363,8 @@ class TabbedPageSet(Frame):
and first active page. If page_names is None or empty, the
TabbedPageSet will be initialized empty.
n_rows, max_tabs_per_row -- Parameters for the TabBarSet which will
manage the tabs. See TabBarSet's docs for details.
n_rows, max_tabs_per_row -- Parameters for the TabSet which will
manage the tabs. See TabSet's docs for details.
page_class -- Pages can be shown/hidden using three mechanisms:
@ -372,7 +384,7 @@ class TabbedPageSet(Frame):
TabbedPageSet to resize when the page is changed.
"""
Frame.__init__(self, parent, kw)
Frame.__init__(self, parent, **kw)
self.page_class = page_class
self.pages = {}
@ -390,9 +402,9 @@ class TabbedPageSet(Frame):
self.pages_frame.rowconfigure(0, weight=1)
# the order of the following commands is important
self._tab_set = TabBarSet(self, self.change_page, n_rows=n_rows,
max_tabs_per_row=max_tabs_per_row,
expand_tabs=expand_tabs)
self._tab_set = TabSet(self, self.change_page, n_rows=n_rows,
max_tabs_per_row=max_tabs_per_row,
expand_tabs=expand_tabs)
if page_names:
for name in page_names:
self.add_page(name)
@ -453,7 +465,7 @@ class TabbedPageSet(Frame):
self._current_page = page_name
self.pages[page_name]._show()
self._tab_set.select_tab(page_name)
self._tab_set.set_selected_tab(page_name)
if __name__ == '__main__':
# test dialog

View File

@ -1,113 +0,0 @@
"""
a couple of classes for implementing partial tabbed-page like behaviour
"""
from Tkinter import *
class InvalidTabPage(Exception): pass
class AlreadyExists(Exception): pass
class PageTab(Frame):
"""
a 'page tab' like framed button
"""
def __init__(self,parent):
Frame.__init__(self, parent,borderwidth=2,relief=RIDGE)
self.button=Radiobutton(self,padx=5,pady=5,takefocus=FALSE,
indicatoron=FALSE,highlightthickness=0,
borderwidth=0,selectcolor=self.cget('bg'))
self.button.pack()
class TabPageSet(Frame):
"""
a set of 'pages' with TabButtons for controlling their display
"""
def __init__(self,parent,pageNames=[],**kw):
"""
pageNames - a list of strings, each string will be the dictionary key
to a page's data, and the name displayed on the page's tab. Should be
specified in desired page order. The first page will be the default
and first active page.
"""
Frame.__init__(self, parent, kw)
self.grid_location(0,0)
self.columnconfigure(0,weight=1)
self.rowconfigure(1,weight=1)
self.tabBar=Frame(self)
self.tabBar.grid(row=0,column=0,sticky=EW)
self.activePage=StringVar(self)
self.defaultPage=''
self.pages={}
for name in pageNames:
self.AddPage(name)
def ChangePage(self,pageName=None):
if pageName:
if pageName in self.pages.keys():
self.activePage.set(pageName)
else:
raise InvalidTabPage, 'Invalid TabPage Name'
## pop up the active 'tab' only
for page in self.pages.keys():
self.pages[page]['tab'].config(relief=RIDGE)
self.pages[self.GetActivePage()]['tab'].config(relief=RAISED)
## switch page
self.pages[self.GetActivePage()]['page'].lift()
def GetActivePage(self):
return self.activePage.get()
def AddPage(self,pageName):
if pageName in self.pages.keys():
raise AlreadyExists, 'TabPage Name Already Exists'
self.pages[pageName]={'tab':PageTab(self.tabBar),
'page':Frame(self,borderwidth=2,relief=RAISED)}
self.pages[pageName]['tab'].button.config(text=pageName,
command=self.ChangePage,variable=self.activePage,
value=pageName)
self.pages[pageName]['tab'].pack(side=LEFT)
self.pages[pageName]['page'].grid(row=1,column=0,sticky=NSEW)
if len(self.pages)==1: # adding first page
self.defaultPage=pageName
self.activePage.set(self.defaultPage)
self.ChangePage()
def RemovePage(self,pageName):
if not pageName in self.pages.keys():
raise InvalidTabPage, 'Invalid TabPage Name'
self.pages[pageName]['tab'].pack_forget()
self.pages[pageName]['page'].grid_forget()
self.pages[pageName]['tab'].destroy()
self.pages[pageName]['page'].destroy()
del(self.pages[pageName])
# handle removing last remaining, or default, or active page
if not self.pages: # removed last remaining page
self.defaultPage=''
return
if pageName==self.defaultPage: # set a new default page
self.defaultPage=\
self.tabBar.winfo_children()[0].button.cget('text')
if pageName==self.GetActivePage(): # set a new active page
self.activePage.set(self.defaultPage)
self.ChangePage()
if __name__ == '__main__':
#test dialog
root=Tk()
tabPage=TabPageSet(root,pageNames=['Foobar','Baz'])
tabPage.pack(expand=TRUE,fill=BOTH)
Label(tabPage.pages['Foobar']['page'],text='Foo',pady=20).pack()
Label(tabPage.pages['Foobar']['page'],text='Bar',pady=20).pack()
Label(tabPage.pages['Baz']['page'],text='Baz').pack()
entryPgName=Entry(root)
buttonAdd=Button(root,text='Add Page',
command=lambda:tabPage.AddPage(entryPgName.get()))
buttonRemove=Button(root,text='Remove Page',
command=lambda:tabPage.RemovePage(entryPgName.get()))
labelPgName=Label(root,text='name of page to add/remove:')
buttonAdd.pack(padx=5,pady=5)
buttonRemove.pack(padx=5,pady=5)
labelPgName.pack(padx=5)
entryPgName.pack(padx=5)
tabPage.ChangePage()
root.mainloop()