Search code examples
pythonpython-3.xwxwidgetswxpython

How to dynamically replace the menubar in wxpython?


I'm making a camera viewer like AmCap with wxpython. Now I'm working on making a menubar which shows connected camera list.(like AMCAP device menu, see fig 1)

Here's my code: (get_all_devices returns list of connected devices and it work fine)

class MainFrame(wx.Frame):
    def __init__(self, parent, fid, title, size):
        wx.Frame.__init__(self, parent, fid, title, wx.DefaultPosition, size)

        self.devices = get_all_devices()
        # init menubar
        self.make_menubar()

        # set timer to check usb connectivity
        self.timer = wx.Timer(self)
        self.timer.Start(100) 
        self.Bind(wx.EVT_TIMER, self.check_device)

    def make_menubar(self):
        self.menubar = wx.MenuBar()
        self.devices_menu = wx.Menu()
        self.options_menu = wx.Menu()
        help_menu = wx.Menu()
        self.make_devices_menu()

        self.options_menu.Append(101, 'resolutions')
        self.menubar.Append(self.devices_menu, 'devices')
        self.menubar.Append(self.options_menu, 'options')
        self.menubar.Append(help_menu, 'help')

        self.SetMenuBar(self.menubar)

    def recreate_menubar(self):
        self.menubar.Destroy()
        self.make_menubar()
        self.Layout()
        self.Refresh()

    def make_devices_menu(self):
        for i in range(len(self.devices)):
            self.devices_menu.Append(CI.MENU_DEVICES + 1 + i, self.devices[i], kind=wx.ITEM_CHECK)
            self.Bind(wx.EVT_MENU, self.click_device_menu, id=CI.MENU_DEVICES + 1 +  i)

    def check_device(self, evt):
        cur_devices = get_all_devices()
        if set(self.devices) != set(cur_devices):
            self.devices = cur_devices
            self.recreate_menubar()

It does work at first, but if I try disconnecting and connecting camera for 4~5 times, it doesn't recreate menubar. for example, assume there are two connected cameras like fig 1. If I disconnect the first camera, the program will be like fig 2.(Only one camera left). Then if I connect the camera again, it will be display two cameras like fig 1 again. This is what I expected.(fig 1 - disconnect -> fig 2 - connect -> fig1)

But in real, after 4~5 times, it doesn't work properly. It doesn't update the menubar.(fig 1 - disconnect -> fig 2 - connect -> fig2 again!)

What did I do wrong? Is it not allowed in wxpython?

If you need more info for my code, please let me know.

any hints will welcome! thanks in advance.

enter image description here

fig 1

it

fig 2

Edit:

I found that if I add the code like this in the end of init:

self.menubar.SetName(str(self.devices))
self.SetMenuBar(self.menubar)
print(self.menubar.GetName())
print(self.GetMenuBar().GetName())

it returns same list of connected cameras. So wxpyhthon makes and sets menubar properly I think.


Solution

  • While recreating the entire menubar looks a bit excessive (you could recreate just the devices menu or you could even just remove the old camera items and add new ones without even doing this), it should still work.

    Have you checked that your recreate_menubar() is being called when you expect? You could show a message box from it to be sure. If it is called but somehow doesn't update the menu bar, the best thing to do would be to try to reproduce the problem in a SSCCE and open a ticket on wxTrac so that it could be debugged and hopefully fixed (please mention your platform if you do this).