Module m45wxcontrols.accessible_listbook

Accessible listbook control backed by wx.ListBox.

Classes

class AccessibleListbook (parent,
id=-1,
pos=wx.Point(-1, -1),
size=wx.Size(-1, -1),
style=524288,
name='AccessibleListbook',
list_width=180)
Expand source code
class AccessibleListbook(wx.Panel):
    """
    Accessible wx.Listbook-style control using wx.ListBox for page selection.

    The category list is a plain wx.ListBox, which is easier to use with
    screen readers on platforms where wx.ListCtrl accessibility is limited.
    Pages are ordinary wx.Window instances that are shown when their matching
    list entry is selected.
    """

    def __init__(
        self,
        parent,
        id=wx.ID_ANY,
        pos=wx.DefaultPosition,
        size=wx.DefaultSize,
        style=wx.TAB_TRAVERSAL,
        name="AccessibleListbook",
        list_width=180,
    ):
        """
        Initialize the accessible listbook.

        Args:
            parent: Parent wx window.
            id: Window id.
            pos: Initial position.
            size: Initial size.
            style: wx.Panel style flags.
            name: Control name.
            list_width: Width of the category list in pixels.
        """
        super().__init__(parent, id=id, pos=pos, size=size, style=style, name=name)

        self._pages = []
        self._page_texts = []
        self._page_images = []
        self._selection = wx.NOT_FOUND
        self._changing_selection = False

        self.list_label = wx.StaticText(self, label="Cate&gories")
        self.listbox = wx.ListBox(self, style=wx.LB_SINGLE | wx.BORDER_SUNKEN)
        self.listbox.SetName(f"{name} categories")
        self.page_area = wx.Panel(self, style=wx.TAB_TRAVERSAL)

        self._page_sizer = wx.BoxSizer(wx.VERTICAL)
        self.page_area.SetSizer(self._page_sizer)

        left = wx.BoxSizer(wx.VERTICAL)
        left.Add(self.list_label, 0, wx.LEFT | wx.RIGHT | wx.TOP, 5)
        left.Add(self.listbox, 1, wx.EXPAND | wx.ALL, 5)

        root = wx.BoxSizer(wx.HORIZONTAL)
        root.Add(left, 0, wx.EXPAND)
        root.SetItemMinSize(left, list_width, -1)
        root.Add(self.page_area, 1, wx.EXPAND | wx.TOP | wx.RIGHT | wx.BOTTOM, 5)
        self.SetSizer(root)

        self.listbox.Bind(wx.EVT_LISTBOX, self._on_listbox_selection)

    def AddPage(self, page, text, select=False, imageId=wx.NOT_FOUND):
        """
        Add a page to the end of the control.

        Args:
            page: wx.Window displayed as page content.
            text: Label shown in the category list.
            select: Whether to select the new page immediately.
            imageId: Stored for Listbook API compatibility. Images are not
                displayed by wx.ListBox.

        Returns:
            bool: True when the page was added.
        """
        return self.InsertPage(self.GetPageCount(), page, text, select, imageId)

    def InsertPage(self, index, page, text, select=False, imageId=wx.NOT_FOUND):
        """
        Insert a page at a specific index.
        """
        index = max(0, min(index, self.GetPageCount()))
        self._prepare_page(page)

        self._pages.insert(index, page)
        self._page_texts.insert(index, str(text))
        self._page_images.insert(index, imageId)
        self.listbox.Insert(str(text), index)
        self._page_sizer.Insert(index, page, 1, wx.EXPAND)
        page.Hide()

        if self._selection == wx.NOT_FOUND or select:
            self.ChangeSelection(index)
        elif index <= self._selection:
            self._selection += 1
            self.listbox.SetSelection(self._selection)

        self.Layout()
        return True

    def DeletePage(self, index):
        """
        Delete and destroy a page.

        Returns:
            bool: True when a page was deleted.
        """
        page = self.RemovePage(index)
        if page is None:
            return False
        page.Destroy()
        return True

    def RemovePage(self, index):
        """
        Remove a page without destroying it.

        Returns:
            wx.Window | None: Removed page, or None for an invalid index.
        """
        if not self._is_valid_index(index):
            return None

        page = self._pages.pop(index)
        del self._page_texts[index]
        del self._page_images[index]
        self.listbox.Delete(index)
        self._page_sizer.Detach(page)
        page.Hide()

        if not self._pages:
            self._selection = wx.NOT_FOUND
            self.listbox.SetSelection(wx.NOT_FOUND)
        elif index == self._selection:
            self._selection = wx.NOT_FOUND
            self.ChangeSelection(min(index, self.GetPageCount() - 1))
        elif index < self._selection:
            self._selection -= 1
            self.listbox.SetSelection(self._selection)

        self.Layout()
        return page

    def DeleteAllPages(self):
        """
        Delete and destroy all pages.
        """
        for page in self._pages:
            self._page_sizer.Detach(page)
            page.Destroy()
        self._pages.clear()
        self._page_texts.clear()
        self._page_images.clear()
        self.listbox.Clear()
        self._selection = wx.NOT_FOUND
        self.Layout()

    def GetPage(self, index):
        """
        Return the page at index, or None when the index is invalid.
        """
        if not self._is_valid_index(index):
            return None
        return self._pages[index]

    def GetCurrentPage(self):
        """
        Return the currently selected page, or None when no page is selected.
        """
        return self.GetPage(self._selection)

    def GetPageCount(self):
        """
        Return the number of pages.
        """
        return len(self._pages)

    def GetPageText(self, index):
        """
        Return a page label.
        """
        if not self._is_valid_index(index):
            return ""
        return self._page_texts[index]

    def SetPageText(self, index, text):
        """
        Set a page label.

        Returns:
            bool: True when the label was updated.
        """
        if not self._is_valid_index(index):
            return False
        self._page_texts[index] = str(text)
        self.listbox.SetString(index, str(text))
        return True

    def GetPageImage(self, index):
        """
        Return the stored image id for API compatibility.
        """
        if not self._is_valid_index(index):
            return wx.NOT_FOUND
        return self._page_images[index]

    def SetPageImage(self, index, imageId):
        """
        Store an image id for API compatibility.

        wx.ListBox does not display images, but callers can still keep their
        Listbook-oriented code paths intact.
        """
        if not self._is_valid_index(index):
            return False
        self._page_images[index] = imageId
        return True

    def GetSelection(self):
        """
        Return the selected page index, or wx.NOT_FOUND.
        """
        return self._selection

    def SetSelection(self, index):
        """
        Select a page and emit listbook page changing/changed events.

        Returns:
            int: Previously selected index, or wx.NOT_FOUND if selection failed.
        """
        return self._set_selection(index, emit_events=True)

    def ChangeSelection(self, index):
        """
        Select a page without emitting page changing/changed events.

        Returns:
            int: Previously selected index, or wx.NOT_FOUND if selection failed.
        """
        return self._set_selection(index, emit_events=False)

    def AdvanceSelection(self, forward=True):
        """
        Move selection to the next or previous page.
        """
        count = self.GetPageCount()
        if count == 0:
            return

        if self._selection == wx.NOT_FOUND:
            self.SetSelection(0)
            return

        step = 1 if forward else -1
        self.SetSelection((self._selection + step) % count)

    def GetStaticLabel(self):
        """
        Return the text shown above the category list.
        """
        return self.list_label.GetLabel()

    def SetStaticLabel(self, label):
        """
        Set the text shown above the category list.

        This is useful for localized applications because wx.Panel itself does
        not accept a label argument.
        """
        self.list_label.SetLabel(str(label))
        self.Layout()

    def GetListBox(self):
        """
        Return the wx.ListBox used for category navigation.
        """
        return self.listbox

    def GetPageArea(self):
        """
        Return the panel that owns page windows.
        """
        return self.page_area

    def SetListWidth(self, width):
        """
        Set the fixed width of the category list.
        """
        item = self.GetSizer().GetItem(self.listbox)
        if item is not None:
            item.SetMinSize(width, -1)
            self.Layout()

    def _prepare_page(self, page):
        """
        Ensure a page belongs to the internal page area.
        """
        if page.GetParent() is not self.page_area:
            page.Reparent(self.page_area)

    def _is_valid_index(self, index):
        """
        Return whether index points to an existing page.
        """
        return 0 <= index < self.GetPageCount()

    def _set_selection(self, index, emit_events, sync_listbox=True):
        """
        Internal selection implementation.
        """
        if not self._is_valid_index(index):
            return wx.NOT_FOUND

        old_selection = self._selection
        if index == old_selection:
            if sync_listbox:
                self.listbox.SetSelection(index)
            return old_selection

        if emit_events and not self._send_book_event(
            wx.wxEVT_LISTBOOK_PAGE_CHANGING,
            index,
            old_selection,
        ):
            if sync_listbox:
                self.listbox.SetSelection(old_selection)
            return wx.NOT_FOUND

        self._show_page(index, sync_listbox=sync_listbox)

        if emit_events:
            self._send_book_event(wx.wxEVT_LISTBOOK_PAGE_CHANGED, index, old_selection)

        return old_selection

    def _show_page(self, index, sync_listbox=True):
        """
        Show one page and hide the previously selected page.
        """
        old_selection = self._selection
        self._changing_selection = True
        self.page_area.Freeze()
        try:
            if self._is_valid_index(old_selection):
                self._pages[old_selection].Hide()
            self._pages[index].Show()
            self._selection = index
            if sync_listbox:
                self.listbox.SetSelection(index)
            self.page_area.Layout()
        finally:
            self.page_area.Thaw()
            self._changing_selection = False

    def _send_book_event(self, event_type, selection, old_selection):
        """
        Send a wx.BookCtrlEvent and report whether it was allowed.
        """
        event = wx.BookCtrlEvent(event_type, self.GetId(), selection, old_selection)
        event.SetEventObject(self)
        self.GetEventHandler().ProcessEvent(event)
        return event.IsAllowed()

    def _on_listbox_selection(self, event):
        """
        Select pages when users move through the listbox.
        """
        if self._changing_selection:
            return

        previous = self._selection
        selected = event.GetSelection()
        result = self._set_selection(selected, emit_events=True, sync_listbox=False)
        if result == wx.NOT_FOUND and previous != wx.NOT_FOUND:
            self.listbox.SetSelection(previous)

