wxpython 可滚动面板未更新滚动条

5 投票
1 回答
6063 浏览
提问于 2025-04-16 17:07

我正在使用Windows XP和wxPython(wxPython 3.1,Python 2.6)来制作一个图形界面程序,这个程序可以把文本从一个文本框(TextCtrl)复制到一个可滚动面板(ScrollablePanel)里,这个面板里面有一个静态文本(StaticText)。这一切都运行得很好,不过,滚动部分的表现有点问题。它似乎不会实时更新。当我把窗口恢复到最大化状态时,滚动条才会更新。但是,如果我在文本框里继续输入(这样通过一个事件把文本添加到可滚动面板里的静态文本中),面板上的滚动条不会更新,除非我再次最小化窗口等等。

所以问题是,我该如何让可滚动面板实时更新呢?我已经设置了一个事件,等待文本框里的文本变化,我觉得更新应该在这里进行。我尝试过Update、Refresh和Layout,但似乎都没什么效果。更大的问题是,最小化窗口再最大化一次,和Update、Refresh、Layout有什么不同呢?谢谢你的帮助。抱歉我不能发代码,涉及保密。我很感激你能给我任何帮助。我通常可以通过搜索自己找到问题,但我就是没找到关于这种情况的文档。这是我第一次遇到这种情况,但我从你的回答中得到了很多帮助。再次感谢!:)

补充:

import wx
import wx.lib.inspection
from wx.lib.scrolledpanel import ScrolledPanel

class MyFrame( wx.Frame ):

    # TODO: add all class variables here for convention
    tin         = None

    hsizer      = None

    def __init__( self, parent, ID, title ):
        wx.Frame.__init__( self, parent, ID, title,
                     wx.DefaultPosition, wx.Size( 200, 150 ) )

        self.InitWidgets()
        self.InitBindings()
        self.InitFinish()


    def InitWidgets( self ):
        self.hsizer = wx.BoxSizer( wx.HORIZONTAL )

        # Add the TextCtrl
        vsizer = wx.BoxSizer( wx.VERTICAL )
        self.tin = wx.TextCtrl( self, style=wx.TE_MULTILINE )
        vsizer.Add( self.tin, 1, wx.EXPAND  )
        self.hsizer.Add( vsizer, 3, wx.TOP|wx.LEFT|wx.BOTTOM|wx.EXPAND, border=20 )

        # Add ScrolledPanel widget
        self.hsizer.Add( wx.Size( 200, -1 ), 1 )
        vsizer2 = wx.BoxSizer( wx.VERTICAL )
        self.test_panel = ScrolledPanel( self )
        self.test_panel.SetupScrolling()

        # Setup static text ( label ) tvs is the 
        # vertical sizer inside the panel
        self.tvs = wx.BoxSizer( wx.VERTICAL )
        self.tin2 = wx.StaticText( self.test_panel )
        self.tvs.Add( self.tin2, 0, wx.EXPAND )
        vsizer2.Add( self.test_panel, 1, wx.EXPAND  )
        self.hsizer.Add( vsizer2, 3, wx.TOP|wx.LEFT|wx.BOTTOM|wx.EXPAND, border=20 )

        # Add Spacer
        self.hsizer.Add( wx.Size( 500, -1 ), 1 ) 

    def InitBindings( self ):
        self.tin.Bind( wx.EVT_TEXT, self.TextChange ) 

    def InitFinish( self ):

        # Setup sizers and frame
        self.SetAutoLayout( True )
        self.test_panel.SetAutoLayout( True )
        self.test_panel.SetSizer( self.tvs )
        self.SetSizer( self.hsizer )
        self.Layout()
        self.Update()
        self.Maximize()

    def TextChange( self, event ):

        #self.CopyValues()
        self.tin2.Label = self.tin.GetValue()
        self.Layout()
        self.Update()
        self.test_panel.Refresh()
        self.test_panel.Update()
        print self.tin.GetValue().split( "\n" )


class MyApp( wx.App ):

    fr = None

    def OnInit( self ):
        self.fr = MyFrame( None, -1, "TitleX" )
        self.fr.Show( True )
        self.SetTopWindow( self.fr )
        return True

app = MyApp( 0 )
app.MainLoop()

def main():

    win = 1
if ( __name__ == "__main__" ):
    main()    

一般来说,这就是发生的事情,而不想透露程序的太多目的。我设置了虚拟大小,想着这样可以让面板的大小大于它填充的区域,希望能有永久的滚动条,但事实并非如此。

1 个回答

9

如果你能发一些可以直接复制粘贴就能运行的代码,那就更好了。

不过,看起来你遗漏了一些让滚动面板正常工作的必要步骤:

  1. 滚动面板必须有一个布局管理器。你可以通过 self.test_panel.SetSizer(self.a_sizer) 这个方法来设置。面板里面的所有其他控件都必须和这个布局管理器关联起来。

  2. 你必须在你的面板上调用 SetupScrolling() 方法,比如 self.test_panel.SetupScrolling()

  3. 你还需要为面板启用自动布局,比如 self.test_panel.SetAutoLayout(1)

顺便问一下,你见过 wxPython 的代码示例吗?我问这个是因为几乎所有的示例都有像 __do_layout 这样的好方法。使用这种方法会让你的代码更容易阅读。

补充:

你需要在 TextChange 方法中添加 self.test_panel.FitInside(),这样可以强制面板重新计算子控件的大小,并更新它自己的虚拟大小。

这里是完整的解决方案:

import wx
from wx.lib.scrolledpanel import ScrolledPanel

class MyFrame( wx.Frame ):
    def __init__( self, parent, ID, title ):
        wx.Frame.__init__( self, parent, ID, title,
                         wx.DefaultPosition, wx.Size( 600, 400 ) )
        #Controls
        self.tin = wx.TextCtrl( self, 
                                size = wx.Size( 600, 400 ),
                                style=wx.TE_MULTILINE )        
        self.test_panel = ScrolledPanel( self, 
                                         size = wx.Size( 600, 400 ) )
        self.test_panel.SetupScrolling()
        self.tin2 = wx.StaticText( self.test_panel )

        #Layout
        #-- Scrolled Window
        self.panel_sizer = wx.BoxSizer( wx.HORIZONTAL )
        self.panel_sizer.Add( self.tin2, 0, wx.EXPAND )
        self.test_panel.SetSizer( self.panel_sizer )
        self.panel_sizer.Fit(self.test_panel)
        #-- Main Frame
        self.inner_sizer = wx.BoxSizer( wx.HORIZONTAL )        
        self.inner_sizer.Add( self.tin, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 50  )
        self.inner_sizer.Add( self.test_panel, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 50  )

        self.sizer = wx.BoxSizer( wx.VERTICAL )
        self.sizer.Add(self.inner_sizer, 1, wx.ALL | wx.EXPAND, 20)        
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.sizer.Layout()

        self.test_panel.SetAutoLayout(1)

        #Bind Events
        self.tin.Bind( wx.EVT_TEXT, self.TextChange )

    def TextChange( self, event ):
        self.tin2.SetLabel(self.tin.GetValue())
        self.test_panel.FitInside()


class MyApp( wx.App ):
    def OnInit( self ):
        self.fr = MyFrame( None, -1, "TitleX" )
        self.fr.Show( True )
        self.SetTopWindow( self.fr )
        return True

app = MyApp( 0 )
app.MainLoop()

def main():

    win = 1

if ( __name__ == "__main__" ):
    main()    

撰写回答