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

From KDE TechBase
(Created page with '{{Template:I18n/Language Navigation Bar|Development/Tutorials/Using_KConfig_XT}} {{TutorialBrowser (zh_CN)| series=KConfig| pre=[[../KConfig|KConfig入门]]<br>[[../CMake|CMake...')
 
No edit summary
Line 8: Line 8:
next=None|
next=None|


name=Using KConfig XT|
name=使用KConfig XT|


reading=[http://api.kde.org/4.0-api/kdelibs-apidocs/kdecore/html/kconfig_compiler.html The KDE Configuration Compiler (KDE 4)]<br>[http://api.kde.org/3.5-api/kdelibs-apidocs/kdecore/html/kconfig_compiler.html The KDE Configuration Compiler (KDE 3)]
reading=[http://api.kde.org/4.0-api/kdelibs-apidocs/kdecore/html/kconfig_compiler.html KDE配置编译器 (KDE 4)]<br>[http://api.kde.org/3.5-api/kdelibs-apidocs/kdecore/html/kconfig_compiler.html KDE配置编译器 (KDE 3)]


}}
}}


== 使用KConfig XT ==
== 使用KConfig XT ==
''Original Author'': Zack Rusin <[mailto:[email protected] [email protected]]>
''原作者'': Zack Rusin <[mailto:[email protected] [email protected]]>