Accessible wx.Listbook-style control using wx.ListBox for page selection.

The category list is a plain wx.ListBox, which is easier to use with screen readers on platforms where wx.ListCtrl accessibility is limited. Pages are ordinary wx.Window instances that are shown when their matching list entry is selected.

Initialize the accessible listbook.

Args

parent
Parent wx window.
id
Window id.
pos
Initial position.
size
Initial size.
style
wx.Panel style flags.
name
Control name.
list_width
Width of the category list in pixels.

Ancestors

  • wx._core.Panel
  • wx._core.Window
  • wx._core.WindowBase
  • wx._core.EvtHandler
  • wx._core.Object
  • wx._core.Trackable
  • sip.wrapper
  • sip.simplewrapper

Methods

def AddPage(self, page, text, select=False, imageId=-1)
Expand source code
def AddPage(self, page, text, select=False, imageId=wx.NOT_FOUND):
    """
    Add a page to the end of the control.

    Args:
        page: wx.Window displayed as page content.
        text: Label shown in the category list.
        select: Whether to select the new page immediately.
        imageId: Stored for Listbook API compatibility. Images are not
            displayed by wx.ListBox.

    Returns:
        bool: True when the page was added.
    """
    return self.InsertPage(self.GetPageCount(), page, text, select, imageId)

