为什么在wxPython中使用.Hide()和.Show()隐藏或显示面板会导致Sizer改变布局?

12

如我在之前的问题中提到的,我正在尝试实现类似向导的功能。我已经确定了一个单一的框架并向其中添加了一个布局管理器。我为希望用户看到的每个屏幕构建了面板,并将它们添加到框架的布局管理器中,然后通过.Hide()一个面板,然后在下一个面板调用自定义的.ShowYourself()来切换面板。显然,我希望按钮在用户完成进程时保持在同一位置。

我通过其"返回"和"下一步"按钮将两个面板链接成一个无限循环,以便您可以看到发生了什么。第一个面板看起来很好;tom10的代码在这方面很好,因为它避免了我的初始过于复杂的边框问题。然后第二个面板似乎缩小到了最低限度。当我们返回到第一个面板时,这种缩小也发生在这里。为什么第一个面板看起来很好,但在我返回那里后不是?如果我不想要一个10像素乘10像素的灰色块,为什么要调用.Fit()?如果必须使用.Fit(),为什么.Fit()结果不一致?

这个无限循环似乎是我在这方面的经验:我修复了一个面板上的布局问题,发现切换面板会破坏其他面板的布局。我通过使用sizer_h.Add(self.panel1, 0)而不是sizer_h.Add(self.panel1, 1, wx.EXPAND)来解决这个问题,现在我的布局又出现了问题。

到目前为止,我的"解决方案"是为每个面板的主布局管理器添加一个mastersizer.SetMinSize((475, 592))(在下面的代码中已注释掉)。这个解决方案很差,因为1)我不得不通过试错找到有效的数字(宽度减5像素,高度减28像素)。2)我不明白为什么根本问题仍然存在。

正确的、非丑陋的解决方案是什么?是否应该将所有面板一次性添加到框架的布局管理器中,而切换面板是否涉及从框架的布局管理器中.Detach()该面板,然后.Add()下一个面板到框架的布局管理器中?是否有一个.JustMakeThisFillThePanel()方法隐藏在我错过了wxWidgets和wxPython在线文档中?

我的布局模型中显然有什么问题。以下是最小化的代码:

enter image description here

import wx
import sys


class My_App(wx.App):

    def OnInit(self):
        self.frame = My_Frame(None)
        self.frame.Show()
        self.SetTopWindow(self.frame)
        return True

    def OnExit(self):
        print 'Dying ...'


class My_Frame(wx.Frame):

    def __init__(self, image, parent=None,id=-1, title='Generic Title', pos=wx.DefaultPosition, style=wx.CAPTION | wx.STAY_ON_TOP):     

        size = (480, 620)
        wx.Frame.__init__(self, parent, id, 'Program Title', pos, size, style)

        sizer_h = wx.BoxSizer(wx.HORIZONTAL)

        self.panel0 = User_Interaction0(self)       
        sizer_h.Add(self.panel0, 1, wx.EXPAND)

        self.panel1 = User_Interaction1(self)       
        sizer_h.Add(self.panel1, 1, wx.EXPAND)

        self.SetSizer(sizer_h)

        self.panel0.ShowYourself()

    def ShutDown(self):
        self.Destroy()


