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
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.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 theEvent
Ancestors
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
Rect
representing 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
:add
add_anchor
add_centered
anchor
attention_lost
augment_mouse_event
bg_color
bg_image
border_color
border_width
call_handler
call_parent_handler
defer_drawing
dismiss
draw
draw_over
fg_color
focus
focus_switch
font
get_cursor
get_focus
get_margin_rect
get_root
get_top_widget
get_visible
global_to_local
has_focus
inherited
invalidate
is_gl_container
key_down
key_up
local_to_global
margin
menu_bar
parent
parent_resized
present
rect
relative_mode
remove
remove_anchor
resized
scale_bg
sel_color
set_parent
set_size_for_text
tab_stop
visible