Archive:Development/Tutorials/Using KConfig XT (zh CN)

From KDE TechBase
Revision as of 10:38, 12 December 2010 by Shen (talk | contribs) (Created page with '{{Template:I18n/Language Navigation Bar|Development/Tutorials/Using_KConfig_XT}} {{TutorialBrowser (zh_CN)| series=KConfig| pre=[[../KConfig|KConfig入门]]<br>[[../CMake|CMake...')
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)


Development/Tutorials/Using_KConfig_XT

Template:TutorialBrowser (zh CN)

使用KConfig XT

Original Author: Zack Rusin <[email protected]>

本教程介绍了Kconfig XT配置框架的主要概念,并演示了如何在应用程序里快速使用它。本文假设读者已经开发过KDE应用程序,并且熟悉KConfig。同时也要求了解XML及XML Schema的概念。

KConfig XT框架背后的主要想法是,更方便地管理庞大的KDE安装,让应用程序开发者生活得更惬意。

这个新框架有四个基本部分:

  • KConfigSkeleton - libkdecore里的一个类。提供了对配置项的便捷操作
  • XML文件,包含了配置项的信息(.kcfg文件)
  • 一个ini格式的文件,配置生产代码时的细节(.kcfgc文件)
  • kconfig_compiler,从.kcfg.kcfgc文件中生成C++代码。自动生成的类继承于KConfigSkeleton,并且为应用程序提供了API来访问它的配置项。
Note
本教程中里KConfig XT框架的高级可选特性及其描述将标记为斜体。如果您首次阅读本教程时跳过了它们,请在必要时回顾这些内容。


.kcfg 结构

{{path}.kcfg}}文件的结构由它的XML Schema描述(kcfg.xsd - 可从这里或者kdecore库里下载)。请在继续阅读本教程之前,略读一遍这个小文件。

Lets create a simple .kcfg file. Please reference the code below as we go through each step. 首先让我们来创建一个简单的.kcfg文件,请参看下面这段示例,来学习每个步骤。

<?xml version="1.0" encoding="UTF-8"?> <kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
                         http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
 <kcfgfile name="kjotsrc"/>
 <include>kglobalsettings.h</include>
 <group name="kjots">
   <entry name="SplitterSizes" type="IntList">
     <label>How the main window is divided.</label>
   </entry>
   <entry name="Width" type="Int">
     <label>Width of the main window.</label>
     <default>600</default>
   </entry>
   <entry name="Height" type="Int">
     <label>Height of the main window.</label>
     <default>400</default>
   </entry>
   <entry name="OpenBooks" type="StringList">
     <label>All books that are opened.</label>
   </entry>
   <entry name="CurrentBook" type="String">
     <label>The book currently opened.</label>
   </entry>
   <entry name="Font" type="Font">
     <label>The font used to display the contents of books.</label>
     <default code="true">KGlobalSettings::generalFont()</default>
   </entry>
 </group>

