1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
|
<chapter>
<chapterinfo>
<releaseinfo>$Progeny: frontend.xml,v 1.19 2002/01/18 06:19:44 dsp Exp $</releaseinfo>
</chapterinfo>
<title>Front End Programming</title>
<section>
<title>Introduction</title>
<para>
The <emphasis>front end</emphasis> is the program that takes care of
much of the housekeeping duties for the configlets. It is
responsible for starting the configlets, providing them a GUI
toplevel widget of some kind to live in, handling global user
interaction (such as navigation between the configlets), reading from
and writing to the &debconf; database, and so on.
</para>
<para>
Front ends generally distinguish themselves by the way they allow
navigation between the configlets, as most of the rest of the front
end's duties are rather mundane. For example, the front end that
ships with the configlet <ulink url="api/index.html">API</ulink> package places each configlet in a
separate tab; other front ends exist that present the configlets in a
&gnome; Druid widget (similar to Microsoft's Wizards) or that present
the configlets as items in the &gnome; Control Center. For this
reason, most of the housekeeping code is provided by the <ulink url="api/index.html">API</ulink> itself,
leaving the front end with little else except presentation and
navigation functions.
</para>
</section>
<section>
<title>Writing a Front End</title>
<para>
Front ends generally perform a standard set of tasks in the same
order.
</para>
<itemizedlist>
<listitem>
<para>
Initialize and start the configlet(s) to be displayed.
</para>
</listitem>
<listitem>
<para>
Read the &debconf; database, and pass that data to the configlets.
</para>
</listitem>
<listitem>
<para>
Draw (or otherwise create) the top level widgets to contain the
configlets, and insert the configlets into them.
</para>
</listitem>
<listitem>
<para>
Interact with the user. This includes handling of navigation events,
OK/Cancel, or other possible events that could be generated by the
user.
</para>
</listitem>
<listitem>
<para>
When the user is finished with the configlets, read the changed
&debconf; information back from them and write it to the &debconf;
database.
</para>
</listitem>
<listitem>
<para>
Shut down the configlets, destroy the user interface, and terminate.
</para>
</listitem>
</itemizedlist>
<section id="sec-basicconfiggroup">
<title>The <classname>BasicConfigGroup</classname> Class</title>
<para>
For many of these functions, the <ulink url="api/index.html">API</ulink> provides helper functions that
handle the most common cases. These functions are provided as
methods to the <ulink url="api/configlet_BasicConfigGroup.py.html"><classname>BasicConfigGroup</classname></ulink> class. Most
front ends will need to merely create an instance of this class, or
optionally inherit from it and create an instance of the derived
class.
</para>
<para>
A <ulink url="api/configlet_BasicConfigGroup.py.html"><classname>BasicConfigGroup</classname></ulink> object presents the
interface of a Python sequence, containing a set of configlets in
priority-sorted order. This sequence is created by the constructor
using a directory path; this directory is search for subdirectories
containing configlets, and each configlet found is instantiated and
added to the sequence. The directory defaults to
<filename>/usr/share/configlets</filename>, which means that the
group will instantiate all configlets installed in the standard
location by default.
</para>
<para>
In addition to the normal sequence interface,
<ulink url="api/configlet_BasicConfigGroup.py.html"><classname>BasicConfigGroup</classname></ulink> provides other helper methods
which act on all configlets within the group. These will be
described below.
</para>
</section>
<section>
<title>Starting Configlets</title>
<para>
All configlets must be started using the
<ulink url="api/configlet.py.html#start_configlet"><function>start_configlet</function></ulink> function. It takes one
argument: the directory in which the configlet resides. It returns a
fully instantiated configlet object.
</para>
<para>
Configlets are only meant to be instantiated once. If
<ulink url="api/configlet.py.html#start_configlet"><function>start_configlet</function></ulink> is called more than once for the
same directory, it will return a reference to the object it created
previously, rather than creating a new one.
</para>
<para>
If you use <ulink url="api/configlet_BasicConfigGroup.py.html"><classname>BasicConfigGroup</classname></ulink>, it uses
<ulink url="api/configlet.py.html#start_configlet"><function>start_configlet</function></ulink> to start all of the configlets
it finds. Thus, you do not need to call
<ulink url="api/configlet.py.html#start_configlet"><function>start_configlet</function></ulink> yourself. Be aware, however,
that the configlets in <ulink url="api/configlet_BasicConfigGroup.py.html"><classname>BasicConfigGroup</classname></ulink> have
been created this way, and in particular cannot be "recreated"
outside the group.
</para>
</section>
<section>
<title>Interacting with Debconf</title>
<para>
In order for the configlets to be useful, they need to be fed data
from the &debconf; database, and they need to be able to feed changes
back to &debconf;. The front end is responsible for doing this, using
two classes from the <ulink url="api/index.html">API</ulink>: the privileged runner classes and the
<ulink url="api/configlet_DebConf.py.html"><classname>DebConf</classname></ulink> class.
</para>
<para>
The privileged runner classes are intended to provide an interface
for front ends to perform tasks as root. They have a simple
interface: a <ulink url="api/configlet_SimplePrivilegedRunner.py.html#run"><function>run</function></ulink> function, that takes the
command to run as the only argument. No return value is provided;
<ulink url="api/configlet_SimplePrivilegedRunner.py.html#run"><function>run</function></ulink> will throw a RuntimeError exception if there
is any problem running the command. Because the interface is so
simple, the privileged runner classes do not inherit; a class only
needs to provide a <function>run</function> method in order to be a
privileged runner.
</para>
<para>
The <ulink url="api/index.html">API</ulink> provides two privileged runner classes: <ulink
url="api/configlet_SimplePrivilegedRunner.py.html"><classname>SimplePrivilegedRunner</classname></ulink>
and <ulink url="api/configlet_GnomeSudoPrivilegedRunner.py.html"><classname>GnomeSudoPrivilegedRunner</classname></ulink>. The default
privileged runner, <ulink
url="api/configlet_SimplePrivilegedRunner.py.html"><classname>SimplePrivilegedRunner</classname></ulink>,
simply assumes that the front end already has root privileges and runs
commands accordingly. <ulink url="api/configlet_GnomeSudoPrivilegedRunner.py.html"><classname>GnomeSudoPrivilegedRunner</classname></ulink>
uses the <command>gnome-sudo</command> command to obtain root to run the
command; this provides a nice graphical password dialog to the user when
root privilege is needed and caches the successful authentication for
subsequent commands. See the <command>gnome-sudo</command> documentation
for setting this up properly.
</para>
<para>
The <ulink url="api/configlet_DebConf.py.html"><classname>DebConf</classname></ulink> class does the actual work of
getting and setting the &debconf; information. A proper
<ulink url="api/configlet_DebConf.py.html"><classname>DebConf</classname></ulink> object for the version of &debconf; can
be obtained by calling <ulink url="api/configlet.py.html#get_debconf"><function>get_debconf</function></ulink>, passing in
the list of packages that the configlets configure. (This
information can be retrieved by calling <function>get_packages</function> on the
configlet, and calling get_packages on a
<ulink url="api/configlet_BasicConfigGroup.py.html"><classname>BasicConfigGroup</classname></ulink> will get all of the packages
configured by all of the configlets in the group.) As the
<ulink url="api/configlet_DebConf.py.html"><classname>DebConf</classname></ulink> class uses a privileged runner class
to do its work, you should set an appropriate privileged runner class
for it by calling the <ulink url="api/configlet_BasicConfigGroup.py.html#set_privileged_runner"><function>set_privileged_runner</function></ulink>
method, passing in the privileged runner object as an argument.
</para>
<para>
Once the DebConf object is set up, it provides three methods:
<ulink url="api/configlet_DebConf.py.html#get"><function>get</function></ulink>, <ulink url="api/configlet_DebConf.py.html#set"><function>set</function></ulink>, and
<ulink url="api/configlet_DebConf.py.html#commit"><function>commit</function></ulink>. The <ulink url="api/configlet_DebConf.py.html#get"><function>get</function></ulink> method
returns an array of strings containing the &debconf; data in the format
the configlets need. The <ulink url="api/configlet_DebConf.py.html#set"><function>set</function></ulink> method takes a
similarly formatted array of strings as &debconf; data and sets it in
the database. The <ulink url="api/configlet_DebConf.py.html#commit"><function>commit</function></ulink> method takes it upon
itself to cause the configured packages to reread their &debconf; data
and reconfigure themselves accordingly.
</para>
<para>
Once &debconf; information is read, it must be passed to the
configlets. This is done by taking the data returned from the
<ulink url="api/configlet_DebConf.py.html#get"><function>get</function></ulink> method and passing it to the configlet's
<ulink url="api/configlet_Configlet.py.html#load_debconf"><function>load_debconf</function></ulink> method. The configlet will search
through the data, finding the appropriate values it needs and saving
them internally in some way.
</para>
<para>
Saving &debconf; information involves several more steps. First, all
configlets must be told to prepare for shutdown; this is done with
the <ulink url="api/configlet_Configlet.py.html#on_gnome_close"><function>on_gnome_close</function></ulink> method. Then, call
<ulink url="api/configlet_Configlet.py.html#report_debconf"><function>report_debconf</function></ulink> on each configlet, merge the
results into a single array of strings, and pass that array to
<ulink url="api/configlet_DebConf.py.html"><classname>DebConf</classname></ulink>'s <ulink url="api/configlet_DebConf.py.html#set"><function>set</function></ulink> method.
Finally, call <ulink url="api/configlet_DebConf.py.html#commit"><function>DebConf.commit</function></ulink> to cause the
packages to reconfigure.
</para>
<para>
The <ulink url="api/configlet_BasicConfigGroup.py.html"><classname>BasicConfigGroup</classname></ulink> class does much of this
work for you. By calling the <ulink url="api/configlet_BasicConfigGroup.py.html#load_all_debconf"><function>load_all_debconf</function></ulink>
method, you can cause the group to load the &debconf; information and
send it to all of the configlets in the group. Similarly, the
<ulink url="api/configlet_BasicConfigGroup.py.html#save_and_commit_all_debconf"><function>save_and_commit_all_debconf</function></ulink> method will read all
of the &debconf; information from the configlets, save it to the
&debconf; database, and commit it. The
<ulink url="api/configlet_BasicConfigGroup.py.html"><classname>BasicConfigGroup</classname></ulink> takes care of creating a
DebConf object and managing it for you; you should, however, create
an appropriate privileged runner and set it for the group by calling
the group's <ulink url="api/configlet_BasicConfigGroup.py.html#set_privileged_runner"><function>set_privileged_runner</function></ulink> method on it.
As an alternative, you can get access to the group's
<ulink url="api/configlet_DebConf.py.html"><classname>DebConf</classname></ulink> object with the
<ulink url="api/configlet.py.html#get_debconf"><function>get_debconf</function></ulink> method; you can then iterate across
all of the configlets in the group to load or save data, or use the
group's <ulink url="api/configlet_Configlet.py.html#load_debconf"><function>load_debconf</function></ulink> and
<ulink url="api/configlet_Configlet.py.html#report_debconf"><function>report_debconf</function></ulink> methods, which do the iteration
for you.
</para>
</section>
</section>
</chapter>
<!-- Local variables: -->
<!-- eval: (sgml-load-dtd "../../doctools/docbook.ced") -->
<!-- End: -->
|