2

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.

foresightyj
  • 2,006
  • 2
  • 26
  • 40

1 Answers1

2

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()
Angel
  • 360
  • 6
  • 13
  • Sorry for the late reply as I was on vacation. You are right. I shouldn't call from different thread. However, I still wonder about the difference in new-style and old style. I thought they were equivalent but in fact weren't. Maybe this is undocumented behavior? – foresightyj Aug 13 '13 at 00:53
  • Well, they are equivalent (as far as i know). The only difference, is that the new style is more pythonic. The old way is how it is done in C++. Source: http://qt-project.org/wiki/Signals_and_Slots_in_PySide – Angel Aug 13 '13 at 02:11
  • Yes. That is what I think when I read the signal&slot doc. In my case, I have to use threads (for long background processing) and my current solution is to just use old-style connection. I feel bad for my code that works by magic... Note that in my case, either removing Slot decorator or using old-style connections work. – foresightyj Aug 13 '13 at 02:35
  • @Angle Put new/old style connection aside. Do you know why removing Slot decorator also works? My guess is that, without slot decorator, it is just a Python method. The method gets called in the sender()'s thread context (which I am a bit unsure of). But in this regard, it is synonymous to using DirectConnection, isn't it? Warning 2 says that this is also not invalid. – foresightyj Aug 13 '13 at 02:40
  • From another answer from SO (http://stackoverflow.com/questions/14421897/is-the-pyside-slot-decorator-necessary?rq=1), it seems that it only improves speed/memory, and makes happy the C++ library. So, no much difference. Why works when you don't use the slot decorator? Well, that is a good question. No idea. Maybe the "C++ signature" when the @slot is being used. The interesting thing is that all other connection types doesn't work either. I test it all, and in all cases i get None – Angel Aug 13 '13 at 03:00
  • Probably this is undocumented behavior. Anyway, I will use the current workaround. Thanks a lot for your help! – foresightyj Aug 13 '13 at 06:36