Декоратор PySide QtCore.Slot не работает с self.sender() внутри метода

Я столкнулся с этой странностью при использовании декоратора PySide Slot. Если я декорирую свой метод, используя QtCore.Slot, и если я пытаюсь получить доступ к self.sender() внутри метода, я получаю None. Если я удалю декоратор QtCore.Slot(). Я правильно понимаю отправителя. Вот минимальный пример.

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()

Однако, если я изменю подключение нового стиля на старый, как показано в строке с комментариями.

Оно работает. Может кто-нибудь объяснить такое поведение? Большое спасибо.


person foresightyj    schedule 02.08.2013    source источник


Ответы (1)


Проблема в том, что объект, который получает сигнал (ваш рабочий класс), живет в другом потоке.

Из документов Qt:

QObject * QObject::sender () const [protected] Возвращает указатель на объект, отправивший сигнал, если он вызывается в слоте, активированном сигналом; в противном случае он возвращает 0. Указатель действителен только во время выполнения слота, который вызывает эту функцию из контекста потока этого объекта.

Указатель, возвращаемый этой функцией, становится недействительным, если отправитель уничтожен или если слот отключен от сигнала отправителя.

Предупреждение: эта функция нарушает объектно-ориентированный принцип модульности. Однако получение доступа к отправителю может быть полезным, когда к одному слоту подключено много сигналов.

Предупреждение. Как упоминалось выше, возвращаемое значение этой функции недопустимо, когда слот вызывается через Qt::DirectConnection из потока, отличного от потока этого объекта. Не используйте эту функцию в этом типе сценария.

Если вы не переместите объект в другой поток, он работает (примеры в python3, но он будет работать в python 2 после изменения строк печати):

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()
person Angel    schedule 02.08.2013
comment
Извините за поздний ответ, так как был в отпуске. Ты прав. Я не должен звонить из другого потока. Тем не менее, я все еще задаюсь вопросом о разнице в новом стиле и старом стиле. Я думал, что они эквивалентны, но на самом деле это не так. Может быть, это недокументированное поведение? - person foresightyj; 13.08.2013
comment
Ну, они эквивалентны (насколько я знаю). Единственная разница в том, что новый стиль более питонический. Старый способ - это то, как это делается в C++. Источник: qt-project.org/wiki/Signals_and_Slots_in_PySide - person Angel; 13.08.2013
comment
да. Вот что я думаю, когда читаю документацию по сигналу и слоту. В моем случае я должен использовать потоки (для длительной фоновой обработки), и мое текущее решение состоит в том, чтобы просто использовать соединение в старом стиле. Я чувствую себя плохо из-за того, что мой код работает по волшебству... Обратите внимание, что в моем случае работает либо удаление декоратора Slot, либо использование соединений в старом стиле. - person foresightyj; 13.08.2013
comment
@Angle Отложите соединение нового/старого стиля в сторону. Знаете ли вы, почему удаление декоратора слотов также работает? Я предполагаю, что без декоратора слотов это просто метод Python. Метод вызывается в контексте потока sender() (в чем я немного не уверен). Но в этом отношении это равносильно использованию DirectConnection, не так ли? Предупреждение 2 говорит, что это также не является недопустимым. - person foresightyj; 13.08.2013
comment
Из другого ответа от SO (stackoverflow.com/questions/14421897/), похоже, это только улучшает скорость/память и радует библиотеку C++. Так что особой разницы нет. Почему работает, если вы не используете декоратор слотов? Что ж, это хороший вопрос. Без понятия. Возможно, подпись C++ при использовании @slot. Интересно, что все остальные типы подключения тоже не работают. Я проверяю все это, и во всех случаях я получаю None - person Angel; 13.08.2013
comment
Вероятно, это недокументированное поведение. В любом случае, я буду использовать текущий обходной путь. Спасибо большое за вашу помощь! - person foresightyj; 13.08.2013