Module albow.containers.TabPanel
Source code
from typing import Callable
import logging
from pygame import Rect
from pygame.locals import USEREVENT
from pygame.event import Event
from albow.core.ui.Widget import Widget
from albow.themes.ThemeProperty import ThemeProperty
from albow.themes.FontProperty import FontProperty
from albow.utils import brighten
class TabPanel(Widget):
"""
A `TabPanel` is a widget that manages a collection of pages and displays one of them at a time. Switching
between pages is accomplished by means of a row of 'tabs' at the top of the TabPanel, one for each page.
Clicking on a tab brings its corresponding page to the front.
"""
tab_font = FontProperty('tab_font')
"""
Font in which to display the page titles in the tabs.
"""
tab_height = ThemeProperty('tab_height')
"""
Height of the tabs.
"""
tab_border_width = ThemeProperty('tab_border_width')
"""
Width of the border, if any, to draw around each tab.
"""
tab_spacing = ThemeProperty('tab_spacing')
"""
Width of space to leave between adjacent tabs.
"""
tab_margin = ThemeProperty('tab_margin')
"""
Width of space to leave before the first tab and after the last tab.
"""
tab_fg_color = ThemeProperty('tab_fg_color')
"""
Color in which to display the page titles.
"""
default_tab_bg_color = ThemeProperty('default_tab_bg_color')
"""
Color with which to fill the background of any tab whose page does not specify a tab color.
"""
tab_area_bg_color = ThemeProperty('tab_area_bg_color')
"""
Color with which to fill any background areas of the tab region not covered by a tab.
"""
tab_dimming = ThemeProperty('tab_dimming')
"""
Factor by which to dim the background colour of tabs other than the currently selected tab. The range is
0.0 to 1.0.
"""
def __init__(self, pages = None, enterTabAction: Callable=None, exitTabAction: Callable=None, **kwds):
"""
Args:
pages: The pages, if provided should be a sequence of (title, widget) pairs, which will
be added by the add_page() method.
**kwds:
"""
super().__init__(**kwds)
self.logger = logging.getLogger(__name__)
self.enterTabAction = enterTabAction
"""
The method to call when a tab is switched to. The method is called with an augmented event that has the tab index
that is getting the focus. The tab index is 0 based
"""
self.exitTabAction = exitTabAction
"""
The method to call when a tab is switched from. The method is called with an augmented event that has the tab index
of the tab losing the focus. The tab index is 0 based
"""
self.pages = []
self.current_page = None
if pages:
w = h = 0
for title, page in pages:
w = max(w, page.width)
h = max(h, page.height)
self._add_page(title, page)
self.size = (w, h)
self.show_page(pages[0][1])
def content_size(self) -> tuple:
"""
The area that a page will take up when it is displayed.
Returns: A tuple `(width, height)` of the size of the content area
"""
return self.width, self.height - self.tab_height
def content_rect(self) -> Rect:
"""
Returns: A `Rect` representing the content area in the local coordinate system of the `TabPanel`.
"""
return Rect((0, self.tab_height), self.content_size())
def page_height(self) -> int:
"""
Returns: The height of a page (equal to the height of the content_rect()).
"""
return self.height - self.tab_height
def add_page(self, theTitle: str, thePage: Widget):
"""
Adds the given widget as a new page, with the specified title to be displayed in its tab.
.. Note::
The widget should **NOT** also be added using add(); the TabPanel will do that itself when appropriate.
Args:
theTitle: The page title
thePage: The widget that is the page
"""
self._add_page(theTitle, thePage)
if not self.current_page:
self.show_page(thePage)
def _add_page(self, title, page):
page.tab_title = title
page.anchor = 'ltrb'
self.pages.append(page)
def remove_page(self, thePage: Widget):
"""
Removes the specified page, if present.
Args:
thePage: The page to remove
"""
try:
i = self.pages.index(thePage)
del self.pages[i]
except IndexError:
pass
if thePage is self.current_page:
self.show_page(None)
def show_page(self, newPage: Widget, theEvent: Event=None):
if self.current_page:
self._doExitTabAction(theEvent)
self.remove(self.current_page)
self.current_page = newPage
if newPage:
th = self.tab_height
newPage.rect = Rect(0, th, self.width, self.height - th)
self._doEnterTabAction(theEvent=theEvent, newPage=newPage)
self.add(newPage)
newPage.focus()
def draw(self, surf):
self.draw_tab_area_bg(surf)
self.draw_tabs(surf)
def draw_tab_area_bg(self, surf):
bg = self.tab_area_bg_color
if bg:
surf.fill(bg, (0, 0, self.width, self.tab_height))
def draw_tabs(self, surf):
font = self.tab_font
fg = self.tab_fg_color
b = self.tab_border_width
if b:
surf.fill(fg, (0, self.tab_height - b, self.width, b))
for i, title, page, selected, rect in self.iter_tabs():
x0 = rect.left
w = rect.width
h = rect.height
r = rect
if not selected:
r = Rect(r)
r.bottom -= b
self.draw_tab_bg(surf, page, selected, r)
if b:
surf.fill(fg, (x0, 0, b, h))
surf.fill(fg, (x0 + b, 0, w - 2 * b, b))
surf.fill(fg, (x0 + w - b, 0, b, h))
buf = font.render(title, True, page.fg_color or fg)
r = buf.get_rect()
r.center = (x0 + w // 2, h // 2)
surf.blit(buf, r)
def iter_tabs(self):
pages = self.pages
current_page = self.current_page
n = len(pages)
b = self.tab_border_width
s = self.tab_spacing
h = self.tab_height
m = self.tab_margin
width = self.width - 2 * m + s - b
x0 = m
for i, page in enumerate(pages):
x1 = m + (i + 1) * width // n
selected = page is current_page
yield i, page.tab_title, page, selected, Rect(x0, 0, x1 - x0 - s + b, h)
x0 = x1
def draw_tab_bg(self, surf, page, selected, rect):
bg = self.tab_bg_color_for_page(page)
if not selected:
bg = brighten(bg, self.tab_dimming)
surf.fill(bg, rect)
def tab_bg_color_for_page(self, page):
return getattr(page, 'tab_bg_color', None) \
or page.bg_color \
or self.default_tab_bg_color
def mouse_down(self, theEvent: Event):
x, y = theEvent.local
if y < self.tab_height:
i = self.tab_number_containing_x(x)
if i is not None:
self.show_page(self.pages[i], theEvent)
def tab_number_containing_x(self, x):
n = len(self.pages)
m = self.tab_margin
width = self.width - 2 * m + self.tab_spacing - self.tab_border_width
i = (x - m) * n // width
if 0 <= i < n:
return i
def _doExitTabAction(self, theEvent: Event):
leavingIndex: int = self.pages.index(self.current_page)
self.logger.debug(f"leavingIndex: {leavingIndex}")
if self.exitTabAction is not None:
augmentedEvent: Event = self._augmentEvent(theEvent=theEvent, theIndex=leavingIndex)
self.exitTabAction(augmentedEvent)
def _doEnterTabAction(self, theEvent: Event, newPage):
if newPage is not None:
enterIndex: int = self.pages.index(newPage)
self.logger.debug(f"enterIndex: {enterIndex}")
if self.exitTabAction is not None:
augmentedEvent: Event = self._augmentEvent(theEvent=theEvent, theIndex=enterIndex)
self.enterTabAction(augmentedEvent)
def _augmentEvent(self, theEvent: Event, theIndex: int) -> Event:
if theEvent is None:
theEvent = Event(USEREVENT, {'index': theIndex})
else:
theEvent.dict['index'] = theIndex
return theEvent
Classes
class TabPanel (pages=None, enterTabAction=None, exitTabAction=None, **kwds)-
A
TabPanelis a widget that manages a collection of pages and displays one of them at a time. Switching between pages is accomplished by means of a row of 'tabs' at the top of the TabPanel, one for each page. Clicking on a tab brings its corresponding page to the front.Args
pages- The pages, if provided should be a sequence of (title, widget) pairs, which will be added by the add_page() method.
**kwds:
Source code
class TabPanel(Widget): """ A `TabPanel` is a widget that manages a collection of pages and displays one of them at a time. Switching between pages is accomplished by means of a row of 'tabs' at the top of the TabPanel, one for each page. Clicking on a tab brings its corresponding page to the front. """ tab_font = FontProperty('tab_font') """ Font in which to display the page titles in the tabs. """ tab_height = ThemeProperty('tab_height') """ Height of the tabs. """ tab_border_width = ThemeProperty('tab_border_width') """ Width of the border, if any, to draw around each tab. """ tab_spacing = ThemeProperty('tab_spacing') """ Width of space to leave between adjacent tabs. """ tab_margin = ThemeProperty('tab_margin') """ Width of space to leave before the first tab and after the last tab. """ tab_fg_color = ThemeProperty('tab_fg_color') """ Color in which to display the page titles. """ default_tab_bg_color = ThemeProperty('default_tab_bg_color') """ Color with which to fill the background of any tab whose page does not specify a tab color. """ tab_area_bg_color = ThemeProperty('tab_area_bg_color') """ Color with which to fill any background areas of the tab region not covered by a tab. """ tab_dimming = ThemeProperty('tab_dimming') """ Factor by which to dim the background colour of tabs other than the currently selected tab. The range is 0.0 to 1.0. """ def __init__(self, pages = None, enterTabAction: Callable=None, exitTabAction: Callable=None, **kwds): """ Args: pages: The pages, if provided should be a sequence of (title, widget) pairs, which will be added by the add_page() method. **kwds: """ super().__init__(**kwds) self.logger = logging.getLogger(__name__) self.enterTabAction = enterTabAction """ The method to call when a tab is switched to. The method is called with an augmented event that has the tab index that is getting the focus. The tab index is 0 based """ self.exitTabAction = exitTabAction """ The method to call when a tab is switched from. The method is called with an augmented event that has the tab index of the tab losing the focus. The tab index is 0 based """ self.pages = [] self.current_page = None if pages: w = h = 0 for title, page in pages: w = max(w, page.width) h = max(h, page.height) self._add_page(title, page) self.size = (w, h) self.show_page(pages[0][1]) def content_size(self) -> tuple: """ The area that a page will take up when it is displayed. Returns: A tuple `(width, height)` of the size of the content area """ return self.width, self.height - self.tab_height def content_rect(self) -> Rect: """ Returns: A `Rect` representing the content area in the local coordinate system of the `TabPanel`. """ return Rect((0, self.tab_height), self.content_size()) def page_height(self) -> int: """ Returns: The height of a page (equal to the height of the content_rect()). """ return self.height - self.tab_height def add_page(self, theTitle: str, thePage: Widget): """ Adds the given widget as a new page, with the specified title to be displayed in its tab. .. Note:: The widget should **NOT** also be added using add(); the TabPanel will do that itself when appropriate. Args: theTitle: The page title thePage: The widget that is the page """ self._add_page(theTitle, thePage) if not self.current_page: self.show_page(thePage) def _add_page(self, title, page): page.tab_title = title page.anchor = 'ltrb' self.pages.append(page) def remove_page(self, thePage: Widget): """ Removes the specified page, if present. Args: thePage: The page to remove """ try: i = self.pages.index(thePage) del self.pages[i] except IndexError: pass if thePage is self.current_page: self.show_page(None) def show_page(self, newPage: Widget, theEvent: Event=None): if self.current_page: self._doExitTabAction(theEvent) self.remove(self.current_page) self.current_page = newPage if newPage: th = self.tab_height newPage.rect = Rect(0, th, self.width, self.height - th) self._doEnterTabAction(theEvent=theEvent, newPage=newPage) self.add(newPage) newPage.focus() def draw(self, surf): self.draw_tab_area_bg(surf) self.draw_tabs(surf) def draw_tab_area_bg(self, surf): bg = self.tab_area_bg_color if bg: surf.fill(bg, (0, 0, self.width, self.tab_height)) def draw_tabs(self, surf): font = self.tab_font fg = self.tab_fg_color b = self.tab_border_width if b: surf.fill(fg, (0, self.tab_height - b, self.width, b)) for i, title, page, selected, rect in self.iter_tabs(): x0 = rect.left w = rect.width h = rect.height r = rect if not selected: r = Rect(r) r.bottom -= b self.draw_tab_bg(surf, page, selected, r) if b: surf.fill(fg, (x0, 0, b, h)) surf.fill(fg, (x0 + b, 0, w - 2 * b, b)) surf.fill(fg, (x0 + w - b, 0, b, h)) buf = font.render(title, True, page.fg_color or fg) r = buf.get_rect() r.center = (x0 + w // 2, h // 2) surf.blit(buf, r) def iter_tabs(self): pages = self.pages current_page = self.current_page n = len(pages) b = self.tab_border_width s = self.tab_spacing h = self.tab_height m = self.tab_margin width = self.width - 2 * m + s - b x0 = m for i, page in enumerate(pages): x1 = m + (i + 1) * width // n selected = page is current_page yield i, page.tab_title, page, selected, Rect(x0, 0, x1 - x0 - s + b, h) x0 = x1 def draw_tab_bg(self, surf, page, selected, rect): bg = self.tab_bg_color_for_page(page) if not selected: bg = brighten(bg, self.tab_dimming) surf.fill(bg, rect) def tab_bg_color_for_page(self, page): return getattr(page, 'tab_bg_color', None) \ or page.bg_color \ or self.default_tab_bg_color def mouse_down(self, theEvent: Event): x, y = theEvent.local if y < self.tab_height: i = self.tab_number_containing_x(x) if i is not None: self.show_page(self.pages[i], theEvent) def tab_number_containing_x(self, x): n = len(self.pages) m = self.tab_margin width = self.width - 2 * m + self.tab_spacing - self.tab_border_width i = (x - m) * n // width if 0 <= i < n: return i def _doExitTabAction(self, theEvent: Event): leavingIndex: int = self.pages.index(self.current_page) self.logger.debug(f"leavingIndex: {leavingIndex}") if self.exitTabAction is not None: augmentedEvent: Event = self._augmentEvent(theEvent=theEvent, theIndex=leavingIndex) self.exitTabAction(augmentedEvent) def _doEnterTabAction(self, theEvent: Event, newPage): if newPage is not None: enterIndex: int = self.pages.index(newPage) self.logger.debug(f"enterIndex: {enterIndex}") if self.exitTabAction is not None: augmentedEvent: Event = self._augmentEvent(theEvent=theEvent, theIndex=enterIndex) self.enterTabAction(augmentedEvent) def _augmentEvent(self, theEvent: Event, theIndex: int) -> Event: if theEvent is None: theEvent = Event(USEREVENT, {'index': theIndex}) else: theEvent.dict['index'] = theIndex return theEventAncestors
Class variables
var default_tab_bg_color-
Color with which to fill the background of any tab whose page does not specify a tab color.
var tab_area_bg_color-
Color with which to fill any background areas of the tab region not covered by a tab.
var tab_border_width-
Width of the border, if any, to draw around each tab.
var tab_dimming-
Factor by which to dim the background colour of tabs other than the currently selected tab. The range is 0.0 to 1.0.
var tab_fg_color-
Color in which to display the page titles.
var tab_font-
Font in which to display the page titles in the tabs.
var tab_height-
Height of the tabs.
var tab_margin-
Width of space to leave before the first tab and after the last tab.
var tab_spacing-
Width of space to leave between adjacent tabs.
Instance variables
var enterTabAction-
The method to call when a tab is switched to. The method is called with an augmented event that has the tab index that is getting the focus. The tab index is 0 based
var exitTabAction-
The method to call when a tab is switched from. The method is called with an augmented event that has the tab index of the tab losing the focus. The tab index is 0 based
Methods
def add_page(self, theTitle, thePage)-
Adds the given widget as a new page, with the specified title to be displayed in its tab.
Note
The widget should NOT also be added using add(); the TabPanel will do that itself when appropriate.
Args
theTitle- The page title
thePage- The widget that is the page
Source code
def add_page(self, theTitle: str, thePage: Widget): """ Adds the given widget as a new page, with the specified title to be displayed in its tab. .. Note:: The widget should **NOT** also be added using add(); the TabPanel will do that itself when appropriate. Args: theTitle: The page title thePage: The widget that is the page """ self._add_page(theTitle, thePage) if not self.current_page: self.show_page(thePage) def content_rect(self)-
Returns: A
Rectrepresenting the content area in the local coordinate system of theTabPanel.Source code
def content_rect(self) -> Rect: """ Returns: A `Rect` representing the content area in the local coordinate system of the `TabPanel`. """ return Rect((0, self.tab_height), self.content_size()) def content_size(self)-
The area that a page will take up when it is displayed.
Returns: A tuple
(width, height)of the size of the content areaSource code
def content_size(self) -> tuple: """ The area that a page will take up when it is displayed. Returns: A tuple `(width, height)` of the size of the content area """ return self.width, self.height - self.tab_height def draw_tab_area_bg(self, surf)-
Source code
def draw_tab_area_bg(self, surf): bg = self.tab_area_bg_color if bg: surf.fill(bg, (0, 0, self.width, self.tab_height)) def draw_tab_bg(self, surf, page, selected, rect)-
Source code
def draw_tab_bg(self, surf, page, selected, rect): bg = self.tab_bg_color_for_page(page) if not selected: bg = brighten(bg, self.tab_dimming) surf.fill(bg, rect) def draw_tabs(self, surf)-
Source code
def draw_tabs(self, surf): font = self.tab_font fg = self.tab_fg_color b = self.tab_border_width if b: surf.fill(fg, (0, self.tab_height - b, self.width, b)) for i, title, page, selected, rect in self.iter_tabs(): x0 = rect.left w = rect.width h = rect.height r = rect if not selected: r = Rect(r) r.bottom -= b self.draw_tab_bg(surf, page, selected, r) if b: surf.fill(fg, (x0, 0, b, h)) surf.fill(fg, (x0 + b, 0, w - 2 * b, b)) surf.fill(fg, (x0 + w - b, 0, b, h)) buf = font.render(title, True, page.fg_color or fg) r = buf.get_rect() r.center = (x0 + w // 2, h // 2) surf.blit(buf, r) def iter_tabs(self)-
Source code
def iter_tabs(self): pages = self.pages current_page = self.current_page n = len(pages) b = self.tab_border_width s = self.tab_spacing h = self.tab_height m = self.tab_margin width = self.width - 2 * m + s - b x0 = m for i, page in enumerate(pages): x1 = m + (i + 1) * width // n selected = page is current_page yield i, page.tab_title, page, selected, Rect(x0, 0, x1 - x0 - s + b, h) x0 = x1 def mouse_down(self, theEvent)-
Source code
def mouse_down(self, theEvent: Event): x, y = theEvent.local if y < self.tab_height: i = self.tab_number_containing_x(x) if i is not None: self.show_page(self.pages[i], theEvent) def page_height(self)-
Returns: The height of a page (equal to the height of the content_rect()).
Source code
def page_height(self) -> int: """ Returns: The height of a page (equal to the height of the content_rect()). """ return self.height - self.tab_height def remove_page(self, thePage)-
Removes the specified page, if present.
Args
thePage- The page to remove
Source code
def remove_page(self, thePage: Widget): """ Removes the specified page, if present. Args: thePage: The page to remove """ try: i = self.pages.index(thePage) del self.pages[i] except IndexError: pass if thePage is self.current_page: self.show_page(None) def show_page(self, newPage, theEvent=None)-
Source code
def show_page(self, newPage: Widget, theEvent: Event=None): if self.current_page: self._doExitTabAction(theEvent) self.remove(self.current_page) self.current_page = newPage if newPage: th = self.tab_height newPage.rect = Rect(0, th, self.width, self.height - th) self._doEnterTabAction(theEvent=theEvent, newPage=newPage) self.add(newPage) newPage.focus() def tab_bg_color_for_page(self, page)-
Source code
def tab_bg_color_for_page(self, page): return getattr(page, 'tab_bg_color', None) \ or page.bg_color \ or self.default_tab_bg_color def tab_number_containing_x(self, x)-
Source code
def tab_number_containing_x(self, x): n = len(self.pages) m = self.tab_margin width = self.width - 2 * m + self.tab_spacing - self.tab_border_width i = (x - m) * n // width if 0 <= i < n: return i
Inherited members
Widget:addadd_anchoradd_centeredanchorattention_lostaugment_mouse_eventbg_colorbg_imageborder_colorborder_widthcall_handlercall_parent_handlerdefer_drawingdismissdrawdraw_overfg_colorfocusfocus_switchfontget_cursorget_focusget_margin_rectget_rootget_top_widgetget_visibleglobal_to_localhas_focusinheritedinvalidateis_gl_containerkey_downkey_uplocal_to_globalmarginmenu_barparentparent_resizedpresentrectrelative_moderemoveremove_anchorresizedscale_bgsel_colorset_parentset_size_for_texttab_stopvisible