I came across this weirdness in using PySide Slot decorator. If I decorate my method using QtCore.Slot and if I try to access self.sender() inside the method, I get None. If I remove the QtCore.Slot() decorator. I get the sender properly. Here is a minimal example.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Worker(QObject):
def init(self):
print "worker is ready."
@Slot()
def work(self):
print "i am tired, %s" % self.sender()
app = QApplication(sys.argv)
button = QPushButton("Kick!")
button.show()
worker = Worker()
thread = QThread()
worker.moveToThread(thread)
thread.started.connect(worker.init)
button.clicked.connect(worker.work)
# app.connect(button, SIGNAL("clicked()"), worker, SLOT("work()"))
thread.start()
app.exec_()
sys.exit()
However, if I change the new style connection to the old way, as shown in the commented line.
It works. Can someone explain this behavior? Thanks a lot.
The problem is that the object that receive the signal (your Worker class) lives in another thread.
From the Qt docs:
QObject * QObject::sender () const [protected]
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; otherwise it returns 0. The pointer is valid only during the execution of the slot that calls this function from this object's thread context.
The pointer returned by this function becomes invalid if the sender is destroyed, or if the slot is disconnected from the sender's signal.
Warning: This function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.
Warning: As mentioned above, the return value of this function is not valid when the slot is called via a Qt::DirectConnection from a thread different from this object's thread. Do not use this function in this type of scenario.
If you don't move the object to the other thread, it works (the examples is in python3, but it will work in python 2, after changing the print lines):
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Worker(QObject):
def init(self):
print("worker is ready.")
@Slot()
def work(self):
derp = self.sender()
print ("i am tired, {}".format(derp))
derp.setText("It works!")
app = QApplication(sys.argv)
button = QPushButton("Kick!")
button.show()
worker = Worker()
button.clicked.connect(worker.work)
app.exec_()
sys.exit()