【问题标题】:wxPython problems with wrapping staticTextwxPython 包装 staticText 的问题
【发布时间】:2011-06-03 17:40:17
【问题描述】:

下面发布了代码的简化版本(删除了空格、cmets 等以减小大小 - 但我的程序的一般格式保持大致相同)。

当我运行脚本时,静态文本会正确换行,但面板中的其他项目不会向下移动(它们的行为就好像静态文本只有一行,因此并非所有内容都可见)。

如果我手动调整窗口/框架的大小,即使只是很小的量,一切都会得到纠正,并按应有的方式显示。

为什么一开始就不能正确显示?我尝试了GetParent().Refresh()Update()GetTopLevelParent().Update()Refresh() 的各种组合。我也尝试了我能想到的一切,但如果不手动调整框架/窗口的大小,就无法正确显示。一旦重新调整大小,它就可以完全按照我的意愿工作。

信息:

  • Windows XP
  • Python 2.5.2
  • wxPython 2.8.11.0 (msw-unicode)

我的代码:

#! /usr/bin/python

import wx

class StaticWrapText(wx.PyControl):
   def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition,
                size=wx.DefaultSize, style=wx.NO_BORDER,
                validator=wx.DefaultValidator, name='StaticWrapText'):
      wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)
      self.statictext = wx.StaticText(self, wx.ID_ANY, label, style=style)
      self.wraplabel = label
      #self.wrap()
   def wrap(self):
      self.Freeze()
      self.statictext.SetLabel(self.wraplabel)
      self.statictext.Wrap(self.GetSize().width)
      self.Thaw()
   def DoGetBestSize(self):
      self.wrap()
      #print self.statictext.GetSize()
      self.SetSize(self.statictext.GetSize())
      return self.GetSize()

class TestPanel(wx.Panel):
   def __init__(self, *args, **kwargs):
      # Init the base class
      wx.Panel.__init__(self, *args, **kwargs)
      self.createControls()
   def createControls(self):
      # --- Panel2 -------------------------------------------------------------
      self.Panel2 = wx.Panel(self, -1)
      msg1 =  'Below is a List of Files to be Processed'
      staticBox      = wx.StaticBox(self.Panel2, label=msg1)
      Panel2_box1_v1 = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
      Panel2_box2_h1 = wx.BoxSizer(wx.HORIZONTAL)
      Panel2_box3_v1 = wx.BoxSizer(wx.VERTICAL)

      self.wxL_Inputs = wx.ListBox(self.Panel2, wx.ID_ANY, style=wx.LB_EXTENDED)

      sz = dict(size=(120,-1))
      wxB_AddFile    = wx.Button(self.Panel2, label='Add File',        **sz)
      wxB_DeleteFile = wx.Button(self.Panel2, label='Delete Selected', **sz)
      wxB_ClearFiles = wx.Button(self.Panel2, label='Clear All',       **sz)
      Panel2_box3_v1.Add(wxB_AddFile,    0, wx.TOP, 0)
      Panel2_box3_v1.Add(wxB_DeleteFile, 0, wx.TOP, 0)
      Panel2_box3_v1.Add(wxB_ClearFiles, 0, wx.TOP, 0)

      Panel2_box2_h1.Add(self.wxL_Inputs, 1, wx.ALL|wx.EXPAND, 2)
      Panel2_box2_h1.Add(Panel2_box3_v1,  0, wx.ALL|wx.EXPAND, 2)

      msg =  'This is a long line of text used to test the autowrapping '
      msg += 'static text message.  '
      msg += 'This is a long line of text used to test the autowrapping '
      msg += 'static text message.  '
      msg += 'This is a long line of text used to test the autowrapping '
      msg += 'static text message.  '
      msg += 'This is a long line of text used to test the autowrapping '
      msg += 'static text message.  '
      staticMsg = StaticWrapText(self.Panel2, label=msg)

      Panel2_box1_v1.Add(staticMsg,      0, wx.ALL|wx.EXPAND, 2)
      Panel2_box1_v1.Add(Panel2_box2_h1, 1, wx.ALL|wx.EXPAND, 0)
      self.Panel2.SetSizer(Panel2_box1_v1)

      # --- Combine Everything -------------------------------------------------
      final_vbox = wx.BoxSizer(wx.VERTICAL)
      final_vbox.Add(self.Panel2, 1, wx.ALL|wx.EXPAND, 2)
      self.SetSizerAndFit(final_vbox)