Add a page to the end of the control.

Args

page
wx.Window displayed as page content.
text
Label shown in the category list.
select
Whether to select the new page immediately.
imageId
Stored for Listbook API compatibility. Images are not displayed by wx.ListBox.

Returns

bool
True when the page was added.
def AdvanceSelection(self, forward=True)
Expand source code
def AdvanceSelection(self, forward=True):
    """
    Move selection to the next or previous page.
    """
    count = self.GetPageCount()
    if count == 0:
        return

    if self._selection == wx.NOT_FOUND:
        self.SetSelection(0)
        return

    step = 1 if forward else -1
    self.SetSelection((self._selection + step) % count)

Move selection to the next or previous page.

def ChangeSelection(self, index)
Expand source code
def ChangeSelection(self, index):
    """
    Select a page without emitting page changing/changed events.

    Returns:
        int: Previously selected index, or wx.NOT_FOUND if selection failed.
    """
    return self._set_selection(index, emit_events=False)

Select a page without emitting page changing/changed events.

Returns

int
Previously selected index, or wx.NOT_FOUND if selection failed.
def DeleteAllPages(self)
Expand source code
def DeleteAllPages(self):
    """
    Delete and destroy all pages.
    """
    for page in self._pages:
        self._page_sizer.Detach(page)
        page.Destroy()
    self._pages.clear()
    self._page_texts.clear()
    self._page_images.clear()
    self.listbox.Clear()
    self._selection = wx.NOT_FOUND
    self.Layout()

Delete and destroy all pages.

def DeletePage(self, index)
Expand source code
def DeletePage(self, index):
    """
    Delete and destroy a page.

    Returns:
        bool: True when a page was deleted.
    """
    page = self.RemovePage(index)
    if page is None:
        return False
    page.Destroy()
    return True

Delete and destroy a page.

Returns

bool
True when a page was deleted.
def GetCurrentPage(self)
Expand source code
def GetCurrentPage(self):
    """
    Return the currently selected page, or None when no page is selected.
    """
    return self.GetPage(self._selection)

Return the currently selected page, or None when no page is selected.

def GetListBox(self)
Expand source code
def GetListBox(self):
    """
    Return the wx.ListBox used for category navigation.
    """
    return self.listbox

Return the wx.ListBox used for category navigation.

def GetPage(self, index)
Expand source code
def GetPage(self, index):
    """
    Return the page at index, or None when the index is invalid.
    """
    if not self._is_valid_index(index):
        return None
    return self._pages[index]

Return the page at index, or None when the index is invalid.

def GetPageArea(self)
Expand source code
def GetPageArea(self):
    """
    Return the panel that owns page windows.
    """
    return self.page_area

