Search code examples
pythonpython-2.7pluginscalibre

How one can store user's settings in Calibre's plugin?


I am developing a Calibre's plugin and I want to store user settings (e.g. if the plugin should make a post-import conversion from one format to another - in my case: pdf to djvu).

How can I store user settings? Does Calibre have a build-in method to do this?

For example I have a dictionary prefs_org_dict with keys and values representing preferences set by an user. How can I store this data reliably and read it later?


Solution

  • The method suggested by the manual is to create the JSONConfig object and store user preferences in it.

    Basically just:

    import os
    from calibre.utils.config import JSONConfig
    
    prefs = JSONConfig(os.path('plugins', 'PLUGINNAME'))
    # JSONConfig inherits after Dict so use it as dictionary
    
    for key, val in prefs_org_dict.iteritems():
        prefs[key] = val
    prefs.commit() # explanation in 3rd section of this post
    

    But this method has some caveats:

    1. Name of the settings file. Paraphrasing manual:

      Remember that this name (i.e. 'plugins/PLUGINNAME') is also in a global namespace, so make it as unique as possible. You should always prefix your config file name with plugins/, so as to ensure you don't accidentally clobber a calibre config file.

      After you save anything to this object you can see your PLUGINNAME.json file inside Calibre's plugin's config folder (for Windows: %APPDATA%\calibre\plugins) (you can get this path programmatically using: from calibre.utils.config import config_dir and appending /plugins).

    2. Default settings.

      prefs.defaults is a dictionary which value is returned if given key doesn't exist in your prefs object. So you can create some default values for your plugin's settings, e.g.:

      prefs.defaults['postimport'] = False
      

      The main problem is when you trying to use other dict methods like .values(), .items() or .iteritems() they return "real" prefs, not defaults, i.e. for our example, if prefs['postimport'] was not further defined:

      >>> prefs.defaults['postimport']
      False
      >>> prefs.defaults.items()
      [('postimport', False)]
      >>> prefs['postimport']
      False
      >>> prefs.items()
      []
      
    3. Committing nested dict.

      If you want to use JSONConfig object as real .json storage you probably want to use nested dictionaries. For example:

      prefs['pdf'] = {}
      prefs['pdf']['convert'] = True
      

      But if you set (or delete) value to nested dictionary prefs['pdf'] it will not be saved to .json file. You have to:

      prefs.commit()
      

      to save data to file after setting them to nested dict.

    4. Constraints of JSON format.

      A few of Python features cannot be translated to JSON format, e.g. JSON has not tuples, so json.dumps translates tuples to arrays. Also in Python you can have an every hashable object (e.g. tuple, or frozenset) as a key. JSON accepts only strings.