本教程介绍了Kconfig XT配置框架的主要概念,并演示了如何在应用程序里快速使用它。本文假设读者已经开发过KDE应用程序,并且熟悉KConfig。同时也要求了解XML及XML Schema的概念。
本教程介绍了Kconfig XT配置框架的主要概念,并演示了如何在应用程序里快速使用它。本文假设读者已经开发过KDE应用程序,并且熟悉KConfig。同时也要求了解XML及XML Schema的概念。
Line 34: Line 34:
{{path}.kcfg}}文件的结构由它的XML Schema描述(kcfg.xsd - 可从[http://www.kde.org/standards/kcfg/1.0/kcfg.xsd 这里]或者[http://websvn.kde.org/trunk/KDE/kdelibs/kdecore/kconfig_compiler/kcfg.xsd?view=markup kdecore库里]下载)。请在继续阅读本教程之前,略读一遍这个小文件。
{{path}.kcfg}}文件的结构由它的XML Schema描述(kcfg.xsd - 可从[http://www.kde.org/standards/kcfg/1.0/kcfg.xsd 这里]或者[http://websvn.kde.org/trunk/KDE/kdelibs/kdecore/kconfig_compiler/kcfg.xsd?view=markup kdecore库里]下载)。请在继续阅读本教程之前,略读一遍这个小文件。


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


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


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


After creating the .kcfg and .kcfgc files the next step is to adjust the
创建完.kcfg文件和.kcfgc文件后,下一步是调整编译参数,让kconfig_compiler在编译时生成必要的类。对于在源码目录里的编译,调整很少,只需要添加下面这两行代码到CMakeLists.txt中(这里假设你的配置文件叫做settings.kcfg和settings.kcfgc):
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)
  kde4_add_kcfg_files(<project name>_SRCS settings.kcfgc)
  install(FILES settings.kcfg DESTINATION ${KCFG_INSTALL_DIR})
  install(FILES settings.kcfg DESTINATION ${KCFG_INSTALL_DIR})


Alternatively, if a .moc file needs to be generated before compiling the generated source code, use
另外,如果在编译生成的源代码之前要生成.moc文件,使用下面这两行代码:


  kde4_add_kcfg_files(<project name>_SRCS GENERATE_MOC settings.kcfgc)
  kde4_add_kcfg_files(<project name>_SRCS GENERATE_MOC settings.kcfgc)
  install(FILES settings.kcfg DESTINATION ${KCFG_INSTALL_DIR})
  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).
以上代码确保了配置类自动生成(kde4_add_kcfg_files),并且.kcfg文件正确地安装以便被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"''?
'''问题:'''对于在源码目录外的编译,kconfig_compiler所生成的代码被保存在了编译目录中。此时,如何在源代码中简单地使用''#include "settings.h"''来包含kconfig_compiler所生成的头文件呢?


'''The solution:''' Probably you have yet a line similar to the following line in your CMakeLists.txt:
'''方案:'''也许你的CMakeLists.txt里有一行下面这样的代码:


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


Add the variable ''CMAKE_CURRENT_BINARY_DIR'', so that you get something like this:
给它加上一个变量''CMAKE_CURRENT_BINARY_DIR'',结果如下:


  include_directories(${QT_INCLUDE} ${KDE4_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
  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.)
需要注意的是,在你的工程中应当只调用一次''include_directories''。(如果你在源码根目录里调用了一次''include_directories'',但是又在其他的子目录(由''add_subdirectory''添加的目录)中调用了一次,那么第二次''include_directories''调用将会被忽略。)


== Use and Dialogs ==
== 使用对话框 ==


After making all of the above changes you're ready to use KConfig XT. The
做完以上的步骤之后,你已经为使用KConfig XT框架准备完毕了。kconfig_compiler将生成与.kcfg文件同名的代码文件(后缀替换成".h")。在你需要访问配置项的源代码中''#include''该头文件即可。
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 <tt>Singleton=true</tt> entry to your .kcfgc file.
只有在.kcfgc文件中添加了<tt>Singleton=true</tt>,你才能使用本特性。


One the nicest features of the KConfig XT is its seamless integration with the Qt
KConfig XT的最强之处在于,它与Qt设计器生成的对话框能无缝集成。你可是通过使用{{class|KConfigDialog}}来实现。步骤如下:
Designer generated dialogs. You can do that by using {{class|KConfigDialog}}. The
steps to do that are as follows:


# 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,然后将你的配置数据对象作为参数传递给它。构造函数的调用类似下面的代码:


<code cppqt>
<code cppqt>
Line 159: Line 154:
</code>
</code>


assuming that YourAppSettings is the value of the ClassName variable from the kcfgc file and the settings class is a singelton.
这里假设YourAppSettings是kcfgc中ClassName变量的值。并且该配置类是单例模式。
# 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:
# 在Qt设计器中创建widget来配置你的程序选项。为了让这些widget与kcfg集成,你需要按照如下的规则来命名widget里的变量:
## Prefix the Name of the widget which should control one of the options with "kcfg_"
## 控制某选项的widget的名字必须以"kcfg_"开头
## Append the "name" attribute value from your kcfg file which corresponds to option the given widget should control.
## 紧接着是name属性的值。这个name属性是该widget对应的kcfg中的选项的name属性。
# Add the Qt Designer generated widget to the KConfigDialog.
# 将该Qt设计器生成的widget添加到KConfigDialog中。
# Show the dialog when you're done.
# 搞定后,显示该对话框。


== Example ==
== 示例 ==


Here's an example usage of KConfig XT for the application named Example.
此处使用KConfig XT框架的例子。应用程序的名字叫做Example。
With the following example.kcfg file:
 
它的.kcfg文件如下:


<code xml>
<code xml>
Line 192: Line 188:
</code>
</code>


And here's how to actually use the generated class. for the given kcfgc file.
然后配置生成的配置类的细节。.kcfgc文件如下:


<code ini>
<code ini>
Line 201: Line 197:
</code>
</code>


The header files wouldn't change, but the cpp files must now contain the
程序源代码的头文件不需要变动,只需要在cpp文件中包含如下的代码来访问和修改配置项数据:
following code to access and store the configuration data :


<code cppqt>
<code cppqt>
Line 219: Line 214:
</code>
</code>


self() returns the current instance of the object. You need to call writeConfig() this way, since it's a virtual method.
self()返回的是对象的实例。所有的writeConfig()都得这样调用,因为它是一个虚函数方法。(其他的读取和设置选项的方法是静态方法。)


To add a dialog you need to create a Qt Designer widget with the widget names
要想增加一个配置对话框,你的Qt设计器生成的widget的名字需要与它们对应的选项的名字相同,并且加上"kcfg_"前缀。例如:
corresponding to the names of the options they should edit and prefixed with "kcfg_".
It could be something along the lines of:


[[Image:kconfigxt.png|center|256px]]
[[Image:kconfigxt.png|center|256px]]


And you can use the dialog with the following code:
然后你就可以照着下面的代码来使用该配置对话框:


<code cppqt>
<code cppqt>
//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" ) )
if ( KConfigDialog::showDialog( "settings" ) )
   return;
   return;


// KConfigDialog didn't find an instance of this dialog, so lets
// KConfigDialog并没有找到对话框的缓存,所有创建新的吧:
// create it :
KConfigDialog* dialog = new KConfigDialog(this, "settings",
KConfigDialog* dialog = new KConfigDialog(this, "settings",
                                           ExampleSettings::self());
                                           ExampleSettings::self());
Line 245: Line 237:
dialog->addPage( confWdg, i18n("Example"), "example" );
dialog->addPage( confWdg, i18n("Example"), "example" );


//User edited the configuration - update your local copies of the
// 用户更改对话框里的配置项,然后你更新本地的配置
//configuration data
connect( dialog, SIGNAL(settingsChanged()),
connect( dialog, SIGNAL(settingsChanged()),
         this, SLOT(updateConfiguration()) );
         this, SLOT(updateConfiguration()) );
Line 253: Line 244:
</code>
</code>


And that's all it takes. You can have a look at [http://websvn.kde.org/trunk/KDE/kdegames/kreversi KReversi] and [http://websvn.kde.org/trunk/KDE/kdegames/ktron/ KTron] code in the [http://websvn.kde.org/trunk/KDE/kdegames kdegames module] to see a live example of KConfig XT!
这就是全部的内容。你可以查看一下[http://websvn.kde.org/trunk/KDE/kdegames kdegames模块]里面的[http://websvn.kde.org/trunk/KDE/kdegames/kreversi KReversi][http://websvn.kde.org/trunk/KDE/kdegames/ktron/ KTron]的源代码,看看KConfig XT的真实的例子!


== Common Pitfalls and Tips ==
== 常见的陷阱和技巧 ==


* Do not forget to add the "type" attribute to the "entry" tag in your .kcfg file.
* 别忘了给.kcfg文件里的&lt;entry&gt;标签添加type属性。
* Always try to add both the &lt;label&gt;, &lt;whatsthis&gt; and &lt;tooltip&gt; tags to each entry.
* 给所有的&lt;entry&gt;增加&lt;label&gt;&lt;whatsthis&gt;&lt;tooltip&gt;等标签。
* 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.
* 在.kcfgc文件中添加MemberVariables=public通常是脑残的做法。碰到变量的访问竞争时,你就可以去领杯具了。
* 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.
* 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.
* 如果你的应用程序中没有一个中心对象(在其他对象生成之前生成,其他对象销毁后才销毁),那么请务必给.kcfgs文件添加上''Singleton=true''。


== Additional References ==
== 其他参考 ==

Revision as of 15:04, 12 December 2010


Development/Tutorials/Using_KConfig_XT

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 结构

{{path}.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文件中包含如下的代码来访问和修改配置项数据:

...

  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()返回的是对象的实例。所有的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通常是脑残的做法。碰到变量的访问竞争时,你就可以去领杯具了。
  • 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.
  • 如果你的应用程序中没有一个中心对象(在其他对象生成之前生成,其他对象销毁后才销毁),那么请务必给.kcfgs文件添加上Singleton=true

其他参考