1

As part of my first project of developing a desktop GUI app with Python, is there a standard or conventional location or means of storing application defaults? As an example, there may be resources such as icons or other images used by the application which shouldn't be stored in the source tree.

With user-specific config files it's straightforward to set the default config directory to '~/.app_config' and use os.path.expanduser() to generate an appropriate path on both Windows and Linux (ignoring the fact that recent versions of Windows store settings under "~/Application Data" or "~/Local Settings") That's fine for user-level configs, but I'm looking for language or toolkit conventions for resource storage in a professionally packaged application.

I'm currently using Jython, but this should be applicable to wxPython, other GUI toolkits, and CLI applications. I appreciate the flexibility I have but frankly, I want to put things in a "standard" location and I'm too lazy and not arrogant enough to just make up what "standard" is. :)

Update: The appdirs module seems to do exactly what I'm looking for. In retrospect, I found this question and this question which are essentially duplicates.

Community
  • 1
  • 1
arclight
  • 1,608
  • 1
  • 15
  • 19
  • 1
    For Linux & most UNIX systems, you want to use the XDG standard (`~/.config`, `~/.cache`); there's a [nice Python module](http://freedesktop.org/wiki/Software/pyxdg/) for this... – Martin Tournoij Nov 30 '14 at 06:20
  • 2
    No, please don't. Don't go platform dependent. If you're using wxPython, just use what's returned by wx.StandardPaths and its friends, you're guaranteed to have a platform-independent, straightforward way to handle app settings. I've been doing that since forever and it has always worked out beautifully. – Infinity77 Nov 30 '14 at 20:52

1 Answers1

1

You should use wx.StandardPaths. There is a good example in the wxPython demo. I took that example and created a runnable version below:

import wx

#----------------------------------------------------------------------
class TestPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1)

        sizer = wx.FlexGridSizer(0, 3, 5, 5)
        sizer.AddGrowableCol(1)
        box = wx.BoxSizer(wx.VERTICAL)
        fs = self.GetFont().GetPointSize()
        bf = wx.Font(fs+4, wx.SWISS, wx.NORMAL, wx.BOLD)

        t = wx.StaticText(self, -1, "StandardPaths")
        t.SetFont(bf)
        box.Add(t, 0, wx.CENTER|wx.ALL, 4)
        box.Add(wx.StaticLine(self, -1), 0, wx.EXPAND)

        # get the global (singleton) instance of wx.StandardPaths
        sp = wx.StandardPaths.Get()

        # StandardPaths will use the value of wx.App().GetAppName()
        # for some of the stnadard path components.  Let's set it to
        # something that makes that obvious for the demo.  In your own
        # apps you'll set it in to something more meaningfull for your
        # app in your OnInit, (or just let it default.)
        wx.GetApp().SetAppName("AppName")

        self.help = {}

        # Loop through all of the getters in wx.StandardPaths and make
        # a set of items in the sizer for each.
        def makeitem(name, *args):
            func = getattr(sp, name)
            sizer.Add(wx.StaticText(self, -1, "%s%s:" %(name, repr(args))),
                      0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
            sizer.Add(wx.TextCtrl(self, -1, func(*args),
                                  size=(275,-1), style=wx.TE_READONLY),
                      0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL)

            btn = wx.Button(self, wx.ID_HELP)
            sizer.Add(btn)
            self.help[btn] = func.__doc__

        for x in ['GetConfigDir',
                  'GetUserConfigDir',
                  'GetDataDir',
                  'GetLocalDataDir',
                  'GetUserDataDir',
                  'GetUserLocalDataDir',
                  'GetDocumentsDir',
                  'GetPluginsDir',
                  'GetInstallPrefix',
                  'GetResourcesDir',
                  'GetTempDir',
                  'GetExecutablePath',
                  ]:
            makeitem(x)

        # this one needs parameters
        makeitem('GetLocalizedResourcesDir', 'en',
                 wx.StandardPaths.ResourceCat_Messages )

        self.Bind(wx.EVT_BUTTON, self.OnShowDoc, id=wx.ID_HELP)

        box.Add(sizer, 0, wx.CENTER|wx.EXPAND|wx.ALL, 20)
        self.SetSizer(box)

    def OnShowDoc(self, evt):
        doc = self.help[evt.GetEventObject()]

        # trim the whitespace from each line
        lines = []
        for line in doc.split('\n'):
            lines.append(line.strip())
        doc = '\n'.join(lines)
        wx.TipWindow(self, doc, 500)    

#----------------------------------------------------------------------
class MyFrame(wx.Frame):
    """"""

    def __init__(self):
        wx.Frame.__init__(self, None, title="Standard Paths",
                          size=(800,600))
        panel = TestPanel(self)
        self.Show()

if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

For more information, I recommend reading the documentation:

Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
  • Thanks for the detailed explanation; between wx.StandardPaths and appdirs I should have what I need. – arclight Dec 10 '14 at 19:04