wxPython:在 windows 上使用多个 sizer 时出现问题,但在 linux 上运行良好

wxPython : Issues on windows using multiple sizers, but works well on linux

我正在编写一个在 linux 上运行良好的小应用程序,但我在 windows 上遇到了一些问题。这是代码示例:

import wx
#####################################################################    
class Main(wx.Frame):
    def __init__(self):

        wx.Frame.__init__(self, None, title="Whosebug",  pos=wx.DefaultPosition, size=(800,600))
        self.SetMinSize( self.GetSize() )

        p = wx.Panel(self)
        nb = wx.Notebook(p)

        page1 = AddToCollection(nb)
        page2 = CollectionStatistics(nb)
        nb.AddPage(page1, "Page 1")
        nb.AddPage(page2, "Page 2")

        # finally, put the notebook in a sizer for the panel to manage
        # the layout
        sizer = wx.BoxSizer()
        sizer.Add(nb, 1, wx.EXPAND)
        p.SetSizer(sizer)
#########################################################################
class CollectionStatistics(wx.Panel):
    def __init__(self, parent):          

        wx.Panel.__init__(self, parent)
#########################################################################
class AddToCollection(wx.Panel):
    def __init__(self, parent):          

        wx.Panel.__init__(self, parent)

        self.v1_qty_list = [str(x) for x in range(9)]
        self.v2_qty_list = [str(x) for x in range(9)]

        self.sizername = wx.GridBagSizer(5, 5)
        self.sizername.AddGrowableCol(0,0)

        self.name_txt = wx.StaticText(self, label="Enter Name :")
        self.sizername.Add(self.name_txt,(2,0),(1,1),wx.EXPAND)

        self.name = wx.TextCtrl(self,style=wx.TE_PROCESS_ENTER,value=u"")
        self.sizername.Add(self.name,(3,0),(1,1),wx.EXPAND)
        self.Bind(wx.EVT_TEXT_ENTER, self.OnPressEnter, self.name)

        self.SetSizerAndFit(self.sizername)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )
##########################################################################
    def OnPressEnter(self,event):
        self.selected_name = self.name.GetValue()
        self.AddToCol()
##########################################################################
    def AddToCol(self):

        self.sizerAdd = wx.GridBagSizer(5, 5)
        self.sizerAdd.AddGrowableCol(0, 0)

        self.name.Enable(False)

        ### Expansion
        self.expansion = wx.Choice(self, -1, choices=['test 1', 'test 2'])
        self.expansion.SetSelection(0)
        self.sizerAdd.Add(self.expansion,(5,0),(1,6),wx.EXPAND)

        ### Quantities txt
        self.v1_txt = wx.StaticText(self, label="V1 Quantity :")
        self.sizerAdd.Add(self.v1_txt,(7,0),(1,1),wx.EXPAND)

        self.v2_txt = wx.StaticText(self, label="V2 Quantity :")
        self.sizerAdd.Add(self.v2_txt,(8,0),(1,1),wx.EXPAND)

        ### Quantities choices
        self.v1_qty = wx.Choice(self, -1, choices=self.v1_qty_list)
        self.v1_qty.SetSelection(0)
        self.sizerAdd.Add(self.v1_qty,(7,5),(1,1),wx.EXPAND)

        self.v2_qty = wx.Choice(self, -1, choices=self.v1_qty_list)
        self.v2_qty.SetSelection(0)
        self.sizerAdd.Add(self.v2_qty,(8,5),(1,1),wx.EXPAND)

        ### Ok Button        
        self.Add_btn = wx.Button(self, -1, "Add")
        self.Add_btn.Bind(wx.EVT_BUTTON, self.OnAdd)
        self.sizerAdd.Add(self.Add_btn,(9,5),(1,1),wx.EXPAND)        

        ### Reset Button        
        self.Reset_btn = wx.Button(self, -1, "Reset")
        self.Reset_btn.Bind(wx.EVT_BUTTON, self.OnResetPanel)
        self.sizerAdd.Add(self.Reset_btn,(9,4),(1,1),wx.EXPAND)             

        self.SetSizerAndFit(self.sizerAdd)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )     
######################################################################
    def OnResetPanel(self,event):
        ### Kill all children
        self.expansion.Destroy()
        self.v1_txt.Destroy()
        self.v1_qty.Destroy()
        self.v2_txt.Destroy()
        self.v2_qty.Destroy()
        self.Add_btn.Destroy()
        self.Reset_btn.Destroy()
        ### Reinitialise sizer
        self.name.Enable(True)
        self.name.SetValue("") 
