dynamically appearing scroll bars?

Hi am a newbie trying to write a GUI where I am dynamically generating some controlls which I will hook up later.
I am having a hard time understanding the interaction between scrollbars and sizers .

In the code below , if you click on the “Add plate” button more than 10 times , the Add plate buttons disappears under the Grey panel below it, as I want it to .

Now if I want a scrollbar to appear that allows you to see the contents of the yellow panel. How can I achieve this . The scrollbar can scroll only this panel or the entire parent MaFrame .

Can you point me in the direction of how to implement a scroll bar that only shows up when you need it ? And a scrolbar that only appears for a child window .

Thanks a lot for your help
Hari

Code example ( works well on Linux )

To change this template, choose Tools | Templates

and open the template in the editor.

import wx

class MaFrame(wx.Frame):

def __init__(self,*args,**kwds):
    kwds["size"] = (780,400)
    wx.Frame.__init__(self,*args, **kwds)
    self.scroll=wx.ScrolledWindow(self,-1)
    x = 20
    y = 20
    self.CreateStatusBar(1,0)
    kwds["style"] = wx.THICK_FRAME
    frame_sizer = wx.BoxSizer(wx.VERTICAL)
    self.SetSizer(frame_sizer)
   
def do_layout(self):
    self.Layout()
    self.scroll.SetScrollbars(0,20,0,self.GetSizer().GetMinSize()[1])

self.Fit()

class PlatePanel(wx.Panel):

Yellow tint Panel

num_subplates = 1
subplate_array_sizers = []
plate_customizer_dict = {1:("A1","H12"),4:("A1","D6","A7","D12","D1","H6","D7","H12")}
def __init__(self,*args,**kwds):
    wx.Panel.__init__(self,*args,**kwds)
    self.SetBackgroundColour(wx.Colour(252,255,225))
    self.platelabel = wx.StaticText(parent=self,id=-1,label="Plate %s" % self.num_subplates)
    self.text_ctrl_1 = wx.TextCtrl(self, -1, "A1",(50,-1))
    self.text_ctrl_2 = wx.TextCtrl(self,-1,"A12",(50,-1))
    self.plate_add_button = wx.Button(self,label="AddPlate")
    self.do_connections()
    self.do_layout()
   
def do_layout(self):
    self.sizer_top = wx.BoxSizer(wx.VERTICAL)
    sizer_widgets = wx.BoxSizer(wx.HORIZONTAL)
    sizer_widgets.Add(self.platelabel,1,wx.ALIGN_LEFT)
    sizer_widgets.Add(self.text_ctrl_1,1,wx.ALIGN_RIGHT)
    sizer_widgets.Add(self.text_ctrl_2,1,wx.ALIGN_RIGHT)
    self.sizer_top.Add(sizer_widgets,0,wx.EXPAND)
    self.subplate_array_sizers.append(sizer_widgets)
    self.sizer_top.Add(self.plate_add_button,0,wx.ALIGN_RIGHT|wx.ALL,10)
    self.SetSizer(self.sizer_top)
    self.sizer_top.Layout()
    self.sizer_top.Fit(self) 

def do_connections(self):
    self.Bind(wx.EVT_BUTTON,self.add_plate_def,self.plate_add_button)
   

def add_plate_def(self,event):
    self.GetParent().GetStatusBar().SetStatusText("Adding Plate %s" % self.num_subplates)
    self.num_subplates = self.num_subplates + 1
    # Get the add plate Button and hold on to it
    for sizers in self.subplate_array_sizers:
        self.sizer_top.Detach(sizers)

    self.sizer_top.Detach(self.plate_add_button)
    platelabel = wx.StaticText(parent=self,id=-1,label="Plate %s" % self.num_subplates )
    text_ctrl_1 = wx.TextCtrl(self, -1, "",(50,-1))
    text_ctrl_2 = wx.TextCtrl(self,-1,"",(50,-1))
    sizer_widgets = wx.BoxSizer(wx.HORIZONTAL)
    sizer_widgets.Add(platelabel,1,wx.ALIGN_LEFT)
    sizer_widgets.Add(text_ctrl_1,1,wx.ALIGN_RIGHT)
    sizer_widgets.Add(text_ctrl_2,1,wx.ALIGN_RIGHT)
    self.subplate_array_sizers.append(sizer_widgets)

    for w in self.subplate_array_sizers:
        self.sizer_top.Add(w,0,wx.EXPAND)
       
       
    self.sizer_top.Add(self.plate_add_button,0,wx.ALIGN_RIGHT|wx.ALL,10)
    #self.sizer_top.Add(self.plate_add_button,1,wx.RIGHT|wx.ALIGN_BOTTOM,10)
    self.sizer_top.Layout()
    self.sizer_top.Fit(self)
    self.has_config()
    self.GetParent().do_layout()
    #self.GetParent().Fit()

   
def has_config(self):
    # Local variable to run along plate config tuple using self.plate_customizer_dict[self.num_subplates][scanner]
    scanner = 0
    sample_textctrl_class = wx.TextCtrl(None,-1,"",(50,-1))
    if self.plate_customizer_dict.has_key(self.num_subplates):
        for w in self.subplate_array_sizers:
            for possible_plate in w.GetChildren():
                if type(possible_plate.GetWindow()) == type(sample_textctrl_class):
                    self.GetParent().GetStatusBar().SetStatusText("settingplate boundaries automatic done")
                    possible_plate.GetWindow().SetValue(self.plate_customizer_dict[self.num_subplates][scanner])
                    scanner = scanner + 1

class ComponentPanel(wx.Panel):
num_components = 0
component_array_sizers = []

def __init__(self,*args, **kwds):
    wx.Panel.__init__(self,*args,**kwds)
    self.SetBackgroundColour(wx.Colour(222,221,220))
    self.fileselector_button = wx.Button(parent=self,id=-1,label="ComponentFile")
    self.componentlabel = wx.StaticText(parent=self,id=-1,label="Component %s" % self.num_components)
    self.text_ctrl_1 = wx.TextCtrl(self, -1, "",(50,-1))
    self.text_ctrl_2 = wx.TextCtrl(self,-1,"",(50,-1))
    self.do_connections()
    self.do_layout()

def do_connections(self):
    self.Bind(wx.EVT_BUTTON , self.get_and_parse_file,self.fileselector_button)
    pass

def do_layout(self):
    self.sizer_top = wx.BoxSizer(wx.VERTICAL)
    self.sizer_top.Add(self.fileselector_button,0,wx.ALIGN_LEFT)
    sizer_widgets = wx.BoxSizer(wx.HORIZONTAL)
    sizer_widgets.Add(self.componentlabel,1,wx.ALIGN_LEFT)
    sizer_widgets.Add(self.text_ctrl_1,1,wx.ALIGN_RIGHT)
    sizer_widgets.Add(self.text_ctrl_2,1,wx.ALIGN_RIGHT)
    self.sizer_top.Add(sizer_widgets,0,wx.EXPAND)
    self.component_array_sizers.append(sizer_widgets)
    self.SetSizer(self.sizer_top)

def get_and_parse_file(self,event):
    self.component_file = open(str(wx.FileSelector("Component File \"*.csv\" format","","","csv","*.csv")), "r")
    for line in self.component_file:
        print line

if name==“main”:
app = wx.PySimpleApp()
maframe = MaFrame(parent=None,title=“GZilla”)
plate_panel = PlatePanel(parent=maframe)
maframe.GetSizer().Add(plate_panel,4,wx.EXPAND)
component_panel = ComponentPanel(parent=maframe)
maframe.GetSizer().Add(component_panel,1,wx.ALIGN_BOTTOM|wx.EXPAND)
maframe.Layout()
maframe.Show()
app.MainLoop()

hari jayaram wrote:

Hi am a newbie trying to write a GUI where I am dynamically generating some controlls which I will hook up later.
I am having a hard time understanding the interaction between scrollbars and sizers .

In the code below , if you click on the "Add plate" button more than 10 times , the Add plate buttons disappears under the Grey panel below it, as I want it to .

Now if I want a scrollbar to appear that allows you to see the contents of the yellow panel. How can I achieve this . The scrollbar can scroll only this panel or the entire parent MaFrame .

Can you point me in the direction of how to implement a scroll bar that only shows up when you need it ? And a scrolbar that only appears for a child window .

Derive your PlatePanel class from wx.ScrolledWindow instead of wx.Panel. You may also want to look at wx.lib.scrolledpanel.

           def has_config(self):
        # Local variable to run along plate config tuple using self.plate_customizer_dict[self.num_subplates][scanner]
        scanner = 0
        sample_textctrl_class = wx.TextCtrl(None,-1,"",(50,-1))

BTW, creating a non top-level window with no parent is illegal. Even if it worked you wouldn't want to do it like this because you'll end up with lots of textctrl widgets that never get destroyed.

        if self.plate_customizer_dict.has_key(self.num_subplates):
            for w in self.subplate_array_sizers:
                for possible_plate in w.GetChildren():
                    if type(possible_plate.GetWindow()) == type(sample_textctrl_class):

There is a *much* easier and safer way to do this, with no need to create an instance of the widget to compare types. Python has the isinstance built-in that can be used like this:

  if isinstance(possible_plate.GetWindow(), wx.TextCtrl): ...

if you want to ensure an exact type and not allow subclasses, then you can just compare the types themselves:

  if type(possible_plate.GetWindow()) is wx.TextCtrl: ...
or
  if possible_plate.GetWindow().__class__ is wx.TextCtrl: ...

···

--
Robin Dunn
Software Craftsman

Hi Robin and everyone…

Thanks a tonne Robin for your great and extremely helpful and timely reply.
I have used the wx.lib.scrolled panel class as demo-ed here and got exactly the behavior I wanted…thanks for that tip

The modification is posted below…

The class does work like magic …I was starting to sweat a little thinking of how to calculate the scroll positions after looking at other scrolling examples …so much to learn…

Thanks again
Hari

Working code follows :

To change this template, choose Tools | Templates

and open the template in the editor.

import wx
import wx.lib.scrolledpanel as myscrolledpanel
class MaFrame(wx.Frame):

def __init__(self,*args,**kwds):
    kwds["size"] = (780,400)
    wx.Frame.__init__(self,*args, **kwds)
    self.CreateStatusBar(1,0)
    kwds["style"] = wx.THICK_FRAME
    frame_sizer = wx.BoxSizer(wx.VERTICAL)
    self.SetSizer(frame_sizer)
   
def do_layout(self):
    self.Layout()

class PlatePanel(myscrolledpanel.ScrolledPanel):
num_subplates = 1
subplate_array_sizers =
plate_customizer_dict = {1:(“A1”,“H12”),4:(“A1”,“D6”,“A7”,“D12”,“D1”,“H6”,“D7”,“H12”)}
def init(self,*args,**kwds):
myscrolledpanel.ScrolledPanel.init(self,*args,**kwds)

    self.SetBackgroundColour(wx.Colour(252,255,225))
    self.platelabel = wx.StaticText(parent=self,id=-1,label="Plate %s" % self.num_subplates)
    self.text_ctrl_1 = wx.TextCtrl(self, -1, "A1",(50,-1))
    self.text_ctrl_2 = wx.TextCtrl(self,-1,"A12",(50,-1))
    self.plate_add_button = wx.Button(self,label="Add Plate")
    self.do_connections()
    self.do_layout()
    self.SetupScrolling()
   
def do_layout(self):
    self.sizer_top = wx.BoxSizer(wx.VERTICAL)
    sizer_widgets = wx.BoxSizer(wx.HORIZONTAL)
    sizer_widgets.Add(self.platelabel,1,wx.ALIGN_LEFT)
    sizer_widgets.Add(self.text_ctrl_1,1,wx.ALIGN_RIGHT)
    sizer_widgets.Add(self.text_ctrl_2,1,wx.ALIGN_RIGHT)
    self.sizer_top.Add(sizer_widgets,0,wx.EXPAND)
    self.subplate_array_sizers.append(sizer_widgets)
    self.sizer_top.Add(self.plate_add_button,0,wx.ALIGN_RIGHT|wx.ALL,10)
    self.SetSizer(self.sizer_top)
    self.sizer_top.Layout()
    self.sizer_top.FitInside(self)

def do_connections(self):
    self.Bind(wx.EVT_BUTTON,self.add_plate_def,self.plate_add_button)
   

def add_plate_def(self,event):
    self.GetParent().GetStatusBar().SetStatusText("Adding Plate %s" % self.num_subplates)
    self.num_subplates = self.num_subplates + 1
    # Get the add plate Button and hold on to it
    for sizers in self.subplate_array_sizers:
        self.sizer_top.Detach(sizers)

    self.sizer_top.Detach(self.plate_add_button)
    platelabel = wx.StaticText(parent=self,id=-1,label="Plate %s" % self.num_subplates )
    text_ctrl_1 = wx.TextCtrl(self, -1, "",(50,-1))
    text_ctrl_2 = wx.TextCtrl(self,-1,"",(50,-1))
    sizer_widgets = wx.BoxSizer(wx.HORIZONTAL)
    sizer_widgets.Add(platelabel,1,wx.ALIGN_LEFT)
    sizer_widgets.Add(text_ctrl_1,1,wx.ALIGN_RIGHT)
    sizer_widgets.Add(text_ctrl_2,1,wx.ALIGN_RIGHT)
    self.subplate_array_sizers.append(sizer_widgets)
    for w in self.subplate_array_sizers:
        self.sizer_top.Add(w,0,wx.EXPAND)
    self.sizer_top.Add(self.plate_add_button,0,wx.ALIGN_RIGHT|wx.ALL,10)
    #self.sizer_top.Add(self.plate_add_button,1,wx.RIGHT|wx.ALIGN_BOTTOM,10)
    self.sizer_top.Layout()
    self.sizer_top.Fit(self)
    self.has_config()
    self.GetParent().do_layout()
    #self.GetParent().Fit()

   
def has_config(self):
    # Local variable to run along plate config tuple using self.plate_customizer_dict[self.num_subplates][scanner]
    scanner = 0
    if self.plate_customizer_dict.has_key(self.num_subplates):
        for w in self.subplate_array_sizers:
            for possible_plate in w.GetChildren():
                if isinstance(possible_plate.GetWindow(), wx.TextCtrl):
                    self.GetParent().GetStatusBar().SetStatusText("settingplate boundaries automatic done")
                    possible_plate.GetWindow().SetValue(self.plate_customizer_dict[self.num_subplates][scanner])
                    scanner = scanner + 1

class ComponentPanel(wx.Panel):
num_components = 0
component_array_sizers =

def __init__(self,*args, **kwds):
    wx.Panel.__init__(self,*args,**kwds)
    self.SetBackgroundColour(wx.Colour(222,221,220))
    self.fileselector_button = wx.Button(parent=self,id=-1,label="ComponentFile")
    self.componentlabel = wx.StaticText(parent=self,id=-1,label="Component %s" % self.num_components)
    self.text_ctrl_1 = wx.TextCtrl(self, -1, "",(50,-1))
    self.text_ctrl_2 = wx.TextCtrl(self,-1,"",(50,-1))
    self.do_connections()
    self.do_layout()

def do_connections(self):
    self.Bind(wx.EVT_BUTTON , self.get_and_parse_file,self.fileselector_button)
    pass

def do_layout(self):
    self.sizer_top = wx.BoxSizer(wx.VERTICAL)
    self.sizer_top.Add(self.fileselector_button,0,wx.ALIGN_LEFT)
    sizer_widgets = wx.BoxSizer(wx.HORIZONTAL)
    sizer_widgets.Add(self.componentlabel,1,wx.ALIGN_LEFT)
    sizer_widgets.Add(self.text_ctrl_1,1,wx.ALIGN_RIGHT)
    sizer_widgets.Add(self.text_ctrl_2,1,wx.ALIGN_RIGHT)
    self.sizer_top.Add(sizer_widgets,0,wx.EXPAND)
    self.component_array_sizers.append(sizer_widgets)
    self.SetSizer(self.sizer_top)

def get_and_parse_file(self,event):
    self.component_file = open(str(wx.FileSelector("Component File \"*.csv\" format","","","csv","*.csv")), "r")
    for line in self.component_file:
        print line

if name==“main”:
app = wx.PySimpleApp()
maframe = MaFrame(parent=None,title=“GZilla”)
plate_panel = PlatePanel(parent=maframe)
maframe.GetSizer().Add(plate_panel,4,wx.EXPAND)
component_panel = ComponentPanel(parent=maframe)
maframe.GetSizer().Add(component_panel,0,wx.ALIGN_BOTTOM|wx.EXPAND)
maframe.Layout()
maframe.Show()
app.MainLoop()

···

On Thu, Jul 16, 2009 at 3:40 PM, Robin Dunn robin@alldunn.com wrote:

hari jayaram wrote:

Hi am a newbie trying to write a GUI where I am dynamically generating

some controlls which I will hook up later.

I am having a hard time understanding the interaction between scrollbars

and sizers .

In the code below , if you click on the “Add plate” button more than 10

times , the Add plate buttons disappears under the Grey panel below it,

as I want it to .

Now if I want a scrollbar to appear that allows you to see the contents

of the yellow panel. How can I achieve this . The scrollbar can scroll

only this panel or the entire parent MaFrame .

Can you point me in the direction of how to implement a scroll bar that

only shows up when you need it ? And a scrolbar that only appears for a

child window .

Derive your PlatePanel class from wx.ScrolledWindow instead of wx.Panel.

You may also want to look at wx.lib.scrolledpanel.

def has_config(self):
    # Local variable to run along plate config tuple using

self.plate_customizer_dict[self.num_subplates][scanner]

    scanner = 0
    sample_textctrl_class = wx.TextCtrl(None,-1,"",(50,-1))

BTW, creating a non top-level window with no parent is illegal. Even if

it worked you wouldn’t want to do it like this because you’ll end up

with lots of textctrl widgets that never get destroyed.

    if self.plate_customizer_dict.has_key(self.num_subplates):
        for w in self.subplate_array_sizers:
            for possible_plate in w.GetChildren():
                if type(possible_plate.GetWindow()) ==

type(sample_textctrl_class):

There is a much easier and safer way to do this, with no need to

create an instance of the widget to compare types. Python has the

isinstance built-in that can be used like this:

    if isinstance(possible_plate.GetWindow(), wx.TextCtrl): ...

if you want to ensure an exact type and not allow subclasses, then you

can just compare the types themselves:

    if type(possible_plate.GetWindow()) is wx.TextCtrl: ...

or

    if possible_plate.GetWindow().__class__ is wx.TextCtrl: ...

Robin Dunn

Software Craftsman

http://wxPython.org