wx.ScrolledPanel Navigation

Hi All,

Below is a sample of a wx.ScrolledPanel. The vertical grid has 25
wx.TextCtrl fields.
Is there ANY way to automatically (by pressing a button) move the
scroll so the 10th field is the first one visible? The 20th field?
And back to the 0th field. I just want to save the user time
scrolling and possibly passing the field they want. Maybe this is an
enhancement request but it seems like a feature wx.ScrolledPanel could
use if it does not already have it.

···

#-------------------------------------------------------------------------------------------------------------------------------------------------------
import wx
import wx.lib.scrolledpanel as scrolled

#----------------------------------------------------------------------

text = "one two buckle my shoe three four shut the door five six pick
up sticks seven eight lay them straight nine ten big fat hen"

class TestPanel(scrolled.ScrolledPanel):
    def __init__(self, parent, log):
        self.log = log
        scrolled.ScrolledPanel.__init__(self, parent, -1)

        vbox = wx.BoxSizer(wx.VERTICAL)
        desc = wx.StaticText(self, -1,
                            "ScrolledPanel extends wx.ScrolledWindow,
adding all "
                            "the necessary bits to set up scroll
handling for you.\n\n"
                            "Here are three fixed size examples of its
use. The "
                            "demo panel for this sample is also using
it -- the \nwxStaticLine "
                            "below is intentionally made too long so a
scrollbar will be "
                            "activated."
                            )
        desc.SetForegroundColour("Blue")
        vbox.Add(desc, 0, wx.ALIGN_LEFT|wx.ALL, 5)
        vbox.Add(wx.StaticLine(self, -1, size=(1024,-1)), 0, wx.ALL,
5)
        vbox.Add((20,20))

        words = text.split()

        panel1 = scrolled.ScrolledPanel(self, -1, size=(140, 300),
                                 style = wx.TAB_TRAVERSAL|
wx.SUNKEN_BORDER, name="panel1" )
        fgs1 = wx.FlexGridSizer(cols=2, vgap=4, hgap=4)

        for word in words:
            label = wx.StaticText(panel1, -1, word+":")

            # A test for scrolling with a too big control
            #if word == "three":
            # tc = wx.TextCtrl(panel1, -1, word, size=(150,-1))
            #else:
            # tc = wx.TextCtrl(panel1, -1, word, size=(50,-1))

            tc = wx.TextCtrl(panel1, -1, word, size=(50,-1))

            fgs1.Add(label, flag=wx.ALIGN_RIGHT |
wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=10)
            fgs1.Add(tc, flag=wx.RIGHT, border=10)

        panel1.SetSizer( fgs1 )
        panel1.SetAutoLayout(1)
        panel1.SetupScrolling()

        panel2 = scrolled.ScrolledPanel(self, -1, size=(350, 50),
                                 style = wx.TAB_TRAVERSAL|
wx.SUNKEN_BORDER, name="panel2")
        panel3 = scrolled.ScrolledPanel(self, -1, size=(200,100),
                                 style = wx.TAB_TRAVERSAL|
wx.SUNKEN_BORDER, name="panel3")

        fgs2 = wx.FlexGridSizer(cols=25, vgap=4, hgap=4)
        fgs3 = wx.FlexGridSizer(cols=5, vgap=4, hgap=4)

        for i in range(len(words)):
            word = words[i]
            if i % 5 != 4:
                label2 = wx.StaticText(panel2, -1, word)
                fgs2.Add(label2, flag=wx.ALIGN_LEFT |
wx.ALIGN_CENTER_VERTICAL)
                label3 = wx.StaticText(panel3, -1, word)
                fgs3.Add(label3, flag=wx.ALIGN_LEFT |
wx.ALIGN_CENTER_VERTICAL)
            else:
                tc2 = wx.TextCtrl(panel2, -1, word, size=(50,-1))
                fgs2.Add(tc2, flag=wx.LEFT, border=5)
                tc3 = wx.TextCtrl(panel3, -1, word )
                fgs3.Add(tc3, flag=wx.LEFT, border=5)

        panel2.SetSizer( fgs2 )
        panel2.SetAutoLayout(1)
        panel2.SetupScrolling(scroll_y = False)

        panel3.SetSizer( fgs3 )
        panel3.SetAutoLayout(1)
        panel3.SetupScrolling()

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add((20,20))
        hbox.Add(panel1, 0, wx.FIXED_MINSIZE)
        hbox.Add((40, 10))

        vbox2 = wx.BoxSizer(wx.VERTICAL)
        vbox2.Add(panel2, 0, wx.FIXED_MINSIZE)
        vbox2.Add((20, 50))

        vbox2.Add(panel3, 0, wx.FIXED_MINSIZE)
        vbox2.Add((20, 10))
        hbox.Add(vbox2)

        vbox.Add(hbox, 0)
        self.SetSizer(vbox)
        self.SetAutoLayout(1)
        self.SetupScrolling()

