Search code examples
pythonwxpython

wxPython placing form code in discrete .py files (not using pubsub)


I am migrating a little Excel/VBA-based application to wxPython/SQLite3, learning the latter as I go. As time goes on I'm finding navigating the code a more and more unwieldy - it's only 880 lines currently, but it's a bit more than I'm used to managing - and so was wondering whether there was a simple solution to putting form code for each form each in discrete .pys.

For example, at the bottom of my current .py, I have the bit that starts the main form:

class AuditApp(wx.App):
  def OnInit(self):
    self.aframe = audFrame(None, -1, "Audits Manager")
    self.aframe.Show(True)
    self.SetTopWindow(self.aframe)  # has to be *after* the Show()
    return True

def main():
  app = AuditApp(0)
  app.MainLoop()

if __name__ == '__main__':
  main()

That audFrame currently displays a menu and a list:

class audFrame(wx.Frame):
  def __init__(self, parent, id, title):    
    wx.Frame.__init__(self, parent, id, title)
    self.scwd = os.getcwd()
    if AppCompiled:
      image = "/home/robyn//dev/Audits/FoggyM.jpg"
    else:
      image = self.scwd + "/FoggyM.jpg"
    self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
    self.bg = wx.Bitmap(image)
    self._width, self._height = self.bg.GetSize()
    # Menu Section
    audmbar = wx.MenuBar()
    mfile = wx.Menu()
    medit = wx.Menu()
    mhelp = wx.Menu()
    mfile.Append(101, "Post-Op &Times", "Post-Op Stay in Minutes")
    mfile.Append(102, "&Patient Satisfaction", "Post-discharge telephone interview")
    mfile.AppendSeparator()
    mquit = wx.MenuItem(mfile, 110, "&Quit\tCtrl+Q", "Leave Audits Manager")
    mfile.AppendItem(mquit)
    mhelp.Append(150, "&Help", "Audit Manager Help")
    mhelp.AppendSeparator()
    mhelp.Append(159, "&About AuditsMgr", "About Audits Manager")
    audmbar.Append(mfile, "&Modules")
    audmbar.Append(medit, "&Edit")
    audmbar.Append(mhelp, "&Help")
    # Data
    self.mconn = self.doConnect()
    self.mcurs = self.mconn.cursor()
    # "SELECT patid, anaesthetist, proctype, intvdate FROM interv ORDER BY intvdate"
    self.mcurs.execute("SELECT patid || '  ' || intvdate || '  ' || proctype || '  ' || anaesthetist AS intvlist FROM interv")
    patList = [item[0] for item in self.mcurs.fetchall()]
    # Top
    line01 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
    pnlTop = wx.Panel(self, -1, style=wx.SUNKEN_BORDER|wx.TAB_TRAVERSAL)
    BxSzr1 = wx.BoxSizer(wx.HORIZONTAL)
    BxSzr1.Add(pnlTop, 0, wx.ALL, 5)
    self.listBox1 = wx.ListBox(choices=patList, id=160, name='Results', parent=pnlTop, 
                    pos=wx.Point(20, 20), size=wx.Size(450, 300), style=0)
    self.listBox1.SetBackgroundColour(wx.Colour(255, 255, 128))
    #self.listBox1.Bind(wx.EVT_LISTBOX, self.OnListBox1Listbox, id=wxID_FRAME1LISTBOX1)
    btnSizer = wx.BoxSizer(wx.HORIZONTAL)
    self.btnFind = wx.Button(self, 180, u"&Find", wx.DefaultPosition, wx.DefaultSize, 0)        
    self.btnClose = wx.Button(self, 181, u"&Close", wx.DefaultPosition, wx.DefaultSize, 0)
    btnSizer.Add(self.btnFind, 0, wx.ALL|wx.EXPAND, 5)
    btnSizer.Add(self.btnClose, 0, wx.ALL|wx.EXPAND, 5)
    # Main Sizer
    MainSizer = wx.BoxSizer(wx.VERTICAL)
    MainSizer.Add(BxSzr1, 14, wx.ALL|wx.EXPAND, 5)    
    MainSizer.Add(line01, 0, wx.ALL|wx.EXPAND, 5)    
    MainSizer.Add(btnSizer, 1, wx.ALIGN_RIGHT, 5)        
    """
    ====> MAIN LAYOUT CODE <====
    """
    self.Size = wx.Size(1000, 700)
    self.SetMenuBar(audmbar)
    self.CreateStatusBar()
    self.SetSizer(MainSizer)
    self.Layout()
    self.Centre(wx.BOTH)
    self.Bind(wx.EVT_MENU, self.doPOTimes, id = 101)
    self.Bind(wx.EVT_MENU, self.doInterv, id = 102)
    self.Bind(wx.EVT_MENU, self.onQuit, id = 110)
    self.Bind(wx.EVT_MENU, self.onHelp, id = 150)
    self.Bind(wx.EVT_MENU, self.onAbout, id = 159)
    self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
    self.Bind(wx.EVT_BUTTON, self.clkFind, id = 180)
    self.Bind(wx.EVT_BUTTON, self.onQuit, id = 181)

  def clkFind(self, event):
  #  findRec = wx.MessageDialog(self, )
  #  self.popForm()
    event.Skip()

  def clkClose(self, event):
    self.curs.close()
    self.conn.close()
    self.Close()

  def doPOTimes(self, event):
    frm = frmTimes(None, -1, "Post-Op Times")
    frm.Show()

  def doInterv(self, event):
    frm = frmIntervw(None, -1, "Post-Discharge Interview")
    frm.Show()

  def doConnect(self):
    conStr = "/home/robyn/data/audits.db"
    return sqlite3.connect(conStr)

  def onHelp(self, event):
    pass

The menu brings up other forms and dialogues. It was for those forms that I was wondering if it were possible to place the code in a discrete .py.

I had a look at pubsub, but it seemed overkill - the forms simply did data collection and didn't really need to communicate anything with the main form, at least, that's what I gathered from this excellent tutorial:

I see a number of questions on the wxPython mailing list or its IRC channel about communicating between frames and most of the time what the developer needs is the PubSub module. The Publisher / Subscriber model is a way to send messages to one or more listeners.

All that seemed a bit more than I needed, unless I misunderstood what the author of the tutorial meant by 'Communicating between two frames'.

I guess what I'm asking is: can code for the same application live in several .py files, where each form would have its own .py file, to be called by the bound method, to sort-of replace that:

def dodoInterv(self, event):
    frm = frmIntervw(None, -1, "Post-Discharge Interview")
    frm.Show()

with something that will call (import?) a .py instead. Am I opening a can of worms, here?


Solution

  • Although the example in the PyPubSub tutorial may seem pointless, it is only to show how to use the module. PyPubSub does come in handy when you need to handle multiple threads running independently of the main thread. It is true though, at least from my experience that any need of PyPubSub can be overcome by passing a reference of one frame to another.

    As for your question, yes you can comfortably write the code of a single application on several modules. Just import those modules in the ones where you need them. People do it all the time, otherwise it would be impossible to write the entire code in a single py file.

    For the code you have provided, you can write the codes for the classes in different files and simply import the classes to the main .py file.

    Say for example audFrame is defined in a separate frame.py file. You can change your code for the main.py file to something like this:

    import frame
    
    class AuditApp(wx.App):
      def OnInit(self):
        self.aframe = frame.audFrame(None, -1, "Audits Manager")
        self.aframe.Show(True)
        self.SetTopWindow(self.aframe)  # has to be *after* the Show()
        return True
    
    def main():
      app = AuditApp(0)
      app.MainLoop()
    
    if __name__ == '__main__':
      main()