mh00h
mh00h

Reputation: 1856

WxPython: Multiple Widgets in a Child Window

I'm teaching myself (Wx) Python and got stuck. I want to create a child window that has a recurring set of information inside of my TestFrame, which contains most of my code. The problem is, it only shows one widget in my code. I am trying to figure out the following, in order of importance to me.

**Note that this code is an extension to this page.*

  1. How can I allow multiple widgets in the "AddBox" class to appear correctly?
  2. When my window is resized, it corrupts the button images as in the screen attached. How could I fix this?
  3. How do you call/bind each of these dynamically created widgets?
  4. Bonus: is the "OnSize" module needed here?

Thank you for your help. If allowed/appropriate, I'm willing to contribute $5 via Paypal to the winner if you PM me.

import wx

class AddBox(wx.Window):
    def __init__(self, parent):
        wx.Window.__init__(self, parent)        

        pbox = wx.BoxSizer(wx.VERTICAL)

        controlback = wx.Button(self, label="Back")
        controlforward = wx.Button(self, label="Forward")

        pbox.AddMany([(controlback, 1, wx.ALL), (controlforward, 1, wx.ALL)])

class TestFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, size=(1000, 550))

        self.Bind(wx.EVT_SIZE, self.OnSize)

        pbox0 = wx.BoxSizer(wx.VERTICAL)
        controlback0 = wx.Button(self, label="Back0")
        controlforward0 = wx.Button(self, label="Forward0")
        pbox0.AddMany([(controlback0, 1, wx.ALL), (controlforward0, 1, wx.ALL)])
        pbox2 = wx.BoxSizer(wx.VERTICAL)

        self.scrolling_window = wx.ScrolledWindow( self )
        self.scrolling_window.SetScrollRate(1,6)
        self.scrolling_window.EnableScrolling(True,True)
        self.sizer_container = wx.BoxSizer( wx.VERTICAL )
        self.sizer = wx.BoxSizer( wx.VERTICAL )
        self.sizer_container.Add(self.sizer,1,wx.CENTER,wx.EXPAND)
        self.child_windows = []
        for i in range(0,8):
            wind = AddBox(self.scrolling_window)
            self.sizer.Add(wind, 0, wx.CENTER|wx.ALL, 5)
            self.child_windows.append(wind)

        self.scrolling_window.SetSizer(self.sizer_container)
        #self.Layout() #not needed?


        pbox2.AddMany([(self.sizer_container, 1, wx.ALL)])

    def OnSize(self, event):
        self.scrolling_window.SetSize(self.GetClientSize())

if __name__=='__main__':
    app = wx.PySimpleApp()
    f = TestFrame()
    f.Show()
    app.MainLoop()

Upvotes: 1

Views: 1724

Answers (2)

Mike Driscoll
Mike Driscoll

Reputation: 33071

The code here is pretty convoluted and hard to follow. You almost never need to use wx.Window. In fact, in almost 6 years of using wxPython, I have NEVER used it directly. PySimpleApp is deprecated as well. Anyway, I cleaned up the code a bunch and re-did it below. I'm not sure if this is what you're looking for or not though. Also note that I swapped out ScrolledWindow for ScrolledPanel as I think the latter is easier to use:

import wx
import  wx.lib.scrolledpanel as scrolled

class TestFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, size=(1000, 550))
        panel = wx.Panel(self)

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        pbox0 = wx.BoxSizer(wx.VERTICAL)
        controlback0 = wx.Button(panel, label="Back0")
        controlforward0 = wx.Button(panel, label="Forward0")
        pbox0.Add(controlback0, 0, wx.ALL)
        pbox0.Add(controlforward0, 0, wx.ALL)
        mainSizer.Add(pbox0)

        self.scrolling_window = scrolled.ScrolledPanel( panel )
        self.scrolling_window.SetAutoLayout(1)
        self.scrolling_window.SetupScrolling()
        self.sizer = wx.BoxSizer( wx.VERTICAL )
        self.child_windows = []
        for i in range(0,8):
            wind = self.addBox()
            self.sizer.Add(wind, 0, wx.CENTER|wx.ALL, 5)
        self.scrolling_window.SetSizer(self.sizer)

        mainSizer.Add(self.scrolling_window, 1, wx.EXPAND)
        panel.SetSizer(mainSizer)

    def addBox(self):
        pbox = wx.BoxSizer(wx.VERTICAL)

        controlback = wx.Button(self.scrolling_window, label="Back")
        controlforward = wx.Button(self.scrolling_window, label="Forward")

        pbox.AddMany([(controlback, 0, wx.ALL), (controlforward, 0, wx.ALL)])
        return pbox

    def OnSize(self, event):
        self.scrolling_window.SetSize(self.GetClientSize())

if __name__=='__main__':
    app = wx.App(False)
    f = TestFrame()
    f.Show()
    app.MainLoop()

You should also take a look at the Widget Inspection Tool. It will help you figure out layout problems like this: http://wiki.wxpython.org/Widget%20Inspection%20Tool

EDIT: I think the code above takes care of items #1 and 2. For #3, see the following article I wrote last year: http://www.blog.pythonlibrary.org/2011/09/20/wxpython-binding-multiple-widgets-to-the-same-handler/

Basically you bind in the loop itself and then in the event handler, you can use event.GetEventObject() to return the calling widget and set its value or whatever. As for your 4th question, I would say no, you don't need the OnSize method. You almost never need to override that in wxPython and I certainly didn't in my example code.

Upvotes: 1

dav1d
dav1d

Reputation: 6055

I just have an answer to 3 for now: Assign to each dynamically created widget an ID, store this ID in a dictionary ({id : widget }) and pass as the callback function a lambda-function or use functools.partial to pass the ID to the "real" callback. (of course you can pass the widget directly over the lambda-function, but I like to have instances to the created widgets somewhere, if I need to access these sometime)

    def __init__(...):
    self.event_dispatch = dict()

    ...

    for id in xrange(500, 508):
        wind = AddBox(self.scrolling_window, id)
        ...
        self.event_dispatch[id] = wind
        self.scrolling_window.Bind(wx.MY_EVENT, lambda evt: self.on_event(evt, id), id=id)


def on_event(event, id):
    widget = self.event_dispatch[id]
    # do something with widget

Upvotes: 0

Related Questions