</kcfg>

  • 使用你最爱的文本编辑器打开 your_application_name.kcfg 文件(记得将your_application_name要替换成实际的应用程序名哦~)。
  • 首先给该文件添加<kcfgfile>标签,该标签决定了配置项数据存储于哪个KConfig文件中。这里有三种可能:
    1. 如果&t;kcfgfile>标签没有任何属性,那么自动生成的代码将使用程序默认的KConfig文件(通常是$HOME/.kde/config/<appname>rc)。
    2. 使用“name”属性可以手动指定一个文件名。如果“name”的值不是绝对路径,那么文件将在KDE默认配置文件夹中存放(一般是在$HOME/.kde/config/)。
    3. 如果你希望在程序运行时决定配置文件的路径,那么请使用<kcfgfile arg="true">。这个会让生成的代码里的构造函数使用KSharedConfig::Ptr作为参数,这就允许你能够创建多个实例,指向不同的文件。
  • 添加可选的<include>标签,以便代码编译阶段计算默认值时,能够包含指定的C++头文件,
  • XML文件中得其他<entry>由<group>标签来分组,描述了配置文件中的对应分组。
    1. 每个独立的<entry>至少得有一个name属性或者key属性。key属性用作配置文件中的键名。name属性则用来作为代码中的变量名、标识符等。如果不提供key属性,那么name属性将作为配置文件中的键名;如果提供了key属性,但是不提供name属性,那么会自动将key属性的内容去掉空白符后作为name属性值。
    2. 务必添加<label>、<tooltip>和<whatsthis>标签来描述您的应用程序里的配置项。<label>标签是简短的表述,而<tooltip>标签和<whatsthis>标签则是详细的描述与说明。这些标签很重要,例如系统管理员使用KConfigEditor工具在网络上配置机器时就需要这些描述信息。请注意,如果不再你的.kcfgc文件中配置SetUserTexts=true,那这些选项会被忽略(请查阅下面章节)。
    3. 每个<entry>必须有个类型(type属性)。目前可用的类型有:String, Url, StringList, Font, Rect, Size, Color, Point, Int, UInt, Bool, Double, DataTime, Int64, UInt64,Password。除了这些基本类型以外,以下特殊类型也是支持的:
      • Path - 其实还是一个字符串,但是会特别作为文件路径处理而已。对于那些在家目录下的路径,存储到配置文件里时会使用$HOME作为前缀。
      • Enum - 枚举类型。可能的枚举类型要在<choices>标签中提供。应用程序以整数型来访问这些枚举值,但以字符串形式存储到配置文件中。即使以后添加更多的枚举值也不会破坏兼容性。
      • IntList - 整数列表。这将使得提供给应用程序的数据格式是QList<int>。在存储QSplitter的位置时很方便。
      • PathList - 路径列表。没啥好说得。
    4. min和max属性能够用来限制整数型选项的取值范围。
    5. 每个<entry>还可以有个<default>标签来指定默认值。当任何配置文件中都没有指定该选项的值时就使用这个默认值。默认值将被解释为字面常量。如果某默认值需要被计算,或者它是某个函数调用得返回值,那<default>标签应该包含一个code="true"属性,这样<default>标签里的内容就会当作C++表达式来处理。
    6. 若还需要代码来计算默认值,那可在<code>标签中提供。该标签里的内容将被原样插入到代码中。典型的用途是,计算一个通用的默认值,给接下来的多处<entry>使用。
    7. 还可以,给<entry>标签添加hidden属性。可选的值是true和false。

.kcfgc文件

创建完一个.kcfg文件后,还需要创建一个.kcfgc文件来描述C++代码的生成细节。.kcfgc文件是一个简单的ini格式文件,使用"entry=value"格式。可以按照以下的步骤来创建一个简单的.kcfgc文件:

  1. 使用你喜欢得文本编辑器打开一个新文件。
  2. 首先添加一行"File=your_application_name.kcfg",指定你的应用程序得配置选项的位置。
  3. 添加一行"ClassName=YourConfigClassName",指明由.kcfg文件生成的代码的类名。请记住,生成的类将继承于KConfigSkeleton类。也请确保这个YourConfigClassName类名未在您的应用程序里使用。将此文件保存为yourconfigclassname.kcfgc。这将确保yourconfigclassname.{h,cpp}的生成,您得配置类也在其中。
  4. 添加其他可选项。也许您得应用程序会用到。有以下几种:
    • NameSpace - 指定配置类所在的名字空间
    • Inherits - 继承你的自定义类
    • Singleton - 指定您得配置类是否为单例模式
    • MemberVariables - 指出成员变量得访问级别。默认是private。
    • ItemAccessors - 与上面那选项有关。如果成员变量是public,那么生成成员变量的get方法就没啥意义了。默认,生成成员变量的get方法。
    • Mutators - 类似于上面那个,但是变成了是否生成成员变量的set方法。
    • GlobalEnums - specifies whether enums should be class wide of whether they should be always explicitly prefixed with their type name,
    • UseEnumTypes - 指明在set方法、get方法、信号函数中,枚举值是以int型来传递,还是以enum xxx枚举型传递。
    • SetUserTexts - 指明是否处理<label>、<tooltip>、<whatsthis>等标签。这个选项能让KConfigDialog之类的工具利用这几个标签。默认,这仨标签会被忽略。

更详细的kconfig_compiler说明: [1]

调整 CMakeLists.txt 文件

After creating the .kcfg and .kcfgc files the next step is to adjust the build to let kconfig_compiler generate the required class at compile time. For in-source builds, doing this is trivial and requires only one step, adding this two lines to the CMakeLists.txt file example (asuming your files are named settings.kcfg and settings.kcfgc):

kde4_add_kcfg_files(<project name>_SRCS settings.kcfgc)
install(FILES settings.kcfg DESTINATION ${KCFG_INSTALL_DIR})

Alternatively, if a .moc file needs to be generated before compiling the generated source code, use

kde4_add_kcfg_files(<project name>_SRCS GENERATE_MOC settings.kcfgc)
install(FILES settings.kcfg DESTINATION ${KCFG_INSTALL_DIR})

This assures that the configuration class is properly generated (kde4_add_kcfg_files) and that the .kcfg is installed so it can be used by tools like the KConfigEditor (install).

