index.lxp@lxpwrap=x6082_252ehtm.htm

来自「GUI Programming with Python」· HTM 代码 · 共 1,086 行 · 第 1/2 页

HTM
1,086
字号
    <table border="0" cellspacing="0" cellpadding="3" width="100%"><tr><td>    <div align="center" id="bldcontent">      <a href="../default.htm"><img src="../images/opendocs.png" width="63" height="76" border="0"></a>      <br>      <div class="symbol">Your OpenSource Publisher&#153;</div>    </div>      </td></tr></table>    <div align="center" class="author">      	<a href="../products.lxp">Products</a>	&nbsp;|&nbsp;	<a href="../wheretobuy.lxp">Where to buy</a>	&nbsp;|&nbsp;	<a href="../bookstore.lxp">Retailers</a>	&nbsp;|&nbsp;	<a href="../faq.lxp">FAQ</a>	&nbsp;|&nbsp;        <a href="../writeforus.lxp">Write for Us.</a>        &nbsp;|&nbsp;        <a href="#contact">Contact Us.</a>  </div>    <table border="0" cellspacing="3" cellpadding="0" width="100%"><tr><td width="100%">      <div class="content">        <table border="0" cellspacing="2" cellpadding="0" width="100%"><tr><td width="100%">          <div align="center"><H4 CLASS="AUTHOR"><A NAME="AEN5">Boudewijn Rempt</A><br><a href="../../https@secure.linuxports.com/opendocs/default.htm"><img src=odpyqt125.png></a><br>ISBN: 0-97003300-4-4<br><a href="../../https@secure.linuxports.com/opendocs/default.htm">Available from bookstores everywhere or you can order it here.</a><p>You can download the source files for the book <a href="pyqtsrc.tgz">(code / eps) here.</a><hr></div>                    <HTML><HEAD><TITLE>Implementing configurations settings for      Kalam</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.72"><LINKREL="HOME"TITLE="GUI Programming with Python: QT Edition"HREF="book1.htm"><LINKREL="UP"TITLE="Application Configuration"HREF="c6013.htm"><LINKREL="PREVIOUS"TITLE="The Python way of handling configuration      settings"HREF="x6053.htm"><LINKREL="NEXT"TITLE="Settings in Qt 3.0"HREF="x6300.htm"></HEAD><BODYCLASS="SECT1"BGCOLOR="#FFFFFF"TEXT="#000000"LINK="#0000FF"VLINK="#840084"ALINK="#0000FF"><DIVCLASS="NAVHEADER"><TABLESUMMARY="Header navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">GUI Programming with Python: QT Edition</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><A accesskey="P" href="index.lxp@lxpwrap=x6053_252ehtm.htm">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">Chapter 18. Application Configuration</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><A accesskey="N" href="index.lxp@lxpwrap=x6300_252ehtm.htm">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1">Implementing configurations settings for      <SPANCLASS="APPLICATION">Kalam</SPAN></A></H1><P>Working with configuration settings can be      divided into two main procedures: giving your application      classes access to the configuration data, and loading and saving      that data. We'll start by looking at the first problem, and then      at loading and saving. In the next chapter, we'll round out      <SPANCLASS="APPLICATION">Kalam</SPAN> by creating a preferences      dialog.</P><DIVCLASS="SECT2"><H2CLASS="SECT2">Handling configuration data in your        application</A></H2><P>Before we start saving and restoring        configuration settings, we should have a clear idea of how to        handle them in the application. Configuration data typically        must be available everywhere in the application, because all        objects must be able to query and store settings at        will.</P><P>In other languages, such as Visual        Basic, you would use a module with global variables to store        configuration data; in a language like Java or C++, you would        use a singleton object&#8212;that is, an object with a hidden        constructor that can only be instantiated once. Python,        however, does not support these constructions.</P><P>Of course, there is an alternative. In a sense,        class definitions are global. Every module that imports a        certain class gets exactly the same class. Keep in mind that a        class is just an object, of the type        <TTCLASS="VARNAME">class</TT>. You can associate variables not only        with an object, as in:</P><PRECLASS="PROGRAMLISTING">class SomeClass:   def __init__(self):       self.someVariable=1someInstance=SomeClass()print someInstance.someVariable    </PRE><P>But also with a class:</P><PRECLASS="PROGRAMLISTING">class SomeClass:    classVariable=1print SomeClass.classVariable    </PRE><P>These class variables are accessed via the        <SPAN><ICLASS="EMPHASIS">name</I></SPAN> of the class, instead of the name of        an instance of that class. Class variables are shared by all        instances of a class.</P><P>The ideal solution to creating a        "global" configuration repository is to define a class that        contains all configuration data as class variables. It's also        possible to encapsulate the configuration data repository in a        single class variable. You cannot call functions on a        class - there is no equivalent to the &#8216;static' methods of        Java. If we need functions to work on the configuration data,        we must either define those functions at module level, or as        functions of an object that is a class variable of the        configuration module. An example would be a function to create        a <TTCLASS="CLASSNAME">QFont</TT> out of a fontname        string.</P><P>Well &#8212; that was the theory. Let's        now look at the code needed to implement configuration data        for <SPANCLASS="APPLICATION">Kalam</SPAN>. It's pretty similar to        the snippets we saw above:</P><PRECLASS="PROGRAMLISTING">"""kalamconfig.py - Configuration class for the Kalam Unicode Editorcopyright: (C) 2001, Boudewijn Remptemail:     boud@rempt.xs4all.nl"""import sys, osfrom qt import *class Config:    APPNAME = "kalam"    APPVERSION = "ch13"    CONFIGFILE = ".kalam-ch13"    currentStyle="Platinum"    viewmanager="tabmanager"    app_x=0    app_y=0    app_w=640    app_h=420    fontfamily="courier"    pointsize=12    weight=50    italic=0    encoding=22def getApplicationFont():    return QFont(Config.fontfamily,                 Config.pointsize,                 Config.weight,                 Config.italic,                 Config.encoding )      </PRE><P>As you can see, it's just a simple matter of a class with a bunch        of class variables that represent pertinent values.        However,        because these values will be saved to a file, you cannot        associate real objects with the keys. To make it easier to        retrieve a font based on the values stored in        the configuration file, there is module-level helper function,        <TTCLASS="FUNCTION">getApplicationFont()</TT>, which constructs a        <TTCLASS="CLASSNAME">QFont</TT> on the fly.</P><P>A similar function exists to set the font:</P><PRECLASS="PROGRAMLISTING">def setApplicationFont(qfont):     Config.fontfamily = qfont.family()     Config.pointsize = qfont.pointSize()     Config.weight = qfont.weight()     Config.italic = qfont.italic()     Config.encoding = qfont.encoding()      </PRE><P>As you can see, we store our settings in        a flat namespace, in which every key must be unique. This is        just like the properties system used in Java, but more complex        systems can be very useful. For instance, the Windows registry        is one gigantic tree, and even the files created by        <TTCLASS="FILENAME">ConfigParser</TT> have sections and        subsections. For highly complex configuration needs, there is        the <TTCLASS="FILENAME">shlex</TT> Python module, which you can        use to define configuration languages.</P></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2">Saving and loading the configuration data</A></H2><P>Retrieving and saving the configuration        data can be made as complex or easy as you want. We have        already discussed the possibility of using        <TTCLASS="FILENAME">_winreg</TT> or        <TTCLASS="FILENAME">ConfigParser</TT> for the saving and        retrieving of configuration data.</P><P>What we are going to, however, is far        more simple. When we load the settings, we just read every        line in the configuration file, and add a variable to the        <TTCLASS="CLASSNAME">Config</TT> class that represents the        value:</P><PRECLASS="PROGRAMLISTING">def readConfig(configClass = Config):    sys.stderr.write( "Initializing configuration\n")    try:        for line in open(os.path.join(os.environ["HOME"],                                      Config.CONFIGFILE)).readlines():            k, v=tuple(line.split("="))            v=v[:-1]            if v=="None\n":                v=None            elif type:               try:                    v=int(v)                except ValueError:                    pass            setattr(configClass, k, v)    except IOError:        sys.stderr.write( "Creating first time configuration\n")      </PRE><P>To add the variable to the        <TTCLASS="CLASSNAME">Config</TT> we use the standard Python        function <TTCLASS="FUNCTION">setattr()</TT> &#8212; this function        is one of the delights that make Python so dynamic.</P><P>Note the special treatment of the value        that is represented by "None" in the configuration file: if        "None" is encountered the value of the configuration key is        set to a real None object. This contrast with the situation        where the value is simply empty: then the value is set to an        empty string ("").</P><P>Currently, the configuration file format        only supports two types: strings and integers. The distinction        is made by brute force: we simply try to convert the value to        an integer, and if we succeed, it stays an integer. If the        conversion raises a <TTCLASS="VARNAME">ValueError</TT>, we assume        the value should remain a string.</P><P>By now you might be wondering        <SPAN><ICLASS="EMPHASIS">when</I></SPAN> we will be reading in the        configuration values. The simple answer is that we will do so        when the <TTCLASS="FILENAME">KalamConfig</TT> module is first        imported. At the bottom of the module the function        <TTCLASS="FUNCTION">readConfig(Config)</TT> is called, and is only        executed once:</P><PRECLASS="PROGRAMLISTING">readConfig()      </PRE><P>Saving the configuration values to disk        is a simple matter of looping over the contents of the        attributes of the <TTCLASS="CLASSNAME">Config</TT> class &#8212;        that is, the <TTCLASS="VARNAME">__dict__</TT>,        <TTCLASS="VARNAME">__methods__</TT> and        <TTCLASS="VARNAME">__members__</TT> dictionaries that are part of        the object's hidden attributes. We retrieve these with the        <TTCLASS="FUNCTION">dir()</TT> function:</P><PRECLASS="PROGRAMLISTING">def writeConfig(configClass = Config):    sys.stderr.write( "Saving configuration\n")    configFile=open(os.path.join(os.environ["HOME"],".kalamrc"),"w+")    for key in dir(Config):        if key[:2]!='__':            val=getattr(Config, key)            if val==None or val=="None":                line=str(key) + "=\n"            else:                line=str(key) + "=" + str(val) + "\n"            configFile.write(line)    configFile.flush()      </PRE><P>The actual values are retrieved with        the opposite of <TTCLASS="FUNCTION">setattr()</TT>:        <TTCLASS="FUNCTION">getattr()</TT>. As a first check, attributes        with a double underscore as prefix are not saved: those are        internal attributes to the <TTCLASS="CLASSNAME">Config</TT>        class. If the value is the <TTCLASS="VARNAME">None</TT> object, we        print the string "None". Because it is quite possible that        some values are <TTCLASS="CLASSNAME">QString</TT> objects, and        because you cannot save these, everything is converted to a plain        Python string.</P><P>Finally, you might need functions that get and set more      complex objects in the <TTCLASS="CLASSNAME">Config</TT>. These can      be simple module level functions that work on the class:</P><PRECLASS="PROGRAMLISTING">def getTextFont():    return QFont(Config.fontfamily,                 Config.pointsize,                 Config.weight,                 Config.italic,                 Config.encoding )def setTextFont(qfont):     Config.fontfamily = qfont.family()     Config.pointsize = qfont.pointSize()     Config.weight = qfont.weight()     Config.italic = qfont.italic()     Config.encoding = qfont.encoding()      </PRE></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2">Using configuration data from the application</A></H2><P>By now we have a simple configuration        data mechanism, and it's time to use it. Earlier we defined a        few settings: the position and size of the application window,        the widget style that is to be used, and the interface        paradigm. First, we will write some code to actually use these        settings. Then we will write code to save changes when the        application is closed.</P><DIVCLASS="SECT3"><H3CLASS="SECT3">Font settings</A></H3><P>The font to be used in the editor window can be        set and retrieved with the get and set functions we defined        above. The <TTCLASS="CLASSNAME">KalamView</TT> class is the place        to use this setting.</P><PRECLASS="PROGRAMLISTING">"""from qt import *import kalamconfigfrom resources import TRUE, FALSEclass KalamView(QWidget):    def __init__(self, parent, doc, *args):        apply(QWidget.__init__,(self, parent) + args)        ...        self.editor=QMultiLineEdit(self)        self.editor.setFont(kalamconfig.getTextFont())        self.layout.addWidget(self.editor)      </PRE><P>We import the configuration module,          <SPAN><ICLASS="EMPHASIS">not</I></SPAN> the <TT

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?