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&#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>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 &#8212; 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 &#8212; 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&#8212;and there are more than seventy      event types in PyQt&#8212;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      &#8212; 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 &#8212; 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&#8212; 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 + -
显示快捷键?