Development/Tutorials/KConfig (de): Difference between revisions
Neverendingo (talk | contribs) m (Text replace - "<code cppqt n>" to "<syntaxhighlight lang="cpp-qt" line>") |
No edit summary |
||
(One intermediate revision by one other user not shown) | |||
Line 1: | Line 1: | ||
{{TutorialBrowser (de)| | {{TutorialBrowser (de)| | ||
Line 60: | Line 60: | ||
// Ausserhalb der normalen Konfigurations-Ressource | // Ausserhalb der normalen Konfigurations-Ressource | ||
KConfig dataResource( "data", "myapp/somefile" ); | KConfig dataResource( "data", "myapp/somefile" ); | ||
</ | </syntaxhighlight> | ||
Line 94: | Line 94: | ||
KConfig* config = KGlobal::config(); | KConfig* config = KGlobal::config(); | ||
} | } | ||
</ | </syntaxhighlight> | ||
Auf das Standard-Konfigurationsobjekt für die Anwendung wird zugegriffen, wenn kein Name definiert wurde, als das {{class|KConfig}}-Objekt erzeugt wurde. Also könnten wir stattdessen auch das tun: | Auf das Standard-Konfigurationsobjekt für die Anwendung wird zugegriffen, wenn kein Name definiert wurde, als das {{class|KConfig}}-Objekt erzeugt wurde. Also könnten wir stattdessen auch das tun: | ||
Line 105: | Line 105: | ||
KConfig config; | KConfig config; | ||
} | } | ||
</ | </syntaxhighlight> | ||
Zusätzlich könnte jede Komponente sein eigenes Konfigurationsobjekt haben. Auf dieses wird normalerweise durch die <tt>componentData()</tt> des Plugins zugegriffen, anstatt durch die <tt>{{class|KGlobal}}::mainComponent()</tt>. | Zusätzlich könnte jede Komponente sein eigenes Konfigurationsobjekt haben. Auf dieses wird normalerweise durch die <tt>componentData()</tt> des Plugins zugegriffen, anstatt durch die <tt>{{class|KGlobal}}::mainComponent()</tt>. | ||
Line 127: | Line 127: | ||
kDebug() << "next group:" << group; | kDebug() << "next group:" << group; | ||
} | } | ||
</ | </syntaxhighlight> | ||
== KSharedConfig == | == KSharedConfig == | ||
Line 137: | Line 137: | ||
<syntaxhighlight lang="cpp-qt" line> | <syntaxhighlight lang="cpp-qt" line> | ||
KSharedConfigPtr config = KSharedConfig::openConfig(); | KSharedConfigPtr config = KSharedConfig::openConfig(); | ||
</ | </syntaxhighlight> | ||
<tt>openConfig()</tt> nimmt die selben Parameter wie {{class|KConfig}}'s Konstruktoren, was es einem ermöglicht zu definieren, welche Konfigurationsdatei zu öffnen und Markierungen das Verschmelzen von nicht-<tt>config</tt>-Ressourcen zu kontrollieren. | <tt>openConfig()</tt> nimmt die selben Parameter wie {{class|KConfig}}'s Konstruktoren, was es einem ermöglicht zu definieren, welche Konfigurationsdatei zu öffnen und Markierungen das Verschmelzen von nicht-<tt>config</tt>-Ressourcen zu kontrollieren. | ||
Line 151: | Line 151: | ||
KConfigGroup generalGroup( &config, "General" ); | KConfigGroup generalGroup( &config, "General" ); | ||
KConfigGroup colorsGroup = config.group( "Colors" ); // ...oder so ähnlich... | KConfigGroup colorsGroup = config.group( "Colors" ); // ...oder so ähnlich... | ||
</ | </syntaxhighlight> | ||
Man kann{{class|KConfigGroup}} {{class|KConfig}}- oder {{class|KSharedConfig}}-Objekte übergeben. | Man kann{{class|KConfigGroup}} {{class|KConfig}}- oder {{class|KSharedConfig}}-Objekte übergeben. | ||
Line 160: | Line 160: | ||
KConfigGroup subGroup1( &generalGroup, "LessGeneral" ); | KConfigGroup subGroup1( &generalGroup, "LessGeneral" ); | ||
KConfigGroup subGroup2 = colorsGroup.group( "Dialogs" ); | KConfigGroup subGroup2 = colorsGroup.group( "Dialogs" ); | ||
</ | </syntaxhighlight> | ||
== Lesen von Einträgen == | == Lesen von Einträgen == | ||
Line 175: | Line 175: | ||
QString path = generalGroup.readPathEntry( "SaveTo", | QString path = generalGroup.readPathEntry( "SaveTo", | ||
defaultPath ); | defaultPath ); | ||
</ | </syntaxhighlight> | ||
Wie man hier oben sehen kann, kann man Lesevorgänge von verschiedenen {{class|KConfigGroup}}-Objekten, die auf dem selben {{class|KConfig}}-Objekt erstellt wurden, problemlos mischen. Die Lese-Methoden nehmen den Schlüssel, der Gross- und Kleinschreibung unterscheidet, als erstes Argument und den Standardwert als zweites Argument. Dieses Argument bestimmt auch welche Art von Daten, also z.B. eine Farbe in Zeile 3, erwartet wird und auch die Art von Objekt, die zurückgegeben werden soll.Das zurückgegebene Objekt wird in eine {{qt|QVariant}} gepackt um diese Hexerei zu ermöglichen. | Wie man hier oben sehen kann, kann man Lesevorgänge von verschiedenen {{class|KConfigGroup}}-Objekten, die auf dem selben {{class|KConfig}}-Objekt erstellt wurden, problemlos mischen. Die Lese-Methoden nehmen den Schlüssel, der Gross- und Kleinschreibung unterscheidet, als erstes Argument und den Standardwert als zweites Argument. Dieses Argument bestimmt auch welche Art von Daten, also z.B. eine Farbe in Zeile 3, erwartet wird und auch die Art von Objekt, die zurückgegeben werden soll.Das zurückgegebene Objekt wird in eine {{qt|QVariant}} gepackt um diese Hexerei zu ermöglichen. | ||
Line 192: | Line 192: | ||
colorGroup.writeEntry( "background", color ); | colorGroup.writeEntry( "background", color ); | ||
generalGroup.config()->sync(); | generalGroup.config()->sync(); | ||
</ | </syntaxhighlight> | ||
Beachten sie die Benutzung von <tt>writePathEntry</tt> und wie die Art von Objekt, das wir benutzen, wie zum Beispiel {{qt|QColor}} auf Zeile 3, bestimmt, wie die Daten angeordnet werden. Zusätzlich, wenn wir mit den Einträgen fertig sind, muss <tt>sync()</tt> auf das Konfigurationsobjekt ausgeführt werden, um es auf die Festplatte zu speichern. Man kann auch einfach warten, bis das Objekt zerstört wird, was falls nötig automatisch einen <tt>sync()</tt> auslöst. | Beachten sie die Benutzung von <tt>writePathEntry</tt> und wie die Art von Objekt, das wir benutzen, wie zum Beispiel {{qt|QColor}} auf Zeile 3, bestimmt, wie die Daten angeordnet werden. Zusätzlich, wenn wir mit den Einträgen fertig sind, muss <tt>sync()</tt> auf das Konfigurationsobjekt ausgeführt werden, um es auf die Festplatte zu speichern. Man kann auch einfach warten, bis das Objekt zerstört wird, was falls nötig automatisch einen <tt>sync()</tt> auslöst. | ||
Line 223: | Line 223: | ||
kDebug() << "Der URL Eintrag in der Gruppe 'General' ist unveränderlich"; | kDebug() << "Der URL Eintrag in der Gruppe 'General' ist unveränderlich"; | ||
} | } | ||
</ | </syntaxhighlight> | ||
Das kann in gewissen Situationen, wo eine Aktion ausgeführt werden soll, falls ein Objekt unveränderlich ist. | Das kann in gewissen Situationen, wo eine Aktion ausgeführt werden soll, falls ein Objekt unveränderlich ist. |
Latest revision as of 15:46, 14 July 2012
Anleitungsserie | KConfig |
Voriges Kapitel | Keine |
Nächstes Kapitel | Benutzen von KConfig XT |
Weiterführende Texte | Keine |
Navigation | Deutsche Startseite |
Dieser Abschnitt muss verbessert werden: Bitte hilf mit, verwirrende Abschnitte zu bereinigen und Abschnitte zu reparieren die ein todo beinhalten
Zusammenfassung
Dieser Artikel beschäftigt sich mir dem KDE Einstellungdaten-System, angefangen mit einer Übersicht über den grundlegenden Aufbau aus der Sicht eines Entwicklers. Es wirft ausserdem einen Blick auf jede der Klassen, die für die Entwicklung von Anwendungen relevant sind und fährt dann mit kiosk (Benutzer- und Gruppenprofile) Integration fort.
Grundlegender Aufbau
KConfig ist ausgelegt um das Konzept vom eigentlichen Speichern und Auffinden von Konfigurationsdaten hinter eine Programmierschnittstelle zu abstrahieren die es ermöglicht, auf einfache Weise Informationen abzurufen und zu setzen. Wo oder in welcher Form Daten gespeichert werden ist für eine Anwendung die KConfig benutzt irrelevant.
Das behält alle KDE Anwendungen konsistent in ihrer Art, Konfigurationen handzuhaben und nimmt einem Anwendungs-Entwickler die Arbeit so ein System von Grund auf neu zu schreiben, was ein sehr fehleranfälliger Prozess sein kann.
Jedes KConfig Objekt verkörpert ein einziges Konfigurationsobjekt. Jedes Konfigurationsobjekt ist durch seinen einzigartigen Namen gekennzeichnet und kann von mehreren lokalen oder externen Daten oder Services gelesen werden. Jede Anwendung hat standardmässig ein zugehöriges Konfigurationsobjekt und es gibt auch ein globales Konfigurationsobjekt.
Diese Konfigurationsobjekte sind nach einer zweistufigen Hierarchie getrennt: Gruppen und Schlüssel. Ein Konfigurationsobjekt kann eine beliebige Anzahl an Gruppen haben und jede Gruppe kann einen oder mehrere Schlüssel mit zugehörigen Werten haben.
Die gespeicherten Werte können aus beliebig vielen verschiedenen Datentypen bestehen. Sie werden gespeichert und abgerufen als das Objekt selbst. Zum Beispiel wird ein QObject-Objekt direkt einem Konfigurationsobjekt übergeben, wenn es einen Farbwert speichert und wenn ein Farbwert abgerufen wird, wird ein QObject-Objekt zurückgegeben.
Anwendungen selbst müssen sich deshalb nicht um das Gliedern und Entgliedern von Objekten kümmern.
Die KConfig Klasse
Das KConfig-Objekt wird benutzt um ein bestehendes Konfigurationsobjekt anzusteuern. Es gibt eine Reihe von Möglichkeiten ein Konfigurationsobjekt zu erstellen.
// ein einfaches, altes lesen/schreiben Konfigurationsobjekt
KConfig config("myapprc");
// eine bestimmte Datei im Dateisystem
// currently must be an INI style file
KConfig fullPath("/etc/kderc");
// nicht mit globalen Werten verflochten
KConfig globalFree( "localsrc", KConfig::NoGlobals );
// nicht mit globalen Werten orde der $KDEDIRS Hierarchie verflochten
KConfig simpleConfig( "simplerc", KConfig::SimpleConfig );
// Komponenten-spezifische Konfiguration, in diesem Fall ein Plugin
KConfig pluginConfig( componentData(), "pluginrc" );
// Ausserhalb der normalen Konfigurations-Ressource
KConfig dataResource( "data", "myapp/somefile" );
"verschmolzen" oder "verflochten"
der Anwendung selber transparent" tönt auch nicht sehr gut
Das Kconfig Objekt, das wir auf Zeile 2 erstellen ist ein gewöhnliches Konfigurationsobjekt. Wir können Werte aus ihm lesen, neue Einträge schreiben und nach verschiedenen Eigenschaften des Objekts fragen. Dieses Objekt wird aus der Konfigurations-Ressource geladen, wie es durch KStandardDirs bestimmt wird, das heisst, dass jede Instanz des myapprc-Objekts in jedem der Ordner in der Konfigurations-Ressourcen-Hierarchie verschmolzen werden um die Werte zu erzeugen, die in diesem Objekt gesehen werden. So werden Systemweite und pro-Benutzer/Gruppen Profile erstellt und unterstützt. Das alles geschieht der Anwendung selber transparent.
Auf Zeile 6 öffnen wir eine bestimmte Lokale Datei, in diesem Fall /etc/kderc. Das führt kein Verschmelzen von Werten aus und erwartet eine Datei im INI Format.
Zeile 9 erzeugt ein Konfigurationsobjekt, dass nicht mit dem globalen kdeglobals-Konfigurationsobjekt verflochten ist, während das Konfigurationsobjekt auf Zeile 12 zusätzlich mit keinem der Dateien in der $KDEDIRS-Hierarchie verflochten ist. Das kann die Leistung spürbar verbessern, falls man einfach nur Werte aus einer einfachen Konfuguration liest und globale Werte keine Rolle spielen.
Zeile 15 erzeugt ein Konfigurationsobjekt unter Benutzung des KComponentData-Objekts das zu einem Plugin oder einer anderen Komponente gehört. Das Plugin kann verschiedene Ordner aufweisen, die in seinen KStandardDirs definiert sind und das ist eine Möglichkeit sich zu versichern, dass KConfig das respektiert.
Und schliesslich auf Zeile 18 erzeugen wir ein Konfigurationsobjekt, das nicht in der config-Ressource sondern in der data-Ressource existiert. Man kann kann auf diese Weise jede Ressource verwenden, von denen KStandardDirs Kenntnis hat, auch solche die während der Laufzeit hinzugefügt werden.
Spezielle Konfigurationsobjekte
Jede Anwendung hat sein eigenes Konfigurationsobjekt das den Namen benutzt, der von KAboutData bereitgestellt wird mit angehängtem "rc". Also hätte eine Anwendung mit dem Namen "myapp" das Standard-Konfigurationsobjekt "myapprc". Dieses Konfigurationsobjekt kann auf diese Weise aufgerufen werden:
#include <KComponentData>
#include <KConfig>
#include <KGlobal>
MyClass::MyClass()
{
// beachten sie, dass dies eigentlich ein KSharedConfig ist
// mehr über diese Klasse siehe weiter unten
KConfig* config = KGlobal::config();
}
Auf das Standard-Konfigurationsobjekt für die Anwendung wird zugegriffen, wenn kein Name definiert wurde, als das KConfig-Objekt erzeugt wurde. Also könnten wir stattdessen auch das tun:
#include <KConfig>
MyClass::MyClass()
{
KConfig config;
}
Zusätzlich könnte jede Komponente sein eigenes Konfigurationsobjekt haben. Auf dieses wird normalerweise durch die componentData() des Plugins zugegriffen, anstatt durch die KGlobal::mainComponent().
Und schliesslich gibt es noch ein globales Konfigurationsobjekt, kdeglobals, das von jeder Anwendung verwendet wird. Es enthält Informationen wie die Standard-Tatenkombinationen für verschiedene Aktionen. Es wird in das Konfigurationsobjekt "gemischt" wenn der KConfig::IncludeGlobals Bitschalter dem KConfig Konstruktor übergeben wird, was standardmässig der Fall ist.
Allgemein nützliche Methoden
Um den momentanen Zustand des Konfigurationsobjektes zu speichern, rufen wir die sync()-Methode auf. Diese Methode wird also aufgerufen, wenn ein Objekt zerstört wird. Falls keine Änderungen gemacht wurden, oder die Ressource weist sich als nicht beschreibbar aus (Wenn der Benutzer zum Beispiel nicht genug Rechte hat, um die Datei zu bearbeiten), dann wird keine Beschäftigung der Festplatte hervorgerufen. sync() verschmilzt Änderungen die gleichzeitig von anderen Prozessen ausgeführt werden - lokale Änderungen haben aber trotzdem Vorrang.
Wenn wir sicher gehen wollen, dass wir die aktuellsten Werte von der Festplatte haben, dann rufen wir reparseConfiguration() auf, welches sync() aufruft und dann die Daten von der Festplatte aktualisiert.
Falls wir verhindern müssen, dass das Konfigurationsobjekt bereits gemachte Änderungen nicht auf die Festplatte speichert, müssen wir markAsClean() aufrufen. Ein besonderer Anwendungsfall ist es, die Konfiguration zum Festplatten-Zustand zurückzusetzen indem man markAsClean() gefolgt von reparseConfiguration() aufruft.
Alle Gruppen in einem Konfigurationsobjekt aufzurufen ist so einfach wie hier in diesem Code-Abschnitt groupList() aufzurufen.
KConfig* config = KGlobal::mainComponent().config();
foreach ( const QString& group, config.groupList() ) {
kDebug() << "next group:" << group;
}
Die KSharedConfig Klasse ist eine Referenz-gezählte Version von KConfig. Darum stellt es eine Möglichkeit bereit, das selbe Konfigurationsobjekt von unterschiedlichen Orten zu referenzieren ohne zusätzlich die Objekte trennen zu müssen oder sich um die Synchronisation des Schreibvorgangs zu kümmern, selbst wenn das Konfigurationsobjekt von verschiedenen Code-Pfaden aktualisiert wird.
Auf ein KSharedConfig-Objekt zuzugreifen ist so einfach wie das hier:
KSharedConfigPtr config = KSharedConfig::openConfig();
openConfig() nimmt die selben Parameter wie KConfig's Konstruktoren, was es einem ermöglicht zu definieren, welche Konfigurationsdatei zu öffnen und Markierungen das Verschmelzen von nicht-config-Ressourcen zu kontrollieren.
Es wird grundsätzlich empfohlen KSharedConfig anstatt KConfig selbst zu benutzen, und auch das Objekt, das von KComponentData zurückgegeben wird ist in der Tat ein KSharedConfig-Objekt.
KConfigGroup
Nun da wir ein Konfigurationsobjekt haben, ist der nächste Schritt es tatsächlich zu nutzen. Das erste, das wir tun müssen, ist zu bestimmen, auf welche Gruppe von Schlüssel-/Wertepaare wir in unserem Objekt zugreifen wollen. Wir tun das, indem wir ein KConfigGroup-Objekt erstellen.
KConfig config;
KConfigGroup generalGroup( &config, "General" );
KConfigGroup colorsGroup = config.group( "Colors" ); // ...oder so ähnlich...
Man kannKConfigGroup KConfig- oder KSharedConfig-Objekte übergeben.
Konfigurationsgruppen können ebenfalls verschachtelt werden.
KConfigGroup subGroup1( &generalGroup, "LessGeneral" );
KConfigGroup subGroup2 = colorsGroup.group( "Dialogs" );
Lesen von Einträgen
Wenn man erst einmal ein KConfigGroup-Objekt erstellt hat, ist das Lesen von Einträgen ziemlich einfach:
QString accountName = generalGroup.readEntry( "Account",
QString() );
QColor color = colorsGroup.readEntry( "background",
Qt::white );
QStringList list = generalGroup.readEntry( "List",
QStringList() );
QString path = generalGroup.readPathEntry( "SaveTo",
defaultPath );
Wie man hier oben sehen kann, kann man Lesevorgänge von verschiedenen KConfigGroup-Objekten, die auf dem selben KConfig-Objekt erstellt wurden, problemlos mischen. Die Lese-Methoden nehmen den Schlüssel, der Gross- und Kleinschreibung unterscheidet, als erstes Argument und den Standardwert als zweites Argument. Dieses Argument bestimmt auch welche Art von Daten, also z.B. eine Farbe in Zeile 3, erwartet wird und auch die Art von Objekt, die zurückgegeben werden soll.Das zurückgegebene Objekt wird in eine QVariant gepackt um diese Hexerei zu ermöglichen.
Es gibt eine Anzahl von speziellen Methoden, zum Beispiel readPathEntry, das einen Datei-Systempfad zurückgibt. Es ist entscheidend, dass man readPathEntry benutzt, wenn man nach einem Pfad fragt, das es Funktionen wie Erreichbarkeits-Profile ermöglicht richtig zu funktionieren.
Wenn kein solcher Schlüssel im Konfigurationsobjekt existiert, dann wird stattdessen der Standardwert zurückgegeben. Falls es eine lokalisierte (d.h. in eine andere Sprache übersetzte) Version für den Schlüssel gibt, die der eingestellten Sprache entspricht, dann wird diese zurückgegeben.
Schreiben neuer Einträge
Neue Werte zu definieren ist ähnlich einfach:
generalGroup.writeEntry( "Account", accountName );
generalGroup.writePathEntry( "SaveTo", savePath );
colorGroup.writeEntry( "background", color );
generalGroup.config()->sync();
Beachten sie die Benutzung von writePathEntry und wie die Art von Objekt, das wir benutzen, wie zum Beispiel QColor auf Zeile 3, bestimmt, wie die Daten angeordnet werden. Zusätzlich, wenn wir mit den Einträgen fertig sind, muss sync() auf das Konfigurationsobjekt ausgeführt werden, um es auf die Festplatte zu speichern. Man kann auch einfach warten, bis das Objekt zerstört wird, was falls nötig automatisch einen sync() auslöst.
KDesktopFile: Ein besonderer Fall
Wann ist eine Konfigurationsdatei keine Konfigurationsdatei? Wenn es eine Desktop-Datei ist. Diese Dateien, die im Grunde genommen Konfigurationsdateien sind, werden benutzt um Einträge für Anwendungsmenüs, Dateitypen, Plugins und verschiedene Services.
Wenn auf eine .desktop Datei zugegriffen wird, sollte man stattdessen die KDesktopFile-Klasse verwenden, die, während eine KConfig-Klasse über alle oben genannten Ressourcen verfügt, über eine Anzahl von Methoden verfügt, um das Zugreifen auf Standard-Attribute dieser Dateien konsistent und zuverlässig zu machen.
Kiosk: Abriegelung und Benutzer-/Gruppenprofile
KConfig stellt eine Reihe von leistungsstarken Abriegelungs- und Konfigurations-Definitionen bereit, insgesamt als "Kiosk" bekannt, worauf viele System-Administratoren und -Integratoren basieren. Während ein Grossteil dieses Systems der Anwendung transparent bereitgestellt wird, ist es möglich dass man den Schreib/Lese-Status eines Konfigurationsobjekts prüfen will.
Einträge in Konfigurationsobjekten die mithilfe von Kiosk abgeriegelt sind, werden "unveränderlich" genannt. Eine Anwendung kann nach der Unveränderlichkeit von ganzen Konfigurationsobjekten, Gruppen oder Schlüsseln, wie in diesem Beispiel gezeigt, fragen:
KConfig* config = KGlobal::config();
if ( config->isImmutable() ) {
kDebug() << "Konfigurationsobjekt ist unveränderlich";
}
KConfigGroup group(config, "General");
if ( group.isImmutable() ) {
kDebug() << "Die Gruppe 'General' ist unveränderlich";
}
if ( group.entryIsImmutable("URL") ) {
kDebug() << "Der URL Eintrag in der Gruppe 'General' ist unveränderlich";
}
Das kann in gewissen Situationen, wo eine Aktion ausgeführt werden soll, falls ein Objekt unveränderlich ist.
KConfig XT
Es gibt einen Weg, gewisse Anwendungsfälle von KConfig einfacher, schneller und zuverlässiger zu machen: KConfig XT. Im Bestimmten kann KConfig XT bei Hauptanwendungs- oder Plug-In-Konfigurationsobjekten und bei Konfigurationsdialogen und anderen Benutzeroberflächen mit diesen Werten sehr hilfreich sein. Es dokumentiert gleichzeitig auch alle verfügbaren Konfigurations-Optionen, was alle System-Administratoren und -Integratoren, die KDE benutzen so viel glücklicher macht.
Die nächste Anleitung handelt über KConfig XT und seine Verwendung.