views:

828

answers:

2

In my wxPython application I've created a wx.ScrolledPanel, in which there is a big wx.StaticBitmap that needs to be scrolled.

The scroll bars do appear and I can scroll with them, but I'd also like to be able to scroll with the mouse wheel and the arrow keys on the keyboard. It would be nice if the "Home", "Page Up", and those other keys would also function as expected.

How do I do this?

UPDATE:

I see the problem. The ScrolledPanel is able to scroll, but only when it is under focus. Problem is, how do I get to be under focus? Even clicking on it doesn't do it. Only if I put a text control inside of it I can focus on it and thus scroll with the wheel. But I don't want to have a text control in it. So how do I make it focus?

UPDATE 2:

Here is a code sample that shows this phenomena. Uncomment to see how a text control makes the mouse wheel work.

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()
A: 

Here's an example that should do what you want, I hope. (Edit: In retrospect, this doesnt' quite work, for example, when there are two scrolled panels... I'll leave it up here though so peole can downvote it or whatever.) Basically I put everything in a panel inside the frame (generally a good idea), and then set the focus to this main panel.

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()

For keyboard control, like setting action from the home key, I think you'll need to bind to those events, and respond appropriately, such as using mypanel.Scroll(0,0) for the home key (and remember to call evt.Skip() for the keyboard events you don't act on). (Edit: I don't think there are any default key bindings for scrolling. I'm not sure I'd want any either, for example, what should happen if there's a scrolled panel within a scrolled panel?)

tom10
But where to put it? I may want to change the focus to some other widget, and then return to the ScrolledPanel again.
cool-RR
1. See the question in my first comment. 2. Yes, I have called SetupScrolling. 3. Do I really need to reinvent the wheel with the home key thing? Doesn't wxPython implement this?
cool-RR
Something is odd. For example in the wxPython demo, when the mouse if over each panel, that panel will scroll with the wheel. So a few questions. If you call `SetFocus` does it work? If you try to scroll in the normal way, by clicking on the scrollbar and moving, does it move? And if you immediately to use the scroll wheel, does it work then?
tom10
1. I'm having trouble checking that out, working on it. 2. Yes. 3. No.
cool-RR
It seems something else is getting the focus and it's probably more worthwhile to figure out what and why of this rather than spending time with SetFocus. And without a piece of code that mimics this behavior, I'm out of thoughts on this.
tom10
+1  A: 

Problem is on window Frame gets the focus and child panel is not getting the Focus (on ubuntu linux it is working fine). Workaround can be as simple as to redirect Frame focus event to set focus to panel e.g.

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()

or onmouse move over panel, set focus to it, and all keys + mousewheeel will start working e.g.

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()
Anurag Uniyal
Regarding your first code sample, it won't work for more than one ScrolledPanel, would it?Regarding your second code sample, I think this is the way to go. (Except a bit less sensitive, I don't want the focus to change every time you accidentally hover over the panel.) I think I'll subclass ScrolledPanel and post it here.
cool-RR
to scroll a panel it should be in focus, but if you don't like focus on mouse move, you can do same thing on click
Anurag Uniyal