Seite 1 von 1
PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Freitag 15. Oktober 2010, 23:58
von DrFaust
Hallo allerseits,
ich schreibe eine Anwendung, in der ich den Qt-Signal-Slot-Mechanismus verwende um Informationen innerhalb des Programms auszutauschen. Die meisten meiner Klassen sind QObjects und per signal-slot verbunden. Nun habe ich eine Klasse A, deren Objekte je eine ganze Liste von Objekten der Klasse B hält. Nun sollen die einzelnen Objekte der Klasse B ihren 'Papa' darüber informieren, wenn bei ihnen bestimmte Ereignisse passieren. Damit das 'Papa'-Objekt weiß von welcher der B Instanzen die Benachrichtigung kommt, sollen die Klassen beim emit jeweils self, also einen Pointer auf sich selber übergeben. Das ganze sieht etwa so aus:
Code: Alles auswählen
class A(QtCore.QObject):
def __init__(self):
super(A,self).__init__()
def get_signal(self,child):
print(child)
class B(QtCore.QObject):
mySignal = QtCore.pyqtSignal(B)
def __init__(self):
super(B,self).__init__()
Problem: Ich kann das signal von B nicht wie da angegeben erstellen, da die Klasse B zu diesem Zeitpunkt noch gar nicht existiert. In C ergibt sich das Problem nicht, aber in Python eben schon (fühlt sich komisch an das zu sagen). Wie löse ich so was?
Danke schon mal
DrFaust
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Samstag 16. Oktober 2010, 00:43
von BlackJack
Die Klasse wird erst an den Namen `B` gebunden, wenn die Klassendefinition komplett abgearbeitet wurde. Hast Du schon versucht *danach* das Attribut zu erstellen?
Code: Alles auswählen
class B(QtCore.QObject):
def __init__(self):
super(B, self).__init__()
B.mySignal = QtCore.pyqtSignal(B)
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Samstag 16. Oktober 2010, 13:05
von ichisich
Hi,
Versuch mal im Slot mit
an die Referenz des sendenden Objektes zu kommen.
Gruß
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Samstag 16. Oktober 2010, 13:08
von DrFaust
@BlackJack
Warum das nicht funktioniert ist klar. Ich habe deinen Lösungsansatz auch versucht. Ich kann aber mit denn auftauchenden Fehlermeldung nicht viel anfangen:
Code: Alles auswählen
Object::connect: Use the SIGNAL macro to bind B::(QObject*)
Traceback (most recent call last):
File "/home/nils/ma_workspace/PyDDHelper/experiments/foren_experiment.py", line 32, in <module>
b1.mySignal.connect(a.get_signal)
TypeError: connect() failed between [B] and unislot()
Der Code den ich benutzt habe ist der folgende:
Code: Alles auswählen
from PyQt4 import QtCore
class A(QtCore.QObject):
def __init__(self):
super(A,self).__init__()
def get_signal(self,child):
print(child)
class B(QtCore.QObject):
def __init__(self,name):
super(B,self).__init__()
self.name = name
def __str__(self):
return self.name
def emit_signal(self):
self.mySignal.emit(self)
B.mySignal = QtCore.pyqtSignal(B)
a = A()
b1 = B("b1")
b2 = B("b2")
b1.mySignal.connect(a.get_signal)
b2.mySignal.connect(a.get_signal)
b1.emit_signal()
b2.emit_signal()
Außerdem vermute ich mal, dass man da Erstellen des signals vor mehrfacher Ausführung schützen sollte. Ich bin mir nicht sicher, was passiert, wenn das entsprechende Modul an mehreren Stellen eingebunden wird. Wird der Code dann mehrfach ausgeführt?!?
Ein solcher Schutz könnte etwa so aussehen:
Code: Alles auswählen
if not "mySignal" in dir(B):
B.mySignal = QtCore.pyqtSignal(B)
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Samstag 16. Oktober 2010, 13:13
von DrFaust
@ichisich
Der entsprechende Code wäre dann wohl dieser:
Code: Alles auswählen
from PyQt4 import QtCore
class A(QtCore.QObject):
def __init__(self):
super(A,self).__init__()
def get_signal(self):
print(self.sender())
class B(QtCore.QObject):
mySignal = QtCore.pyqtSignal()
def __init__(self,name):
super(B,self).__init__()
self.name = name
def __str__(self):
return self.name
def emit_signal(self):
self.mySignal.emit()
a = A()
b1 = B("b1")
b2 = B("b2")
b1.mySignal.connect(a.get_signal)
b2.mySignal.connect(a.get_signal)
b1.emit_signal()
b2.emit_signal()
Scheint zu funktionieren. Löst zwar nicht das Problem wie man signals erstellt, die die Objekte der eigenen Klasse als Parameter haben. Löst aber mein Problem.
Danke
DrFaust
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Samstag 16. Oktober 2010, 13:22
von lunar
Statt des impliziten Durchgriffs auf den Sender halte ich es für sinnvoller, eine partielle Funktion als Slot zu verwenden. Die Signatur bleibt also weiterhin "get_signal(self, child)", und die Verbindung der Slots sieht wie folgt aus:
Code: Alles auswählen
from functools import partial
b1.mySignal.connect(partial(a.get_signal, b1))
b2.mySignal.connect(partial(a.get_singal, b2))
Diese Vorgehensweise hat den Vorteil, auch bei solchen Slots zu funktionieren, die nicht Attribut einer von QObject abgeleiteten Klasse sind.
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Samstag 16. Oktober 2010, 20:32
von DrFaust
@lunar
Ich kenne partials nicht, aber nach dem was ich gerade darüber gelesen habe bleibt aber ja das Problem, dass ich kein signal definieren kann, das ein Objekt der eigenen Klasse als Parameter hat bestehen. Deine Lösung würde verändert ja nur den connect Aufruf, nicht aber die Definition des Signals. Und da geht es ja schon schief. Oder habe ich dich falsch verstanden?
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Sonntag 17. Oktober 2010, 11:19
von cofi
Und genau dieses Problem loest partial:
http://docs.python.org/library/functool ... ls.partial
Und nein, nicht der `connect`-Aufruf wird veraendert, sondern das Funktionsobjekt, das an `connect` uebergeben wird.
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Sonntag 17. Oktober 2010, 11:35
von lunar
@DrFaust: "functools.partial" erzeugt einfach eine neue Funktion, indem gewissen Argumente der ursprünglichen Funktion mit festen Werten vorbelegt werden. Probiere es doch einfach einmal im Interpreter aus. Du kannst mir mithin vertrauen, dass dieser Ansatz Dein Problem löst.
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Sonntag 17. Oktober 2010, 20:16
von DrFaust
Hmm, das Problem war eher, dass ich mir nicht sicher war, was ich jetzt wie ändern sollte (was wohl wiederum mit meinem mangelnden Verständnis von partials zu tun hat). Aber ich habe jetzt so lange rumprobiert, bis es ging. Und ich denke, ich blicke so langsam, was da passiert. partial(a.get_signal, b1) Definiert mir quasi einen neuen Slot, der den Aufruf an get_signal durchtunnelt, dabei aber den Parameter auf b1 setzt. Genau genommen habe ich also hinterher für n B-Objekte auch n slots. Korrekt?
Wie auch immer, das löst das Problem vollständig. Danke euch allen.
Ciao
DrFaust
Re: PyQt4 Signal mit eigener Klasse als Parameter
Verfasst: Montag 18. Oktober 2010, 11:13
von lunar
@DrFaust: Ja, korrekt (einigermaßen

). Versuche aber vielleicht, das Konzept noch allgemeiner zu begreifen. Diese Technik funktioniert nicht nur für Slots, sondern für jede Art von Funktion.