index.lxp@lxpwrap=x5469_252ehtm.htm
来自「GUI Programming with Python」· HTM 代码 · 共 855 行 · 第 1/2 页
HTM
855 行
><P>The next section is concerned with the implementation of the slots called by the <TTCLASS="CLASSNAME">QAction</TT> objects that we just created:</P><PRECLASS="PROGRAMLISTING"> # # Slot implementations # def slotFileNew(self): document = self.docManager.createDocument(MDIDoc, MDIView) </PRE><P>Creating a document is now simply a matter of asking the document manager to do it — just as we did in the test script.</P><PRECLASS="PROGRAMLISTING"> def slotFileOpen(self): fileName = QFileDialog.getOpenFileName(None, None, self) if not fileName.isEmpty(): document=MDIDoc() document.open(fileName) view = self.docManager.addDocument(document, MDIView) view.setFocus() </PRE><P>Opening a file is slightly more complicated; we need to be sure that the user actually selected a file before a file can be opened. Remember that all Qt classes return <TTCLASS="CLASSNAME">QString</TT> objects, not Python <TTCLASS="CLASSNAME">string</TT> objects. As a result, we have to use <TTCLASS="FUNCTION">isEmpty()</TT> instead of comparing with <TTCLASS="CLASSNAME">None</TT>.</P><P>If the filename is not empty, we create an empty document, ask that document to open the file, and then add the document to the document manager. Of course, this complexity can also be removed to the document manager, by adding an <TTCLASS="FUNCTION">openDocument(self, fileName, documentClass, viewClass)</TT> function to <TTCLASS="CLASSNAME">DocManager</TT>.</P><PRECLASS="PROGRAMLISTING"> def slotFileSave(self, document=None): if document == None: document = self.docManager.activeDocument() if document.pathName() == None: self.slotFileSaveAs() else: try: document.save() except Exception, e: QMessageBox.critical(self, "Error", "Could not save the current document") def slotFileSaveAs(self, doc=None): fileName = QFileDialog.getSaveFileName(None, None, self) if not fileName.isEmpty(): if doc == None: doc = self.docManager.activeDocument() try: doc.save(str(fileName)) except: QMessageBox.critical(self, "Error", "Could not save the current document") </PRE><P>Saving a document entails some complexity: the document may or may not have a filename; if not, the user should supply one. Saving could fail for a variety of reasons. Nothing is so frustrating as losing your data because you simply wanted to save it. An application should handle save errors very carefully to ensure no data is lost.</P><PRECLASS="PROGRAMLISTING"> def slotFileClose(self): doc=self.docManager.activeDocument() self.docManager.closeDocument(doc) def slotFileQuit(self): try: self.docManager.closeAllDocuments() except: return qApp.quit() </PRE><P>Closing a document and quitting the application are closely related processes. Note the call to <TTCLASS="FUNCTION">qApp.quit()</TT> — this is only reached when closing all documents succeeds.</P><PRECLASS="PROGRAMLISTING"> def slotEditDoc(self): doc = self.docManager.activeDocument() doc.slotModify() def slotWindowCloseWindow(self): self.workspace.activeWindow().close() </PRE><P>Closing a single window might mean that the document will be closed, too — if it is the last or only view the document has. By retrieving the active window from the workspace, and calling the <TTCLASS="FUNCTION">close()</TT> function on it, a <TTCLASS="FUNCTION">closeEvent</TT> will be generated. This will be caught by the event filter defined below, which calls the appropriate functions in the document manager.</P><PRECLASS="PROGRAMLISTING"> def slotWindowNewWindow(self): doc = self.docManager.activeDocument() self.docManager.addView(doc, MDIView) def slotHelpAbout(self): QMessageBox.about(self, "About...", "MDI Framework\n" + "Inspired by the KDevelop templates.\n" + "(c) 2001 by Boudewijn Rempt") </PRE><P>Adding a new window is very simple: retrieve the currently active document, and ask the document manager to add a view for that document.</P><PRECLASS="PROGRAMLISTING"> def slotWindowMenuAboutToShow(self): self.windowMenu.clear() self.actions["windowNewWindow"].addTo(self.windowMenu) self.actions["windowCascade"].addTo(self.windowMenu) self.actions["windowTile"].addTo(self.windowMenu) self.windowMenu.insertSeparator() self.actions["windowCloseWindow"].addTo(self.windowMenu) if self.workspace.windowList()==[]: self.actions["windowAction"].setEnabled(FALSE) else: self.actions["windowAction"].setEnabled(TRUE) self.windowMenu.insertSeparator() i=0 # window numbering self.menuToWindowMap={} for window in self.workspace.windowList(): i+=1 index=self.windowMenu.insertItem(("&%i " % i) + str(window.caption()), self.slotWindowMenuActivated) self.menuToWindowMap[index]=window if self.workspace.activeWindow()==window: self.windowMenu.setItemChecked(index, TRUE) def slotWindowMenuActivated(self, index): self.menuToWindowMap[index].setFocus() </PRE><P>Here, we dynamically create the window menu just before it is shown. The four menu options—new window, cascade, tile and close—are part of a single <TTCLASS="CLASSNAME">QActionGroup</TT>, and can be enabled or disabled together. Of course, the same could be done with the other actions that are only enabled when there are actually documents in existence. Note also that we add accelerators by numbering the views (this will, of course, stop being sensible once we have more than nine open windows).</P><PRECLASS="PROGRAMLISTING"> # # Toplevel event filter # def eventFilter(self, object, event): if (event.type() == QEvent.Close): if (object != self): if self.docManager.closeView(object): event.accept() else: event.ignore() else: try: self.docManager.closeAllDocuments() event.accept() except Exception, e: return TRUE return QWidget.eventFilter(self, object, event) </PRE><P>Qt events contrast with Qt signals in that they are typically created by user actions, such as key presses or mouse actions. Signals are mostly emitted by objects on themselves.</P><P>An event filter is an object that receives all events for the object to which it applies. You can install eventfilters that are created for one object in other objects. In this case, all views share the same event filter as the application object. An eventfilter must return either true or false—true if the event should not be propagated further, and false if someone should handle the event.</P><P>Here, we check whether the event is of the type <TTCLASS="VARNAME">QEvent.close</TT> — if that is so, we check whether it is meant for the main application window (that's us— the <TTCLASS="VARNAME">self</TT>). In that case, all documents must be closed. This event is generated when the user closes the application.</P><P>If the event is meant for one of the sub-windows, the document manager is asked to close the view. If that is successful, the event is <TTCLASS="FUNCTION">accept()</TT>-ed, and will not be propagated any further.</P><PRECLASS="PROGRAMLISTING"> # # Functions called from the document manager #def queryCloseDocument(self, document): r = QMessageBox.information(self, str(self.caption()), "Do you want to close %s?" % document.title(), "Yes", "No", None, 0, 1) if r == 0: return QMessageBox.Yes else: return QMessageBox.No def querySaveDocument(self, document): r = QMessageBox.information(self, str(self.caption()), "Do you want to save your changes to " + "%s?" % document.title(), "Yes", "No", "Cancel", 0, 2) if r == 0: return QMessageBox.Yes elif r == 1: return QMessageBox.No else: return QMessageBox.Cancel def queryDiscardDocument(self, document): r = QMessageBox.warning(self, str(self.caption()), "Could not save %s.\n" % document.title() + "Do you want to discard your changes?", "Yes", "No", None, 0, 1) if r == 0: return QMessageBox.Yes else: return QMessageBox.No def queryFileName (self, document=None): fileName = QFileDialog.getSaveFileName(None, None, self) if not fileName.isEmpty(): return str(fileName) else: return "untitled" </PRE><P>These calls to <TTCLASS="CLASSNAME">QMessageBox</TT> and the standard file dialog <TTCLASS="CLASSNAME">QFileDialog</TT> are made from the document manager. This makes sure that the document manager can also work without a GUI.</P><P>The <TTCLASS="CLASSNAME">QMessageBox</TT> class is a bit messy, by Qt standards. There are two ways of specifying buttons: by string, or by identity. These identities, like <TTCLASS="VARNAME">QMessageBox.Yes</TT> are defined in the class. If you use these constants in your calls to <TTCLASS="FUNCTION">QMessageBox.warning()</TT>, for instance, then the return value will be the identity of the button pressed.</P><P>However, if you want the added flexibility of translatable strings, you cannot use the identities. You can call functions like <TTCLASS="FUNCTION">QMessageBox.warning()</TT> with strings, but the return value will be the position of the key pressed, starting with 0 and going from left to right.</P><P>I want to use the identities in the document manager — this makes the code a lot clearer. But I wanted to use strings in the actual message boxes. That's why I translate the position of the button pressed to the correct identity.</P></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=x5458_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=x5581_252ehtm.htm">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">View</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><A accesskey="U" href="index.lxp@lxpwrap=c5288_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 + -
显示快捷键?