index.lxp@lxpwrap=x7295_252ehtm.htm

来自「GUI Programming with Python」· HTM 代码 · 共 551 行

HTM
551
字号
    <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>Creating a macro API from an application</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="A Macro Language for Kalam"HREF="c6996.htm"><LINKREL="PREVIOUS"TITLE="Integrating macros with a GUI"HREF="x7161.htm"><LINKREL="NEXT"TITLE="Conclusion"HREF="x7388.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=x7161_252ehtm.htm">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">Chapter 20. A Macro Language for <SPANCLASS="APPLICATION">Kalam</SPAN></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><A accesskey="N" href="index.lxp@lxpwrap=x7388_252ehtm.htm">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1">Creating a macro API from an application</A></H1><P>Enabling users to execute any bit of      Python code they might have lying around, from a document, the      menu or keyboard, isn't enough to macro-enable      <SPANCLASS="APPLICATION">Kalam</SPAN>. For this, we must offer a      clean and clear set of functions that can be used to manipulate      the data and interface of our application. This is the hardest      part, actually. If you can't accomplish this, you might as well      tell users to hack your application directly.</P><P>One problem is already apparent in the startup script we      created in the previous section. The macro writer needs to      call a nebulous entity named <TTCLASS="VARNAME">self</TT>, but how      is he to know that this is a reference to the application      itself?</P><P>It would be more effective to allow access      to the application using a logical name. The solution is to add      an extra entry to the namespace that is used to initialize the      macro manager. This is as simple as adding a key to a      dictionary. Let's revisit the      <TTCLASS="FUNCTION">initMacroManager()</TT> function and add the      current <TTCLASS="VARNAME">KalamApp</TT> object:</P><PRECLASS="PROGRAMLISTING">    def initMacroManager(self):        g=globals()        g["kalam"]=self        self.macroManager = MacroManager(self, g)    </PRE><P>Another, and perhaps slightly less hackish      way of adding items to the global namespace is the use of the      <TTCLASS="FUNCTION">global</TT> keyword:</P><PRECLASS="PROGRAMLISTING">    def initMacroManager(self):        global kalam        kalam = self        self.macroManager = MacroManager(self, globals())    </PRE><P>By declaring a variable      <TTCLASS="VARNAME">global</TT>, it becomes part of the global      namespace. Passing that namespace to <TTCLASS="FUNCTION">exec</TT>      gives all executed code access to the variable.</P><DIVCLASS="SECT2"><H2CLASS="SECT2">Accessing the application itself</A></H2><P>As an example, I have created a few        functions that simplify the creation of new documents and        macro's. Because a macro is wrapped in        <TTCLASS="CLASSNAME">MacroAction</TT>, which is a subclass of        <TTCLASS="CLASSNAME">QAction</TT>, it's very easy to add them to        the menu.</P><PRECLASS="PROGRAMLISTING">    # kalamapp.py    #    # Macro API    #    def installMacro(self,                     action,                     menubar = None,                     toolbar = None):        """        Installs a certain macro action in the menu and/or the toolbar        """        if menubar != None:            action.addTo(menubar)        if toolbar != None:            action.addTo(toolbar)    def removeMacro(self, action):        action.remove()    def createDocument(self):        doc, view = self.docManager.createDocument(KalamDoc, KalamView)        return (doc, view)    def createMacro(self, name, code):        return self.macroManager.addMacro(name, code)      </PRE><P>These methods are part of the      <TTCLASS="CLASSNAME">KalamApp</TT> class, but it would be nice      to not have to prefix class with <TTCLASS="VARNAME">kalam</TT> from      every macro. So these functions are added to the global      namespace, too:</P><PRECLASS="PROGRAMLISTING">    def initMacroManager(self):        g=globals()        g["kalam"]=self        g["docManager"]=self.docManager        g["workspace"]=self.workspace        g["installMacro"]=self.installMacro        g["removeMacro"]=self.removeMacro        g["createDocument"]=self.createDocument        g["createMacro"]=self.createMacro        self.macroManager = MacroManager(self, g)      </PRE><P>Later, we will be writing a nice macro        that resides in a file called <TTCLASS="FILENAME">edmund.py</TT>.        Here's how the <TTCLASS="FILENAME">startup.py</TT> script uses the        API to install the macro:</P><PRECLASS="PROGRAMLISTING">## startup.py - Kalam startup macro file"#edmund = createMacro("edmund", open("edmund.py").read())edmund.setMenuText("Edmund")edmund.setText("Edmund")edmund.setToolTip("Psychoanalyze Edmund")edmund.setStatusTip("Psychoanalyze Edmund")installMacro(edmund, kalam.macroMenu)      </PRE><P>Using the <TTCLASS="VARNAME">kalam</TT> instance of        <TTCLASS="CLASSNAME">KalamApp</TT>, the macro writer has access        to all menus.  In this case the edmund macro is added to the        macro menu, <TTCLASS="VARNAME">kalam.macroMenu</TT>.</P></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2">Accessing application data</A></H2><P>This application collects its data in the      <TTCLASS="CLASSNAME">KalamDoc</TT> object and shows it using the      <TTCLASS="CLASSNAME">KalamView</TT> object. By giving a user access      to the entire internal object model via the      <TTCLASS="CLASSNAME">DocManager</TT> object, we make it possible to      script the creation, modification and saving of documents.      </P></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2">Accessing and extending the GUI</A></H2><P>The function        <TTCLASS="FUNCTION">installMacro</TT>, which we have already seen,        is used to add a macro to any menubar or toolbar. What text        the macro shows, what tooltips, what icon and what accelerator        key is all determined by the settings of the underlying        <TTCLASS="FUNCTION">QAction</TT> object. In the example above, we        didn't set a shortcut or an icon.        </P><P>By not hiding the underlying gui toolkit, clever        users can do almost anything to your application. It would be        a trivial exercise to integrate a Python class browser into        <SPANCLASS="APPLICATION">Kalam</SPAN>, especially since I have        already made a PyQt based standalone class browser, which you        can find at http://www.valdyas.org/python. However, let's not        be so serious and sensible, and implement something a little        more frivolous.</P></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2"><SPANCLASS="APPLICATION">Kalam</SPAN> rivals    <SPANCLASS="APPLICATION">Emacs</SPAN>: an <SPANCLASS="APPLICATION">Eliza    macro</SPAN></A></H2><P>One of the first extensions to the        <SPANCLASS="APPLICATION">Emacs</SPAN> editor, way back in the        beginning of the eighties, was an        <SPANCLASS="APPLICATION">Eliza</SPAN> application. A kind of        Rogerian psychoanalyst that took the user's input, analyzed it        a bit, and said something comprehensible back.</P><P>This is actually a very nice example of        working with documents in an editor, since the macro must be        aware (more or less) of what the user typed in, and be able        react to the pressing of the Enter key. Surely having all the        power of Python at our disposal means that we can at least        achieve equal status with the doyen of editors!</P><P>So, without further ado, I present        Edmund - who doesn't really listen, but does answer back, in        his accustomed vein:</P><PRECLASS="PROGRAMLISTING">import randomclass Edmund(QObject):    """    An Edmund macro for the Kalam Editor.    Of course, if I really re-implemented Eliza, the responses would bear    some relevance to the input. Anyway.    """    def __init__(self, *args):        QObject.__init__(self)        self.responses = [            "First Name?",            "Come on, you MUST have a first name.",            "Sod Off?",            "Madam, without you, life was like a broken pencil...pointless.",            "So what you are saying, Percy, is something you have never" +            " seen is slightly less blue than something else . . that you " +            "have never seen?",            "I'm afraid that might not be far enough. " +            "Apparently the head Mongol and the Duke are good friends. " +            "They were at Eton together.",            "Ah ah, not so fast! Not that it would make any difference. " +            "We have the preliminary sketches...",            "You have absolutely no idea what irony is, have you Baldrick?",            "Baldric, you wouldn't recognize a subtle plan if it painted  " +            "itself purple and danced naked on a harpsichord singing " +            "'subtle plans are here again'.",            "Baldric, you have the intellectual capacity of a dirty potato.",            "Ah, yes. A maternally crazed gorilla would come in handy " +            "at this very moment.",            "That would be as hard as finding a piece of hay in an " +            "incredibly large stack of needles.",            "Normal procedure, Lieutenant, is to jump 200 feet in the air " +            "and scatter oneself over a wide area.",            "I think I'll write my tombstone - Here lies Edmund Blackadder" +            ", and he's bloody annoyed.",            "As a reward, Baldrick, take a short holiday. " +            ".... Did you enjoy it?"    ]        self.doc, self.view = createDocument()        self.doc.setTitle("Talk to Edmund BlackAdder")        self.connect(self.view,                     PYSIGNAL("returnPressed"),                     self.respond)        self.view.append("Welcome\n")        self.view.goEnd()    def respond(self):        input = str(self.view.textLine(self.view.numLines() - 2))        if input.find("love") &#62; 0:            response = self.responses[3]        elif input.find("dead") &#62; 0:            response = self.responses[15]        elif input.find("fear") &#62; 0:            response = self.responses[5]        else:            choice = random.randrange(0,len(self.responses),1)            response = self.responses[choice]        self.view.append(response + "\n\n")        self.view.goEnd()edmund = Edmund()      </PRE><P>Of course, this is an extremely        primitive form of amusement, but you get the idea. By        accessing the API's of the <TTCLASS="CLASSNAME">KalamDoc</TT> and        <TTCLASS="CLASSNAME">KalamView</TT> classes, the macro author can        do all kinds of fun things, like reading out lines of the text        or adding text to the document.         </P><DIVCLASS="MEDIAOBJECT"><P><DIVCLASS="CAPTION"><P>Talking heart to heart with your computer.</P></DIV></P></DIV></DIV></DIV><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLESUMMARY="Footer navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><A accesskey="P" href="index.lxp@lxpwrap=x7161_252ehtm.htm">Prev</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><A accesskey="H" href="index.lxp@lxpwrap=book1_252ehtm">Home</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><A accesskey="N" href="index.lxp@lxpwrap=x7388_252ehtm.htm">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">Integrating macros with a GUI</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><A accesskey="U" href="index.lxp@lxpwrap=c6996_252ehtm.htm">Up</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">Conclusion</TD></TR></TABLE></DIV></BODY></HTML>      </td>      </tr>      </table>      </td>    </tr>  </table>      

⌨️ 快捷键说明

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