#----------------------------------------------------------------------

def runTest(frame, nb, log):
    win = TestPanel(nb, log)
    return win

#----------------------------------------------------------------------

overview = """<html><body>
ScrolledPanel fills a "hole" in the implementation of
wx.ScrolledWindow,
providing automatic scrollbar and scrolling behavior and the tab
traversal
mangement that wx.ScrolledWindow lacks.
</body></html>
"""
if __name__ == '__main__':
    import sys,os
    import run
    run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])

I just looked at the docs for ScrolledPanel, which are here:

http://www.wxpython.org/docs/api/wx.lib.scrolledpanel.ScrolledPanel-class.html

and found this:

ScrollChildIntoView(self, child)
Scrolls the panel such that the specified child window is in view.

Sounds like what you need, no? Just keep track of your textCtrls by
an index and there you go.

Che

···

On Fri, Jan 1, 2010 at 5:03 PM, RLRandallx <robinlrandall@gmail.com> wrote:

Hi All,

Below is a sample of a wx.ScrolledPanel. The vertical grid has 25
wx.TextCtrl fields.
Is there ANY way to automatically (by pressing a button) move the
scroll so the 10th field is the first one visible? The 20th field?
And back to the 0th field. I just want to save the user time
scrolling and possibly passing the field they want. Maybe this is an
enhancement request but it seems like a feature wx.ScrolledPanel could
use if it does not already have it.

OK attached is a simple case of what I want to see. I don't see a way
to attach a file here, so I have to cut and paste. I call it SPanel.py
and it should run in the wx.Python demo directory.

Forgeting the Focus problem it works fine as far as the scrolling
goes, but when I press the keys to "jump" around to different "topics"
it prints the debug numbers but does not seem to move to the child
"windows/TextCtrls". I expected pressing "Topic 1" to bring me to the
top. Topic 2 should have gone to the middle and Topic 3 should have
jumped near the end of the scroll. Removing "wx.CallAfter" did
nothing. Any new ideas? And can someone tell me how to do the without
using global variables?
If I do not use globals it says "Global variable x undefined".
RLRandallx

···

#----------------------------------------------------------------------
import wx
import wx.lib.scrolledpanel as scrolled

#----------------------------------------------------------------------

text = "one two buckle my shoe three four shut the door five six pick
up sticks seven eight lay them straight nine ten big fat hen"

