【问题标题】:Modify inbuilt WxPython widget修改内置的 WxPython 小部件
【发布时间】:2017-09-07 12:36:14
【问题描述】:

如何修改 wxPython 中的组合框以下拉复选框,以便我可以选择选项?

类似上面的东西。我知道 ComboBox 类可用,我需要继承它,将复选框的功能添加到下拉列表中。但我没有找到正确的开始方式。

【问题讨论】:

    标签: python-2.7 user-interface wxpython


    【解决方案1】:

    看看ComboCtrl 类。它允许您提供实现组合下拉菜单的窗口。 wxPython demo中有一些例子。

    【讨论】:

      【解决方案2】:

      我没有时间仔细研究ComboCtrl,它看起来令人生畏。
      但是,可以通过折磨wx.Dialog 来实现您想要的近似值。

      import wx
      import wx.lib.scrolledpanel as scrolled
      class MyFrame(wx.Frame):
          def __init__(self, parent):
              wx.Frame.__init__(self, parent, -1, "CheckBox Dialog",size=(400,250))
              self.panel = wx.Panel(self)
              sizer = wx.BoxSizer(wx.VERTICAL)
              self.log = wx.TextCtrl(self.panel, wx.ID_ANY, size=(350,150),style = wx.TE_MULTILINE|wx.TE_READONLY|wx.VSCROLL)
              self.button = wx.Button(self.panel, label="Choose Colours")
              sizer.Add(self.log, 0, wx.EXPAND | wx.ALL, 10)
              sizer.Add(self.button, 0, wx.EXPAND | wx.ALL, 10)
              self.panel.SetSizer(sizer)
              self.Bind(wx.EVT_BUTTON, self.OnButton)
              self.panel.options = ['Red','Green','Black','White','Orange','Blue','Yellow']
              self.panel.selected = [0,0,0,0,0,0,0]
      
          def OnButton(self,event):
              dlg = ShowOptions(parent = self.panel)
              dlg.ShowModal()
              if dlg.result:
                  result_text = 'Selected: '
                  for item in range(len(dlg.result)):
                      if dlg.result[item]:
                          result_text += self.panel.options[item]+' '
                  self.log.AppendText(result_text+'\n\n')
                  self.panel.selected = dlg.result
              else:
                  self.log.AppendText("No selection made\n\n")
              dlg.Destroy()
      
      class ShowOptions(wx.Dialog):
          def __init__(self, parent):
              self.options = parent.options
              self.selected = parent.selected
              o_str = ''
              for item in self.options:
                  o_str = o_str+item+','
              wx.Dialog.__init__(self, parent, wx.ID_ANY, "CheckBoxes", size= (400,250))
              self.top_panel = wx.Panel(self,wx.ID_ANY)
              self.avail_options = wx.TextCtrl(self.top_panel, wx.ID_ANY, o_str,style = wx.TE_READONLY)
              self.bot_panel = wx.Panel(self,wx.ID_ANY)
              self.scr_panel = scrolled.ScrolledPanel(self,wx.ID_ANY)
              top_sizer = wx.BoxSizer(wx.VERTICAL)
              scr_sizer = wx.BoxSizer(wx.VERTICAL)
              bot_sizer = wx.BoxSizer(wx.VERTICAL)
              self.items = []
              for item in range(len(self.options)):
                  self.item = wx.CheckBox(self.scr_panel,-1,self.options[item])
                  self.item.SetValue(self.selected[item])
                  self.items.append(self.item)
                  self.item.Bind(wx.EVT_CHECKBOX, self.Select)
              self.saveButton =wx.Button(self.bot_panel, label="Save")
              self.closeButton =wx.Button(self.bot_panel, label="Cancel")
              self.saveButton.Bind(wx.EVT_BUTTON, self.SaveOpt)
              self.closeButton.Bind(wx.EVT_BUTTON, self.OnQuit)
              self.Bind(wx.EVT_CLOSE, self.OnQuit)
      
              top_sizer.Add(self.avail_options,0,flag=wx.EXPAND)
              for item in self.items:
                  scr_sizer.Add(item,0)
              bot_sizer.Add(self.saveButton,0,flag=wx.CENTER)
              bot_sizer.Add(self.closeButton,0,flag=wx.CENTER)
              self.scr_panel.SetupScrolling()
      
              self.top_panel.SetSizer(top_sizer)
              self.scr_panel.SetSizer(scr_sizer)
              self.bot_panel.SetSizer(bot_sizer)
      
              mainsizer = wx.BoxSizer(wx.VERTICAL)
              mainsizer.Add(self.top_panel,0,flag=wx.EXPAND)
              mainsizer.Add(self.scr_panel,1,flag=wx.EXPAND)
              mainsizer.Add(self.bot_panel,0,flag=wx.EXPAND)
              self.SetSizer(mainsizer)
              self.Select(None)
              self.Show()
      
          def Select(self, event):
              selection = []
              for item in self.items:
                  x = item.GetValue()
                  selection.append(x)
              selected_text = ''
              for item in range(len(selection)):
                  if selection[item]:
                          selected_text += self.options[item]+' '
                  self.avail_options.SetValue(selected_text)
      
          def OnQuit(self, event):
              self.result = None
              self.Destroy()
      
          def SaveOpt(self, event):
              self.result = []
              for item in self.items:
                  x = item.GetValue()
                  self.result.append(x)
              self.Destroy()
      
      app = wx.App()
      frame = MyFrame(None)
      frame.Show()
      app.MainLoop()
      

      【讨论】:

        【解决方案3】:

        按照 Robin Dunn 的建议,我使用 wx.ComboCtrl 创建了一个小部件。 随意使用它,或以任何你喜欢的方式修改它。

        您还可以找到它的更高级版本on my GitHub account

        import wx
        import wx.lib.mixins.listctrl
        
        import operator
        import functools
        import contextlib
        
        @contextlib.contextmanager
        def asCM(function, *args, **kwargs):
            """Used to build with wxWidgets as context managers to help organize code."""
        
            yield function(*args, **kwargs)
        
        class CheckListCtrl(wx.ComboCtrl):
            """A wxListCtrl-like widget where each item in the drop-down list has a check box.
            Modified code from: https://github.com/wxWidgets/Phoenix/blob/master/demo/ComboCtrl.py
            """
        
            def __init__(self, parent, myId = None, initial = None, position = None, size = None, readOnly = False, **kwargs):
                """
                parent (wxWindow) – Parent window (must not be None)
                initial (str) – Initial selection string
                readOnly (bool) - Determiens if the user can modify values in this widget
        
                Example Input: CheckListCtrl(self)
                """
        
                self.parent = parent
        
                #Configure settings
                style = []
        
                if (readOnly):
                    style.append(wx.CB_READONLY)
        
                #Create object
                super().__init__(parent, 
                    id = myId or wx.ID_ANY, 
                    value = initial or "", 
                    pos = position or wx.DefaultPosition, 
                    size = size or wx.DefaultSize, 
                    style = functools.reduce(operator.ior, style or (0,)))
        
                self.popup = self.MyPopup(self, **kwargs)
        
            def Append(self, *args, **kwargs):
                self.popup.Append(*args, **kwargs)
        
            class MyPopup(wx.ComboPopup):
                """The popup control used by CheckListCtrl."""
        
                def __init__(self, parent, *, popupId = None, multiple = True, prefHeight = None,
                    image_check = None, image_uncheck = None, lazyLoad = False):
                    """
                    multiple (bool) - Determines if the user can check multiple boxes or not
                    lazyLoad (bool) - Determines if when Create() is called
                        - If True: Waits for the first time the popup is called
                        - If False: Calls it during the build process
        
                    prefHeight (int) - What height you would prefer the popup box use
                        - If None: Will calculate what hight to use based on it's contents
                        - If -1: Will use the default height
                    """
        
                    self.parent = parent
                    self.prefHeight = prefHeight
        
                    self._buildVar_myId = popupId 
                    self._buildVar_multiple = multiple 
                    self._buildVar_lazyLoad = lazyLoad 
                    self._buildVar_image_check = image_check 
                    self._buildVar_image_uncheck = image_uncheck 
        
                    super().__init__()
        
                    parent.SetPopupControl(self)
        
                def Create(self, parent):
                    self.checkList = self.MyListCtrl(self, parent, 
                        myId = self._buildVar_myId, 
                        multiple = self._buildVar_multiple, 
                        image_check = self._buildVar_image_check, 
                        image_uncheck = self._buildVar_image_uncheck)
        
                    return True
        
                def Append(self, *args, **kwargs):
                    self.checkList.Append(*args, **kwargs)
        
                def GetControl(self):
                    return self.checkList
        
                def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
                    if (self.prefHeight is -1):
                        return super().GetAdjustedSize(minWidth, prefHeight, maxHeight)
        
                    elif (self.prefHeight is not None):
                        return (minWidth, min(self.prefHeight, maxHeight))
        
                    return self.checkList.GetBestSize(minWidth, prefHeight, maxHeight)
        
                def LazyCreate(self):
                    return self._buildVar_lazyLoad
        
                class MyListCtrl(wx.ListCtrl, wx.lib.mixins.listctrl.CheckListCtrlMixin):
                    """Modified code from: https://github.com/wxWidgets/wxPython/blob/master/demo/CheckListCtrlMixin.py"""
        
                    def __init__(self, parent, root, *, myId = None, multiple = False, image_check = None, image_uncheck = None):
                        """
                        multiple (bool) - Determines if the user can check multiple boxes or not
                        """
        
                        self.parent = parent
        
                        #Configure settings
                        style = [wx.LC_LIST, wx.SIMPLE_BORDER]
        
                        if (not multiple):
                            style.append(wx.LC_SINGLE_SEL)
        
                        #Create object
                        wx.ListCtrl.__init__(self, root, id = myId or wx.ID_ANY, style = functools.reduce(operator.ior, style or (0,)))
                        wx.lib.mixins.listctrl.CheckListCtrlMixin.__init__(self, check_image = image_check, uncheck_image = image_uncheck)
        
                    def Append(self, value, default = False):
                        """Appends the given item to the list.
        
                        value (str) - What the item will say
                        default (bool) - What state the check box will start out at
        
                        Example Input: Append("lorem")
                        Example Input: Append("lorem", default = True)
                        """
        
                        n = self.GetItemCount()
                        self.InsertItem(n, value)
        
                        if (default):
                            self.CheckItem(n)
        
                    def GetBestSize(self, minWidth, prefHeight, maxHeight):
                        return (minWidth, min(prefHeight, maxHeight, sum(self.GetItemRect(i)[3] for i in range(self.GetItemCount())) + self.GetItemRect(0)[3]))
        
                    # this is called by the base class when an item is checked/unchecked
                    def OnCheckItem(self, index, state):
                        print(index, state)
        
        if (__name__ == "__main__"):
            class TestFrame(wx.Frame):
                def __init__(self):
                    super().__init__(None, wx.ID_ANY, "Lorem Ipsum")
        
                    with asCM(wx.Panel, self, wx.ID_ANY) as myPanel:
                        with asCM(wx.BoxSizer, wx.VERTICAL) as mySizer:
        
                            with asCM(CheckListCtrl, myPanel, prefHeight = None) as myWidget:
                                myWidget.Append("lorem")
                                myWidget.Append("ipsum", default = True)
                                myWidget.Append("dolor")
        
                                mySizer.Add(myWidget, 0, wx.ALL, 5)
        
                            myPanel.SetSizer(mySizer)
        
            ####################################
        
            app = wx.App(False)
            frame = TestFrame()
            frame.Show()
            app.MainLoop()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多