NeoFahrenheit
NeoFahrenheit

Reputation: 422

Figuring out where the user clicked in a nested wx.Menu - wxPython

I'm having trouble figuring out where the user clicked in a nested wx.Menu.

I have the following setup... enter image description here

I would like a method to retrive the client's name, if he's checked in or if he's checkout and how long he ins't allowed in. The client's themselves doens't have any unique ID to them. Should I do that?

I haven't done that yet because clients will be appended and removed from those sub-menus at runtime, so I just don't know how to keep that organized and coese in the code.

Thank you!

Edit: Here's my minimal example code:

As I said before, I would hope to get the client's name trought the menu label itself to identify them. But if that's not possible, let's give them a unique ID. Mind that I will be removing and adding clients to those menus at runtime (don't know how to do that yet. It seems pretty simple).

import wx

class ID:
    PUT_BACK = 1000
    WAIT_8 = 1001
    WAIT_16 = 1002
    WAIT_24 = 1003
    
    CHECK_IN = 1004
    CHECK_OUT = 1005

class Frame(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent)

        menu = wx.MenuBar()
        lobby = wx.Menu()
        check_in = wx.Menu()
        check_out = wx.Menu()

        wait_time = wx.Menu()
        wait_time.Append(ID.PUT_BACK, 'and put back in the queue')
        wait_time.Append(ID.WAIT_8, "and don't allow in for 8 hours")
        wait_time.Append(ID.WAIT_16, "and don't allow in for 16 hours")
        wait_time.Append(ID.WAIT_24, "and don't allow in for 24 hours")

        queue = ['George', 'Vanessa', 'Erick']
        for client in queue:
            _in = check_in.Append(ID.CHECK_IN, client)
            self.Bind(wx.EVT_MENU, self.OnCheckIn, _in)

            _out = check_out.AppendSubMenu(wait_time, client)
            self.Bind(wx.EVT_MENU, self.OnCheckOut, _out)

        lobby.AppendSubMenu(check_in, 'Check in...' )
        lobby.AppendSubMenu(check_out, 'Check out...')

        menu.Append(lobby, 'Lobby')
        self.SetMenuBar(menu)

    def OnCheckIn(self, event):
        obj = event.GetEventObject()
        print('On Check in...')

    def OnCheckOut(self, event):
        obj = event.GetEventObject()
        print(f'On Check out...')
        

app = wx.App()
frame = Frame(None).Show()
app.MainLoop()

Upvotes: 0

Views: 124

Answers (1)

Rolf of Saxony
Rolf of Saxony

Reputation: 22443

You are making life difficult for yourself, taking the menu route. You may be better off choosing something like a wx.Treectrl.

With that caveat out of the way, menus rely on an Id reference but the menu itself and therefore sub-menus, don't have a user assigned Id, just the menu items within the menu.

That reference id must be unique.

This can make life tricky, if you need to carry some external reference forward through the menus, down into sub-menus, like a client reference, in this case.

You can create a dict, as complicated as required, and store the menu id's as you go. Then use that dictionary to retrieve information, based on those item id's, which are passed in the subsequent events.

It probably sounds more complicated than it is.

For example:

import wx

Actions = ["Check In", "Put Back", "Wait 8", "Wait 16", "Wait 24"]

class Frame(wx.Frame):
    def __init__(self, parent, title):
        super().__init__(parent)
        self.SetTitle(title)
        self.CreateStatusBar()
        
        menu = wx.MenuBar()
        lobby = wx.Menu()
        check_in = wx.Menu()
        check_out = wx.Menu()

        # create dictionary of clients to store reference id's
        self.queue = {'George':[], 'Vanessa':[], 'Erick':[]}

        for client in self.queue:
            id = wx.NewIdRef()
            self.queue[client].append(id.Value)
            _in = check_in.Append(id, client, client)
            self.Bind(wx.EVT_MENU, self.OnCheckIn, _in)

            PUT_BACK = wx.NewIdRef()
            WAIT_8 = wx.NewIdRef()
            WAIT_16 = wx.NewIdRef()
            WAIT_24 = wx.NewIdRef()
            self.queue[client].extend((PUT_BACK.Value,WAIT_8.Value,WAIT_16.Value,WAIT_24.Value))

            wait_time = wx.Menu()
            wait_time.Append(PUT_BACK, "and put back in the queue", client+" Back")
            wait_time.Append(WAIT_8, "and don't allow in for 8 hours", client+" +8")
            wait_time.Append(WAIT_16, "and don't allow in for 16 hours", client+" +16")
            wait_time.Append(WAIT_24, "and don't allow in for 24 hours", client+" +24")
            self.Bind(wx.EVT_MENU, self.OnCheckOut, id=PUT_BACK)
            self.Bind(wx.EVT_MENU, self.OnCheckOut, id=WAIT_8)
            self.Bind(wx.EVT_MENU, self.OnCheckOut, id=WAIT_16)
            self.Bind(wx.EVT_MENU, self.OnCheckOut, id=WAIT_24)
            _out = check_out.AppendSubMenu(wait_time, client, client)

        lobby.AppendSubMenu(check_in, 'Check in...' )
        lobby.AppendSubMenu(check_out, 'Check out...')

        menu.Append(lobby, 'Lobby')
        self.SetMenuBar(menu)

    def OnCheckIn(self, event):
        id = event.GetId()
        # quiz the dictionary for the Client based on unique reference id's
        client = [c for c in self.queue if id in self.queue[c]]
        print('Check in...', client[0])

    def OnCheckOut(self, event):
        id = event.GetId()
        # quiz the dictionary for the Client based on unique reference id's
        client = [c for c in self.queue if id in self.queue[c]]
        # quiz the client entry for the action based on position (index)
        index = self.queue[client[0]].index(id)
        print(f'Check out..', client[0], "Action:",Actions[index])

app = wx.App()
frame = Frame(None, title="Check In/Out").Show()
app.MainLoop()

enter image description here

Upvotes: 1

Related Questions