📄 index.lxp@lxpwrap=x7161_252ehtm.htm
字号:
<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™</div> </div> </td></tr></table> <div align="center" class="author"> <a href="../products.lxp">Products</a> | <a href="../wheretobuy.lxp">Where to buy</a> | <a href="../bookstore.lxp">Retailers</a> | <a href="../faq.lxp">FAQ</a> | <a href="../writeforus.lxp">Write for Us.</a> | <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>Integrating macros with a GUI</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="A Macro Language for Kalam"HREF="c6996.htm"><LINKREL="NEXT"TITLE="Creating a macro API from an application"HREF="x7295.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=c6996_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=x7295_252ehtm.htm">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1">Integrating macros with a GUI</A></H1><P>Before we can start defining what we allow our users to do to <SPANCLASS="APPLICATION">Kalam</SPAN>, we need to build the core macro execution functionality. The first step is to make sure users can execute the contents of a document.</P><DIVCLASS="SECT2"><H2CLASS="SECT2">Executing the contents of a document</A></H2><P>Unless you have skipped all previous occasions of creating and adding an action to the menubar and the toolbar, you will know by now how to do so. I have supplied the slot code that executes the contents of the currently active document. You will find the complete code in the <TTCLASS="FILENAME">kalamapp.py</TT> file that belongs to this chapter.</P><PRECLASS="PROGRAMLISTING"># kalamapp.py...class KalamApp(QMainWindow):... # Macro slots... def slotMacroExecuteDocument(self): if self.docManager.activeDocument() == None: QMessageBox.critical(self, "Kalam", "No document to execute as a macro ") return try: bytecode = compile(str(self.docManager.activeDocument().text()), "<string>", "exec") except Exception, e: QMessageBox.critical(self, "Kalam", "Could not compile " + self.docManager.activeDocument().title() + "\n" + str(e)) try: exec bytecode # Note: we don't yet have a separate namespace # for macro execution except Exception, e: QMessageBox.critical(self, "Kalam", "Error executing " + self.docManager.activeDocument().title() + "\n" + str(e))... </PRE><P>We are being a bit careful here, and thus compile the code first to check for syntax errors. These, along with execution errors, will be shown in a dialog box. Note that anything you print from here will go to standard output—that is, a black hole if you run <SPANCLASS="APPLICATION">Kalam</SPAN> by activating an icon, or the terminal window if you run <SPANCLASS="APPLICATION">Kalam</SPAN> from the shell prompt. It would be a logical step to redirect any output to a fresh <TTCLASS="CLASSNAME">Kalam</TT> document (this is what <SPANCLASS="APPLICATION">Emacs</SPAN> does). It is quite easy to achieve. You can reassign the standard and error output channels to any object you want, as long as it has a <TTCLASS="FUNCTION">write()</TT> function that accepts a string. We might want to add a <TTCLASS="FUNCTION">write()</TT> function to <TTCLASS="CLASSNAME">KalamDoc</TT>.</P><P>The implementation of <TTCLASS="FUNCTION">write()</TT> in <TTCLASS="CLASSNAME">KalamDoc</TT> is very simple:</P><PRECLASS="PROGRAMLISTING"># kalamdoc.py - fragment... def write(self, text, view = None): self.text().append(text) self.emit(PYSIGNAL("sigDocTextChanged"), (self._text, view)) </PRE><P>Having done that, redirecting all output is easy:</P><PRECLASS="PROGRAMLISTING"> ... def slotMacroExecuteDocument(self): ... import sys document = self.docManager.createDocument(KalamDoc, KalamView) document.setTitle("Output of " + title) oldstdout = sys.stdout oldstderr = sys.stderr sys.stdout = document sys.stderr = document exec bytecode # Note: we don't yet have a separate namespace # for macro execution sys.stdout = oldstdout sys.stderr = oldstderr ... </PRE><P>It is necessary to save the "real" standard output and standard error channels in order to be able to restore them when we are done printing to the output document. Otherwise all output, from anywhere inside Kalam, would go forever to that document, with nasty consequences if the user were to remove the document.</P><P>Until we create a namespace specially for executing macros, everything runs locally to the function that executes the macro. That is, you can use <TTCLASS="VARNAME">self</TT> to refer to the current instance of <TTCLASS="CLASSNAME">KalamApp</TT>.</P><DIVCLASS="MEDIAOBJECT"><P><DIVCLASS="CAPTION"><P>Executing a bit of code from a document.</P></DIV></P></DIV><P>Of course, littering the <TTCLASS="CLASSNAME">KalamApp</TT> with macro execution code isn't the best of ideas. This leads us to the creation of a macro manager class, <TTCLASS="CLASSNAME">MacroManager</TT>, which keeps a dictionary of compiled code objects that can be executed at will. I won't show the unit tests here: it is available with the full source code.</P><PRECLASS="PROGRAMLISTING">"""macromanager.py - manager class for macro administration and executioncopyright: (C) 2001, Boudewijn Remptemail: boud@rempt.xs4all.nl"""from qt import *import sysclass MacroError(Exception):passclass NoSuchMacroError(MacroError): def __init__(self, macro): ERR = "Macro %s is not installed" self.errorMessage = ERR % (macro) def __repr__(self): return self.errorMessage def __str__(self): return self.errorMessageclass CompilationError(MacroError): def __init__(self, macro, error): ERR = "Macro %s could not be compiled. Reason: %s" self.errorMessage = ERR % (macro, str(error)) self.compilationError = error def __repr__(self): return self.errorMessage def __str__(self): return self.errorMessageclass ExecutionError(MacroError): def __init__(self, error): ERR = "Macro could not be executed. Reason: %s" self.errorMessage = ERR % (str(error)) self.executionError = error def __repr__(self): return self.errorMessage def __str__(self): return self.errorMessage </PRE><P>First, a couple of exceptions are defined. We want to separate the GUI handling of problems with the macro from the actual execution, so that whenever something goes wrong, an exception is thrown.</P><PRECLASS="PROGRAMLISTING">class MacroAction(QAction): def __init__(self, code, *args): apply(QAction.__init__,(self,) + args) self.code = code self.bytecode = self.__compile(code) self.locations=[] self.connect(self, SIGNAL("activated()"), self.activated) def activated(self): self.emit(PYSIGNAL("activated"),(self,)) def addTo(self, widget): apply(QAction.addTo,(self, widget)) self.locations.append(widget) def removeFrom(self, widget): QAction.removeFrom(self, widget) del self.locations[widget] def remove(self): for widget in self.locations:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -