dynamic widget addition/deletion

Greetings:

I am trying to add and remove widgets dynamically in response to button

events, e.g. creating an arbitrary number of TextCtrl widgets. This is

easily accomplished; however, I can’t get the resizing to work correctly

or consistently, and when the panel on which the widgets are placed is

a ScrolledWindow, large regions of the panel are lost when redrawing if the

viewable area is less than the total panel size. I’ve appended the most

minimal example I could come up with below this message. I’ve tried

fiddling with Fit() and the various scrollarea methods, but no luck so

far. Refreshing and resizing the frame after each widget is drawn does

keep the entire panel accessible, but this causes other problems instead.

For what it’s worth, when I’m doing this in the context of a modal dialog

window instead of a Frame/ScrolledWindow combination, the window size is

not always re-adjusted properly (sometimes it resizes, sometimes it doesn’t;

this doesn’t seem to follow any pattern).

Other problems:

  1. Sometimes it simply refuses to redraw the scrollbars. I’ve confirmed

that the size obtained by GetSizeTuple() is what it should be, but

SetScrollbars() is not working. (Some of the time, that is.) Here’s

what this looks like:

http://ucxray.berkeley.edu/~nat/img/wx_cutoff.png

  1. I seem to be left with ghost widgets when I try to delete a section.

I’m actually only deleting the sizer that contains them, because this should

destroy the widgets too. (At any rate, if I try to Destroy() them

individually, I get a wx._core.PyDeadObjectError. If I try to destroy

the widgets but not the sizer, it gets weirder.)

http://ucxray.berkeley.edu/~nat/img/wx_ghost.png

I’ve been doing most of my development on Mac (native widgets, not X11), but

I get similar results with Linux. (Slightly worse, actually - it segfaults

when I close the program.) We appear to be using wxPython 2.8.0 on both

systems (wrapping GTK2 on Linux).

Any advice would be greatly appreciated.

#— begin code

import wx

class testApp (wx.App) :

def OnInit (self) :

frame = testFrame(self)

self.SetTopWindow(frame)

frame.Show()

return True

class testFrame (wx.Frame) :

def init (self, parent) :

wx.Frame.init(self, None, -1, “Test frame”)

self.widget_area = scrollArea(self)

self.sizer = wx.BoxSizer(wx.VERTICAL)

self.sizer.Add(self.widget_area, 1, wx.EXPAND, 5)

self.SetAutoLayout(1)

self.SetSizer(self.sizer)

self.Fit()

def resetLayout (self) :

pass

class scrollArea (wx.ScrolledWindow) :

def init (self, parent) :

wx.ScrolledWindow.init(self, parent, -1, style=wx.SUNKEN_BORDER)

self.panel = self

self.main_sizer = wx.BoxSizer(wx.VERTICAL)

self.panel.SetSizer(self.main_sizer)

self.frame = parent

self.widget_handler = widgetCopier(self)

self.main_sizer.Add(self.widget_handler)

self.widget_handler.newCopy()

self.resetLayout()

def resetLayout (self) :

self.main_sizer.Fit(self.panel)

(w, h) = self.panel.GetSizeTuple()

self.SetScrollbars(1, 1, w, h)

self.frame.resetLayout()

class widgetCopier (wx.BoxSizer) :

current_sizer = None

widget_stack = []

def init (self, parent) :

wx.BoxSizer.init(self, wx.VERTICAL)

self.parent = parent

add_btn = wx.Button(self.parent.panel, -1, “Add copy”)

del_btn = wx.Button(self.parent.panel, -1, “Delete copy”)

self.btns = wx.BoxSizer(wx.HORIZONTAL)

self.btns.Add(del_btn, 0, wx.RIGHT, 5)

self.btns.Add(add_btn)

self.parent.frame.Bind(wx.EVT_BUTTON, self.newCopy, add_btn)

self.parent.frame.Bind(wx.EVT_BUTTON, self.deleteCopy, del_btn)

self.newCopy()

def setButtons (self) :

if self.current_sizer != None :

self.current_sizer.Add(self.btns, 0, wx.ALL, 5)

def detachButtons (self) :

if self.current_sizer != None :

self.current_sizer.Detach(self.btns)

def newCopy (self, event=None) :

self.detachButtons()

new_copy = wx.BoxSizer(wx.HORIZONTAL)

new_copy_widget = wx.CheckBox(self.parent.panel, -1,

“This is checkbox %d” % (len(self.widget_stack) + 1))

new_copy.Add(new_copy_widget, 0, wx.ALL, 5)

self.Add(new_copy, 0, wx.ALL, 5)

self.widget_stack.append(new_copy)

self.current_sizer = new_copy

self.setButtons()

self.Layout()

self.parent.resetLayout()

def deleteCopy (self, event=None) :

self.detachButtons()

if len(self.widget_stack) > 0 :

last_copy = self.widget_stack.pop()

self.Detach(last_copy)

last_copy.Destroy()

if len(self.widget_stack) > 0 :

self.current_sizer = self.widget_stack[-1]

else :

self.current_sizer = None

self.setButtons()

self.Layout()

self.parent.resetLayout()

if name == “main” :

prog = testApp(0)

prog.MainLoop()

#—end

Nathaniel Echols wrote:

Greetings:
   I am trying to add and remove widgets dynamically in response to button
events, e.g. creating an arbitrary number of TextCtrl widgets. This is
easily accomplished; however, I can't get the resizing to work correctly
or consistently, and when the panel on which the widgets are placed is
a ScrolledWindow, large regions of the panel are lost when redrawing if the
viewable area is less than the total panel size. I've appended the most
minimal example I could come up with below this message. I've tried
fiddling with Fit() and the various scrollarea methods, but no luck so
far. Refreshing and resizing the frame after each widget is drawn does
keep the entire panel accessible, but this causes other problems instead.
   For what it's worth, when I'm doing this in the context of a modal dialog window instead of a Frame/ScrolledWindow combination, the window size is
