【问题标题】:Unable to add custom Sizer as parent to its children widgets in wxPython无法将自定义 Sizer 作为父级添加到 wxPython 中的子级小部件
【发布时间】:2019-05-26 19:52:45
【问题描述】:

我目前正在尝试在 wxPython 中制作 GUI。布局是 GUI 右侧的一个面板,带有 3 个 StaticBoxSizer。其中一个比其他的复杂得多,可以通过单击按钮向其中添加更多元素,因此我决定分解 GUI 文件并为其创建一个自定义类以尝试使代码更多清除。

类的当前初始化器如下所示:

class MonitorBox(wx.StaticBoxSizer):
    def __init__(self, monitors, pins, devices, *args, **kw):
        super().__init__(*args, **kw)
        self.monitors = monitors
        self.pin_choices = pins
        self.dev_choices = devices
        self.InitUI()

    def InitUI(self):
        monList = wx.FlexGridSizer(cols=4, vgap=5, hgap=5)
        add_monitor = wx.Button(self, wx.ID_ANY, "Add Monitor")
        add_monitor.Bind(wx.EVT_BUTTON, self.on_add_button)

        # create list headings
        nametxt = wx.StaticText(self, wx.ID_ANY, "Name:")
        devtxt = wx.StaticText(self, wx.ID_ANY, "Device:")
        pintxt = wx.StaticText(self, wx.ID_ANY, "Pin:")
        blnktxt = wx.StaticText(self, wx.ID_ANY, "")

        # Add Headings to sizer
        monList.AddMany([nametxt, devtxt, pintxt, blnktxt])
        self.monitor_rows = {}  # dictionary to store references to all the widgets

        # Create a row for each item in the mons dictionary
        for monitor, dev in self.mons.items():
            del_mon_button = wx.Button(self, wx.ID_ANY, '-', name=monitor, style=wx.BU_EXACTFIT)
            del_mon_button.Bind(wx.EVT_BUTTON, self.on_del_monitor)
            mon_name = wx.TextCtrl(self, wx.ID_ANY, monitor,
                                   style=wx.TE_PROCESS_ENTER,
                                   name=monitor)
            dev_name = wx.ComboBox(self, wx.ID_ANY, choices=self.dev_choices,
                                   style=wx.CB_READONLY, value=dev[0],
                                   name=monitor)
            pin_name = wx.ComboBox(self, wx.ID_ANY, choices=self.pin_choices[dev[0]],
                                   style=wx.CB_READONLY, value=dev[1],
                                   name=monitor)

            dev_name.Bind(wx.EVT_COMBOBOX, self.on_dev_choice)
            pin_name.Bind(wx.EVT_COMBOBOX, self.on_pin_choice)
            self.monitor_rows[monitor] = [mon_name, dev_name, pin_name, del_mon_button]
            monList.AddMany(self.monitor_rows[monitor])
        # TODO: make sure all the combo boxes are the same size
        self.monList = monList
        self.Add(monList, 1, wx.ALL, 5)
        self.Add(add_monitor, 0, wx.ALL, 5)

但是,当我尝试运行 GUI 时出现错误:

add_monitor = wx.Button(self, wx.ID_ANY, "Add Monitor")
TypeError: Button(): arguments did not match any overloaded call:
  overload 1: too many arguments
  overload 2: argument 1 has unexpected type 'MonitorBox'

我相信这是因为我试图将我的自定义类用作其中所有小部件的父类,但我对为什么会发生这种情况感到困惑?

我对创建 GUI 非常陌生(这是我的第一个项目),但我目前正在处理和修复对父母等的所有引用。所以我很难知道要搜索的正确术语看看其他人有没有这个问题。

我的原始代码只是从 GUI 的主 wx.Frame 调用的 InitUI(self) 函数,并将 monBox 创建为 StaticBoxSizer,它也直接添加了所有子小部件。如开头所述,我想将其拆分的原因是因为此面板创建了许多事件处理程序和其他对话框,我想将它们保存在单独的文件中以帮助保持代码可读性。

原始代码(未拆分成单独的类)生成如图所示的Monitors面板:

【问题讨论】:

    标签: python user-interface wxpython


    【解决方案1】:

    问题是您从wx.StaticBoxSizer 派生MonitorBox 类,而wx.Button 只能是窗口的子级(wx.Framewx.Panel 等)而不是sizer(wx.StaticBoxSizer)。

    如果你想分离代码,那么你的 MonitorBox 类应该来自wx.Panel。有关执行此操作的方法,请参见下面的代码(及其 cmets)。

    import wx
    
    class MonitorBox(wx.Panel):
        """
        This is the class with all the widgets in your MonitorBox class.
        You need to arrange the widget in a sizer here. 
        """
        def __init__(self, parent):
            super().__init__(parent=parent)
    
            #### Parent panel
            # No need to define a parent panel because the class itself is already a
            # panel. So self refers to a panel.
    
            #### Widgets
            # Here self is a panel not a sizer like in your question. So no problem
            # adding the button.
            self.add_monitor = wx.Button(self, wx.ID_ANY, "Add Monitor")
            #### Sizers
            self.sizer = wx.BoxSizer()
            #### Add widgets to sizer
            self.sizer.Add(self.add_monitor, flag=wx.CENTER)
            #### Add sizer to panel
            self.SetSizer(self.sizer)
            self.Fit()
    
    class Main(wx.Frame):
        def __init__(self):
            super().__init__(None, title='Panel 3 comes from a different class',
                             size=(500, 500))
            #### Parent panel
            self.panel = wx.Panel(self)
    
            #### Some widgets
            ## The Static Box widget
            self.monitorsBox = wx.StaticBox(self.panel, label="Monitors")
    
            self.panelB = wx.Panel(self.panel, size=(200, 100))
            self.panelB.SetBackgroundColour((0, 0, 255))
            self.panelR = wx.Panel(self.panel, size=(200, 100))
            self.panelR.SetBackgroundColour((255, 0, 0))
            ## Add panel 3 as a child of the wx.StaticBox!!!
            self.panel3 = MonitorBox(self.monitorsBox)
    
            #### Sizers
            ## Main sizer
            self.sizer = wx.FlexGridSizer(3, 1, 5, 5)
            ## Sizers for the static box
            # The static box sizer
            self.monitorsBoxSizer = wx.StaticBoxSizer(self.monitorsBox, wx.VERTICAL)
            # Sizer for the content of the static box
            self.monitorsBoxSizerContent = wx.BoxSizer()
    
            #### Add widgets to sizers
            ## Static box content
            self.monitorsBoxSizerContent.Add(self.panel3, flag=wx.EXPAND)
            self.monitorsBoxSizer.Add(self.monitorsBoxSizerContent)
            ## Add everything to the main sizer
            self.sizer.Add(self.panelB, flag=wx.EXPAND|wx.ALL, border=10)
            self.sizer.Add(self.panelR, flag=wx.EXPAND|wx.ALL, border=10)
            self.sizer.Add(self.monitorsBoxSizer, flag=wx.EXPAND|wx.ALL, border=10)
    
            #### Add sizer to self.panel
            self.panel.SetSizer(self.sizer)
            self.panel.Fit()
    
    if __name__ == '__main__':
        app = wx.App()                            
        frame = Main()
        frame.Show()  
        app.MainLoop()
    

    很可能在原始代码中主类是从 wx.Frame 派生的,对吧?如果没有,请忽略其余部分。这就是它起作用的原因。因为您使按钮成为框架的子项,而不是大小调整器。

    【讨论】:

    • 谢谢你 - 只是为了澄清一下,那么 Sizer 和 Panel 之间有什么区别?我认为 Panel 是 Sizer 的东西,然后小部件被附加到,而不是相反?
    • Sizers 控制小部件在 GUI 中的相对定位,如果窗口大小发生变化并且允许小部件改变大小,也会改变小部件的大小。 GUI 中不显示 Sizer。小部件是将在 GUI 中显示的元素。
    • 这是有道理的,并为阅读此内容的任何人回答您的问题:是的,在我拆分从 wx.Frame 派生的代码之前,最初是在我的主课中
    猜你喜欢
    • 2023-03-17
    • 1970-01-01
    • 2020-03-21
    • 1970-01-01
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多