Я хочу создать интерфейс Qt для управления съемкой камеры.
Что я хочу:
Прежде чем перейти к аппаратной связи, я тестирую графический интерфейс, который управляет «поддельной камерой», непрерывным циклом, который, если он запущен, выдает случайное изображение каждые 100 мс. Получение изображения выполняется в отдельном потоке, чтобы пользователь мог взаимодействовать с GUI. Пользователь может начать и остановить сбор данных с помощью кнопки.
Как я хочу это сделать:
Моей первой попыткой было просто istanziate QThread
и вызов метода run()
, который затем содержал бы бесконечный цикл с получением одного изображения, чередующимся с QThread.sleep(0.1)
. Я заметил, что после остановки и перезапуска потока, программа начинала зависать и через некоторое время зависала.
Прочитав несколько постов вокруг и главную Qt веб-страницу , я узнал, что предпочтительный способ сделать то, что я хочу, это:
подкласс QObject
для создания работника. Создать экземпляр этого работника
объект и QThread
. Переместите рабочий в новую ветку.
Более того, следуя идее в этом посте , я добавил объект QTimer
, чтобы бесконечно повторять работника внутри потока, и я реализую флаг active
, который просто заставляет поток работать без выполнения что угодно, если установлено False
.
Это решение, казалось, сработало в начале. Я могу запускать, останавливать и перезапускать регистрацию столько раз, сколько захочу.
Проблемы:
1) Процессор всегда берет довольно много ресурсов (около 30% в моем случае, согласно диспетчеру задач Windows), когда камера не получает.
2) Иногда, после запуска сбора данных, память начинает заполняться, как если бы каждое новое изображение выделялось в новой памяти (хотя, я полагаю, предполагается, что оно перезаписывается), пока программа не перестает отвечать, а затем вылетает.
Следующее изображение - это то, что я вижу в диспетчере задач, когда это происходит:
Красные стрелки соответствуют времени начала сбора.
Где я не так делаю? Это правильный путь?
Код
from PyQt5 import QtCore, QtWidgets
import sys
import numpy as np
import matplotlib
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('MyWindow')
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
# generate matplotlib canvas
self.fig = matplotlib.figure.Figure(figsize=(4,4))
self.canvas = FigureCanvasQTAgg(self.fig)
self.ax = self.fig.add_subplot(1,1,1)
self.im = self.ax.imshow(np.zeros((1000, 1000)), cmap='viridis')
self.im.set_clim(vmin=0,vmax=1)
self.canvas.draw()
# Add widgets and build layout
self.startcambutton = QtWidgets.QPushButton('Start', checkable=True)
self.startcambutton.released.connect(self.acquire)
self.contincheck = QtWidgets.QCheckBox("Continuous")
self.contincheck.clicked.connect(self.continuous_acquisition)
self.contincheck.setChecked(True)
layout = QtWidgets.QGridLayout(self._main)
layout.addWidget(self.canvas, 0, 0)
layout.addWidget(self.startcambutton, 1, 0)
layout.addWidget(self.contincheck, 2, 0)
# Initialize worker and timer and moveToThread
self.fake_camera_thread = QtCore.QThread()
self.fake_camera_timer = QtCore.QTimer()
self.fake_camera_timer.setInterval(0)
self.fake_camera_worker = FakeCamera(self)
self.fake_camera_worker.moveToThread(self.fake_camera_thread)
self.fake_camera_timer.timeout.connect(self.fake_camera_worker.acquire)
self.fake_camera_thread.started.connect(self.fake_camera_timer.start)
self.fake_camera_thread.finished.connect(self.fake_camera_worker.deleteLater)
self.fake_camera_thread.finished.connect(self.fake_camera_timer.deleteLater)
self.fake_camera_thread.finished.connect(self.fake_camera_thread.deleteLater)
self.fake_camera_thread.start()
self.camera_thread = self.fake_camera_thread
self.camera = self.fake_camera_worker
self.camera.image.connect(self.image_acquired)
def continuous_acquisition(self):
if self.contincheck.isChecked(): self.startcambutton.setCheckable(True)
else: self.startcambutton.setCheckable(False)
def acquire(self):
if self.startcambutton.isCheckable() and not self.startcambutton.isChecked():
self.startcambutton.setText('Start')
self.contincheck.setEnabled(True)
elif self.startcambutton.isCheckable() and self.startcambutton.isChecked():
self.startcambutton.setText('Stop')
self.contincheck.setDisabled(True)
self.camera.toggle()
@QtCore.pyqtSlot(object)
def image_acquired(self, image):
self.im.set_data(image)
self.canvas.draw()
def closeEvent(self, event):
""" If window is closed """
self.closeApp()
event.accept() # let the window close
def closeApp(self):
""" close program """
self.camera_thread.quit()
self.camera_thread.wait()
self.close()
return
class FakeCamera(QtCore.QObject):
image = QtCore.pyqtSignal(object)
def __init__(self, parent):
QtCore.QObject.__init__(self)
self.parent = parent
self.active = False
def toggle(self):
self.active = not self.active
def acquire(self):
""" this is the method running indefinitly in the associated thread """
if self.active:
self.new_acquisition()
def new_acquisition(self):
noise = np.random.normal(0, 1, (1000, 1000))
self.image.emit(noise)
if not self.parent.startcambutton.isChecked():
self.active = False
QtCore.QThread.sleep(0.1)
if __name__ == '__main__':
app = QtCore.QCoreApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
mainGui = MyWindow()
mainGui.show()
app.aboutToQuit.connect(app.deleteLater)
app.exec_()