######################################################################
    def OnAdd(self,event):
        print 'Add'
        self.OnResetPanel(self)
######################################################################
######################################################################
if __name__ == "__main__":
    app = wx.App()
    Main().Show()
    app.MainLoop()

基本上,我在等待输入的第一个 sizer 中有一个 TextCtrl。一旦用户点击 enter,几个对象就会出现在第二个 sizer 中。

windows 上的问题似乎来自两个 gridbagsizerssizernamesizerAdd)的使用。按下回车后(__init__ 中的等待事件),sizerAdd 中定义的对象不会出现。当我扩展脚本为 运行 的 window 时,这些对象神奇地出现了!

有什么想法吗?

编辑:代码现在可以运行了

我认为您的代码中的问题是 AddToCol 方法末尾的这两行:

        self.SetSizerAndFit(self.sizerAdd)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y )     

此时,您正在将 AddToCollection 面板的 sizer 从 self.sizername 更改为 self.sizerAdd。然而,Enter Name: 标签和文本框仍在 self.sizername sizer 内。然而,这个 sizer 不是任何 window 的 sizer,也没有被添加到任何其他 sizer。

通常,在wxPython中,每个sizer都应该被设置为一个window的sizer,或者添加到另一个sizer。这个另一个 sizer 将成为 window 的 sizer,或者包含在另一个 sizer 中,依此类推。在您的情况下,您的 self.sizername sizer 最终都不是,在这种情况下,我希望出现不可预测的行为。如果您的代码在 Linux 上工作,那么我会说它碰巧工作。

我能想到一些你可以在这里做的事情:

  1. 添加 self.sizerAdd 作为 self.sizername 的 child。这可以通过用

    替换上面的两行来完成
            self.sizername.Add(self.sizerAdd,(4,0),(1,1),wx.EXPAND)
            self.sizername.Layout()
    
  2. AddToCol中,将小部件直接添加到self.sizername sizer,而不是将它们添加到self.sizerAdd

  3. 创建一个垂直方向的 wx.BoxSizer(),将其设置为 AddToCollection 面板的 sizer,并添加 self.sizernameself.sizerAdd BoxSizer.

  4. 的 sizer

在所有这三种情况下,在创建新的小部件之后,您需要在 top-level sizer 上调用 Layout() 方法,无论是 self.sizername 还是 top-level BoxSizer。选项 1 下的代码片段已包含此行。

此外,您可能需要修改 OnResetPanel() 方法。如果您选择选项 1 或 3,则需要从您添加它的任何 sizer 中删除 self.sizerAdd sizer。例如,在选项 1 中,您将添加行

        self.sizername.Remove(self.sizerAdd)

另一种方法是让您的 AddToCol 方法在一个面板中创建所有小部件并将其添加到最后的主面板。然后,您的 AddToCol 方法需要创建一个 child 面板,添加额外的控件作为此面板的 children 而不是主面板 (self),设置 sizer将 child 面板添加到 self.sizerAdd,最后将此面板添加到 self.sizername sizer。

    def AddToCol(self):

        self.sizerAdd = wx.GridBagSizer(5, 5)
        self.sizerAdd.AddGrowableCol(0, 0)

        self.name.Enable(False)

        self.child_panel = wx.Panel(self)

        ### Expansion
        self.expansion = wx.Choice(self.child_panel, -1, choices=['test 1', 'test 2'])
        self.expansion.SetSelection(0)
        self.sizerAdd.Add(self.expansion,(5,0),(1,6),wx.EXPAND)

        # Create other widgets as before but with the parent set to self.child_panel
        # instead of self.

        self.child_panel.SetSizer(self.sizerAdd)
        self.sizername.Add(self.child_panel,(4,0),(1,1),wx.EXPAND)
        self.sizername.Layout()

然后您还需要替换行

       self.sizername.Remove(self.sizerAdd)

OnResetPanel() 中有两行:

       self.sizername.Remove(self.child_panel)
       self.child_panel.Destroy()

上面的方法 1 让我感到困扰的一件事是,我看到小部件在出现在正确位置之前短暂地出现在 top-left 角落。这种改编解决了这个问题,因此使 GUI 本身表现得更好一些。我无法重现您在评论中提到的黑色区域问题,但希望这种方法也能解决您的问题。