On Sun, 12 Jun 2005, James Graham wrote:

Right, there's a patch attached that should make settings use QSettings.
It might not be ideal (the design principle was "touch as few files as
possible" rather than "create the best interface possible"). There is
probably more goodness to squeeze out of QSettings (the existing interface does have the nice property of being quite pythonic though). Safer serialization
might also be nice... (though I guess not actually that important).

Thanks James. I've implemented a patch based on your code.

I've taken the opportunity to do some code clean ups. There didn't seem any point having two objects. settingdb['foo'] is now used instead of settingdb.database['foo']. I've gone round and changed all the references.

I also read the settings into a dict at startup, and write them out on exit (removing those removed). The QSettings docs give warnings about not writing from two apps at once, so I think this change helps. There are still potential corruption issues here - goodness why Qt can't do locking - maybe this is fixed in Qt 4.

I think I remember why I used my own code the first time around. I could implement having settings which overrode master settings (in /etc). The new code writes out all the settings to the user's dot file, and so you can't later change the default setting in /etc if the user has run the program.

I can't see any way around this using QSettings (unless I manually read in each file, if the system is unix). It's probably not that important as I doubt sysadmins want to change default settings.

Jamie - can you try out the new code in cvs on Windows??

Thanks

Jeremy

--
Jeremy Sanders <[EMAIL PROTECTED]>
http://www.jeremysanders.net/                Cambridge, UK
Public Key Server PGP Key ID: E1AAE053
#    Copyright (C) 2005 Jeremy S. Sanders
#    Email: Jeremy Sanders <[EMAIL PROTECTED]>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
###############################################################################

# $Id: settingdb.py,v 1.4 2005/06/12 17:12:45 jeremysanders Exp $

"""A database for default values of settings."""

import sys
import os
import os.path
import atexit

import qt

class _SettingDB(object):
    """A class which provides access to a persistant settings database.
    
    Items are accesses as a dict, with items as key=value
    """

    def __init__(self):
        """Initialise the object, reading the settings."""

        # This domain name is fictional!
        self.domain = 'veusz.org'
        self.product = 'veusz'
        self.database = {}
        self.removedsettings = []

        # read settings using QSettings
        self.readSettings()

        # Import old settings
        if 'importPerformed' not in self.database:
            self.database['importPerformed'] = True

            oldsettings = _OldSettingDB()
            if oldsettings.filefound:
                self.database.update( oldsettings.database )
                print ("Imported settings from $HOME/.veusz.def "
                       "and /etc/veusz.conf")

    def readSettings(self):
        """Read the settings using QSettings."""

        s = qt.QSettings()
        s.setPath(self.domain, self.product)
        path = '/%s/%s' % (self.domain, self.product)

        # read each entry, keeping track of what has been read
        for key in s.entryList(path):
            key = unicode(key)
            val, ok = s.readEntry( '%s/%s' % (path, key) )
            assert ok

            try:
                self.database[key] = eval(unicode(val))
            except:
                print >>sys.stderr, ('Error interpreting item "%s" in '
                                     'settings file' % key)

    def writeSettings(self):
        """Write the settings using QSettings.

        This is called by the atexit handler below
        """

        s = qt.QSettings()
        s.setPath(self.domain, self.product)
        path = '/%s/%s' % (self.domain, self.product)

        # write each entry, keeping track of which ones haven't been written
        for key, value in self.database.iteritems():
            if not s.writeEntry('%s/%s' % (path, key), repr(value)):
                print >>sys.stderr, 'Error writing setting "%s"' % key

        # now remove all the values which have been removed
        for key in self.removedsettings:
            if not s.removeEntry( '%s/%s' % (path, key) ):
                print >>sys.stderr, 'Error removing setting "%s"' % key

    def __getitem__(self, key):
        """Get the item from the database."""
        return self.database[key]

    def __setitem__(self, key, value):
        """Set the value in the database."""
        self.database[key] = value

    def __delitem__(self, key):
        """Remove the key from the database."""
        del self.database[key]
        self.removedsettings.append(key)

    def __contains__(self, key):
        """Is the key in the database."""
        return key in self.database

class _OldSettingDB:
    """A singleton class to handle the settings file.
    
    Reads the settings file on activation, and updates the settings
    file on destruction.
    """

    def __init__(self):
        """Read the default settings.

        This reads the settings from a global configuration file,
        and then from a user configuration file.
        
        FIXME: Unix specific, fix for other OS."""

        self.systemdefaultfile = '/etc/veusz.conf'
        try:
            self.userdefaultfile = os.path.join(os.environ['HOME'],
                                                '.veusz.def')
        except KeyError:
            self.userdefaultfile = ''

        self.database = {}

        # Unix specific (this is why we moved to QSettings)
        defaults = self.importFile(self.systemdefaultfile)
        self.sysdefaults = self.database.copy()
        user = self.importFile(self.userdefaultfile)

        # Did we manage to find either settings file?
        self.filefound = (defaults or user)

    def importFile(self, filename):
        """Read in a configuration file made up of a=b strings."""

        try:
            f = open(filename, 'r')
        except IOError:
            # no error if the file does not exist
            return False

        for l in f:
            l = l.strip()

            # ignore comment and blank lines
            if len(l) == 0 or l[0] == '#':
                continue

            # lines should be A=B
            pos = l.find('=')

            # in case of error
            if pos == -1:
                sys.stderr.write('Error in configuration file "%s", '
                                 'line is:\n>>>%s<<<\n' % (filename, l))
                continue

            key = l[:pos].strip()
            val = l[pos+1:].strip()
            self.database[key] = eval(val)
        return True

# create the SettingDB singleton
settingdb = _SettingDB()

# write out settings at exit
atexit.register(settingdb.writeSettings)

Répondre à