使用鼠标滚轮和箭头键在`wx.ScrolledPanel`中滚动

5 投票
2 回答
5251 浏览
提问于 2025-04-15 12:59

在我的wxPython应用程序中,我创建了一个wx.ScrolledPanel,里面有一个很大的wx.StaticBitmap,需要能够滚动。

滚动条是可以显示的,我也可以用它们来滚动,但我还想用鼠标滚轮和键盘上的箭头键来滚动。如果“Home”、“Page Up”等键也能正常工作,那就更好了。

我该怎么做呢?

更新:

我发现问题了。这个ScrolledPanel是可以滚动的,但只有在它获得焦点的时候才能滚动。问题是,怎么才能让它获得焦点呢?就算点击它也不行。只有当我在里面放一个文本控件时,我才能聚焦到它,从而用滚轮滚动。但我不想在里面放一个文本控件。那么我该怎么让它获得焦点呢?

更新 2:

这里有一段代码示例,展示了这个现象。取消注释后可以看到文本控件如何让鼠标滚轮工作。

import wx, wx.lib.scrolledpanel

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        scrolled_panel = \
            wx.lib.scrolledpanel.ScrolledPanel(parent=self, id=-1)
        scrolled_panel.SetupScrolling()

        text = "Ooga booga\n" * 50
        static_text=wx.StaticText(scrolled_panel, -1, text)
        sizer=wx.BoxSizer(wx.VERTICAL)
        sizer.Add(static_text, wx.EXPAND, 0)

        #    Uncomment the following 2 lines to see how adding
        #    a text control to the scrolled panel makes the
        #    mouse wheel work.
        #
        #text_control=wx.TextCtrl(scrolled_panel, -1)
        #sizer.Add(text_control, wx.EXPAND, 0)

        scrolled_panel.SetSizer(sizer)

        self.Show()

if __name__=="__main__":
    app = wx.PySimpleApp()
    my_frame=MyFrame(None, -1)
    #import cProfile; cProfile.run("app.MainLoop()")
    app.MainLoop()

2 个回答

0

这里有一个例子,应该能满足你的需求,我希望如此。 (补充说明:回想起来,这个方法并不完全有效,比如当有两个可滚动的面板时……不过我还是把它留在这里,方便大家投票或其他操作。)基本上,我把所有内容放在一个面板里,这个面板放在框架内(通常这是个好主意),然后把焦点设置到这个主面板上。

import wx
import wx, wx.lib.scrolledpanel

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        main_panel = wx.Panel(self, -1)
        main_panel.SetBackgroundColour((150, 100, 100))
        self.main_panel = main_panel

        scrolled_panel = \
            wx.lib.scrolledpanel.ScrolledPanel(parent=main_panel, id=-1)
        scrolled_panel.SetupScrolling()
        self.scrolled_panel = scrolled_panel

        cpanel = wx.Panel(main_panel, -1)
        cpanel.SetBackgroundColour((100, 150, 100))
        b = wx.Button(cpanel, -1, size=(40,40))
        self.Bind(wx.EVT_BUTTON, self.OnClick, b)
        self.b = b

        text = "Ooga booga\n" * 50
        static_text=wx.StaticText(scrolled_panel, -1, text)
        main_sizer=wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(scrolled_panel, 1, wx.EXPAND)
        main_sizer.Add(cpanel, 1, wx.EXPAND)
        main_panel.SetSizer(main_sizer)

        text_sizer=wx.BoxSizer(wx.VERTICAL)
        text_sizer.Add(static_text, 1, wx.EXPAND)
        scrolled_panel.SetSizer(text_sizer)

        self.main_panel.SetFocus()

        self.Show()

    def OnClick(self, evt):
        print "click"


if __name__=="__main__":
    class MyApp(wx.App):

        def OnInit(self):
            frame = MyFrame(None, -1)
            frame.Show(True)
            self.SetTopWindow(frame)
            return True
    app = MyApp(0)
    app.MainLoop()

关于键盘控制,比如用主页键设置动作,我觉得你需要绑定这些事件,并做出相应的反应,比如用 mypanel.Scroll(0,0) 来处理 home 键(记得对那些你不处理的键盘事件调用 evt.Skip())。 (补充说明:我觉得没有默认的键绑定用于滚动。我也不确定我是否想要这些,比如,如果一个可滚动的面板里还有另一个可滚动的面板,该怎么办呢?)

3

问题是,在窗口中,框架得到了焦点,而子面板却没有得到焦点(在Ubuntu Linux上是正常的)。解决方法可以很简单,比如把框架的焦点事件重定向到子面板上,让它获得焦点,例如:

import wx, wx.lib.scrolledpanel

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.panel = scrolled_panel = \
            wx.lib.scrolledpanel.ScrolledPanel(parent=self, id=-1)
        scrolled_panel.SetupScrolling()

        text = "Ooga booga\n" * 50
        static_text=wx.StaticText(scrolled_panel, -1, text)
        sizer=wx.BoxSizer(wx.VERTICAL)
        sizer.Add(static_text, wx.EXPAND, 0)

        scrolled_panel.SetSizer(sizer)

        self.Show()

        self.panel.SetFocus()
        scrolled_panel.Bind(wx.EVT_SET_FOCUS, self.onFocus)

    def onFocus(self, event):
        self.panel.SetFocus()

if __name__=="__main__":
    app = wx.PySimpleApp()
    my_frame=MyFrame(None, -1)
    app.MainLoop()

或者在鼠标移动到面板上时,给它设置焦点,这样所有的键盘和鼠标滚轮操作就都能正常工作,例如:

import wx, wx.lib.scrolledpanel

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.panel = scrolled_panel = \
            wx.lib.scrolledpanel.ScrolledPanel(parent=self, id=-1)
        scrolled_panel.SetupScrolling()

        scrolled_panel.Bind(wx.EVT_MOTION, self.onMouseMove)

        text = "Ooga booga\n" * 50
        static_text=wx.StaticText(scrolled_panel, -1, text)
        sizer=wx.BoxSizer(wx.VERTICAL)
        sizer.Add(static_text, wx.EXPAND, 0)

        scrolled_panel.SetSizer(sizer)

        self.Show()

    def onMouseMove(self, event):
        self.panel.SetFocus()

if __name__=="__main__":
    app = wx.PySimpleApp()
    my_frame=MyFrame(None, -1)
    app.MainLoop()

撰写回答