Archive:Development/Tutorials/Using KConfig XT (zh CN)
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来访问它的配置项。
.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文件中。这里有三种可能:
- 如果<kcfgfile>标签没有任何属性,那么自动生成的代码将使用程序默认的KConfig文件(通常是$HOME/.kde/config/<appname>rc)。
- 使用“name”属性可以手动指定一个文件名。如果“name”的值不是绝对路径,那么文件将在KDE默认配置文件夹中存放(一般是在$HOME/.kde/config/)。
- 如果您希望在程序运行时决定配置文件的路径,那么请使用<kcfgfile arg="true">。这个会让生成的代码里的构造函数使用KSharedConfig::Ptr作为参数,这就允许您能够创建多个实例,指向不同的文件。
- 添加可选的<include>标签,以便代码编译阶段计算默认值时,能够包含指定的C++头文件,
- XML文件中的其他<entry>由<group>标签来分组,描述了配置文件中的对应分组。
- 每个独立的<entry>至少得有一个name属性或者key属性。key属性用作配置文件中的键名。name属性则用来作为代码中的变量名、标识符等。如果不提供key属性,那么name属性将作为配置文件中的键名;如果提供了key属性,但是不提供name属性,那么会自动将key属性的内容去掉空白符后作为name属性值。
- 务必添加<label>、<tooltip>和<whatsthis>标签来描述您的应用程序里的配置项。<label>标签是简短的表述,而<tooltip>标签和<whatsthis>标签则是详细的描述与说明。这些标签很重要,例如系统管理员使用KConfigEditor工具在网络上配置机器时就需要这些描述信息。请注意,如果不在您的.kcfgc文件中配置SetUserTexts=true,那这些选项会被忽略(请查阅下面章节)。
- 每个<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 - 路径列表。没啥好说的。
- min和max属性能够用来限制整数型选项的取值范围。
- 每个<entry>还可以有个<default>标签来指定默认值。当任何配置文件中都没有指定该选项的值时就使用这个默认值。默认值将被解释为字面常量。如果某默认值需要被计算,或者它是某个函数调用的返回值,那<default>标签应该包含一个code="true"属性,这样<default>标签里的内容就会当作C++表达式来处理。
- 若还需要代码来计算默认值,那可在<code>标签中提供。该标签里的内容将被原样插入到代码中。典型的用途是,计算一个通用的默认值,给接下来的多处<entry>使用。
- 还可以,给<entry>标签添加hidden属性。可选的值是true和false。
.kcfgc文件
创建完一个.kcfg文件后,还需要创建一个.kcfgc文件来描述C++代码的生成细节。.kcfgc文件是一个简单的ini格式文件,使用"entry=value"格式。可以按照以下的步骤来创建一个简单的.kcfgc文件:
- 使用您喜欢的文本编辑器打开一个新文件。
- 首先添加一行"File=your_application_name.kcfg",指定您的应用程序的配置选项的位置。
- 添加一行"ClassName=YourConfigClassName",指明由.kcfg文件生成的代码的类名。请记住,生成的类将继承于KConfigSkeleton类。也请确保这个YourConfigClassName类名未在您的应用程序里使用。将此文件保存为yourconfigclassname.kcfgc。这将确保yourconfigclassname.{h,cpp}的生成,您的配置类也在其中。
- 添加其他可选项。也许您的应用程序会用到。有以下几种:
- 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来实现。步骤如下:
- 创建KConfigDialog,然后将您的配置数据对象作为参数传递给它。构造函数的调用类似下面的代码:
KConfigDialog* dialog = new KConfigDialog(
this, "settings", YourAppSettings::self() );
这里假设YourAppSettings是kcfgc中ClassName变量的值。并且该配置类是单例模式。
- 在Qt设计器中创建widget来配置您的程序选项。为了让这些widget与kcfg集成,您需要按照如下的规则来命名widget里的变量:
- 控制某选项的widget的名字必须以"kcfg_"开头
- 紧接着是name属性的值。这个name属性是该widget对应的kcfg中的选项的name属性。
- 将该Qt设计器生成的widget添加到KConfigDialog中。
- 搞定后,显示该对话框。
示例
此处使用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模块里面的KReversi和KTron的源代码,看看KConfig XT的真实的例子!
常见的陷阱和技巧
- 别忘了给.kcfg文件里的<entry>标签添加type属性。
- 给所有的<entry>增加<label>、<whatsthis>、<tooltip>等标签。
- 在.kcfgc文件中添加MemberVariables=public通常是脑残的做法。碰到变量的访问竞争时,您就可以去领杯具了。
- 如果您的应用程序中没有一个中心对象(在其他对象生成之前生成,其他对象销毁后才销毁),那么请务必给.kcfgs文件添加上Singleton=true。