By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,898 Members | 1,245 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,898 IT Pros & Developers. It's quick & easy.

The proper use of QSignalMapper

P: n/a
Hi.

I am trying to implement QTCore.QSignalMapper using PyQT. I finally got
to a point where I don't receive any compile or runtime error messages,
but I also do not see the final slot function fire off. Here is a
snippet of the code:

self.signalMapper = QtCore.QSignalMapper(window)
# Use qsignalmapper to use of one slot function for multiple
# widgets. The map() function sends the slot an extra param
# that identifies the sender.
for idx in range(1, maxIngredients+1):
wName = 'chkProductIngredientsDelete_'+str(idx)
w = self.__dict__[wName]
self.app.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper,
QtCore.SLOT("self.signalMapper.map"))
self.signalMapper.setMapping(w, idx)

self.app.connect(self.signalMapper, QtCore.SIGNAL(
"self.signalMapper.mapped(int)"),
self.deleteProductIngredient)

def deleteProductIngredient(self, state, widgetIdx):
"""Delete a product ingredient."""
print "INSIDE FUNCTION\n"

The idea here is to iterate through several checkbox widgets (named
"chkProductIngredientsDelete_1, _2, etc.) and connect each object's
"stateChanged" signal to the mapper's map() function, then connect the
map() function to the actual slot, deleteProductIngredient().

By the way, I've checked to ensure the statements inside the loop are
being executed.

Jan 17 '07 #1
Share this Question
Share on Google+
6 Replies


P: n/a
borntonetwork wrote:
I am trying to implement QTCore.QSignalMapper using PyQT. I finally got
to a point where I don't receive any compile or runtime error messages,
but I also do not see the final slot function fire off. Here is a
snippet of the code:

self.signalMapper = QtCore.QSignalMapper(window)
# Use qsignalmapper to use of one slot function for multiple
# widgets. The map() function sends the slot an extra param
# that identifies the sender.
for idx in range(1, maxIngredients+1):
wName = 'chkProductIngredientsDelete_'+str(idx)
w = self.__dict__[wName]
self.app.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper,
QtCore.SLOT("self.signalMapper.map"))
You need to pass the C++ signature to the SLOT() function. I believe
you want the form that does not accept any arguments:

self.app.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper,
QtCore.SLOT("map()"))

Alternatively, you can pass the slot directly to the function:

self.app.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper.map)

PyQt accepts Python methods as slots, without requiring that they be
wrapped in calls to SLOT().

David

Jan 17 '07 #2

P: n/a
Thanks, David, for you help.

When I change the slot function to what you show in your second
example, I get the same results: nothing. When I change it to what you
have in your first example, I get the following:

Object::connect: No such slot QApplication::map()
Object::connect: (sender name: 'chkProductIngredientsDelete_1')
Object::connect: (receiver name: 'main.py')
Object::connect: No such slot QApplication::map()
Object::connect: (sender name: 'chkProductIngredientsDelete_2')
Object::connect: (receiver name: 'main.py')
Object::connect: No such slot QApplication::map()
Object::connect: (sender name: 'chkProductIngredientsDelete_3')
Object::connect: (receiver name: 'main.py')
Object::connect: No such slot QApplication::map()
Object::connect: (sender name: 'chkProductIngredientsDelete_4')
Object::connect: (receiver name: 'main.py')
Object::connect: No such slot QApplication::map()
Object::connect: (sender name: 'chkProductIngredientsDelete_5')
Object::connect: (receiver name: 'main.py')
Object::connect: No such slot QApplication::map()
Object::connect: (sender name: 'chkProductIngredientsDelete_6')
Object::connect: (receiver name: 'main.py')
David Boddie wrote:
borntonetwork wrote:
I am trying to implement QTCore.QSignalMapper using PyQT. I finally got
to a point where I don't receive any compile or runtime error messages,
but I also do not see the final slot function fire off. Here is a
snippet of the code:

self.signalMapper = QtCore.QSignalMapper(window)
# Use qsignalmapper to use of one slot function for multiple
# widgets. The map() function sends the slot an extra param
# that identifies the sender.
for idx in range(1, maxIngredients+1):
wName = 'chkProductIngredientsDelete_'+str(idx)
w = self.__dict__[wName]
self.app.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper,
QtCore.SLOT("self.signalMapper.map"))

You need to pass the C++ signature to the SLOT() function. I believe
you want the form that does not accept any arguments:

self.app.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper,
QtCore.SLOT("map()"))

Alternatively, you can pass the slot directly to the function:

self.app.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper.map)

PyQt accepts Python methods as slots, without requiring that they be
wrapped in calls to SLOT().

David
Jan 18 '07 #3

P: n/a
borntonetwork wrote:
Thanks, David, for you help.

When I change the slot function to what you show in your second
example, I get the same results: nothing.
This may be due to something I missed in your code. When you
connect the signal from the signal mapper to your class, you
need to specify the signal as a C++ signature as well:

self.connect(self.signalMapper, QtCore.SIGNAL(
"mapped(int)"),
self.deleteProductIngredient)

The second example should now work.

Looking at the first example, which uses SLOT() rather than specifying
a Python method, the following might be due to the way you call
connect, though it is surprising:
When I change it to what you
have in your first example, I get the following:

Object::connect: No such slot QApplication::map()
Object::connect: (sender name: 'chkProductIngredientsDelete_1')
Object::connect: (receiver name: 'main.py')
[...]

It looks like you should try something like this:

self.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper,
QtCore.SLOT("map()"))

I can't understand why calling self.app.connect() would cause the
connection to be attempted between self.signalMapper and self.app.

Maybe someone on the PyQt/PyKDE mailing list would be able to
explain the behaviour you're seeing:

http://mats.imk.fraunhofer.de/mailman/listinfo/pykde

