【问题标题】:Why Does .Hide()ing and .Show()ing Panels in wxPython Result in the Sizer Changing the Layout?为什么 wxPython 中的 .Hide()ing 和 .Show()ing 面板会导致 Sizer 更改布局?
【发布时间】:2011-02-03 11:02:09
【问题描述】:

正如我的previous question 中提到的,我正在尝试在功能上制作一些有点像向导的东西。我已经确定了一个单独的框架,并添加了一个sizer。我为希望用户看到的每个屏幕构建面板,将它们添加到框架的尺寸器中,然后通过.Hide()ing 一个面板在面板之间切换,然后在下一个面板上调用自定义.ShowYourself()。显然,我希望按钮在用户完成整个过程时保持在同一个位置。

我通过“返回”和“下一步”按钮将两个面板无限循环地连接在一起,这样您就可以看到发生了什么。第一个面板看起来很棒; tom10 的代码在那个级别上工作,因为它避开了我最初的、过度花哨的尝试,边界飞来飞去。然后第二个面板似乎已经缩小到最低限度。当我们回到第一个面板时,这里也发生了收缩。为什么它在第一个面板上看起来很好,但在我回到那里之后就不行了?如果我不想要 10 像素乘 10 像素的一叠灰色,为什么还要调用 .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()ing 从框架的尺寸器中的那个面板,然后 .Add()ing 到框架的尺寸器的下一个面板?是否有 .JustMakeThisFillThePanel() 方法隐藏在我在 wxWidgets 和 wxPython 在线文档中错过的某个地方?

我显然在我的布局心智模型中遗漏了一些东西。下面粘贴的极简代码。

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 中没有考虑到。这很容易知道,史蒂文。这意味着也许我的想法并没有完全关闭。
  • 是的。我试过了,它也不会为我扩展。只是扔掉一些东西...在ShowYourself 末尾调用self.SetSizer(self.GetSizer()) 有什么作用吗?我远离我的开发者。机器自动取款机。
  • 对不起,我是想说self.GetParent().SetSizer(self.GetParent().GetSizer())
  • 我试了一下;没有明显的变化。
  • 嗯... wxPython 也提供了一个 wx.Wizard 类。这是否适合您的需求?

标签: wxpython wxwidgets sizer


【解决方案1】:

是的,我知道这已经被回答了,但无论如何你都去吧:

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

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

【讨论】:

  • 感谢您提及这一点!对我来说,在父母身上拨打Layout() 比其他答案建议的要干净得多
【解决方案2】:

我想我明白了。无需调用面板的ShowHide 方法,您需要调用root 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”,我看到两个面板都挤在框架上。一旦我开始单击“后退”和“下一个”按钮,我们又回到了将面板尺寸缩小到比框架小得多的相同行为。此时,self.GetParent().GetSizer().Show(self) 在 ShowYourself 方法中返回 True。
  • 啊,当你添加布局时,我正在调整我的程序!即将试一试。
  • 成功了!只能在 ShowYourself() 函数的末尾调用。所以,我的概念问题是我忽略了sizer的层次结构,你的解决方案是这些也必须被调用。你有亚马逊的愿望清单,我可以给你买一本书吗?说真的,这个特殊的问题让我很沮丧。
  • 没问题,我有时会花几个小时试图找出为什么某些 wxWidgets 代码块不起作用。 (或者 wxPython)很高兴我能提供帮助。哦,如果你愿意,你可以投票给我的答案。 (点击旁边的向上箭头:)
  • 这很奇怪。当我尝试投票时,它说我没有注册。这以前发生过。是时候深入了解常见问题解答了。
猜你喜欢
  • 1970-01-01
  • 2022-10-14
  • 2020-08-26
  • 2012-12-10
  • 1970-01-01
  • 2013-08-01
  • 2017-07-10
  • 2010-10-18
  • 1970-01-01
相关资源
最近更新 更多