Return the panel that owns page windows.

def GetPageCount(self)
Expand source code
def GetPageCount(self):
    """
    Return the number of pages.
    """
    return len(self._pages)

Return the number of pages.

def GetPageImage(self, index)
Expand source code
def GetPageImage(self, index):
    """
    Return the stored image id for API compatibility.
    """
    if not self._is_valid_index(index):
        return wx.NOT_FOUND
    return self._page_images[index]

Return the stored image id for API compatibility.

def GetPageText(self, index)
Expand source code
def GetPageText(self, index):
    """
    Return a page label.
    """
    if not self._is_valid_index(index):
        return ""
    return self._page_texts[index]

Return a page label.

def GetSelection(self)
Expand source code
def GetSelection(self):
    """
    Return the selected page index, or wx.NOT_FOUND.
    """
    return self._selection

Return the selected page index, or wx.NOT_FOUND.

def GetStaticLabel(self)
Expand source code
def GetStaticLabel(self):
    """
    Return the text shown above the category list.
    """
    return self.list_label.GetLabel()

Return the text shown above the category list.

def InsertPage(self, index, page, text, select=False, imageId=-1)
Expand source code
def InsertPage(self, index, page, text, select=False, imageId=wx.NOT_FOUND):
    """
    Insert a page at a specific index.
    """
    index = max(0, min(index, self.GetPageCount()))
    self._prepare_page(page)

    self._pages.insert(index, page)
    self._page_texts.insert(index, str(text))
    self._page_images.insert(index, imageId)
    self.listbox.Insert(str(text), index)
    self._page_sizer.Insert(index, page, 1, wx.EXPAND)
    page.Hide()

    if self._selection == wx.NOT_FOUND or select:
        self.ChangeSelection(index)
    elif index <= self._selection:
        self._selection += 1
        self.listbox.SetSelection(self._selection)

    self.Layout()
    return True

Insert a page at a specific index.

def RemovePage(self, index)
Expand source code
def RemovePage(self, index):
    """
    Remove a page without destroying it.

    Returns:
        wx.Window | None: Removed page, or None for an invalid index.
    """
    if not self._is_valid_index(index):
        return None

    page = self._pages.pop(index)
    del self._page_texts[index]
    del self._page_images[index]
    self.listbox.Delete(index)
    self._page_sizer.Detach(page)
    page.Hide()

    if not self._pages:
        self._selection = wx.NOT_FOUND
        self.listbox.SetSelection(wx.NOT_FOUND)
    elif index == self._selection:
        self._selection = wx.NOT_FOUND
        self.ChangeSelection(min(index, self.GetPageCount() - 1))
    elif index < self._selection:
        self._selection -= 1
        self.listbox.SetSelection(self._selection)

    self.Layout()
    return page

Remove a page without destroying it.

Returns

wx.Window | None
Removed page, or None for an invalid index.
def SetListWidth(self, width)
Expand source code
def SetListWidth(self, width):
    """
    Set the fixed width of the category list.
    """
    item = self.GetSizer().GetItem(self.listbox)
    if item is not None:
        item.SetMinSize(width, -1)
        self.Layout()

Set the fixed width of the category list.

def SetPageImage(self, index, imageId)
Expand source code
def SetPageImage(self, index, imageId):
    """
    Store an image id for API compatibility.

    wx.ListBox does not display images, but callers can still keep their
    Listbook-oriented code paths intact.
    """
    if not self._is_valid_index(index):
        return False
    self._page_images[index] = imageId
    return True

Store an image id for API compatibility.

wx.ListBox does not display images, but callers can still keep their Listbook-oriented code paths intact.

def SetPageText(self, index, text)
Expand source code
def SetPageText(self, index, text):
    """
    Set a page label.

    Returns:
        bool: True when the label was updated.
    """
    if not self._is_valid_index(index):
        return False
    self._page_texts[index] = str(text)
    self.listbox.SetString(index, str(text))
    return True

Set a page label.

Returns

bool
True when the label was updated.
def SetSelection(self, index)
Expand source code
def SetSelection(self, index):
    """
    Select a page and emit listbook page changing/changed events.

    Returns:
        int: Previously selected index, or wx.NOT_FOUND if selection failed.
    """
    return self._set_selection(index, emit_events=True)

Select a page and emit listbook page changing/changed events.

Returns

int
Previously selected index, or wx.NOT_FOUND if selection failed.
def SetStaticLabel(self, label)
Expand source code
def SetStaticLabel(self, label):
    """
    Set the text shown above the category list.

    This is useful for localized applications because wx.Panel itself does
    not accept a label argument.
    """
    self.list_label.SetLabel(str(label))
    self.Layout()

Set the text shown above the category list.

This is useful for localized applications because wx.Panel itself does not accept a label argument.