index.lxp@lxpwrap=x5798_252ehtm.htm
来自「GUI Programming with Python」· HTM 代码 · 共 521 行
HTM
521 行
<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>The view</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="Creating Application Functionality"HREF="c5783.htm"><LINKREL="PREVIOUS"TITLE="Creating Application Functionality"HREF="c5783.htm"><LINKREL="NEXT"TITLE="The document"HREF="x5879.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=c5783_252ehtm.htm">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">Chapter 17. Creating Application Functionality</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><A accesskey="N" href="index.lxp@lxpwrap=x5879_252ehtm.htm">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1">The view</A></H1><P>It is certainly possible to use Python and PyQt to write a custom editing component — you shoud probably base it on the <TTCLASS="CLASSNAME">QScrollView</TT> class. But making your own editor would entail a lot of very complicated work, mostly involved with datastructures to store text, text attributes, painting text and keeping track of the cursor position. And don't forget font handling, which gets complicated with Unicode. It would make quite an interesting project, but what's the use of a rich GUI library if you don't use it?</P><P>Therefore I propose to start out using the standard <TTCLASS="CLASSNAME">QMultiLineEdit</TT> widget. With PyQt for Qt 3.0, we can convert kalam to use the new editor widget, <TTCLASS="CLASSNAME">QTextEdit</TT>, which supports embedded pictures, hyperlinks and rich text. For now, we will have to be satisfied with plain text in a single font and a single color.</P><P>However, there is one problem with using a <TTCLASS="CLASSNAME">QMultiLineEdit</TT> editor widget as a view on the text: the widget itself contains a copy of the text. A <TTCLASS="CLASSNAME">QMultiLineEdit</TT> is a conflation of document and view. This means that we will have to synchronize the text in the document and in the view — recall that with our framework there can be more than one view on the same document. It is inevitable that we waste a lot of time copying text between views and documents. This shows that we <SPAN><ICLASS="EMPHASIS">should</I></SPAN> have implemented our own editor widget, one that is based on a separation of GUI and data.</P><P>The initial wrapping of <TTCLASS="CLASSNAME">QMultiLineEdit</TT> is pretty easy:</P><PRECLASS="PROGRAMLISTING">"""kalamview.py - the editor view component for Kalamcopyright: (C) 2001, Boudewijn Remptemail: boud@rempt.xs4all.nl"""from qt import *from resources import TRUE, FALSEclass KalamMultiLineEdit(QMultiLineEdit): def event(self, e): if e.type() == QEvent.KeyPress: QMultiLineEdit.keyPressEvent(self, e) return TRUE else: return QMultiLineEdit.event(self, e) </PRE><P>By default the <TTCLASS="CLASSNAME">QWidget</TT>'s <TTCLASS="FUNCTION">event()</TT> function filters out all tab (and shift-tab) presses. Those keys are used for focus management, and move the focus to the next widget. This is not what we want in an editor, where pressing tab should insert a TAB character in the text. By overriding the default <TTCLASS="FUNCTION">event()</TT> function, we can correct this behavior. If the type—and there are more than seventy event types in PyQt—is <TTCLASS="VARNAME">QEvent.KeyPress</TT>, we send the event directly to the <TTCLASS="FUNCTION">keyPressEvent</TT> method, instead of moving focus. In all other cases, we let our parent class, <TTCLASS="CLASSNAME">QMultiLineEdit</TT> handle the event.</P><P>The view class encapsulates the editor widget we previously created:</P><PRECLASS="PROGRAMLISTING">class KalamView(QWidget): """ The KalamView class can represent object of class KalamDoc on screen, using a standard edit control. signals: sigCaptionChanged """ def __init__(self, parent, doc, *args): apply(QWidget.__init__,(self, parent) + args) self.layout=QHBoxLayout(self) self.editor=KalamMultiLineEdit(self) self.layout.addWidget(self.editor) self.doc = doc self.editor.setText(self.doc.text()) self.connect(self.doc, PYSIGNAL("sigDocTitleChanged"), self.setCaption) self.connect(self.doc, PYSIGNAL("sigDocTextChanged"), self.setText) self.connect(self.editor, SIGNAL("textChanged()"), self.changeDocument) self._propagateChanges = TRUE </PRE><P>The basic view is a plain <TTCLASS="CLASSNAME">QWidget</TT> that contains a layout manager (<TTCLASS="CLASSNAME">QHBoxLayout</TT>) that manages a <TTCLASS="CLASSNAME">KalamMultiLineEdit</TT> widget. By strictly wrapping the <TTCLASS="CLASSNAME">KalamMultiLineEdit</TT> functionality, instead of inheriting and extending, it will be easier to swap this relatively underpowered component for something with a bit more oomph and espieglerie, such as <TTCLASS="CLASSNAME">QTextEdit</TT> or KDE's editor component, libkwrite. Or, perhaps, a home-grown editor component we wrote in Python...</P><P>In the framework, we set the background color initially to green; the same principle holds here, only now we set the text initially to the text of the document.</P><P>The first two connections speak for themselves: if the title of the document changes, the caption of the window should change; and if the text of the document changes (perhaps through editing in another view), our text should change, too.</P><P>The last connection is a bit more interesting. Since we are wrapping a <TTCLASS="CLASSNAME">QMultiLineEdit</TT> in the <TTCLASS="CLASSNAME">KalamView</TT> widget, we have to pass changes in the editor to the outside world. The <TTCLASS="FUNCTION">textChanged()</TT> signal is fired whenever the user changes the text in a <TTCLASS="CLASSNAME">QMultiLineEdit</TT> widget (for instance, by pasting a string or by typing characters).</P><P>When you use functions that are not defined as slots in C++ to change the text programmatically, <TTCLASS="FUNCTION">textChanged()</TT> is not emitted. We will wrap these functions and make them emit signals, too.</P><PRECLASS="PROGRAMLISTING"> def setCaption(self, caption): QWidget.setCaption(self, caption) self.emit(PYSIGNAL("sigCaptionChanged"), (self, caption)) def document(self): return self.doc def closeEvent(self, e): pass def close(self, destroy=0): return QWidget.close(self, destroy) def changeDocument(self): if self._propagateChanges: self.doc.setText(self.editor.text(), self) def setText(self, text, view): if self != view: self._propagateChanges = FALSE self.editor.setText(text) self._propagateChanges = TRUE </PRE><P>The function <TTCLASS="FUNCTION">changeDocument()</TT> is called whenever the <TTCLASS="FUNCTION">textChanged()</TT> signal is emitted by the editor widget. Since we have a reference to the document in every view, we can call <TTCLASS="FUNCTION">setText</TT> on the document directly. Note that we pass the document the changed text and a reference to this view.</P><P>The document again passes the view reference on when a sigDocTextChanged Python signal is emitted from the document. This signal is connected to all views that represent the document, and makes sure that the <TTCLASS="FUNCTION">setText()</TT> function is called.</P><P>In the <TTCLASS="FUNCTION">setText()</TT> function the view reference is used to check whether the changes originate from this view: if that is so, then it is nonsense to change the text. If this view is currently a 'slave' view — then the text of the <TTCLASS="CLASSNAME">QMultiLineEdit</TT> should be updated. Updating the text causes a <TTCLASS="FUNCTION">textChanged()</TT> signal to be emitted — creating a recursion into oblivion.</P><P>To avoid the recursion, you can use the flag variable <TTCLASS="VARNAME">_propagateChanges</TT>. If this variable is set to FALSE, then the <TTCLASS="FUNCTION">changeDocument()</TT> will not call the <TTCLASS="FUNCTION">setText()</TT> function of the document.</P><P>Another solution would be to temporarily disconnect the <TTCLASS="FUNCTION">textChanged()</TT> signal from the <TTCLASS="FUNCTION">changeDocument()</TT> function. Theoretically, this would give a small performance benefit, since the signal no longer has to be routed nor the function called— but in practice, the difference is negligible. Connecting and disconnecting signal takes some time, too. Try the following alternative implementation of <TTCLASS="FUNCTION">setText()</TT>:</P><PRECLASS="PROGRAMLISTING"> def setText(self, text, view): if self != view: self.disconnect(self.editor, SIGNAL("textChanged()"), self.changeDocument) self.editor.setText(text) self.connect(self.editor, SIGNAL("textChanged()"), self.changeDocument) </PRE><P>Note that changing the text of a <TTCLASS="CLASSNAME">QMultiLineEdit</TT> does not change the cursor position in the editor. This makes life a lot easier, because otherwise we would have to move the cursor back to the original position ourselves in all dependent views. After all, the purpose of having multiple views on the same document is to enable the user to have more than one cursor location at the same time.</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=c5783_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=x5879_252ehtm.htm">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">Creating Application Functionality</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><A accesskey="U" href="index.lxp@lxpwrap=c5783_252ehtm.htm">Up</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">The document</TD></TR></TABLE></DIV></BODY></HTML> </td> </tr> </table> </td> </tr> </table>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?