2016-02-14 3 views
1

Я застрял. Это должно быть легко, и я делал это много раз, используя C++ API Qt, но по какой-то причине некоторые из моих сигналов/слотов не работают, когда я делаю это в PyQt (я недавно начал с концепции рабочего QObject в PyQt). Я считаю, что он должен что-то делать с отдельным потоком, который я излучаю в/из своих сигналов.Сигналы PyQt между потоками, не излучаемыми

from PyQt4.QtCore import QThread, QObject, pyqtSignal, pyqtSlot, QTimer 
from PyQt4.QtGui import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel 

class Slave(QObject): 

    countSignal = pyqtSignal(int) 

    def __init__(self, parent = None): 
    super(Slave, self).__init__() 

    self.toggleFlag = False 
    self.counter = 0 

    @pyqtSlot() 
    def work(self): 
    if not self.toggleFlag: return 

    if self.counter > 10: self.counter = 0 
    self.counter += self.counter 
    self.countSignal.emit(self.counter) 

    @pyqtSlot() 
    def toggle(self): 
    self.toggleFlag = not self.toggleFlag 


class Master(QWidget): 

    toggleSignal = pyqtSignal() 

    def __init__(self, parent = None): 
    super(Master, self).__init__() 

    self.initUi() 
    self.setupConn() 

    def __del__(self): 
    self.thread.quit() 
    while not self.thread.isFinished(): pass 

    def initUi(self): 
    layout = QVBoxLayout() 
    self.buttonToggleSlave = QPushButton('Start') 
    self.labelCounterSlave = QLabel('0') 
    layout.addWidget(self.buttonToggleSlave) 
    layout.addWidget(self.labelCounterSlave) 
    self.setLayout(layout) 
    self.show() 

    def setupConn(self): 
    self.thread = QThread() 
    slave = Slave() 
    timer = QTimer() 
    timer.setInterval(100) 

    # Make sure that both objects are removed properly once the thread is terminated 
    self.thread.finished.connect(timer.deleteLater) 
    self.thread.finished.connect(slave.deleteLater) 

    # Connect the button to the toggle slot of this widget 
    self.buttonToggleSlave.clicked.connect(self.toggle) 
    # Connect widget's toggle signal (emitted from inside widget's toggle slot) to slave's toggle slot 
    self.toggleSignal.connect(slave.toggle) 

    # Connect timer's timeout signal to slave's work slot 
    timer.timeout.connect(slave.work) 
    timer.timeout.connect(self.timeout) 
    # Connect slave's countSignal signal to widget's viewCounter slot 
    slave.countSignal.connect(self.viewCounter) 

    # Start timer 
    timer.start() 
    # Move timer and slave to thread 
    timer.moveToThread(self.thread) 
    slave.moveToThread(self.thread) 

    # Start thread 
    self.thread.start()  

    @pyqtSlot(int) 
    def viewCounter(self, value): 
    print(value) 
    self.labelCounterSlave.setText(str(value)) 

    @pyqtSlot() 
    def toggle(self): 
    print("Toggle called") 
    self.buttonToggleSlave.setText("Halt" if (self.buttonToggleSlave.text() == "Start") else "Start") 
    self.toggleSignal.emit() 

    @pyqtSlot() 
    def timeout(self): 
    print("Tick") 


if __name__ == "__main__": 
    app = QApplication([]) 
    w = Master() 
    w.setStyleSheet('cleanlooks') 
    app.exec_() 

После вещи не срабатывают/излучаемый:

  • timeout() слот моего виджета - я добавил это понять, почему таймер не вызывает слот моего работника, но все, что я узнал, что это Безразлично «т работать здесь либо ...
  • work() и toggle() слотов внутри моего Slave рабочего класса
  • countSignal - это никогда не испускаются, так как мой виджет viewCounter() никогда не срабатывает.

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

Я что-то упустил?

ответ

1

Существует несколько проблем с кодом, которые препятствуют правильной работе.

  1. Согласно documentation, вы должны начать (и остановки) таймер из потока он находится. Вы не можете запустить его из другого потока. Если вы хотите, чтобы таймер находился в потоке, вы должны перенести код создания на объект Slave и вызвать timer.start() в слот, подключенный к потоку started. Вы должны быть осторожны, хотя метод Slave.__init__ будет продолжать работать в основном потоке. Кроме того, вы можете просто оставить таймер в основном потоке.

  2. slave и timer являются сборщиками мусора, когда setupConn() закончен. Храните их как self.slave и self.timer. (В качестве альтернативы вы должны указать родителя для них, но, похоже, это приводит к сбоям приложений при выходе, поэтому лучше всего хранить их как атрибуты экземпляра).

  3. Я предполагаю, что линия self.counter += self.counter действительно должна быть self.counter += 1? В противном случае счетчик никогда не увеличивается.

+0

Спасибо! Я забыл о таймере. Это была главная проблема. Что касается приращения ... (facepalm) ... Yeap, он никогда не увеличивается: D Btw для 'self.slave' и' self: timer' Я также думал, что это выходит за рамки, но я так привык к версию C++, где вы передаете указатель на поток этих двух, который я не думал об этом изменить здесь. У меня есть один вопрос, хотя - на C++ неверно создавать вещи как члены класса, если вы собираетесь переместить их в другой поток. Наверное, это не так в Python? Разве это не делает 'QObject.deleteLater()' бессмысленным? – rbaleksandar

+0

@rbaleksandar Это может быть не очень хорошая практика (хотя я не уверен, что это была бы альтернатива), но в Python нет ничего принципиально неправильного. Это просто ссылка на объект C++.'deleteLater()' должен по-прежнему работать, и если вы попытаетесь получить доступ к Qt-объекту после его удаления через оставшуюся ссылку, вы получите предупреждение, выведенное на терминал о удаленном объекте C++. –

+0

В C++, если вы используете член класса, он будет (я не могу правильно запомнить сейчас, какое из следующего произойдет) либо сбой, либо сигналы не будут правильно выполнены, и вы в конечном итоге исправите/зафиксируете в неправильном потоке. Вот почему я пошел без «себя», но затем снова передавая аргументы в Python, немного отличается от того, что мы имеем на C++ (с точки зрения контроля, передано ли оно по ссылке или значению). Если подчиненный и таймер являются частью класса виджетов (в моем случае), то после уничтожения виджета он также должен уничтожать все его члены, включая эти два. Hmmm ... – rbaleksandar