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 上的问题似乎来自两个 gridbagsizers
(sizername
和 sizerAdd
)的使用。按下回车后(__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 上工作,那么我会说它碰巧工作。
我能想到一些你可以在这里做的事情:
添加 self.sizerAdd
作为 self.sizername
的 child。这可以通过用
替换上面的两行来完成
self.sizername.Add(self.sizerAdd,(4,0),(1,1),wx.EXPAND)
self.sizername.Layout()
在AddToCol
中,将小部件直接添加到self.sizername
sizer,而不是将它们添加到self.sizerAdd
。
创建一个垂直方向的 wx.BoxSizer()
,将其设置为 AddToCollection
面板的 sizer,并添加 self.sizername
和 self.sizerAdd
BoxSizer
.
的 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 本身表现得更好一些。我无法重现您在评论中提到的黑色区域问题,但希望这种方法也能解决您的问题。
我正在编写一个在 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 上的问题似乎来自两个 gridbagsizers
(sizername
和 sizerAdd
)的使用。按下回车后(__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 上工作,那么我会说它碰巧工作。
我能想到一些你可以在这里做的事情:
添加
替换上面的两行来完成self.sizerAdd
作为self.sizername
的 child。这可以通过用self.sizername.Add(self.sizerAdd,(4,0),(1,1),wx.EXPAND) self.sizername.Layout()
在
AddToCol
中,将小部件直接添加到self.sizername
sizer,而不是将它们添加到self.sizerAdd
。创建一个垂直方向的
wx.BoxSizer()
,将其设置为AddToCollection
面板的 sizer,并添加self.sizername
和self.sizerAdd
BoxSizer
. 的 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 本身表现得更好一些。我无法重现您在评论中提到的黑色区域问题,但希望这种方法也能解决您的问题。