📄 index.lxp@lxpwrap=x7161_252ehtm.htm
字号:
self.removeFrom(widget) def __compile(self, code): try: bytecode = compile(code, "<string>", "exec") return bytecode except Exception, e: raise CompilationError(macroName, e) def execute(self, out, err, globals, locals): try: oldstdout = sys.stdout oldstderr = sys.stderr sys.stdout = out sys.stderr = err exec self.bytecode in globals sys.stdout = oldstdout sys.stderr = oldstderr except Exception, e: print e print sys.exc_info sys.stdout = oldstdout sys.stderr = oldstderr raise ExecutionError(e) </PRE><P>By encapsulating each macro in a <TTCLASS="CLASSNAME">QAction</TT>, it will become very easy to assign shortcut keys, menu items and toolbar buttons to a macro.</P><P>The <TTCLASS="CLASSNAME">MacroAction</TT> class also takes care of compilation and execution. The environment, consisting of the <TTCLASS="VARNAME">globals</TT> and <TTCLASS="VARNAME">locals</TT> dictionaries, is passed in the <TTCLASS="FUNCTION">execute()</TT> function. We also pass two objects that replace the standard output and standard error objects. These can be Kalam documents, for instance. Note how we carefully restore the standard output and standard error channels. The output of the print statement in the exception clause will go to the redefined channel (in this instance, the kalam document).</P><PRECLASS="PROGRAMLISTING">class MacroManager(QObject): def __init__(self, parent = None, g = None, l = None, *args): """ Creates an instance of the MacroManager. Arguments: g = dictionary that will be used for the global namespace l = dictionary that will be used for the local namespace """ apply(QObject.__init__,(self, parent,) + args) self.macroObjects = {} if g == None: self.globals = globals() else: self.globals = g if l == None: self.locals = locals() else: self.locals = l </PRE><P>All macros should be executed in the same environment, which is why the macromanager can be constructed with a <TTCLASS="VARNAME">globals</TT> and a <TTCLASS="VARNAME">locals</TT> environment. This environment will be used later to create a special API for the macro execution environment, and it will include access to the window (i.e. the <TTCLASS="CLASSNAME">KalamApp</TT> object) and to the document objects (via the <TTCLASS="CLASSNAME">DocManager</TT> object).</P><PRECLASS="PROGRAMLISTING"> def deleteMacro(self, macroName): del self.macroObjects[macroName] def addMacro(self, macroName, macroString): action = MacroAction(macroString, self.parent()) self.macroObjects[macroName] = action self.connect(action, PYSIGNAL("activated"), self.executeAction) return action def executeAction(self, action): action.execute(sys.stdout, sys.stderr, self.globals, self.locals) </PRE><P>The rest of the <TTCLASS="CLASSNAME">MacroManager</TT> class is simple, including methods to delete and add macros, and to execute a named macro. Note how the <TTCLASS="VARNAME">activated</TT> signal of the <TTCLASS="CLASSNAME">MacroAction</TT> is connected to the <TTCLASS="FUNCTION">executeAction</TT> slot. This slot then calls <TTCLASS="FUNCTION">execute()</TT> on the macro action with standard output and standard error as default output channels. A macro can, of course, create a new document and divert output to that document.</P><P>The <TTCLASS="CLASSNAME">MacroManager</TT> is instantiated as part of the startup process of the main application:</P><PRECLASS="PROGRAMLISTING"> # kalamapp.py def initMacroManager(self): g=globals() self.macroManager = MacroManager(self, g) </PRE><P>Initializing the macromanager will also entail deciding upon a good API for the macro extensions. This will be covered in a later section.</P><P>Adapting the <TTCLASS="FUNCTION">slotMacroExecuteDocument()</TT> slot function to use the <TTCLASS="CLASSNAME">MacroManager</TT> is quite straightforward:</P><PRECLASS="PROGRAMLISTING"> # kalamapp.py def slotMacroExecuteDocument(self): if self.docManager.activeDocument() == None: QMessageBox.critical(self, "Kalam", "No document to execute as a macro ") return title = self.docManager.activeDocument().title() try: macroText = str(self.docManager.activeDocument().text()) self.macroManager.addMacro(title, macroText) except CompilationError, e: QMessageBox.critical(self, "Kalam", "Could not compile " + self.docManager.activeDocument().title() + "\n" + str(e)) return try: doc, view = self.docManager.createDocument(KalamDoc, KalamView) doc.setTitle("Output of " + title) self.macroManager.executeMacro(title, doc, doc) except NoSuchMacroError, e: QMessageBox.critical(self, "Kalam", "Error: could not find execution code.") except ExecutionError, e: QMessageBox.critical(self, "Kalam", "Error executing " + title + "\n" + str(e)) except Exception, e: QMessageBox.critical(self, "Kalam", "Unpleasant error %s when trying to run %s." \ % (str(e), title)) </PRE><P>Note the careful handling of exceptions. You don't want your application to crash or become unstable because of a silly error in a macro.</P></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2">startup macros</A></H2><P>Executing the contents of a document is very powerful in itself—especially since we have access to the complete <TTCLASS="CLASSNAME">KalamApp</TT> object, from which we can reach the most outlying reaches of <SPANCLASS="APPLICATION">Kalam</SPAN>.</P><P>It would be very unpleasant for a user to have to load his macros as a document every time he wants to execute a macro. Ideally, a user should be able to define a set of macros that run at start-up, and be able to add macros to menu options and the keyboard.</P><P>Solving the first problem takes care of many other problems in one go. Users who are capable of creating macros are probably able to create a startup macro script that loads all their favorite macros.</P><P>We define two keys in the configuration file, <TTCLASS="VARNAME">macrodir</TT> and <TTCLASS="VARNAME">startupscript</TT>. These are the name and location of the Python script that is executed when <SPANCLASS="APPLICATION">Kalam</SPAN> is started. We start a user macro after all standard initialization is complete: </P><PRECLASS="PROGRAMLISTING"># kalamapp.py - fragment...class KalamApp(QMainWindow): def __init__(self, *args): apply(QMainWindow.__init__,(self, ) + args) ... # Run the startup macro script self.runStartup() ... def runStartup(self): """Run a Python script using the macro manager. Which script is run is defined in the configuration variables macrodir and startup. All output, and eventual failures are shown on the command-line. """ try: startupScript = os.path.join(kalamconfig.get("macrodir"), kalamconfig.get("startupscript")) startup = open(startupScript).read() self.macroManager.addMacro("startup", startup) self.macroManager.executeMacro("startup") except Exception, e: print "Could not execute startup macro", e </PRE><P>A sample startup script might start <SPANCLASS="APPLICATION">Kalam</SPAN> with an empty document:</P><PRECLASS="PROGRAMLISTING"># startup.py - startup script for Kalamprint "Kalam startup macro file"self.docManager.createDocument(KalamDoc, KalamView) </PRE><P>It is already possible to do anything you want using these macro extensions, but life can be made easier by providing shortcut functions: a special macro API. We will create one in the next section. However, a serious macro writer would have to buy a copy of this book in order to be able to use all functionality, because hiding the underlying GUI toolkit would remove far too much power from his hands. </P></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=c6996_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=x7295_252ehtm.htm">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">A Macro Language for <SPANCLASS="APPLICATION">Kalam</SPAN></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">Creating a macro API from an application</TD></TR></TABLE></DIV></BODY></HTML> </td> </tr> </table> </td> </tr> </table>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -