Search code examples
python-2.7wxpython

Unable to save persistence file using wxPython PersistenceManager


I'm developing a GUI application using wxpython that has roughly 110 user-chosen parameters. Since I would like for users to be able to save these options to a project file, I decided to use the PersistenceManager module that's included with wxPython.

The persistence works great as long as I don't try to specify the filename in which to save the settings, i.e., I use the default value (C:\users\username\AppData\programName\Persistence_Options), and just have the program save the settings when it exits.

What I'm trying to do is allow the user to choose a file to save the settings (since they might have multiple projects with different options). But, when I use the SetPersistenceFile method with the user-specified filename, no file gets saved, and no error message is returned, even though it's definitely executing those lines of code, which are given below. (The OnSave function is a method of the main window of the program.)

def OnSave(self, e):
    self.dirname = os.getcwd()
    if self.ProjectFile == '':
        dlg = wx.FileDialog(self, "Save project file", self.dirname, "", "Project configuration file (.prj)|*.prj", wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_CANCEL:
            return
        else:
            self.ProjectFile = os.path.join(dlg.GetDirectory(), dlg.GetFilename())
            #print self.ProjectFile

    if self.ProjectFile != '':
        print "Made it to here (Save)..."
        #self.Register(self) # Also tried calling Register in __init__
        self._persistMgr = PM.PersistenceManager.Get()
        print self.ProjectFile # Gives correct filename
        self._persistMgr.SetPersistenceFile(self.ProjectFile)
        self._persistMgr.Save(self)
        print "Finished saving."

I've tried using a local PersistenceManager object, rather than having it as a class member, and this made no difference. Interestingly enough, if I declare the self.__persistMgr object in the window's __init__ function and use the SetPersistenceFile method with a hard-coded filename there, it writes the file, however this is not helpful since the user needs to specify that at runtime.

Does anyone know why the file isn't saving and how I can fix this?


Solution

  • Not sure why your code is giving you grief, the following works on Linux, although that may be of no consolation to you.
    It is cobbled together from a number of sources, not having come across the PersistenceManager myself.

    #!/usr/bin/python
    import wx , os
    import wx.lib.agw.persist as PM
    
    class persist(wx.Frame):
        def __init__(self, parent):
            wx.Frame.__init__(self, parent, -1, "A window that maintains size and position after restart")
            self.Bind(wx.EVT_CLOSE, self.OnClose)
    
            # Very important step!!
            if self.GetName() == "frame":
                self.SetName("My Persist Frame") # Do not use the default name!!
    
            dirname = os.getcwd()
            dlg = wx.FileDialog(self, "Project file", dirname, "", "Project configuration file (.prj)|*.prj|All files (*.*)|*.*", wx.FD_SAVE)
            if dlg.ShowModal() == wx.ID_CANCEL:
                _configFile = os.path.join(os.getcwd(), "persist-saved.prj")    # getname()
            else:
                _configFile = os.path.join(dlg.GetDirectory(), dlg.GetFilename())
            print _configFile
            self._persistMgr = PM.PersistenceManager.Get()
            self._persistMgr.SetPersistenceFile(_configFile)
            self._persistMgr.RegisterAndRestoreAll(self)
            self._persistMgr.Save(self)
    
        def OnClose(self, event):
            self._persistMgr.SaveAndUnregister()
            event.Skip()
    
    if __name__ == '__main__':
        my_app = wx.App()
        p = persist(None)
        p.Show()
        my_app.MainLoop()
    

    Result in my .prj file:

    [Persistence_Options]
    [Persistence_Options/Window]
    [Persistence_Options/Window/My\ Persist\ Frame]
    x=('int', '9')
    y=('int', '134')
    w=('int', '319')
    h=('int', '78')
    Maximized=('bool', 'False')
    Iconized=('bool', 'False')
    

    Note the setting of the name this would be true of whatever it is that you are saving for persistence.

    Edit: With regard to your comment I think that you might be hoping for more than the PersistenceManager can cope with currently.

    wxWidgets has built-in support for a (constantly growing) number of controls. Currently the following classes are supported:

    wxTopLevelWindow (and hence wxFrame and wxDialog) wxBookCtrlBase (i.e. wxNotebook, wxListbook, wxToolbook and wxChoicebook) wxTreebook To automatically save and restore the properties of the windows of classes listed above you need to:

    Set a unique name for the window using wxWindow::SetName(): this step is important as the name is used in the configuration file and so must be unique among all windows of the same class. Call wxPersistenceManager::Register() at any moment after creating the window and then wxPersistenceManager::Restore() when the settings may be restored (which can't be always done immediately, e.g. often the window needs to be populated first). If settings can be restored immediately after the window creation, as is often the case for wxTopLevelWindow, for example, then wxPersistenceManager::RegisterAndRestore() can be used to do both at once. If you do not want the settings for the window to be saved (for example the changes to the dialog size are usually not saved if the dialog was cancelled), you need to call wxPersistenceManager::Unregister() manually. Otherwise the settings will be automatically saved when the control itself is destroyed.
    Source: http://www.ccp4.ac.uk/dist/checkout/wxPython-src-3.0.2.0/docs/doxygen/out/html/overview_persistence.html

    Of course I could be hopelessly wrong, as I have already admitted, I haven't used it before or really investigated it properly.