Search code examples
pythonwxpython

Code Layout guide for multiple frames in wxpython


EDITED ~ Now with code.

I’m new to coding, and struggling getting variables input into ‘frameA’ output into ‘frameB’. My question is, how should the layout of the code be written so that a variable entered into FrameA is usable, (and refreshed on user input - either on a button click or deselecting textctrl etc-) in frameB?

I’m using wxpython, and using Wx builder as a guide, but writing the actual UI code used myself to help understand what’s going on underneath. I’ve tried class -> def -> frame UI and the frames are organised as I want them, show and hide as I want and the variables I want are printable on entry/button clicks etc within that frame, but are not accessible from outside of that frame, even though I am returning the variables needed.

In this example I want to take what is written in frameA's textctrl, and save it as a variable accessible (and updated) to other classes, in this case FrameB.

import wx

var = "This Should change to whatever is typed in FrameA"

class FrameA ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = (0,0), size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) 
        bSizer1 = wx.BoxSizer( wx.HORIZONTAL )
        self.INPUT = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer1.Add( self.INPUT, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
        self.SetSizer( bSizer1 )
        self.INPUT.Bind( wx.EVT_TEXT, self.InputUpdate)

    def InputUpdate(self, Evt):
        var = self.INPUT.GetValue()
        #print (var)
        return var

class FrameB ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = (100,100), size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
        bSizer2 = wx.BoxSizer( wx.HORIZONTAL )
        self.OUTPUT = wx.StaticText( self, wx.ID_ANY, var, wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.OUTPUT, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
        self.SetSizer( bSizer2 )

if __name__ == '__main__':
    app = wx.App()
    frmA = FrameA(None)
    frmB = FrameB(None)
    frmA.Centre()
    frmB.Centre()
    frmA.Show()
    frmB.Show()
app.MainLoop()

As I’m still new I am at the point where I can understand each single line of code, but the larger organisation is still getting messy for me.

My understanding is that the code in its state above the edited 'var' can be accessed from within frameA only, and 'return var' might(?) update the 'global' var, but is not being refreshed into frameB. What am I missing? I've been trying to avoid using global variables as I've heard they're a bad habit to get into this early on...

Thanks, Sundown


Solution

  • If you want the value of var to be accessible to all classes then you need to add the line global var to the method InputUpdate() in Class FrameA before assigning the new value to var.

    However, this will make var accessible to all classes in this module, only. If you split the code into two files, then you will have two modules and var will be accessible only in the module that defines it, unless you also import the module defining var in the other module.

    Code with some relevant comments:

    import wx
    
    var = "This Should change to whatever is typed in FrameA"
    
    class FrameA ( wx.Frame ):
    
        def __init__(self, parent):
            wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=(0,0), size=(500,300), style=wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL) 
            bSizer1 = wx.BoxSizer(wx.HORIZONTAL)
            self.INPUT = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
            bSizer1.Add(self.INPUT, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
            self.SetSizer(bSizer1)
            self.INPUT.Bind(wx.EVT_TEXT, self.InputUpdate)
    
        def InputUpdate(self, Evt):
            #### You need to add the following line before assigning the new value to
            #### var in order to modify the global var variable and not the local
            #### var variable
            global var
    
            var = self.INPUT.GetValue()
            #print (var)
            #### The try/except block is placed to avoid having an error if the text
            #### in frameA changes but frameB does not exist anymore. The code inside
            #### the try statement changes the text in frameB in real time.
            try:
                frmB.OUTPUT.SetLabel(var)
            except Exception:
                pass
    
    class FrameB ( wx.Frame ):
    
        def __init__(self, parent):
            wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=(100,100), size=(500,300), style=wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL)
            bSizer2 = wx.BoxSizer(wx.VERTICAL)
            self.OUTPUT = wx.StaticText(self, wx.ID_ANY, var, wx.DefaultPosition, wx.DefaultSize, 0)
            bSizer2.Add(self.OUTPUT, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
            #### Button to check the value of the global var variable and make sure
            #### that the code in InputUpdate actually changes the value of the global
            #### variable var
            self.button = wx.Button(self, wx.ID_ANY, label='Var Value')
            bSizer2.Add(self.button, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
            self.SetSizer(bSizer2)
    
            self.button.Bind(wx.EVT_BUTTON, self.varValue)
    
        def varValue(self, event):
            #### Print the value of the global variable var
            print(var)
    
    if __name__ == '__main__':
        app = wx.App()
        frmA = FrameA(None)
        frmB = FrameB(None)
        frmA.Centre()
        frmB.Centre()
        frmA.Show()
        frmB.Show()
    app.MainLoop()