Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

PyQT - Signals and Slots?

103 views
Skip to first unread message

Veek M

unread,
Oct 10, 2016, 9:33:24 AM10/10/16
to
I'm reading Rapid GUI Programming - Mark Summerfield with Python and QT
pg 131. Basically the mechanism is an event table which maps a 'signal'
to a 'function/slot' -correct?

self.connect(dial, SIGNAL("valueChanged(int)"), spinbox.setValue)

Here, dial.valueChanged -> spinbox.setValue

s.connect(w, SIGNAL("signalSignature"), functionName)
s.connect(w, SIGNAL("signalSignature"), instance.methodName)
s.connect(w, SIGNAL("signalSignature"), instance,
SLOT("slotSignature"))

Here, w.signalSignature -> functionName
-> instance.methodName
-> instance.slotSignature

If signalSignature is a C++ implemented thingy then we got to pass type
info as part of the mapping so "signalSignature(int, float, const char
*). PyQT signals are any type and any number of args..


If the Slot-function is implemented in C++ it's better to use the SLOT()
mechanism:
self.connect(dial, SIGNAL("valueChanged(int)"), spinbox,
SLOT("setValue(int)"))
Here, we are mapping dial.valueChanged(int) --> spinbox.setValue(int)

----
The part i found tricky was this:

class ZeroSpinBox(QSpinBox):
zeros = 0
def __init__(self, parent=None):
super(ZeroSpinBox, self).__init__(parent)
self.connect(self, SIGNAL("valueChanged(int)"), self.checkzero)

def checkzero(self):
if self.value() == 0:
self.zeros += 1
self.emit(SIGNAL("atzero"), self.zeros)

ZeroSpinBox.valueChanged -> ZeroSpinBox.checkzero? Why is he mapping
back to himself? Shouldn't it be widget-to-widget?

Then he raises a signal 'atzero' with one arg - the lack of a atzero()
implies it's a 'short-circuit' signal so self.zeroes is a python data
type. And in the book he says:

#######################
Here is how we connect to the signal in the form’s __init__() method:
zerospinbox = ZeroSpinBox()
...
self.connect(zerospinbox, SIGNAL("atzero"), self.announce)
Again, we must not use parentheses because it is a short-circuit signal.
And
for completeness, here is the slot it connects to in the form:
def announce(self, zeros):
print "ZeroSpinBox has been at zero %d times" % zeros
###########################

Whaaa...t?? Could someone explain what exactly is his grand design
besides being awfully circuitous? So he has some other Form thingy.. and
in that he sets up another mapping from ZeroSpinBox.atzero -->
ZeroSpinBox.announce ???? where's announce and atzero defined?





Mark Summerfield

unread,
Oct 10, 2016, 10:52:52 AM10/10/16
to

The ZeroSpinBox is a tiny example designed to show how the signal/slot
mechanism works. It is just a QSpinBox with the addition of remembering
how many times (all the) ZeroSpinBox(es) have had a 0 value.

Nowadays the connections would be made with a new improved syntax:

self.connect(self, SIGNAL("valueChanged(int)"), self.checkzero) # OLD
self.valueChanged.connect(self.checkzero) # NEW
# or
self.valueChanged[int].connect(self.checkzero) # NEW

Similarly the emit:
self.emit(SIGNAL("atzero"), self.zeros) # OLD
self.atzero.emit(self.zeros) # NEW

What's happening inside ZeroSpinBox? Whenever its value is set to 0 it
calls its own checkzero() method, and this in turn emits an atzero signal
with the number of zeros so far. Why does it do this? Just to show the
mechanism. It doesn't matter whether you connect a widget to itself
(unusual but done here), or to another widget (the norm).

See the book's website https://www.qtrac.eu/pyqtbook.html
for all the source code including some updated with the new syntax.
(But the book is old, so even the Python 3.1 examples aren't as Pythonic as they would be written in Python 3.4+.)

Veek M

unread,
Oct 10, 2016, 11:48:32 AM10/10/16
to
ah - okay, so i was on the right track - thanks :) cool that you hang
out here :)

Michael Torrie

unread,
Oct 10, 2016, 12:28:51 PM10/10/16
to
On 10/10/2016 07:32 AM, Veek M wrote:
> Whaaa...t?? Could someone explain what exactly is his grand design
> besides being awfully circuitous? So he has some other Form thingy.. and
> in that he sets up another mapping from ZeroSpinBox.atzero -->
> ZeroSpinBox.announce ???? where's announce and atzero defined?

In your example code, "atzero" is implicitly defined as a signal in the
connect() call. Under the hood in Qt, signals are all dispatched by
strings when you emit() the signal. PyQt does allow you to define
signals in a more pythonic way using class variables:

class Foo(QWidget):
atzero = SIGNAL( signature )

The signature can be a list of python types, or strings describing the
C++ types. This is where the C++ nature of Qt leaks into PyQt. In C++
since the moc compiler takes C++ code and C++ type names and generates a
meta-class that defines signals in terms of strings (moc does basic
compile-time type checking), in Python we also have to provide
signatures in strings also if we need something more than core python
types (int, bool, etc). This signature helps PyQt (well Qt really)
marshal the types properly when the signal's callback is called.

But yes the example is convoluted and circuitous. I think it's just to
demonstrate how signals and slots work, though. That's what the
comments make clear. This isn't how you'd implement a real widget with
customized behavior.

In general I don't think widgets normally want to catch their own
signals. There are other mechanisms for that. Usually you subclass the
widget and then override the appropriate *Event() method. For example,
to do something special on value changes I believe you'd subclass
QSpinBox and override the changeEvent() method. Inside that you would
get your value, do your logic, and potentially emit a custom signal. And
of course calling the parent changeEvent() to propagate the event
through to the parent class.


0 new messages