class TestFrame(wx.Frame):
   def __init__(self, *args, **kwargs):
      # Init the base class
      wx.Frame.__init__(self, *args, **kwargs)
      panel = TestPanel(self)
      self.SetClientSize(wx.Size(500,500))
      self.Center()

class wxFileCleanupApp(wx.App):
   def __init__(self, *args, **kwargs):
      # Init the base class
      wx.App.__init__(self, *args, **kwargs)
   def OnInit(self):
      # Create the frame, center it, and show it
      frame = TestFrame(None, title='Test Frame')
      frame.Show()
      return True

if __name__ == '__main__':
   app = wxFileCleanupApp()
   app.MainLoop()

【问题讨论】:

    标签: python static wxpython


    【解决方案1】:

    使用 Mike Driscoll 的代码作为基线,我希望这能说明我的问题。使用“txt”有两种不同的版本。以下是我希望您尝试的三件事:

    1. 按原样运行。使用我的 StaticWrapText。一开始它显示错误,但重新调整窗口大小,它可以完全按照我的意愿工作。 “按钮”之前的文本下方没有空白/浪费空间

    2. 更改这两行(更改 cmets):
      txt = wx.StaticText(面板,标签=文本)
      #txt = StaticWrapText(面板,标签=文本)
      现在您将看到没有换行,并且文本始终仅在一行上。绝对不是我们想要的。这是因为 "sizer.Add(txt, 0, wx.EXPAND, 5) "...所以继续第 3 部分...

    3. 保留第 2 部分的更改并同时更改:
      sizer.Add(txt, 0, wx.EXPAND, 5)
      到:
      sizer.Add(txt, 1, wx.EXPAND, 5)
      所以现在静态文本将扩展。这是接近工作......但我不希望文本和按钮之间的所有浪费空间。如果你把窗户做得很大,就会浪费很多空间。调整窗口大小后,请参阅第 1 部分以查看差异。

    代码:

    import wx
    
    class StaticWrapText(wx.PyControl):
       def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition,
                    size=wx.DefaultSize, style=wx.NO_BORDER,
                    validator=wx.DefaultValidator, name='StaticWrapText'):
          wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)
          self.statictext = wx.StaticText(self, wx.ID_ANY, label, style=style)
          self.wraplabel = label
          #self.wrap()
       def wrap(self):
          self.Freeze()
          self.statictext.SetLabel(self.wraplabel)
          self.statictext.Wrap(self.GetSize().width)
          self.Thaw()
       def DoGetBestSize(self):
          self.wrap()
          #print self.statictext.GetSize()
          self.SetSize(self.statictext.GetSize())
          return self.GetSize()
    
    class MyForm(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
    
            # Add a panel so it looks the correct on all platforms
            panel = wx.Panel(self, wx.ID_ANY)
    
            text = "I'm subclasses the statictext because I want it to act exactly like a static text, but correctly wordwrap as needed. I've found several examples of it on the web, but none that worked how I wanted. The wordwrap makes it look much nicer when the user may decide to re-size the window, so I would definitely like to have it be wordwrapped. I know about the wx.lib.wordwrap, but chose to use the built in Wrap function of the statictext control instead. It basically does the same thing from what I understand."
            #txt = wx.StaticText(panel, label=text)
            txt = StaticWrapText(panel, label=text)
            wxbutton = wx.Button(panel, label='Button', size=wx.Size(120,50))
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(txt,      0, wx.EXPAND, 5)
            sizer.Add(wxbutton, 1, wx.EXPAND, 5)
            panel.SetSizer(sizer)
    
    # Run the program
    if __name__ == "__main__":
        app = wx.PySimpleApp()
        frame = MyForm().Show()
        app.MainLoop()
    

    编辑:

    啊啊啊……终于!我尝试在程序的几乎每个级别上使用 Layout() 方法,但实际上我需要在使用 GetSizer() 方法找到的 SIZER 上使用 Layout() - 或者您可以将 SendSizeEvent() 发送到面板(已评论在下面的代码中)。因此,以下现在正是我想要的!谢谢您的帮助。唯一的其他更改是将带有 self.panel 的面板存储在框架类中。需要注意的是,我必须在 frame.Show() 之后将此语句放置,否则它无法正常工作。

    代码:

    import wx
    
    class StaticWrapText(wx.PyControl):
       def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition,
                    size=wx.DefaultSize, style=wx.NO_BORDER,
                    validator=wx.DefaultValidator, name='StaticWrapText'):
          wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)
          self.statictext = wx.StaticText(self, wx.ID_ANY, label, style=style)
          self.wraplabel = label
          #self.wrap()
       def wrap(self):
          self.Freeze()
          self.statictext.SetLabel(self.wraplabel)
          self.statictext.Wrap(self.GetSize().width)
          self.Thaw()
       def DoGetBestSize(self):
          self.wrap()
          #print self.statictext.GetSize()
          self.SetSize(self.statictext.GetSize())
          return self.GetSize()
    
    class MyForm(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
    
            # Add a panel so it looks the correct on all platforms
            self.panel = wx.Panel(self, wx.ID_ANY)
    
            text = "I'm subclasses the statictext because I want it to act exactly like a static text, but correctly wordwrap as needed. I've found several examples of it on the web, but none that worked how I wanted. The wordwrap makes it look much nicer when the user may decide to re-size the window, so I would definitely like to have it be wordwrapped. I know about the wx.lib.wordwrap, but chose to use the built in Wrap function of the statictext control instead. It basically does the same thing from what I understand."
            txt = StaticWrapText(self.panel, label=text)
            wxbutton = wx.Button(self.panel, label='Button', size=wx.Size(120,50))
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(txt,      0, wx.EXPAND, 5)
            sizer.Add(wxbutton, 1, wx.EXPAND, 5)
            self.panel.SetSizer(sizer)
    
    # Run the program
    if __name__ == "__main__":
        app = wx.PySimpleApp()
        frame = MyForm()
        frame.Show()
        #frame.panel.SendSizeEvent()
        frame.panel.GetSizer().Layout()
        app.MainLoop()
    

    最后一点,在我发布的原始程序中,需要在 frame.Show() 之前或之后添加以下行:
    frame.panel.Panel2.GetSizer().Layout()

    有趣的是...对于原始示例,这可以在 frame.Show() 之前或之后,但另一个示例要求它在 frame.Show() 之后。我不知道为什么,但只要把它放在后面,你就安全了。

    【讨论】:

    • 我早该想到的。您通常希望在小部件的父级或包含小部件的 sizer 上调用 Layout。那好吧。抱歉,我没有发现。
    • 感谢您的帮助!当我最大化/最小化窗口时,它仍然不能正常工作。但就目前而言,这已经足够了。
    • 啊,即使使用了最大化按钮,它也可以工作......在换行功能中,使用 self.statictext.Wrap(self.GetParent().GetSize().width) 而不是self.statictext.Wrap(self.GetSize().width)
    【解决方案2】:

    我用

    width = 200  # panel width
    txt = wx.StaticText(panel, label=text)
    txt.Wrap(width)
    

    这很好用,并且下一个小部件的位置正确。您可以轻松地动态执行txt.Wrap(width)

    【讨论】:

      【解决方案3】:

      你为什么要继承它?你需要自动换行吗?如果是这样,您可以使用 wx.lib.wordwrap 中的模块。

      在回答 OP 的评论时,请查看:

      import wx
      
      class MyForm(wx.Frame):
      
          def __init__(self):
              wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
      
              # Add a panel so it looks the correct on all platforms
              panel = wx.Panel(self, wx.ID_ANY)
      
              text = "I'm subclasses the statictext because I want it to act exactly like a static text, but correctly wordwrap as needed. I've found several examples of it on the web, but none that worked how I wanted. The wordwrap makes it look much nicer when the user may decide to re-size the window, so I would definitely like to have it be wordwrapped. I know about the wx.lib.wordwrap, but chose to use the built in Wrap function of the statictext control instead. It basically does the same thing from what I understand."
              txt = wx.StaticText(panel, label=text)
              sizer = wx.BoxSizer(wx.HORIZONTAL)
              sizer.Add(txt, 1, wx.EXPAND, 5)
              panel.SetSizer(sizer)
      
      # Run the program
      if __name__ == "__main__":
          app = wx.PySimpleApp()
          frame = MyForm().Show()
          app.MainLoop()
      

      我使用了 OP 对文本的评论。无论如何,这对我来说在 Windows XP、Python 2.5 和 wxPython 2.8.10.1 上运行良好。

      【讨论】:

      • 我是静态文本的子类,因为我希望它的行为与静态文本完全一样,但根据需要正确换行。我在网上找到了几个例子,但没有一个能达到我想要的效果。当用户可能决定重新调整窗口大小时,自动换行使它看起来更好,所以我肯定希望它被自动换行。我知道 wx.lib.wordwrap,但选择使用 statictext 控件的内置 Wrap 功能。据我了解,它基本上做同样的事情。
      • 您可能还想使用 SetSizeHints 来防止框架被调整得太小。
      • 好的,你的例子更小,所以更容易使用。请参阅我的答案,以修改您的代码以证明我的问题(我希望更清楚)。
      【解决方案4】:

      我发现了一种我认为更容易和自动的方法来处理这个问题。

      创建 StaticText 控件后,将控件的 wx.EVT_SIZE 绑定到一个处理程序,该处理程序以事件的 GetSize()[0] 作为参数调用 StaticText 的 Wrap() 函数(然后跳过该事件)。

      一个例子:

      class MyDialog(wx.Dialog):
          def __init__(self, parent):
              wx.Dialog.__init__(self, parent = parent, title = "Test Dialog", style = wx.CAPTION)
      
              bigstr = "This is a really long string that is intended to test the wrapping functionality of the StaticText control in this dialog.  If it works correctly, it should appear as multiple lines of text with a minimum of fuss."
      
              self.__label__ = wx.StaticText(parent = self, label = bigstr)
              self.__actionbutton__ = wx.Button(parent = self, label = "Go")
      
              self.__label__.Bind(wx.EVT_SIZE, self.__WrapText__)
              self.__actionbutton__.Bind(wx.EVT_BUTTON, self.__OnButton__)
      
              sizer = wx.BoxSizer(wx.VERTICAL)
              sizer.Add(self.__label__, flag = wx.ALL | wx.EXPAND, border = 5)
              sizer.Add(self.__actionbutton__, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.CENTER, border = 0)
              self.SetSizer(sizer)
      
              self.Layout()
      
          def __OnButton__(self, event):
              self.EndModal(wx.ID_OK)
      
          def __WrapText__(self, event):
              self.__label__.Wrap(event.GetSize()[0])
      
              event.Skip()
      

      这是它在我的系统上的样子(MSW,Python 2.7.5,wx 2.8.12.1):

      【讨论】:

      • 在 EVT_SIZE 处理程序中调用 Wrap 似乎再次调用了相同的处理程序,这次使用了不同的大小。
      猜你喜欢
      • 2011-07-09
      • 2010-09-22
      • 2011-03-21
      • 1970-01-01
      • 2020-09-07
      • 2011-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多