out-of-source builds

Out-of-source builds require one more step.

The problem: In out-of-source builds, the code that is generated by kconfig_compiler is saved in the build tree. So how can we still include the header generated by kconfig_compiler with a simple #include "settings.h"?

The solution: Probably you have yet a line similar to the following line in your CMakeLists.txt:

include_directories(${QT_INCLUDE} ${KDE4_INCLUDES})

Add the variable CMAKE_CURRENT_BINARY_DIR, so that you get something like this:

include_directories(${QT_INCLUDE} ${KDE4_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})

Notice that you should call include_directories only once in your project. (When you call include_directories in your base source directory, but than call it another time in a subdirectory (added by add_subdirectory), then the second call will be ignored.)

Use and Dialogs

After making all of the above changes you're ready to use KConfig XT. The kconfig_compiler generated header file will have the name equal to the name of the .kcfg file but with a ".h" extension. Simply include that file wherever you want to access your configuration options.

The use will depend on whether you have added the Singleton=true entry to your .kcfgc file.

One the nicest features of the KConfig XT is its seamless integration with the Qt Designer generated dialogs. You can do that by using KConfigDialog. The steps to do that are as follows:

  1. Create the KConfigDialog and pass the instance of your configuration data as one of the arguments. The construct would look like the following example:

KConfigDialog* dialog = new KConfigDialog(

        this, "settings", YourAppSettings::self() );

assuming that YourAppSettings is the value of the ClassName variable from the kcfgc file and the settings class is a singelton.

  1. In Qt Designer create widgets which should be used to configure your options. In order to make those widgets interact with the kcfg you have to name each one of them using the following scheme:
    1. Prefix the Name of the widget which should control one of the options with "kcfg_"
    2. Append the "name" attribute value from your kcfg file which corresponds to option the given widget should control.
  2. Add the Qt Designer generated widget to the KConfigDialog.
  3. Show the dialog when you're done.

Example

Here's an example usage of KConfig XT for the application named Example. With the following example.kcfg file:

<?xml version="1.0" encoding="UTF-8"?> <kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
                         http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
 <kcfgfile name="examplerc"/>
 <group name="network">
   <entry name="ServerName" type="String">
     <label>Defines the sample server.</label>
   </entry>
   <entry name="Port" type="Int">
     <label>Defines the server port</label>
     <default>21</default>
     <min>20</min>
     <max>990</max>
   </entry>
 </group>

</kcfg>

And here's how to actually use the generated class. for the given kcfgc file.

File=example.kcfg ClassName=ExampleSettings Singleton=true Mutators=true

The header files wouldn't change, but the cpp files must now contain the following code to access and store the configuration data :

...

  1. include <ExampleSettings.h>

... void ExampleClass::readConfig() {

       m_server  = ExampleSettings::serverName();
       m_port    = ExampleSettings::port();

} void ExampleClass::saveSettings() {

       ExampleSettings::setServerName( m_server );
       ExampleSettings::setPort( m_port );
       ExampleSettings::self()->writeConfig();

}

self() returns the current instance of the object. You need to call writeConfig() this way, since it's a virtual method.

To add a dialog you need to create a Qt Designer widget with the widget names corresponding to the names of the options they should edit and prefixed with "kcfg_". It could be something along the lines of:

And you can use the dialog with the following code:

//An instance of your dialog could be already created and could be // cached, in which case you want to display the cached dialog // instead of creating another one if ( KConfigDialog::showDialog( "settings" ) )

 return;

// KConfigDialog didn't find an instance of this dialog, so lets // create it : KConfigDialog* dialog = new KConfigDialog(this, "settings",

                                         ExampleSettings::self());

ExampleDesignerWidget* confWdg =

                 new ExampleDesignerWidget( 0, "Example" );

dialog->addPage( confWdg, i18n("Example"), "example" );

//User edited the configuration - update your local copies of the //configuration data connect( dialog, SIGNAL(settingsChanged()),

        this, SLOT(updateConfiguration()) );

dialog->show();

And that's all it takes. You can have a look at KReversi and KTron code in the kdegames module to see a live example of KConfig XT!

Common Pitfalls and Tips

  • Do not forget to add the "type" attribute to the "entry" tag in your .kcfg file.
  • Always try to add both the <label>, <whatsthis> and <tooltip> tags to each entry.
  • Putting the MemberVariables=public in your .kcfgc is usually a bad idea - you'll avoid accidental changes to those members by using the aggregation and forcing the use of the mutators.
  • If your application doesn't have one central object (created before and destructed after; all others) then always put the Singleton=true entry in your .kcfgs file.

Additional References