Search code examples
pythonwxpythonpopupwindow

CheckListBox inside PopupWindow


I am trying to figure out why the wx.CheckListBox doesn't work correctly inside a popup window. The scrollbars work, but I am unable to select anything. I have tried multiple types of popup windows and still get the same behavior.

Any ideas for a workaround besides using a Frame?

import wx

class MyFrame(wx.Frame):    
    def __init__(self):
        super().__init__(parent=None, title='Hello World')
        panel = wx.Panel(self)

        

        
        #self.text_ctrl = wx.TextCtrl(panel, pos=(5, 5))
        my_btn = wx.Button(panel, label='Press Me', pos=(5, 55))
        my_btn.Bind(wx.EVT_BUTTON, self.OnShowPopupTransient)

        self.Show()


    def OnShowPopupTransient(self, evt):
        sampleList = ['zero', 'one', 'two', 'three', 'four', 'five',
                      'six', 'seven', 'eight', 'nine', 'ten', 'eleven',
                      'twelve', 'thirteen', 'fourteen']
        win = TestTransientPopup(self,
                                    wx.SIMPLE_BORDER,
                                    sampleList)

        
        # Show the popup right below or above the button
        # depending on available screen space...
        btn = evt.GetEventObject()
        pos = btn.ClientToScreen( (0,0) )
        sz =  btn.GetSize()
        win.Position(pos, (0, sz[1]))

        win.Popup()

class TestTransientPopup(wx.PopupTransientWindow):
    """Adds a bit of text and mouse movement to the wx.PopupWindow"""
    def __init__(self, parent, style, sampleList):
        wx.PopupTransientWindow.__init__(self, parent, style)
    
        panel = wx.Panel(self)
        panel.SetBackgroundColour("#FFB6C1")
        
        lb = wx.CheckListBox(panel, -1, (80, 50), wx.DefaultSize, sampleList)
        
        btn = wx.Button(panel, -1, "Press Me")
        spin = wx.SpinCtrl(panel, -1, "Hello", size=(100,-1))
        #btn.Bind(wx.EVT_BUTTON, self.OnButton)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(lb, 0, wx.ALL, 5)
        sizer.Add(btn, 0, wx.ALL, 5)
        sizer.Add(spin, 0, wx.ALL, 5)
        panel.SetSizer(sizer)

        sizer.Fit(panel)
        sizer.Fit(self)
        self.Layout()

if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame()
    app.MainLoop()


Solution

  • You don't specify your platform or version of wxPython but on Linux wxPython '4.1.1 gtk3 (phoenix) wxWidgets 3.1.5' the following code appears to work as expected. (Unless you expected something else)

    import wx
    
    class MyFrame(wx.Frame):    
        def __init__(self):
            super().__init__(parent=None, title='Hello World')
            panel = wx.Panel(self)
            my_btn = wx.Button(panel, label='Press Me', pos=(5, 55))
            my_btn.Bind(wx.EVT_BUTTON, self.OnShowPopupTransient)
            self.Show()
    
        def OnShowPopupTransient(self, evt):
            sampleList = ['zero', 'one', 'two', 'three', 'four', 'five',
                          'six', 'seven', 'eight', 'nine', 'ten', 'eleven',
                          'twelve', 'thirteen', 'fourteen']
            win = TestTransientPopup(self, wx.BORDER_SIMPLE, sampleList)
            # Show the popup right below or above the button
            # depending on available screen space...
            btn = evt.GetEventObject()
            pos = btn.ClientToScreen( (0, 0) )
            sz =  btn.GetSize()
            win.Position(pos, (0, sz[1]))
            win.Popup(focus=self)
    
    
    class TestTransientPopup(wx.PopupTransientWindow):
        """Adds a bit of text and mouse movement to the wx.PopupWindow"""
        def __init__(self, parent, style, sampleList):
            wx.PopupTransientWindow.__init__(self, parent, style)
            self.SetSize((150, 300))    
            panel = wx.Panel(self, size=(150, 300))
            panel.SetBackgroundColour("dark grey")
            
            self.lb = wx.CheckListBox(panel, -1, choices=sampleList)
            
            btn = wx.Button(panel, -1, "Press Me")
            self.spin = wx.SpinCtrl(panel, -1, "10")
            btn.Bind(wx.EVT_BUTTON, self.OnButton)
    
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(self.lb, 1, wx.ALL, 5)
            sizer.Add(btn, 0, wx.ALL, 5)
            sizer.Add(self.spin, 0, wx.ALL, 5)
            panel.SetSizer(sizer)
            self.Layout()
    
        def OnButton(self, event):
            checked_items = self.lb.GetCheckedStrings()
            spin_value = self.spin.GetValue()
            print("Selected items", checked_items)
            print("Spin value", spin_value)
            self.Dismiss()
    
    if __name__ == '__main__':
        app = wx.App()
        frame = MyFrame()
        app.MainLoop()
    

    enter image description here