• user warning: Table 'deadfood_main.drupal_tableofcontents_node_toc' doesn't exist query: SELECT toc_automatic FROM drupal_tableofcontents_node_toc WHERE nid = 20 in /home/deadfood/domains/deviant-soft.ws/public_html/sites/all/modules/tableofcontents/tableofcontents.module on line 159.
  • warning: Invalid argument supplied for foreach() in /home/deadfood/domains/deviant-soft.ws/public_html/sites/all/modules/cck/theme/content-field.tpl.php on line 35.

XmlSettingsDialog interface

Submitted by 0xd34df00d on Fri, 12/26/2008 - 17:59

BaseSettingsManager

To simplify and reduce required coding, LeechCraft provides a BaseSettingsManager class in xmlsettingsdialog/basesettingsmanager.h, whose subclasses should reimplement only two functions:

  1. virtual QSettings* BeginSettings () const;
  2. virtual void EndSettings (QSettings *settings) const;

First one allocates and returns QSettings object and is used to customize the creation process (such as desired company/program name, grouping etc.), second one is used to correctly close that settings object. It should not delete the passed object, BaseSettingsManager's code would do that instead.

Typical example would be:

  1. class XmlSettingsManager : public BaseSettingsManager
  2. {
  3. // Any additional code you want
  4. protected:
  5. virtual QSettings* BeginSettings () const;
  6. virtual void BeginSettings (QSettings*) const;
  7. };
  8.  
  9. virtual QSettings* XmlSettingsManager::BeginSettings () const
  10. {
  11. QSettings *settings = new QSettings ("Deviant", "LeechCraft");
  12. settings->beginGroup ("Very universal plugin");
  13. settings->beginGroup ("XML Settings");
  14. return settings;
  15. }
  16.  
  17. virtual void XmlSettingsManager::EndSettings (QSettings *settings) const
  18. {
  19. settings->endGroup ();
  20. settings->endGroup ();
  21. }

XmlSettingsDialog. Initialization.

I'll explain the way I do it, but if you get it, you'll be able to write as you want. I usually write those settings managers as singletons as that's common sense — there is usually only one settings manager in every plugin.

To add the settings dialog with bundled parser to your plugin:

  1. #include <xmlsettingsdialog/xmlsettingsdialog.h>
  2. class MyUniversalPlugin : // any classes you inherit from
  3. {
  4. // Lots of your code
  5. XmlSettingsDialog *XmlSettingsDialog_;
  6. // Even more here
  7. };
  8.  
  9. // As you now, this function is called when main LeechCraft app loads your plugin.
  10. void MyUniversalPlugin::Init ()
  11. {
  12. // Initialization code, settings aren't used yet
  13. XmlSettingsDialog_ = new XmlSettingsDialog (this);
  14. XmlSettingsDialog_->RegisterObject (XmlSettingsManager::Instance (), "pathtofilewithsettings.xml");
  15. // Other initialization code where settings should be ready
  16. }

XmlSettingsDialog currently supports only one settings manager object per instance, that limitation would be taken away in near future. There is no correct check now if the registered class is actually a settings manager, so it's entirely your fault when you'll register wrong class. The program wouldn't fail, but you will possibly loose some data.

Using settings

Dynamic properties for settings in XML file are automatically created upon registering settings manager object. So imagine you have a setting ServerAddress (a QString), and ServerPort (integer). To query them somewhere in your code, do the following:

  1. QString address = XmlSettingsManager::Instance ()->property ("ServerAddress").toString ();
  2. int port = XmlSettingsManager::Instance ()->property ("ServerPort").toInt ();

Setting properties from program is of course also possible:

  1. XmlSettingsManager::Instance ()->setProperty ("ServerAddress", address);
  2. XmlSettingsManager::Instance ()->setProperty ("ServerPort", 80);

As both property () and setProperty () use QVariant, make sure your property types work well with it. See the QVariant's docs about it.

Remember that setting the property from code would result in immediate flushing to QSettings, so setting it frequently would result in performance degradation. Future versions would have some kind of delay to accumulate changes.

Retrieving and storing your own settings

You may want to store your own settings not defined in XML file. LeechCraft's settings framework is suitable for that as well.

Imagine you want to get "size" property on startup to resize your plugin's window to previous size. As this is property is not described in XML Settings file, it is not known to XmlSettingsManager, and XmlSettingsManager::Instance ()->property ("size") would return just an invalid QVariant.

A common practice is to check if the returned value is valid, and if it not, use some predefined. So BaseSettingsManager defines a special
Property (const QString& propName, const QVariant& def) member function. If propName already exists, it just returns it's value, otherwise it creates this property, sets it to def and returns def back.

So, our geometry-managing code would look like this:

  1. QSize size = XmlSettingsManager::Instance ()->Property ("size", QSize (800, 600)).toSize ();
  2. resize (size);

Storing your own settings isn't different at all. Remember again that setting the property from code would result in immediate flushing to QSettings, so setting it frequently would result in performance degradation. Future versions would have some kind of delay to accumulate changes.

Subscribing to changes

Program often wants to know when a property changes either by user or by some other code. Imagine user changes port range for the BitTorrent client, and you should somehow rebind to one of those new ports. There are three solutions of this problem.

  • You could say everytime:
    Restart the program for the changes to take effect.

    Reminds something, doesn't it?

  • You could start a timer and query XmlSettingsManager for settings for example once a second. That's quite bad, because there is a slight delay after changes are made and before they are taken into account, sometimes it's critical, especially for good GUI applications. You also should waste resources to query all the properties, sometimes it's also critical. That's not also straight-forward and is even maybe error-prone;
  • You could use the "subscribing" feature of XmlSettingsManager.

Let us consider the third approach.

XmlSettingsManager has a member function
RegisterObject (const QByteArray& propName, QObject* object, const QByteArray& funcName)
Where:

  • propName — name of property you subscribe to;
  • funcName — name of the function that should be called when the property changes;
  • object — object on which -+XmlSettingsManager+- would operate.

funcName should be known to Qt's metaobjects, so it should be a slot. It is always called without any parameters, and it's entirely underlying's code job to retrieve needed settings.

So, following code would do the trick:

  1. class SettingsWatcher : public QObject
  2. {
  3. Q_OBJECT
  4. public slots:
  5. // ...
  6. void onPortRangeChanged ();
  7. // ...
  8. };
  9. // ...
  10. void SettingsWatcher::onPortRangeChanged ()
  11. {
  12. // Get new port range and rebind.
  13. }
  14. // ...
  15. // Or somewhere else, but recommended here
  16. void YourVeryCoolPlugin::Init ()
  17. {
  18. // ...
  19. // SettingsWatcher is a singleton, but it's not usually required
  20. // to create a separate holder for slots
  21. XmlSettingsManager::Instance ()->RegisterObject ("PortRange", SettingsWatcher::Instance (), "onPortRangeChanged");
  22. // ...
  23. }