Dynamically add widgets to panel

Hey all, I'm trying to make a panel or frame where widgets can be
added when certain events occur (like when the user presses a button,
etc).

Typing 'wxpython dynamically add widgets' into Google gave some
results on how to do this but none of them worked. It looks like the
window just hangs or stops drawing itself or something

Here is a quick example of what the code might look like. I'm using
Python 2.6.2 on Windows XP

···

#-------------------------------------------------
# Adds a button to the frame every second for 5 seconds

import wx, time, threading

class MyFrame( wx.Frame ):
    def __init__( self ):
        wx.Frame.__init__( self, None, -1, "Test" )
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(self.sizer)
        self.Show()
        Adder(self)

class Adder (threading.Thread):
    def __init__(self, frame):
        threading.Thread.__init__(self)
        self.frame = frame
        self.start()

    def run(self):
        count = 0
        while count < 5:
            btn = wx.Button(self.frame, -1, 'Button')
            self.frame.sizer.Add(btn, 1)
            self.frame.Layout()

            time.sleep(1)
            count += 1

app = wx.App(0)
MyFrame()
app.MainLoop()

Well its not a problem of "dynamically" - wxPython works in
dynamic manner like that all the time - but a thread-safety problem.
wxPython seems not to be thread-safe to allow .Add()/.Layout()
like this.

When I want to do GUI things comfortably from other threads I put
the function calls like lambda:myAddButton('Button') into a queue
(of funcs) and execute them from the queue from OnIdle/OnTimer -
thus within GUI thread

Robert

knascent wrote:

···

Hey all, I'm trying to make a panel or frame where widgets can be
added when certain events occur (like when the user presses a button,
etc).

Typing 'wxpython dynamically add widgets' into Google gave some
results on how to do this but none of them worked. It looks like the
window just hangs or stops drawing itself or something

Here is a quick example of what the code might look like. I'm using
Python 2.6.2 on Windows XP

#-------------------------------------------------
# Adds a button to the frame every second for 5 seconds

import wx, time, threading

class MyFrame( wx.Frame ):
    def __init__( self ):
        wx.Frame.__init__( self, None, -1, "Test" )
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(self.sizer)
        self.Show()
        Adder(self)

class Adder (threading.Thread):
    def __init__(self, frame):
        threading.Thread.__init__(self)
        self.frame = frame
        self.start()

    def run(self):
        count = 0
        while count < 5:
            btn = wx.Button(self.frame, -1, 'Button')
            self.frame.sizer.Add(btn, 1)
            self.frame.Layout()

            time.sleep(1)
            count += 1

app = wx.App(0)
MyFrame()
app.MainLoop()

>

Robert, thanks a lot, that was it. I think I should be able to make
everything work using your function queue idea.

Here is the modified example code using a Timer for anyone else who
runs into this problem:

···

#-------------------------------------------------
# Adds a button to the frame every second for 5 seconds

import wx, time, threading

class MyFrame( wx.Frame ):
    def __init__( self ):
        wx.Frame.__init__( self, None, -1, "Test" )
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(self.sizer)
        self.Show()

        self.count = 0
        self.Bind(wx.EVT_TIMER, self.AddButton)
        self.tmr = wx.Timer(self)
        self.tmr.Start(500)

    def AddButton (self, event):
        if self.count < 5:
            btn = wx.Button(self, -1, 'Button')
            self.sizer.Add(btn, 1)
            self.sizer.Layout()
            self.Layout()
            self.count += 1

app = wx.App(0)
MyFrame()
app.MainLoop()

On Aug 1, 3:31 am, Robert <kxrobe...@googlemail.com> wrote:

Well its not a problem of "dynamically" - wxPython works in
dynamic manner like that all the time - but a thread-safety problem.
wxPython seems not to be thread-safe to allow .Add()/.Layout()
like this.

When I want to do GUI things comfortably from other threads I put
the function calls like lambda:myAddButton('Button') into a queue
(of funcs) and execute them from the queue from OnIdle/OnTimer -
thus within GUI thread

Robert

knascent wrote:
> Hey all, I'm trying to make a panel or frame where widgets can be
> added when certain events occur (like when the user presses a button,
> etc).

> Typing 'wxpython dynamically add widgets' into Google gave some
> results on how to do this but none of them worked. It looks like the
> window just hangs or stops drawing itself or something

> Here is a quick example of what the code might look like. I'm using
> Python 2.6.2 on Windows XP

> #-------------------------------------------------
> # Adds a button to the frame every second for 5 seconds

> import wx, time, threading

> class MyFrame( wx.Frame ):
> def __init__( self ):
> wx.Frame.__init__( self, None, -1, "Test" )
> self.sizer = wx.BoxSizer(wx.VERTICAL)
> self.SetSizer(self.sizer)
> self.Show()
> Adder(self)

> class Adder (threading.Thread):
> def __init__(self, frame):
> threading.Thread.__init__(self)
> self.frame = frame
> self.start()

> def run(self):
> count = 0
> while count < 5:
> btn = wx.Button(self.frame, -1, 'Button')
> self.frame.sizer.Add(btn, 1)
> self.frame.Layout()

> time.sleep(1)
> count += 1

> app = wx.App(0)
> MyFrame()
> app.MainLoop()

Robert wrote:

When I want to do GUI things comfortably from other threads I put the function calls like lambda:myAddButton('Button') into a queue (of funcs) and execute them from the queue from OnIdle/OnTimer - thus within GUI thread

wx.CallAfter does essentially the same thing, with no need to maintain and process your own queue. wx.CallLater does it with a timer.

···

--
Robin Dunn
Software Craftsman