What can be bound, can be severed, and even for signals and slots there are divorce courts. You can disconnect a signal from a slot using QObject.disconnect(). Why would you want to disconnect signals? Not preparatory to removing a connected widget, for the connections are severed automatically when the signal recipient is deleted. I've never needed disconnect() myself, but with a bit of imagination, a likely scenario can be found.
Imagine therefore that you are writing a monitoring application. There are several data sources, but you want only to look at one at a time. The data keeps flowing in from a host of objects representing the sources. This is a scenario well worth writing a small test for...
First, we design the interface using BlackAdder's designer module or Qt Designer. This is a simple affair, with a combobox that contains the datasources, a read-only multi-line edit control that will show the output of the selected datasource, and a close button. The dialog window will be the main window, too.
Designing the interface
Then, we use Designer to add an extra slot to the form, switchDataSource, which will be called whenever a new item is selected in the datasource combobox. Drawing a simple line from the combobox to the form gives us the opportunity to connect signal and slot:
Connecting the activated(const QString&) signal to the switchDataSource() slot.
This raises an interesting point. If the activated(const QString&) signal passes a QString to the slot, shouldn't we define the slot switchDataSource() in the Designer as having an argument?
The answer is no— we will subclass the generated python code, and in the subclass we will override the generated slot with a function that has the requisite number of arguments. Python does not know the concept of overloading, so all functions with the same name are the same function. It is actually impossible to define the number of arguments a slot has in the Designer— you can only match signals to slots without arguments.
Having designed the form, we can generate it with a single menu-choice and start subclassing it, adding all kinds of interesting bits. First, we create the actual datasources.
Example 7-8. datasource.py — connecting and disconnecting signals and slots
# # datasource.py — a monitor for different datasources # import sys, whrandomfrom time import *
from qt import * from frmdatasource import frmDataSource
![]()
COOKIES=["""That is for every schoolboy and schoolgirl for the nextfour hundred years. Have you any idea how much suffering you are going to cause. Hours spent at school desks trying to find one joke in A Midsummer Night's Dream? Years wearing stupid tights in school plays and saying things like 'What ho, my lord' and 'Oh, look, here comes Othello, talking total crap as usual' Oh, and that is Ken Branagh's endless uncut four-hour version of Hamlet. "", """I've got a cunning plan...""","""A Merry Messy Christmas"? All right, but the main thing is that it should be messy -- messy cake; soggy pudding; great big wet kisses under the mistletoe... """] def randomFunction():
return str(whrandom.randrange(0, 100))
def timeFunction():
return ctime(time())
def cookieFunction():
return COOKIES[whrandom.randrange(0, len(COOKIES))]
![]()
class DataSource(QObject):def __init__(self, dataFunction, *args):
apply(QObject.__init__, (self,) + args) self.timer = self.startTimer(1000)
self.dataFunction = dataFunction
def timerEvent(self, ev):
self.emit(PYSIGNAL("timeSignal"), (self.dataFunction(),))
![]()
class DataWindow(frmDataSource):def __init__(self, *args): apply(frmDataSource.__init__, (self,) + args) self.sources = {
"random" : DataSource(randomFunction), "time" : DataSource(timeFunction), "cookies" : DataSource(cookieFunction) } self.cmbSource.insertStrList(self.sources.keys())
self.currentSource=self.sources.keys()[0]
self.connect(self.sources[self.currentSource],
PYSIGNAL("timeSignal"), self.appendData) def switchDataSource(self, source):
source=str(source)
self.disconnect(self.sources[self.currentSource],
PYSIGNAL("timeSignal"), self.appendData) self.connect(self.sources[source],
PYSIGNAL("timeSignal"), self.appendData) self.currentSource=source def appendData(self, value):
self.mleWindow.insertLine(value) self.mleWindow.setCursorPosition(self.mleWindow.numLines(), 0)
def main(args): a = QApplication(args) QObject.connect(a,SIGNAL('lastWindowClosed()'),a,SLOT('quit()')) w = DataWindow() a.setMainWidget(w) w.show() a.exec_loop() if __name__ == '__main__': main(sys.argv)
As you can see, connecting and disconnecting signals and slots is a natural and intuitive technique. Their use is not limited to connecting GUI widgets, as signals and slots are also useful for the separation of the data model of an application from its interface. In Part III, We will investigate an application model based on the strict separation of model and interface, using signals and slots to tie everything together.