class TestPanel(scrolled.ScrolledPanel):
    def __init__(self, parent, log):
        self.log = log
        scrolled.ScrolledPanel.__init__(self, parent, -1)
        global b1,b2,b3
        global tc
        tc=
        vbox = wx.BoxSizer(wx.VERTICAL)
        desc = wx.StaticText(self, -1,
                            "ScrolledPanel extends wx.ScrolledWindow,
adding all "
                            "the necessary bits to set up scroll
handling for you.\n\n"
                            "Here are three fixed size examples of its
use. The "
                            "demo panel for this sample is also using
it -- the \nwxStaticLine "
                            "below is intentionally made too long so a
scrollbar will be "
                            "activated."
                            )
        desc.SetForegroundColour("Blue")
        vbox.Add(desc, 0, wx.ALIGN_LEFT|wx.ALL, 5)
        vbox.Add(wx.StaticLine(self, -1, size=(1024,-1)), 0, wx.ALL,
5)
        vbox.Add((20,20))

        words = text.split()

        panel1 = scrolled.ScrolledPanel(self, -1, size=(140, 300),
                                 style = wx.TAB_TRAVERSAL|
wx.SUNKEN_BORDER, name="panel1" )
        fgs1 = wx.FlexGridSizer(cols=2, vgap=4, hgap=4)
        i=0
        for word in words :

            if ((i==0) or (i==10) or (i==20)) :
              label = wx.StaticText(panel1, -1, "Topic "+word+":")
            else:
              label = wx.StaticText(panel1, -1, " "+word+":")

            tc.append("")
            tc[i] = wx.TextCtrl(panel1, -1, word, size=(50,-1))

            fgs1.Add(label, flag=wx.ALIGN_RIGHT |
wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=10)
            fgs1.Add(tc[i], flag=wx.RIGHT, border=10)
            i += 1

        panel1.SetSizer( fgs1 )
        panel1.SetAutoLayout(1)
        panel1.SetupScrolling()

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add((20,20))
        hbox.Add(panel1, 0, wx.FIXED_MINSIZE)
        hbox.Add((40, 10))

        vbox.Add(hbox, 0)

        fgs4 = wx.FlexGridSizer(cols=1, vgap=4, hgap=6)
#--------------------------------------------------------------------
  fgs4.Add((25,25))
  self.b1 = b1 = wx.Button(self, 5, "Topic 1", style=wx.BU_EXACTFIT)
  self.b1.Bind(wx.EVT_BUTTON, self.OnClick1)
  b1.SetToolTipString("This jumps to the first Topic.")
  fgs4.Add(b1, 0, wx.ALL, 0)

#---------------------------------------------------------------------
  fgs4.Add((25,25))
  self.b2 = b2 = wx.Button(self, 5, "Topic 2", style=wx.BU_EXACTFIT)
  self.b2.Bind(wx.EVT_BUTTON, self.OnClick2)
  b2.SetToolTipString("This jumps to the Second Topic.")
  fgs4.Add(b2, 0, wx.ALL, 0)

#-----------------------------------------------------------------------
  fgs4.Add((25,25))
        self.b3 = b3 = wx.Button(self, 5, "Topic 3",
style=wx.BU_EXACTFIT)
  self.b3.Bind(wx.EVT_BUTTON, self.OnClick3)
  b3.SetToolTipString("This jumps to the third Topic.")
  fgs4.Add(b3, 0, wx.ALL, 0)

  vbox.Add(fgs4,0)

        self.SetSizer(vbox)
        self.SetAutoLayout(1)
        self.SetupScrolling()
#-----------------------------------------------------------
    def OnClick1(self, event):
        global b1
        global tc
        if (b1.GetLabel() == "Topic 1") :
           print "1"
           wx.CallAfter(self.ScrollChildIntoView, tc[1])

    def OnClick2(self, event):
        global b2
        global tc
        if (b2.GetLabel() == "Topic 2") :
           print "2"
           wx.CallAfter(self.ScrollChildIntoView, tc[10])

    def OnClick3(self, event):
        global b3
        global tc
        if (b3.GetLabel() == "Topic 3") :
           print "3"
           wx.CallAfter(self.ScrollChildIntoView, tc[20])
#----------------------------------------------------------------------

def runTest(frame, nb, log):
    win = TestPanel(nb, log)
    return win

#----------------------------------------------------------------------