David

Jan 18 '07 #4

P: n/a
David, thanks for your help. Unfortunately, all attempts of making this
solution work have failed. I would be interested to know if anyone has
used QSignalMapper successfully in a similar situation. For any
interested, I worked around the problem using a closure, which seems a
bit cleaner from a coding point of view, although I don't have any idea
if it is more efficient or not:

def makeHandler(self, idx):
def handler(state):
print 'The state is:', str(state), 'for checkbox object',
str(idx)
return handler

Each iteration then created a new function based on the closure:

self.handlers = []
for idx in range(1, maxIngredients+1):
wName = 'chkProductIngredientsDelete_'+str(idx)
w = self.__dict__[wName]
self.handlers.append(self.makeHandler(idx - 1))
self.window.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.handlers[idx - 1])

So now the output when each checkbox is clicked is something like this:

The state is: 2 for checkbox object 0
The state is: 2 for checkbox object 1
....

David Boddie wrote:
borntonetwork wrote:
Thanks, David, for you help.

When I change the slot function to what you show in your second
example, I get the same results: nothing.

This may be due to something I missed in your code. When you
connect the signal from the signal mapper to your class, you
need to specify the signal as a C++ signature as well:

self.connect(self.signalMapper, QtCore.SIGNAL(
"mapped(int)"),
self.deleteProductIngredient)

The second example should now work.

Looking at the first example, which uses SLOT() rather than specifying
a Python method, the following might be due to the way you call
connect, though it is surprising:
When I change it to what you
have in your first example, I get the following:

Object::connect: No such slot QApplication::map()
Object::connect: (sender name: 'chkProductIngredientsDelete_1')
Object::connect: (receiver name: 'main.py')

[...]

It looks like you should try something like this:

self.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.signalMapper,
QtCore.SLOT("map()"))

I can't understand why calling self.app.connect() would cause the
connection to be attempted between self.signalMapper and self.app.

Maybe someone on the PyQt/PyKDE mailing list would be able to
explain the behaviour you're seeing:

http://mats.imk.fraunhofer.de/mailman/listinfo/pykde

David
Jan 20 '07 #5

P: n/a
On Saturday 20 January 2007 16:03, borntonetwork wrote:
David, thanks for your help. Unfortunately, all attempts of making this
solution work have failed. I would be interested to know if anyone has
used QSignalMapper successfully in a similar situation.
Looking again at what you originally wrote, and looking at your
solution below, I don't think it's really appropriate to use a
QSignalMapper for this purpose. (That's what I understood from the
signature of the deleteProductIngredient() method you had in your
original code.)

What you appear to want is a way of being notified about state
changes to widgets that also identifies the individual widget
that changed. You can do that by connecting the individual widgets
to the same slot and call sender() to find out which object emitted
the signal; for example:

# ...

********for*idx*in*range(1,*maxIngredients+1):
************wName*=*'chkProductIngredientsDelete_' +str(idx)
************w*=*self.__dict__[wName]
************self.connect(w,*QtCore.SIGNAL("stateCh anged(int)"),
************ self.deleteProductIngredient)

def deleteProductIngredient(self, state):
"""Delete a product ingredient."""
print self.sender(), "changed state to", state

What QSignalMapper does is collect signals from different objects,
assigning an ID to each of them, and emits a single signal with
an ID whenever it receives a signal from one of those objects.
In other words, it allows the actions of a group of objects to be
described by a parameter but discards the signal's arguments.
It's most useful for things like collections of push buttons where
you only need to know that they were clicked.
For any interested, I worked around the problem using a closure,
which seems a bit cleaner from a coding point of view, although
I don't have any idea if it is more efficient or not:
[...]
Each iteration then created a new function based on the closure:
[...]

It's good to know that you got something working in the end. There's
also another solution that involves QSignalMapper, but it may not make
the resulting code any simpler.

David
Jan 20 '07 #6

P: n/a
How simple. I will remember that sender() function for future
reference.
Thanks, David.

David Boddie wrote:
On Saturday 20 January 2007 16:03, borntonetwork wrote:
David, thanks for your help. Unfortunately, all attempts of making this
solution work have failed. I would be interested to know if anyone has
used QSignalMapper successfully in a similar situation.

Looking again at what you originally wrote, and looking at your
solution below, I don't think it's really appropriate to use a
QSignalMapper for this purpose. (That's what I understood from the
signature of the deleteProductIngredient() method you had in your
original code.)

What you appear to want is a way of being notified about state
changes to widgets that also identifies the individual widget
that changed. You can do that by connecting the individual widgets
to the same slot and call sender() to find out which object emitted
the signal; for example:

# ...

for idx in range(1, maxIngredients+1):
wName = 'chkProductIngredientsDelete_'+str(idx)
w = self.__dict__[wName]
self.connect(w, QtCore.SIGNAL("stateChanged(int)"),
self.deleteProductIngredient)

def deleteProductIngredient(self, state):
"""Delete a product ingredient."""
print self.sender(), "changed state to", state

What QSignalMapper does is collect signals from different objects,
assigning an ID to each of them, and emits a single signal with
an ID whenever it receives a signal from one of those objects.
In other words, it allows the actions of a group of objects to be
described by a parameter but discards the signal's arguments.
It's most useful for things like collections of push buttons where
you only need to know that they were clicked.
For any interested, I worked around the problem using a closure,
which seems a bit cleaner from a coding point of view, although
I don't have any idea if it is more efficient or not:

[...]
Each iteration then created a new function based on the closure:

[...]

It's good to know that you got something working in the end. There's
also another solution that involves QSignalMapper, but it may not make
the resulting code any simpler.

David
Jan 21 '07 #7

This discussion thread is closed

Replies have been disabled for this discussion.