lial_slasher
lial_slasher

Reputation: 105

Generate EVT_LIST_ITEM_FOCUSED on ListCtrl update

I have a ListCtrl that can update itself with various items. To do that, I empty it, and than I append several items.

Then I want to catch the EVT_LIST_ITEM_FOCUSED event. On Windows, Unix and MacOS, it works fine.

Finally, I want to catch the event after updating my list. That happen automatically on Unix and MacOS, but it's not the case on Windows. That's why I would like to generate an event at the end of the "update()" method.

Example in code :

import wx


class MainFrame(wx.Frame):
    def __init__(self):
        super().__init__(None)
        self.Show()

        # Create the ListCtrl
        self.list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT)
        self.list_ctrl.AppendColumn("Column")

        # Bind the event to the callback function
        self.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.on_focus, self.list_ctrl)

        # Fill the list with fruits
        self.update(["apples", "bananas", "pears"])

    def update(self, name_list):
        """Refill the ListCtrl with the content of the list."""
        # Empty the ListCtrl
        self.list_ctrl.DeleteAllItems()

        # Refill it
        for element in name_list:
            self.list_ctrl.Append([element])

    def on_focus(self, event):
        """Print what is currently focused."""
        focused = event.GetItem().GetText()
        if focused == "":
            print("No focus.")
        else:
            print(f"{focused} is focused.")


app = wx.App()
main_frame = MainFrame()
app.MainLoop()

This code print "apples is focused" at the start of the program with both Unix and MacOS. On Windows, it print nothing, because the event is not triggered. What I want is getting the message "apples is focused" on Windows.

Constraints :

Thnks for your help and have a nice day.

Upvotes: 0

Views: 326

Answers (2)

Rolf of Saxony
Rolf of Saxony

Reputation: 22448

I provide the code below with reservations.

Your example code and explanation are at odds with each other.
You refer to skipping the event to a higher panel and yet your example has no such code. So we are left in a position where we have a stated requirement but with no idea how, why or when it is to be implemented.

There is a distinct difference between Focused and Selected. I suggest that in this circumstance, you would be better served to use Selected.

For the record, despite your comment, with a listctrl with no items try on Linux either self.list_ctrl.Select(0) or self.list_ctrl.Focus(0) both will crash your code SetItemState(): invalid list ctrl item index in SetItem.

import wx

class MainFrame(wx.Frame):
    def __init__(self):
        super().__init__(None)
        self.some_sets = {
            "fruits": ["apples", "bananas", "pears"],
            "instruments": ["flutes", "drums", "guitars"],
            "empty": [],
        }

        self.list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT)
        self.list_ctrl.AppendColumn("Column")

        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_focus, self.list_ctrl)
        #self.update("empty")
        self.update("fruits")
        self.Show()

    def update(self, set_name):
        """Refill the ListCtrl."""
        # Empty the ListCtrl
        self.list_ctrl.DeleteAllItems()
        # Refill it
        for element in self.some_sets[set_name]:
            self.list_ctrl.Append([element])
        if self.list_ctrl.GetItemCount():
            #self.list_ctrl.Focus(0)
            self.list_ctrl.Select(0)
        else:
            self.on_focus(None)

    def on_focus(self, event):
        """Do something."""
        if event:
            focused = event.GetItem().GetText()
            print(f"{focused} is Selected.")
        else:
            print("No focus.")

app = wx.App()
main_frame = MainFrame()
app.MainLoop()

Upvotes: 1

lial_slasher
lial_slasher

Reputation: 105

Ok, so I found a way to work around the problem by getting rid of one constraint (I wanted to Skip() the event if needed).

Here's the code I use.

    def update(self, name_list):
        """Refill the ListCtrl with the content of the list."""
        self.list_ctrl.DeleteAllItems()

        for element in name_list:
            self.list_ctrl.Append([element])

        # Call the callback functions
        if self.list_ctrl.GetItemCount() == 0:
            # Our custom one if there's no item in the list
            self.on_focus_custom(None)
        else:
            # Else the classic function
            self.list_ctrl.Focus(0)

    def on_focus(self, event):
        """Call the on_focus_custom method with proper arguments."""
        focused = event.GetItem().GetText()
        if focused == "":
            self.on_focus_custom(None)
        else:
            self.on_focus_custom(focused)

    def on_focus_custom(self, focused):
        """Print what is currently focused."""
        if focused is None:
            print("No focus.")
        else:
            print(f"{focused} is focused.")

        # Here, we can't skip the event, because we haven't one.
        # So here a mediocre solution I found.
        # Thats a method I defined in the upper Panel. 
        # self.GetParent().on_focus_custom(focused)

I you have cleaner ways to do it, I'm interested.

Upvotes: 0

Related Questions