Search code examples
pythonwindowswxpythonaccessibility

Python on Windows: wx.CheckListBox from wxPython doesn't behave as checkable listbox but as an ordinary wx.ListBox


In Python 3.10.5 on Windows 11 using the wxPython 4.2.0 package, I am trying to create a listbox with checkable items. For this purpose, wxPython should offer the wx.CheckListBox widget. The problem is that when I create this widget, from the screen reader perspective, it works as an ordinary listbox, like if I created wx.ListBox instead. That is, when I navigate within a wx.CheckListBoxwidget using the arrow keys, the screen reader does not narrate neither the checked nor selected state of the list items, though I verified that spacebar actually toggles the selection but no screen reader feedback is given. A simplified code I am using for such widget createion is the following:

checkListBox = wx.CheckListBox(self.panel, -1, choices=['first', 'second', 'third'])

The questions therefore are: How can I create a wx.CheckListBox widget which would behave as a checkable listbox using the screen reader, or is there any other way how to create such widget in an accessible manner, apart from using wx.CheckListBox? Is there some style which should be applied to the widget so the list items become checkable?

The only resources I could find for this issue are the following, but I have not been able to use them to resolve my problem:

Update

So based on the first anser to this post, what I want to achieve should be possible using a modified version of the wx.ListCtrl widget and some wxPython mixins. But I am unable to make it work. So far, I've got to the following code:

import wx
import wx.lib.mixins.listctrl as listmix

class CheckboxListCtrl(wx.ListCtrl, listmix.CheckListCtrlMixin, listmix.ListCtrlAutoWidthMixin):

    def __init__ (self, *args, **kwargs):
        wx.ListCtrl.__init__(self, *args, **kwargs)
        listmix.CheckListCtrlMixin.__init__(self)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
      
      # Elsewhere in the code, I am using the class as follows:
      
      choices = ['first', 'second', 'third']
list = CheckboxListCtrl(self.panel, wx.ID_ANY, style=wx.LC_LIST)
for choice in choices:
    self.showAppsCheckListbox.Append([choice])

But such code still creates only an ordinary listbox, like I described above,. Please, how can I add accessible checkboxes to the items of my CheckListCtrl widget?


Solution

  • So the problem can be solved using the wx.ListCtrl widget instead of wx.CheckListBox, but since certain version of wxPython 4, a custom Class inheriting from wx.ListCtrl and the wxPyhton mixins mentioned in the question is not necessary - the support for checkboxes has been built into wx.ListCtrl. The whole trick for turning wx.ListCtrl into a list view with checkboxes lies in the use of the EnableCheckBoxes() method call. Therefore the widget can be created and check/uncheck events can be bound and handled like this:

    list = wx.ListCtrl(panel, wx.ID_ANY)
    list.EnableCheckBoxes()
    
    list.Append(['First'])
    list.Append(['Second'])
    list.Append(['Third'])
    
    list.Bind(wx.EVT_LIST_ITEM_CHECKED, onItemCheck)
    list.Bind(wx.EVT_LIST_ITEM_UNCHECKED, onItemUncheck)
    
    def onItemCheck(self, event):
        print(event.Item + '. item has been checked')
    
    def onItemUncheck(self, event):
        print(event.Item + '. item has been unchecked')
    

    For completeness, in the following way, you can find out which items are checked.

    for index in range(list.GetItemCount()):
        if list.IsItemChecked(index):
                print(index + '. item is checked')
        else:
                print(index + '. item is not checked')
    

    An finally, if you want to check an item programmatically, e.g. the third item, you can do:

    list.CheckItem(2)# 2 because the index starts with 0