PyQt: подключение сигнала к слоту для запуска фоновой операции

У меня есть следующий код, который выполняет фоновую операцию (scan_value) при обновлении индикатора выполнения в пользовательском интерфейсе (progress). scan_value перебирает некоторое значение в obj, испускающий сигнал (value_changed) каждый раз, когда значение изменяется. По причинам, которые здесь не актуальны, я должен обернуть это в объект (Scanner) в другой теме. Сканер вызывается, когда кнопка scan является clicked, И тут возникает мой вопрос... следующий код работает нормально (т.е. индикатор выполнения обновляется вовремя).

# I am copying only the relevant code here.

def update_progress_bar(new, old):
    fraction = (new - start) / (stop - start)
    progress.setValue(fraction * 100)

obj.value_changed.connect(update_progress_bar)

class Scanner(QObject):

    def scan(self):
        scan_value(start, stop, step)
        progress.setValue(100)

thread = QThread()
scanner = Scanner()
scanner.moveToThread(thread)
thread.start()

scan.clicked.connect(scanner.scan)

Но если я изменю последнюю часть на это:

thread = QThread()
scanner = Scanner()
scan.clicked.connect(scanner.scan) # This was at the end!
scanner.moveToThread(thread)
thread.start()

Индикатор выполнения обновляется только в конце (я предполагаю, что все работает в одном потоке). Должно ли это быть неактуально, если я подключу сигнал к слоту до или после перемещения объекта, принимающего объект, в поток.

2 ответа

Решение

Не должно иметь значения, установлено ли соединение до или после перемещения рабочего объекта в другой поток. Цитировать из документации Qt:

Qt:: AutoConnection - Если сигнал испускается из потока, отличного от принимающего объекта, сигнал ставится в очередь и ведет себя как Qt:: QueuedConnection. В противном случае слот вызывается напрямую, ведя себя как Qt:: DirectConnection. Тип соединения определяется при подаче сигнала. [выделение добавлено]

Итак, пока type аргумент connect установлен в QtCore.Qt.AutoConnection (который используется по умолчанию), Qt должен гарантировать, что сигналы излучаются соответствующим образом.

Проблема с примером кода, скорее всего, связана со слотом, чем с сигналом. Метод python, к которому подключен сигнал, вероятно, должен быть помечен как слот Qt с использованием декоратора pyqtSlot:

from QtCore import pyqtSlot

class Scanner(QObject):

    @pyqtSlot()
    def scan(self):
        scan_value(start, stop, step)
        progress.setValue(100)

РЕДАКТИРОВАТЬ:

Следует уточнить, что только в сравнительно недавних версиях Qt тип соединения определяется при излучении сигнала. Это поведение было введено (наряду с некоторыми другими изменениями в поддержке многопоточности Qt) с версией 4.4.

Кроме того, возможно, стоит продолжить обсуждение проблемы, связанной с PyQt. В PyQt сигнал может быть подключен к слоту Qt, другому сигналу или любому вызываемому питону. В последнем случае прокси-объект создается внутри, который оборачивает вызываемый Python и предоставляет слот, который требуется для механизма Qt сигнал / слот.

Именно этот прокси-объект является причиной проблемы. Как только прокси создан, PyQt просто сделает это:

    if (rx_qobj)
        proxy->moveToThread(rx_qobj->thread());

что хорошо, если соединение установлено после того, как принимающий объект был перемещен в его поток; но если он сделан раньше, прокси останется в главном потоке.

С использованием @pyqtSlot decorator вообще избегает этой проблемы, потому что он создает слот Qt более напрямую и вообще не использует прокси-объект.

Наконец, следует также отметить, что эта проблема в настоящее время не затрагивает PySide.

Моя проблема была решена путем перемещения соединения с местом инициализации рабочего потока, в моем случае, потому что я обращаюсь к объекту, который существует только после создания экземпляра моего класса Worker Object, который находится в другом потоке.

Просто подключите сигнал после self.createWorkerThread()

С уважением

Это связано с типами соединений Qt.

http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html

http://qt-project.org/doc/qt-4.8/qt.html

Если оба объекта находятся в одном потоке, создается стандартный тип соединения, что приводит к простому вызову функции. В этом случае трудоемкая операция происходит в потоке GUI и в интерфейсных блоках.

В случае, если тип соединения представляет собой соединение в стиле передачи сообщений, сигнал передается с использованием сообщения, которое обрабатывается в другом потоке. Поток GUI теперь свободен для обновления пользовательского интерфейса.

Когда вы не указываете тип соединения в функции соединения, тип определяется автоматически.

Другие вопросы по тегам