class User_Interaction0(wx.Panel):

    def __init__(self, parent, id=-1):

        wx.Panel.__init__(self, parent, id)

        # master sizer for the whole panel
        mastersizer = wx.BoxSizer(wx.VERTICAL)
        #mastersizer.SetMinSize((475, 592))
        mastersizer.AddSpacer(15)


        # build the top row
        txtHeader = wx.StaticText(self, -1, 'Welcome to This Boring\nProgram', (0, 0))
        font = wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.BOLD)
        txtHeader.SetFont(font)
        txtOutOf = wx.StaticText(self, -1, '1 out of 7', (0, 0))                
        rowtopsizer = wx.BoxSizer(wx.HORIZONTAL)
        rowtopsizer.Add(txtHeader, 3, wx.ALIGN_LEFT) 
        rowtopsizer.Add((0,0), 1)  
        rowtopsizer.Add(txtOutOf, 0, wx.ALIGN_RIGHT) 
        mastersizer.Add(rowtopsizer, 0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=15) 


        # build the middle row
        text = 'PANEL 0\n\n'
        text = text + 'This could be a giant blob of explanatory text.\n'

        txtBasic = wx.StaticText(self, -1, text)
        font = wx.Font(11, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
        txtBasic.SetFont(font)
        mastersizer.Add(txtBasic, 1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=15)  


        # build the bottom row
        btnBack = wx.Button(self, -1, 'Back')
        self.Bind(wx.EVT_BUTTON, self.OnBack, id=btnBack.GetId())
        btnNext = wx.Button(self, -1, 'Next')
        self.Bind(wx.EVT_BUTTON, self.OnNext, id=btnNext.GetId())
        btnCancelExit = wx.Button(self, -1, 'Cancel and Exit')
        self.Bind(wx.EVT_BUTTON, self.OnCancelAndExit, id=btnCancelExit.GetId())
        rowbottomsizer = wx.BoxSizer(wx.HORIZONTAL)
        rowbottomsizer.Add(btnBack, 0, wx.ALIGN_LEFT)
        rowbottomsizer.AddSpacer(5)
        rowbottomsizer.Add(btnNext, 0)
        rowbottomsizer.AddSpacer(5)
        rowbottomsizer.AddStretchSpacer(1)
        rowbottomsizer.Add(btnCancelExit, 0, wx.ALIGN_RIGHT)
        mastersizer.Add(rowbottomsizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=15)

        # finish master sizer
        mastersizer.AddSpacer(15)   
        self.SetSizer(mastersizer)

        self.Raise()
        self.SetPosition((0,0))
        self.Fit()  
        self.Hide()


    def ShowYourself(self):
        self.Raise()
        self.SetPosition((0,0))
        self.Fit()
        self.Show()


    def OnBack(self, event):
        self.Hide()
        self.GetParent().panel1.ShowYourself()

    def OnNext(self, event):
        self.Hide()
        self.GetParent().panel1.ShowYourself()

    def OnCancelAndExit(self, event):
        self.GetParent().ShutDown()


class User_Interaction1(wx.Panel):

    def __init__(self, parent, id=-1):

        wx.Panel.__init__(self, parent, id)

        # master sizer for the whole panel
        mastersizer = wx.BoxSizer(wx.VERTICAL)
        #mastersizer.SetMinSize((475, 592))
        mastersizer.AddSpacer(15)


        # build the top row
        txtHeader = wx.StaticText(self, -1, 'Read about This Boring\nProgram', (0, 0))
        font = wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.BOLD)
        txtHeader.SetFont(font)
        txtOutOf = wx.StaticText(self, -1, '2 out of 7', (0, 0))                
        rowtopsizer = wx.BoxSizer(wx.HORIZONTAL)
        rowtopsizer.Add(txtHeader, 3, wx.ALIGN_LEFT) 
        rowtopsizer.Add((0,0), 1)  
        rowtopsizer.Add(txtOutOf, 0, wx.ALIGN_RIGHT) 
        mastersizer.Add(rowtopsizer, 0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=15) 


        # build the middle row
        text = 'PANEL 1\n\n'
        text = text + 'This could be a giant blob of boring text.\n'

        txtBasic = wx.StaticText(self, -1, text)
        font = wx.Font(11, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
        txtBasic.SetFont(font)
        mastersizer.Add(txtBasic, 1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=15)  


        # build the bottom row
        btnBack = wx.Button(self, -1, 'Back')
        self.Bind(wx.EVT_BUTTON, self.OnBack, id=btnBack.GetId())
        btnNext = wx.Button(self, -1, 'Next')
        self.Bind(wx.EVT_BUTTON, self.OnNext, id=btnNext.GetId())
        btnCancelExit = wx.Button(self, -1, 'Cancel and Exit')
        self.Bind(wx.EVT_BUTTON, self.OnCancelAndExit, id=btnCancelExit.GetId())
        rowbottomsizer = wx.BoxSizer(wx.HORIZONTAL)
        rowbottomsizer.Add(btnBack, 0, wx.ALIGN_LEFT)
        rowbottomsizer.AddSpacer(5)
        rowbottomsizer.Add(btnNext, 0)
        rowbottomsizer.AddSpacer(5)
        rowbottomsizer.AddStretchSpacer(1)
        rowbottomsizer.Add(btnCancelExit, 0, wx.ALIGN_RIGHT)
        mastersizer.Add(rowbottomsizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=15)

        # finish master sizer
        mastersizer.AddSpacer(15)   
        self.SetSizer(mastersizer)

        self.Raise()
        self.SetPosition((0,0))
        self.Fit()  
        self.Hide()


    def ShowYourself(self):
        self.Raise()
        self.SetPosition((0,0))
        self.Fit()
        self.Show()


    def OnBack(self, event):
        self.Hide()
        self.GetParent().panel0.ShowYourself()

    def OnNext(self, event):
        self.Hide()
        self.GetParent().panel0.ShowYourself()

    def OnCancelAndExit(self, event):
        self.GetParent().ShutDown()


def main():
    app = My_App(redirect = False)
    app.MainLoop()


if __name__ == '__main__':
    main()

我想知道在GTK和Windows 7(我的目标平台)之间是否存在一些功能上的差异,这些差异可能没有在当前版本的wxPython for Windows中考虑到。这是一个很有用的信息,Steven。这意味着也许我对此的思考方式并不完全错误。 - MetaHyperBolic
是的,我也尝试过了,它对我也不起作用。只是随便说一句...在ShowYourself的结尾调用self.SetSizer(self.GetSizer())会有什么效果吗?我现在离开了我的开发机器。 - Nathan Osman
抱歉,我的意思是 self.GetParent().SetSizer(self.GetParent().GetSizer()) - Nathan Osman
我尝试了一下,但没有看到明显的变化。 - MetaHyperBolic
嗯...好的,wxPython也提供了wx.Wizard类。那个是否更适合您的需求呢? - Nathan Osman
我曾经试着使用那个,但看起来相当复杂。此外,我需要回去看看,也许还有其他高级功能(弹出窗口等)。在我之前提出的问题中,我得到的建议是要避免使用那个类。我可以再试一次。 - MetaHyperBolic
2个回答

18

我想我弄明白了。不要调用面板的ShowHide方法,而是需要调用根Sizer的ShowHide方法:

self.Show()

变成

self.GetParent().GetSizer().Show(self)

......等等。

另外,在每次调用之后,您需要......

self.GetParent().GetSizer().Layout()

我做到了。我把My_Frame类中的self.SetSizer(sizer_h)语句移到了面板被创建之前,否则self.GetParent().GetSizer()将返回None。之后,它肯定会找到sizer。但是,第一次调用self.GetParent().GetSizer().Show(self)会返回“False”,并且我会看到两个面板挤在一起。一旦我开始点击“上一个”和“下一个”按钮,我们就会回到将面板大小缩小到远小于框架的相同行为。此时,在ShowYourself方法中,self.GetParent().GetSizer().Show(self)返回True。 - MetaHyperBolic
啊,当你添加布局时,我正在微调我的程序!准备尝试一下。 - MetaHyperBolic
那个有效了!那个只能在 ShowYourself() 函数的结尾处调用。所以,我的概念问题在于我忽视了 sizer 的层次结构,而你的解决方案是这些也必须被调用。你有一个亚马逊心愿清单吗?我能给你买本书吗?说真的,这个特别的问题让我很沮丧。 - MetaHyperBolic
很奇怪,当我尝试点赞时它说我没有注册。这种情况以前也发生过。是时候去FAQ里挖掘一下了。 - MetaHyperBolic
哦,您需要注册一个OpenID才能投票和获得其他特权。页面顶部应该有一个注册链接。 - Nathan Osman
显示剩余3条评论

5

是的,我知道这个问题已经有答案了,但是还是给你提供一下:

你只需要在面板的父级上调用Layout(),所以像self.GetParent().Layout()这样的代码就可以解决问题。参考这篇文章:http://www.blog.pythonlibrary.org/2010/06/16/wxpython-how-to-switch-between-panels/

如果你希望按钮始终显示,请在一个垂直的sizer中创建两个面板。顶部的面板将显示你的面板,底部的面板将显示按钮。然后使用PubSub或其他方法在它们之间进行通信。


谢谢您提到这个!对我来说,在父级上调用Layout()比其他答案建议的方法更加简洁。 - Gonzo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接