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

From KDE TechBase
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Template:TutorialBrowser (zh CN)

使用KConfig XT

原作者: 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 结构

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

首先让我们来创建一个简单的.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. 如果<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 文件

创建完.kcfg文件和.kcfgc文件后,下一步是调整编译参数,让kconfig_compiler在编译时生成必要的类。对于在源码目录里的编译,调整很少,只需要添加下面这两行代码到CMakeLists.txt中(这里假设您的配置文件叫做settings.kcfg和settings.kcfgc):

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

另外,如果在编译生成的源代码之前要生成.moc文件,使用下面这两行代码:

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

以上代码确保了配置类自动生成(kde4_add_kcfg_files),并且.kcfg文件正确地安装以便被KConfigEditor等工具使用(install)。

在源码目录外编译

对于在源码目录外编译的情况,需要多一个步骤。

问题:对于在源码目录外的编译,kconfig_compiler所生成的代码被保存在了编译目录中。此时,如何在源代码中简单地使用#include "settings.h"来包含kconfig_compiler所生成的头文件呢?

方案:也许您的CMakeLists.txt里有一行下面这样的代码:

include_directories(${QT_INCLUDE} ${KDE4_INCLUDES})

给它加上一个变量CMAKE_CURRENT_BINARY_DIR,结果如下:

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

需要注意的是,在您的工程中应当只调用一次include_directories。(如果您在源码根目录里调用了一次include_directories,但是又在其他的子目录(由add_subdirectory添加的目录)中调用了一次,那么第二次include_directories调用将会被忽略。)

使用对话框

做完以上的步骤之后,您已经为使用KConfig XT框架准备完毕了。kconfig_compiler将生成与.kcfg文件同名的代码文件(后缀替换成".h")。在您需要访问配置项的源代码中#include该头文件即可。

只有在.kcfgc文件中添加了Singleton=true,您才能使用本特性。

KConfig XT的最强之处在于,它与Qt设计器生成的对话框能无缝集成。您可是通过使用KConfigDialog来实现。步骤如下:

  1. 创建KConfigDialog,然后将您的配置数据对象作为参数传递给它。构造函数的调用类似下面的代码:
KConfigDialog* dialog = new KConfigDialog(
         this, "settings", YourAppSettings::self() );

这里假设YourAppSettings是kcfgc中ClassName变量的值。并且该配置类是单例模式。

  1. 在Qt设计器中创建widget来配置您的程序选项。为了让这些widget与kcfg集成,您需要按照如下的规则来命名widget里的变量:
    1. 控制某选项的widget的名字必须以"kcfg_"开头
    2. 紧接着是name属性的值。这个name属性是该widget对应的kcfg中的选项的name属性。
  2. 将该Qt设计器生成的widget添加到KConfigDialog中。
  3. 搞定后,显示该对话框。

示例

此处使用KConfig XT框架的例子。应用程序的名字叫做Example。

它的.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="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>

然后配置生成的配置类的细节。.kcfgc文件如下:

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

程序源代码的头文件不需要变动,只需要在cpp文件中包含如下的代码来访问和修改配置项数据:

...
#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()返回的是对象的实例。所有的writeConfig()都得这样调用,因为它是一个虚函数方法。(其他的读取和设置选项的方法是静态方法。)

要想增加一个配置对话框,您的Qt设计器生成的widget的名字需要与它们对应的选项的名字相同,并且加上"kcfg_"前缀。例如:

然后您就可以照着下面的代码来使用该配置对话框:

// 对话框的实例可能已经创建过并缓存着,
// 因此可以直接显示该缓存的对话框,而无须又创建一个
if ( KConfigDialog::showDialog( "settings" ) )
  return;

// KConfigDialog并没有找到对话框的缓存,所有创建新的吧:
KConfigDialog* dialog = new KConfigDialog(this, "settings",
                                          ExampleSettings::self());
ExampleDesignerWidget* confWdg =
                  new ExampleDesignerWidget( 0, "Example" );

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

// 用户更改对话框里的配置项,然后您更新本地的配置
connect( dialog, SIGNAL(settingsChanged()),
         this, SLOT(updateConfiguration()) );

dialog->show();

这就是全部的内容。您可以查看一下kdegames模块里面的KReversiKTron的源代码,看看KConfig XT的真实的例子!

常见的陷阱和技巧

  • 别忘了给.kcfg文件里的<entry>标签添加type属性。
  • 给所有的<entry>增加<label>、<whatsthis>、<tooltip>等标签。
  • 在.kcfgc文件中添加MemberVariables=public通常是脑残的做法。碰到变量的访问竞争时,您就可以去领杯具了。
  • 如果您的应用程序中没有一个中心对象(在其他对象生成之前生成,其他对象销毁后才销毁),那么请务必给.kcfgs文件添加上Singleton=true

其他参考