overview = """<html><body>
ScrolledPanel fills a "hole" in the implementation of
wx.ScrolledWindow,
providing automatic scrollbar and scrolling behavior and the tab
traversal
mangement that wx.ScrolledWindow lacks.
</body></html>
"""

if __name__ == '__main__':
    import sys,os
    import run
    run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])

On Jan 1, 2:16 pm, C M <cmpyt...@gmail.com> wrote:

On Fri, Jan 1, 2010 at 5:03 PM, RLRandallx <robinlrand...@gmail.com> wrote:
> Hi All,

> Below is a sample of a wx.ScrolledPanel. The vertical grid has 25
> wx.TextCtrl fields.
> Is there ANY way to automatically (by pressing a button) move the
> scroll so the 10th field is the first one visible? The 20th field?
> And back to the 0th field. I just want to save the user time
> scrolling and possibly passing the field they want. Maybe this is an
> enhancement request but it seems like a feature wx.ScrolledPanel could
> use if it does not already have it.

I just looked at the docs for ScrolledPanel, which are here:

wxPython API Documentation — wxPython Phoenix 4.2.2 documentation

and found this:

ScrollChildIntoView(self, child)
Scrolls the panel such that the specified child window is in view.

Sounds like what you need, no? Just keep track of your textCtrls by
an index and there you go.

Che

OK attached is a simple case of what I want to see. I don't see a way
to attach a file here, so I have to cut and paste.

If you are subscribed to the wxPython email list (as opposed to viewing
it on Google Groups), you can just send the list an email and include an
attachment. I find that works far better than through Google Groups.
See here:
http://www.wxpython.org/maillist.php

I call it SPanel.py
and it should run in the wx.Python demo directory.

If you can, when including small runnable sample apps, you should
send apps that don't need to be in a particular directory to work. For
the purposes of this sample app, you didn't need the run module, e.g.
Also, remember to mention your platform and version.

Forgeting the Focus problem

What's the focus problem?

it works fine as far as the scrolling
goes, but when I press the keys

You mean the buttons; keys are the keys on the keyboard, or
keys in a dictionary (minor point but might as well stay clear with the
right terms; just makes communication easier for the purpose of
learning/fixing your code).

to "jump" around to different "topics"
it prints the debug numbers but does not seem to move to the child
"windows/TextCtrls". I expected pressing "Topic 1" to bring me to the
top. Topic 2 should have gone to the middle and Topic 3 should have
jumped near the end of the scroll.

This is because you were calling ScrollChildIntoView() on the
big scrolledpanel (AKA self, AKA the TestPanel itself), not the one
with your textCtrls (AKA panel1). In your edited code, I have changed
that and it works (attached).

Removing "wx.CallAfter" did
nothing.

I can't see why you'd need to use CallAfter in this case in the first
place. I'd get it out of there for simplicity's sake.

Any new ideas?

Based on your code, you just need to slow down, go back to the basics
of Python/wxPython and keep things simpler. Some of what is in my sample
code (scroll_into_view.py) may give you some ideas.

And can someone tell me how to do the without
using global variables?

Yes, you can either:

1) Pass names around to those objects that need them when you call that object.
2) Use self.name to make that name "global" throughout that class.

In your code, you could have called the list self.tc, and then it would have
been available to all the event handlers. By using the prepended self, you
are saying it is an object belonging to that class.

If I do not use globals it says "Global variable x undefined".

Right, because it needs to know about name x by some means.
(1) and (2) above would be the best ways to inform the class what x is.
Really, (1) is preferred if you are not going to need to often make use of
that name in many parts of your code.

See your attached code (testScrolled_original_edited.py) and then my code
(scroll_into_view.py) that shows this same idea with a much simpler sample
app (and one that runs from any folder).

HTH,
Che

testScrolled_original_edited.py (4.12 KB)

scroll_into_view.py (2.68 KB)

···

On Sun, Jan 3, 2010 at 4:05 PM, RLRandallx <robinlrandall@gmail.com> wrote: