修改内置的WxPython小部件

时间:2017-09-07 12:36:14

标签: python-2.7 user-interface wxpython

如何修改wxPython中的组合框以下拉清单框,以便我可以选择选项? enter image description here

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

3 个答案:

答案 0 :(得分:0)

看一下ComboCtrl课程。它允许您提供实现组合下拉列表的窗口。 wxPython演示中有一些例子。

答案 1 :(得分:0)

我还没有时间研究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()

enter image description here enter image description here

答案 2 :(得分:0)

如罗宾·邓恩(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()