index.lxp@lxpwrap=x1408_252ehtm.htm
来自「GUI Programming with Python」· HTM 代码 · 共 1,236 行 · 第 1/2 页
HTM
1,236 行
<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>Connecting with signals and slots</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="Signals and Slots in Depth"HREF="c1267.htm"><LINKREL="PREVIOUS"TITLE="Signals and Slots in Depth"HREF="c1267.htm"><LINKREL="NEXT"TITLE="Disconnecting"HREF="x1631.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=c1267_252ehtm.htm">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">Chapter 7. Signals and Slots in Depth</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><A accesskey="N" href="index.lxp@lxpwrap=x1631_252ehtm.htm">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1">Connecting with signals and slots</A></H1><P>Signals and slots come in two basic varieties: Vanilla, or C++ signals and slots (as defined in the Qt library) and Pythonic (signals and slots defined in Python). Any function of any object can be used as a slot in Python (you don't even have to inherit from <TTCLASS="CLASSNAME">QObject</TT>). This contrasts to C++, where you need to specially mark a function as a slot in order to be able to connect it to a signal (and have to inherit <TTCLASS="CLASSNAME">QObject</TT>).</P><P>Every class that descends from <TTCLASS="CLASSNAME">QObject</TT> is eligible for the sending (<SPAN><ICLASS="EMPHASIS">emitting</I></SPAN> is the technical term) and connecting of signals to its own methods. That means that if your Python class is to emit signals it has to ultimately inherit <TTCLASS="CLASSNAME">QObject</TT>.</P><P>Connections are made using the <TTCLASS="FUNCTION">connect()</TT> method. This is a class method of <TTCLASS="CLASSNAME">QObject</TT>, and you can, according to your preference, use the method on <TTCLASS="CLASSNAME">QObject</TT>, or on the actual object you're working with.</P><P>You can connect signals to slots, but also to other signals, creating a chain of notifications. If you want to disconnect signals from slots, you can use <TTCLASS="CLASSNAME">QObject</TT>.<TTCLASS="FUNCTION">disconnect()</TT>. If you want to emit signals from a Python object, you can use the <TTCLASS="CLASSNAME">QObject</TT>.<TTCLASS="FUNCTION">emit()</TT> function.</P><P>The connect function can take the following parameters:</P><P></P><UL><LI><P>sender — the <TTCLASS="CLASSNAME">QObject</TT> that will send the signal. </P></LI><LI><P>signal — the signal that must be connected </P></LI><LI><P>receiver — the <TTCLASS="CLASSNAME">QObject</TT> that has the slot method that will be called when the signal is emitted. </P></LI><LI><P>slot — the slot method that will be called when the signal is emitted. </P></LI></UL><P>If you're connecting your signals from within a class, you can often omit the third parameter — the receiver.</P><P>PyQt defines three special functions that appear to be macros (because of their all-caps spelling, as in C++) but are in fact just functions. (In fact, there are no macros in Python). These are <TTCLASS="FUNCTION">SLOT()</TT>, <TTCLASS="FUNCTION">SIGNAL()</TT> and <TTCLASS="FUNCTION">PYSIGNAL()</TT>.</P><P>Two of these functions are meant for signals and slots defined in C++; the other is meant for signals defined in Python. Signals and slots defined in C++ are connected on the level of C++ (i.e., not in the sip registry) and can be a bit faster.</P><P>The first function is <TTCLASS="FUNCTION">SLOT()</TT>, which marks its only argument, a string, as a slot defined in the Qt library, i.e. in C++. The corresponding <TTCLASS="FUNCTION">SIGNAL</TT>, which also has one string argument, marks its argument as a signal as defined in Qt.</P><P>For instance, from the documentation of <TTCLASS="CLASSNAME">QListview</TT> we can learn that this class possesses the slot <TTCLASS="FUNCTION">invertSelection()</TT>. From the documentation of <TTCLASS="CLASSNAME">QButton</TT> we learn that it can emit a signal <TTCLASS="FUNCTION">clicked()</TT>. We can connect a button press to this slot as follows:</P><DIVCLASS="EXAMPLE"></A><P><B>Example 7-4. Connecting a signal to a slot</B></P><PRECLASS="PROGRAMLISTING">## lsv.py - connect a button to a listview#import sysfrom qt import *class MainWindow(QMainWindow): def __init__(self, *args): apply(QMainWindow.__init__, (self, ) + args) self.mainWidget=QWidget(self); <IMGSRC="images/callouts/1.gif"HSPACE="0"VSPACE="0"BORDER="0"ALT="(1)"></A> self.vlayout = QVBoxLayout(self.mainWidget, 10, 5) self.lsv = QListView(self.mainWidget) <IMGSRC="images/callouts/2.gif"HSPACE="0"VSPACE="0"BORDER="0"ALT="(2)"></A> self.lsv.addColumn("First column") self.lsv.setSelectionMode(QListView.Multi) self.lsv.insertItem(QListViewItem(self.lsv, "One")) self.lsv.insertItem(QListViewItem(self.lsv, "Two")) self.lsv.insertItem(QListViewItem(self.lsv, "Three")) self.bn = QPushButton("Push Me", self.mainWidget) <IMGSRC="images/callouts/3.gif"HSPACE="0"VSPACE="0"BORDER="0"ALT="(3)"></A> self.vlayout.addWidget(self.lsv) self.vlayout.addWidget(self.bn) QObject.connect(self.bn, SIGNAL("clicked()"), <IMGSRC="images/callouts/4.gif"HSPACE="0"VSPACE="0"BORDER="0"ALT="(4)"></A> self.lsv, SLOT("invertSelection()")) self.setCentralWidget(self.mainWidget)def main(args): app=QApplication(args) win=MainWindow() win.show() app.connect(app, SIGNAL("lastWindowClosed()") , app , SLOT("quit()") ) app.exec_loop() if __name__=="__main__": main(sys.argv) </PRE><DIVCLASS="CALLOUTLIST"><DLCOMPACT="COMPACT"><DT><A href="index.lxp@lxpwrap=x1408_252ehtm.htm#CH6MAINWIDGET"><IMGSRC="images/callouts/1.gif"HSPACE="0"VSPACE="0"BORDER="0"ALT="(1)"></A></DT><DD>We want to combine a pushbutton and a listview in a main window. So we first define a single main widget that can be managed by the layout manager of <TTCLASS="CLASSNAME">QMainWindow</TT>, and then add a new layout manager to that widget. The pushbutton and the listview then become children of the main widget, <TTCLASS="VARNAME">self.mainWidget</TT>. </DD><DD><P>You don't need to keep <TTCLASS="VARNAME">self</TT> references to the widgets, because these widgets are child objects to <TTCLASS="CLASSNAME">QMainWindow</TT>. However, if you later want to access those widgets, it is necessary to have a reference.</P></DD><DT><A href="index.lxp@lxpwrap=x1408_252ehtm.htm#CH6LISTVIEW"><IMGSRC="images/callouts/2.gif"HSPACE="0"VSPACE="0"BORDER="0"ALT="(2)"></A></DT><DD>The <TTCLASS="CLASSNAME">QListView</TT> is a child widget to the <TTCLASS="VARNAME">mainWidget</TT>. It has one column and owns three listview items. In order to give the pushbutton some useful work to do, we allow a multiple selection.</DD><DT><A href="index.lxp@lxpwrap=x1408_252ehtm.htm#CH6PUSHBUTTON"><IMGSRC="images/callouts/3.gif"HSPACE="0"VSPACE="0"BORDER="0"ALT="(3)"></A></DT><DD>A very standard pushbutton — nothing special, except that is is a child of the <TTCLASS="VARNAME">mainWidget</TT>.</DD><DT><A href="index.lxp@lxpwrap=x1408_252ehtm.htm#CH6LSVCONNECT"><IMGSRC="images/callouts/4.gif"HSPACE="0"VSPACE="0"BORDER="0"ALT="(4)"></A></DT><DD>This is the actual connection between the <TTCLASS="FUNCTION">clicked()</TT> signal of the button and the <TTCLASS="FUNCTION">invertSelection()</TT> of the listview. If you press the button, you'll notice the effect.</DD></DL></DIV></DIV><P>Note that the arguments of SIGNAL and SLOT are used as an index of the dictionary <SPANCLASS="APPLICATION">sip</SPAN> keeps of available slots and signals, and that you should match the definition of the signal and slot as given in the class documentation exactly.</P><P>A more complicated signal/slot combination can pass an integer along (or even a complete object). Let's connect the knob of a <TTCLASS="CLASSNAME">QDial</TT> to a few functions, creating an color dialer. A <TTCLASS="CLASSNAME">QDial</TT> generates the <TTCLASS="FUNCTION">valueChanged(int)</TT> signal, which passes the current value of the dial in the form of an integer to every slot that's connected to the signal. You need to explicitly enter the types of the signal arguments, but not their names.</P><DIVCLASS="EXAMPLE"></A><P><B>Example 7-5. Connection a dial to a label with signals and slots</B></P><PRECLASS="PROGRAMLISTING">## dial.py — connecting a QDial to a QLabel or two#import sysfrom qt import *class MainWindow(QMainWindow): def __init__(self, *args): apply(QMainWindow.__init__, (self, ) + args) self.vlayout = QVBoxLayout(self, 10, 5) self.hlayout = QHBoxLayout(None, 10, 5) self.labelLayout=QHBoxLayout(None, 10, 5) self.red = 0 self.green = 0 self.blue = 0 self.dialRed = QDial(0, 255, 1, 0, self) self.dialRed.setBackgroundColor(QColor("red")) self.dialRed.setNotchesVisible(1) self.dialGreen = QDial(0, 255, 1, 0, self) self.dialGreen.setBackgroundColor(QColor("green")) self.dialGreen.setNotchesVisible(1) self.dialBlue = QDial(0, 255, 1, 0, self) self.dialBlue.setBackgroundColor(QColor("blue")) self.dialBlue.setNotchesVisible(1) self.hlayout.addWidget(self.dialRed) self.hlayout.addWidget(self.dialGreen) self.hlayout.addWidget(self.dialBlue) self.vlayout.addLayout(self.hlayout) self.labelRed = QLabel("Red: 0", self) self.labelGreen = QLabel("Green: 0", self) self.labelBlue = QLabel("Blue: 0", self) self.labelLayout.addWidget(self.labelRed) self.labelLayout.addWidget(self.labelGreen) self.labelLayout.addWidget(self.labelBlue) self.vlayout.addLayout(self.labelLayout) QObject.connect(self.dialRed, SIGNAL("valueChanged(int)"), self.slotSetRed) QObject.connect(self.dialGreen, SIGNAL("valueChanged(int)"), self.slotSetGreen) QObject.connect(self.dialBlue, SIGNAL("valueChanged(int)"), self.slotSetBlue) QObject.connect(self.dialRed, SIGNAL("valueChanged(int)"), self.slotSetColor) QObject.connect(self.dialGreen, SIGNAL("valueChanged(int)"), self.slotSetColor) QObject.connect(self.dialBlue, SIGNAL("valueChanged(int)"), self.slotSetColor) def slotSetRed(self, value): self.labelRed.setText("Red: " + str(value)) self.red = value def slotSetGreen(self, value): self.labelGreen.setText("Green: " + str(value)) self.green = value def slotSetBlue(self, value): self.labelBlue.setText("Blue: " + str(value)) self.blue = value def slotSetColor(self, value): self.setBackgroundColor(QColor(self.red, self.green, self.blue)) self.labelRed.setBackgroundColor(QColor(self.red, 128, 128)) self.labelGreen.setBackgroundColor(QColor(128, self.green, 128)) self.labelBlue.setBackgroundColor(QColor(128, 128, self.blue))def main(args): app=QApplication(args) win=MainWindow() win.show() app.connect(app, SIGNAL("lastWindowClosed()") , app , SLOT("quit()") ) app.exec_loop()if __name__=="__main__": main(sys.argv) </PRE></DIV><P>Note that we connect the C++ signals (SIGNAL), to Python functions. You simply give the function object as the slot argument— not the result of the function call. Consider the difference between:</P><PRECLASS="PROGRAMLISTING"> QObject.connect(self.dialBlue, SIGNAL("valueChange(int)"), self.slotSetColor()) </PRE><P>which is wrong, and:</P><PRECLASS="PROGRAMLISTING"> QObject.connect(self.dialBlue, SIGNAL("valueChange(int)"), self.slotSetColor) </PRE><P>which is right. All that difference for two little brackets! This is a rather frequent typo or thinko. (However, to give you a glimpse of the dynamic nature of Python, if you have a function that returns the correct function to connect to the signal, you do want a function call in <TTCLASS="FUNCTION">connect()</TT>.)</P><P>Note also that the number and type of arguments of the signal and the slot you want to connect have to match. When connecting C++ signals to C++ slots, there is also a bit of
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?