not always re-adjusted properly (sometimes it resizes, sometimes it doesn't;
this doesn't seem to follow any pattern).
   Other problems:
  1. Sometimes it simply refuses to redraw the scrollbars. I've confirmed
that the size obtained by GetSizeTuple() is what it should be, but
SetScrollbars() is not working. (Some of the time, that is.) Here's
what this looks like:
    http://ucxray.berkeley.edu/~nat/img/wx_cutoff.png
  2. I seem to be left with ghost widgets when I try to delete a section.
I'm actually only deleting the sizer that contains them, because this should
destroy the widgets too. (At any rate, if I try to Destroy() them individually, I get a wx._core.PyDeadObjectError. If I try to destroy
the widgets but not the sizer, it gets weirder.)
    http://ucxray.berkeley.edu/~nat/img/wx_ghost.png

I've been doing most of my development on Mac (native widgets, not X11), but
I get similar results with Linux. (Slightly worse, actually - it segfaults
when I close the program.) We appear to be using wxPython 2.8.0 on both
systems (wrapping GTK2 on Linux).

Any advice would be greatly appreciated.

Your Fit's are resizing the windows when you probably don't want them to. Try just using Layout instead.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Hi Robin,

    Can I place a keypress into any place and cause a set focus to another
part of the frame?

    What I mean is can I jump around and move focus to another button or
location just by clicking on a button or pressing a key?

    If so, how do I do it? I did a search and could find little, except a
bug issue you asked someone to post a while ago.

    Attached is my present test file for communicating and wondering if I
can expand on my buttons list to allow me to jump around.

        Bruce

File also below:

Description="""
Widgets communicate
The test below gives a voice to the buttons and the text that is changed.
The moving of the mouse fast stops the speech of the previous button.
Also speech is allowed to continue and not hold up the background screen
changes.
It is important to know, how widgets can communicate in application. Follow
the example.
THIS EXAMPLE PLACES ALL BUTTONS IN A LIST,
SO ALL EVENTS AND BINDINGS ARE BASED ON THE BUTTON ON THE LIST AND THE MOUSE
LOCATION!"""
#!/usr/bin/python
# communicate.py
#HOW TO COMMUNICATE INSIDE A FRAME PANELS!
import wx
import Sapi5
tts = Sapi5.Create()
purge = tts._purge
async = tts._async
class LeftPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
        self.text = parent.GetParent().rightPanel.text
        self.number = 0
        self.col = 10
        self.titles = ["Plus", "Minus", "Voice", "Rate", "Pitch", "Volume"]
        self.buttons = []
        self.label4Btn = {}
        for i in range( len(self.titles)):
            self.buttons.append( wx.Button(self, -1, self.titles[i], (10,
10+i*50)))
            self.label4Btn[ self.buttons[i].GetId()] =
self.buttons[i].GetLabel()
            self.buttons[i].Bind(wx.EVT_ENTER_WINDOW, self.OnSpeak) #,
id=self.buttons[i].GetId())
        self.label4Btn[ "btn"] = self.buttons[0].GetId()
        self.button2bind = self.Bind(wx.EVT_BUTTON, self.OnClick,
id=self.label4Btn[ "btn"])
    def OnSpeak(self, event):
        label4btn = self.label4Btn[ event.GetId()]
        self.label4Btn[ "btn"] = event.GetId()
        self.button2bind = self.Bind(wx.EVT_BUTTON, self.OnClick,
id=event.GetId())
        set_value = str( self.number)
        if label4btn == "Voice":
            set_value = tts.getVoiceName()
        elif label4btn == "Rate":
            set_value = str( tts.getRate())
        elif label4btn == "Pitch":
            set_value = str( tts.getPitch())
        elif label4btn == "Volume":
            set_value = str( tts.getVolume())
        text = label4btn +" Button " +set_value
        self.text.SetLabel( set_value)
        tts.Speak( text, async, purge)
    def OnClick(self, event):
        label4btn = self.label4Btn[ event.GetId()]
        if label4btn == "Voice":
            value = tts.getVoiceNum()+1
            if value >= tts.getVoiceCount():
                value = 0
            tts.setVoice( value)
            set_value = tts.getVoiceName()
        else:
            value = int(self.text.GetLabel())
        if label4btn == "Plus":
            self.number += 1
            set_value = str( self.number)
        elif label4btn == "Minus":
            self.number -= 1
            set_value = str( self.number)
        elif label4btn == "Rate":
            value += 1
            if value > 10: value = -10
            tts.setRate( value)
            set_value = str( value)
        elif label4btn == "Pitch":
            value += 1
            if value > 10: value = -10
            tts.setPitch( value)
            set_value = str( value)
        elif label4btn == "Volume":
            value += 10
            if value > 100: value = 0
            tts.setVolume( value)
            set_value = str( value)
        tts.Speak( set_value, async)
        self.text.SetLabel( set_value)
class RightPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
        self.text = wx.StaticText(self, -1, '0', (40, 60))
class Communicate(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(280, 400))
        panel = wx.Panel(self, -1)
        self.rightPanel = RightPanel(panel, -1)
        leftPanel = LeftPanel(panel, -1)
        hbox = wx.BoxSizer()
        hbox.Add(leftPanel, 1, wx.EXPAND | wx.ALL, 5)
        hbox.Add(self.rightPanel, 1, wx.EXPAND | wx.ALL, 5)
        panel.SetSizer(hbox)
        self.Centre()
        self.Show(True)
app = wx.App( False)
Communicate(None, -1, 'widgets communicate')
app.MainLoop()

Coms.py (4.06 KB)

Hi Robin,

    Can I place a keypress into any place and cause a set focus to another
part of the frame?

    What I mean is can I jump around and move focus to another button or
location just by clicking on a button or pressing a key?

    If so, how do I do it? I did a search and could find little, except a
bug issue you asked someone to post a while ago.

    Attached is my present test file for communicating and wondering if I
can expand on my buttons list to allow me to jump around.

        Bruce

File also below:

Description="""
Widgets communicate
The test below gives a voice to the buttons and the text that is changed.
The moving of the mouse fast stops the speech of the previous button.
Also speech is allowed to continue and not hold up the background screen
changes.
It is important to know, how widgets can communicate in application. Follow
the example.
THIS EXAMPLE PLACES ALL BUTTONS IN A LIST,
SO ALL EVENTS AND BINDINGS ARE BASED ON THE BUTTON ON THE LIST AND THE MOUSE
LOCATION!"""
#!/usr/bin/python
# communicate.py
#HOW TO COMMUNICATE INSIDE A FRAME PANELS!
import wx
import Sapi5
tts = Sapi5.Create()
purge = tts._purge
async = tts._async
class LeftPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
        self.text = parent.GetParent().rightPanel.text
        self.number = 0
        self.col = 10
        self.titles = ["Plus", "Minus", "Voice", "Rate", "Pitch", "Volume"]
        self.buttons = []
        self.label4Btn = {}
        for i in range( len(self.titles)):
            self.buttons.append( wx.Button(self, -1, self.titles[i], (10,
10+i*50)))
            self.label4Btn[ self.buttons[i].GetId()] =
self.buttons[i].GetLabel()
            self.buttons[i].Bind(wx.EVT_ENTER_WINDOW, self.OnSpeak) #,
id=self.buttons[i].GetId())
        self.label4Btn[ "btn"] = self.buttons[0].GetId()
        self.button2bind = self.Bind(wx.EVT_BUTTON, self.OnClick,
id=self.label4Btn[ "btn"])
    def OnSpeak(self, event):
        label4btn = self.label4Btn[ event.GetId()]
        self.label4Btn[ "btn"] = event.GetId()
        self.button2bind = self.Bind(wx.EVT_BUTTON, self.OnClick,
id=event.GetId())
        set_value = str( self.number)
        if label4btn == "Voice":
            set_value = tts.getVoiceName()
        elif label4btn == "Rate":
            set_value = str( tts.getRate())
        elif label4btn == "Pitch":
            set_value = str( tts.getPitch())
        elif label4btn == "Volume":
            set_value = str( tts.getVolume())
        text = label4btn +" Button " +set_value
        self.text.SetLabel( set_value)
        tts.Speak( text, async, purge)
    def OnClick(self, event):
        label4btn = self.label4Btn[ event.GetId()]
        if label4btn == "Voice":
            value = tts.getVoiceNum()+1
            if value >= tts.getVoiceCount():
                value = 0
            tts.setVoice( value)
            set_value = tts.getVoiceName()
        else:
            value = int(self.text.GetLabel())
        if label4btn == "Plus":
            self.number += 1
            set_value = str( self.number)
        elif label4btn == "Minus":
            self.number -= 1
            set_value = str( self.number)
        elif label4btn == "Rate":
            value += 1
            if value > 10: value = -10
            tts.setRate( value)
            set_value = str( value)
        elif label4btn == "Pitch":
            value += 1
            if value > 10: value = -10
            tts.setPitch( value)
            set_value = str( value)
        elif label4btn == "Volume":
            value += 10
            if value > 100: value = 0
            tts.setVolume( value)
            set_value = str( value)
        tts.Speak( set_value, async)
        self.text.SetLabel( set_value)
class RightPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
        self.text = wx.StaticText(self, -1, '0', (40, 60))
class Communicate(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(280, 400))
        panel = wx.Panel(self, -1)
        self.rightPanel = RightPanel(panel, -1)
        leftPanel = LeftPanel(panel, -1)
        hbox = wx.BoxSizer()
        hbox.Add(leftPanel, 1, wx.EXPAND | wx.ALL, 5)
        hbox.Add(self.rightPanel, 1, wx.EXPAND | wx.ALL, 5)
        panel.SetSizer(hbox)
        self.Centre()
        self.Show(True)
app = wx.App( False)
Communicate(None, -1, 'widgets communicate')
app.MainLoop()

···

----------------------------------------------------------------------------
----

_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwidgets.org
http://lists.wxwidgets.org/mailman/listinfo/wxpython-users