У меня есть программа, которая взаимодействует с радио, которое я использую, через графический интерфейс, который я написал в PyQt. Очевидно, что одной из основных функций радио является передача данных, но чтобы делать это непрерывно, мне приходится выполнять циклическую запись, что приводит к зависанию графического интерфейса. Поскольку я никогда не имел дела с потоковой обработкой, я попытался избавиться от этих зависаний, используя QCoreApplication.processEvents(). Однако радио должно переходить в режим ожидания между передачами, поэтому графический интерфейс по-прежнему зависает в зависимости от того, как долго длятся эти переходы в режим ожидания.
Есть ли простой способ исправить это с помощью QThread? Я искал руководства по реализации многопоточности с помощью PyQt, но большинство из них касаются настройки серверов и гораздо более продвинуты, чем мне нужно. Честно говоря, мне даже не нужно, чтобы мой поток что-либо обновлял во время выполнения, мне просто нужно запустить его, передать в фоновом режиме и остановить.
Переведено автоматически
Ответ 1
Я создал небольшой пример, который показывает 3 разных и простых способа работы с потоками. Я надеюсь, это поможет вам найти правильный подход к вашей проблеме.
import sys import time
from PyQt5.QtCore import (QCoreApplication, QObject, QRunnable, QThread, QThreadPool, pyqtSignal)
# Using a QRunnable # http://qt-project.org/doc/latest/qthreadpool.html # Note that a QRunnable isn't a subclass of QObject and therefore does # not provide signals and slots. classRunnable(QRunnable):
Кроме того, я бы настоятельно рекомендовал это видео от KDAB о сигналах и интервалах между потоками.
На мой взгляд, вам, вероятно, никогда не следует создавать подкласс thread с намерением перегрузить метод run . Хотя это и работает, вы в основном обходите то, как Qt хочет, чтобы вы работали. Кроме того, вы упустите такие вещи, как события и надлежащие потокобезопасные сигналы и слоты. Плюс, как вы, вероятно, увидите в приведенном выше сообщении в блоге, "правильный" способ обработки потоков вынуждает вас писать более тестируемый код.
Вот пара примеров того, как воспользоваться преимуществами QThreads в PyQt (я опубликовал отдельный ответ ниже, который правильно использует QRunnable и включает сигналы / слоты, этот ответ лучше, если у вас много асинхронных задач, которые вам нужны для балансировки нагрузки).
import sys from PyQt4 import QtCore from PyQt4 import QtGui from PyQt4.QtCore import Qt
# very testable class (hint: you can use mock.Mock for the signals) classWorker(QtCore.QObject): finished = QtCore.pyqtSignal() dataReady = QtCore.pyqtSignal(list, dict)
# this class is solely needed for these two methods, there # appears to be a bug in PyQt 4.6 that requires you to # explicitly call run and start from the subclass in order # to get the thread to actually start an event loop
defstart(self): QtCore.QThread.start(self)
defrun(self): QtCore.QThread.run(self)
app = QtGui.QApplication(sys.argv)
thread = Thread() # no parent! obj = Worker() # no parent! obj.moveToThread(thread)
# if you want the thread to stop after the worker is done # you can always call thread.start() again later obj.finished.connect(thread.quit)
# one way to do it is to start processing as soon as the thread starts # this is okay in some cases... but makes it harder to send data to # the worker object from the main gui thread. As you can see I'm calling # processA() which takes no arguments thread.started.connect(obj.processA) thread.start()
# another way to do it, which is a bit fancier, allows you to talk back and # forth with the object in a thread safe way by communicating through signals # and slots (now that the thread is running I can start calling methods on # the worker object) QtCore.QMetaObject.invokeMethod(obj, 'processB', Qt.QueuedConnection, QtCore.Q_ARG(str, "Hello World!"), QtCore.Q_ARG(list, ["args", 0, 1]), QtCore.Q_ARG(list, []))
# that looks a bit scary, but its a totally ok thing to do in Qt, # we're simply using the system that Signals and Slots are built on top of, # the QMetaObject, to make it act like we safely emitted a signal for # the worker thread to pick up when its event loop resumes (so if its doing # a bunch of work you can call this method 10 times and it will just queue # up the calls. Note: PyQt > 4.6 will not allow you to pass in a None # instead of an empty list, it has stricter type checking
app.exec_()
# Without this you may get weird QThread messages in the shell on exit app.deleteLater()
Ответ 4
Очень хороший пример от Мэтта, я исправил опечатку, а также pyqt4.8 теперь распространен, поэтому я также удалил фиктивный класс и добавил пример для сигнала dataReady
# -*- coding: utf-8 -*- import sys from PyQt4 import QtCore, QtGui from PyQt4.QtCore import Qt
# very testable class (hint: you can use mock.Mock for the signals) classWorker(QtCore.QObject): finished = QtCore.pyqtSignal() dataReady = QtCore.pyqtSignal(list, dict)
thread = QtCore.QThread() # no parent! obj = Worker() # no parent! obj.dataReady.connect(onDataReady)
obj.moveToThread(thread)
# if you want the thread to stop after the worker is done # you can always call thread.start() again later obj.finished.connect(thread.quit)
# one way to do it is to start processing as soon as the thread starts # this is okay in some cases... but makes it harder to send data to # the worker object from the main gui thread. As you can see I'm calling # processA() which takes no arguments thread.started.connect(obj.processA) thread.finished.connect(app.exit)
thread.start()
# another way to do it, which is a bit fancier, allows you to talk back and # forth with the object in a thread safe way by communicating through signals # and slots (now that the thread is running I can start calling methods on # the worker object) QtCore.QMetaObject.invokeMethod(obj, 'processB', Qt.QueuedConnection, QtCore.Q_ARG(str, "Hello World!"), QtCore.Q_ARG(list, ["args", 0, 1]), QtCore.Q_ARG(list, []))
# that looks a bit scary, but its a totally ok thing to do in Qt, # we're simply using the system that Signals and Slots are built on top of, # the QMetaObject, to make it act like we safely emitted a signal for # the worker thread to pick up when its event loop resumes (so if its doing # a bunch of work you can call this method 10 times and it will just queue # up the calls. Note: PyQt > 4.6 will not allow you to pass in a None # instead of an empty list, it has stricter type checking