Python - PyQt: Memory issue with QThread
I want to build a Qt interface to control a camera acquisition.
What I want:
Before going into the hardware communication, I am testing a GUI which control a "fake camera", a continuous loop which, if started, gives a random image every 100 ms. The image acquisition is running in a separate thread so that the user could interact with the GUI. The user can start and stop the acquisition via a button.
How I want to do it:
My first attempt was to simply istanziate a QThread
and call the run()
method which would then contain an infinite loop with single image acquisitions interleaved by a QThread.sleep(0.1)
. I noticed that after stopping and restarting the thread, the program was starting to lag and crashed after some time.
By reading some posts around and the main Qt webpage, I then learned that the preferable way to do what I want is to:
subclass a
QObject
to create a worker. Instantiate this worker
object and aQThread
. Move the worker to the new thread.
Moreover, following the idea in this post, I added a QTimer
object to iterate indefinitely the worker inside the thread, and I implement an active
flag that just makes the thread run without doing anything if it's set to False
.
This solution seemed to work at the beginning. I can start, stop and restart the acquisition as many times as I want.
Problems:
1) The CPU is always taking quite some resources (about 30% in my case, according to windows task menager) when the camera is not acquiring.
2) Sometimes, after acquisition is started, the memory start to be filled, like if every new image is allocated in new memory (while it is suppose to be overwritten I guess), till the program becomes irresponsive and then crashes.
The following image is what I see in task menager when this happens:
Red arrows correspond to the time the acquisition start.
Where am I doing wrong? Is it the right way to procede?
The code
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_()
python multithreading pyqt pyqt5
add a comment |
I want to build a Qt interface to control a camera acquisition.
What I want:
Before going into the hardware communication, I am testing a GUI which control a "fake camera", a continuous loop which, if started, gives a random image every 100 ms. The image acquisition is running in a separate thread so that the user could interact with the GUI. The user can start and stop the acquisition via a button.
How I want to do it:
My first attempt was to simply istanziate a QThread
and call the run()
method which would then contain an infinite loop with single image acquisitions interleaved by a QThread.sleep(0.1)
. I noticed that after stopping and restarting the thread, the program was starting to lag and crashed after some time.
By reading some posts around and the main Qt webpage, I then learned that the preferable way to do what I want is to:
subclass a
QObject
to create a worker. Instantiate this worker
object and aQThread
. Move the worker to the new thread.
Moreover, following the idea in this post, I added a QTimer
object to iterate indefinitely the worker inside the thread, and I implement an active
flag that just makes the thread run without doing anything if it's set to False
.
This solution seemed to work at the beginning. I can start, stop and restart the acquisition as many times as I want.
Problems:
1) The CPU is always taking quite some resources (about 30% in my case, according to windows task menager) when the camera is not acquiring.
2) Sometimes, after acquisition is started, the memory start to be filled, like if every new image is allocated in new memory (while it is suppose to be overwritten I guess), till the program becomes irresponsive and then crashes.
The following image is what I see in task menager when this happens:
Red arrows correspond to the time the acquisition start.
Where am I doing wrong? Is it the right way to procede?
The code
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_()
python multithreading pyqt pyqt5
add a comment |
I want to build a Qt interface to control a camera acquisition.
What I want:
Before going into the hardware communication, I am testing a GUI which control a "fake camera", a continuous loop which, if started, gives a random image every 100 ms. The image acquisition is running in a separate thread so that the user could interact with the GUI. The user can start and stop the acquisition via a button.
How I want to do it:
My first attempt was to simply istanziate a QThread
and call the run()
method which would then contain an infinite loop with single image acquisitions interleaved by a QThread.sleep(0.1)
. I noticed that after stopping and restarting the thread, the program was starting to lag and crashed after some time.
By reading some posts around and the main Qt webpage, I then learned that the preferable way to do what I want is to:
subclass a
QObject
to create a worker. Instantiate this worker
object and aQThread
. Move the worker to the new thread.
Moreover, following the idea in this post, I added a QTimer
object to iterate indefinitely the worker inside the thread, and I implement an active
flag that just makes the thread run without doing anything if it's set to False
.
This solution seemed to work at the beginning. I can start, stop and restart the acquisition as many times as I want.
Problems:
1) The CPU is always taking quite some resources (about 30% in my case, according to windows task menager) when the camera is not acquiring.
2) Sometimes, after acquisition is started, the memory start to be filled, like if every new image is allocated in new memory (while it is suppose to be overwritten I guess), till the program becomes irresponsive and then crashes.
The following image is what I see in task menager when this happens:
Red arrows correspond to the time the acquisition start.
Where am I doing wrong? Is it the right way to procede?
The code
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_()
python multithreading pyqt pyqt5
I want to build a Qt interface to control a camera acquisition.
What I want:
Before going into the hardware communication, I am testing a GUI which control a "fake camera", a continuous loop which, if started, gives a random image every 100 ms. The image acquisition is running in a separate thread so that the user could interact with the GUI. The user can start and stop the acquisition via a button.
How I want to do it:
My first attempt was to simply istanziate a QThread
and call the run()
method which would then contain an infinite loop with single image acquisitions interleaved by a QThread.sleep(0.1)
. I noticed that after stopping and restarting the thread, the program was starting to lag and crashed after some time.
By reading some posts around and the main Qt webpage, I then learned that the preferable way to do what I want is to:
subclass a
QObject
to create a worker. Instantiate this worker
object and aQThread
. Move the worker to the new thread.
Moreover, following the idea in this post, I added a QTimer
object to iterate indefinitely the worker inside the thread, and I implement an active
flag that just makes the thread run without doing anything if it's set to False
.
This solution seemed to work at the beginning. I can start, stop and restart the acquisition as many times as I want.
Problems:
1) The CPU is always taking quite some resources (about 30% in my case, according to windows task menager) when the camera is not acquiring.
2) Sometimes, after acquisition is started, the memory start to be filled, like if every new image is allocated in new memory (while it is suppose to be overwritten I guess), till the program becomes irresponsive and then crashes.
The following image is what I see in task menager when this happens:
Red arrows correspond to the time the acquisition start.
Where am I doing wrong? Is it the right way to procede?
The code
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_()
python multithreading pyqt pyqt5
python multithreading pyqt pyqt5
asked Nov 13 '18 at 19:35
TommasoTommaso
225
225
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
QThread.sleep()
only accepts whole arguments, when passing a floating it will round it and in your case 0.1 that will be rounded to 0 so there is no pause, so the signal will be continuously issued but the painting takes a while so the data it will be stored in a queue for it increases the memory. On the other hand if a QTimer
is going to call a task continuously it is better to live in the thread of the object that handles the task so that it is enough that the QTimer
is the son of FakeCamera. Another improvement is the use of the decorator @QtCore.pyqtSlot()
since the connection is given in C++ making it more efficient. And finally I have improved the design since FakeCamera
should not interact directly with the GUI because if you want to use it with another GUI you will have to modify a lot of code, instead it is better to create slots.
from PyQt5 import QtCore, QtWidgets
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.toggled.connect(self.startcambutton.setCheckable)
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
fake_camera_thread = QtCore.QThread(self)
self.fake_camera_worker = FakeCamera()
self.fake_camera_worker.moveToThread(fake_camera_thread)
self.startcambutton.toggled.connect(self.fake_camera_worker.setState)
self.fake_camera_worker.image.connect(self.image_acquired)
fake_camera_thread.started.connect(self.fake_camera_worker.start)
fake_camera_thread.finished.connect(self.fake_camera_worker.deleteLater)
fake_camera_thread.finished.connect(fake_camera_thread.deleteLater)
fake_camera_thread.start()
@QtCore.pyqtSlot()
def acquire(self):
if self.startcambutton.isCheckable():
text = "Stop" if self.startcambutton.isChecked() else "Start"
self.startcambutton.setText(text)
self.contincheck.setEnabled(not self.startcambutton.isChecked())
@QtCore.pyqtSlot(object)
def image_acquired(self, image):
self.im.set_data(image)
self.canvas.draw()
class FakeCamera(QtCore.QObject):
image = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(FakeCamera, self).__init__(parent)
self.active = False
self.fake_camera_timer = QtCore.QTimer(self, interval=0)
self.fake_camera_timer.timeout.connect(self.acquire)
@QtCore.pyqtSlot()
def start(self):
self.fake_camera_timer.start()
@QtCore.pyqtSlot(bool)
def setState(self, state):
self.active = state
@QtCore.pyqtSlot()
def toggle(self):
self.active = not self.active
@QtCore.pyqtSlot()
def acquire(self):
""" this is the method running indefinitly in the associated thread """
if self.active:
self.new_acquisition()
QtCore.QThread.msleep(100)
def new_acquisition(self):
noise = np.random.normal(0, 1, (1000, 1000))
self.image.emit(noise)
if __name__ == '__main__':
import sys
app = QtCore.QCoreApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
mainGui = MyWindow()
mainGui.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())
Thanks for all the suggestions. Vary instructive, as always!
– Tommaso
Nov 14 '18 at 14:12
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53288308%2fpython-pyqt-memory-issue-with-qthread%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
QThread.sleep()
only accepts whole arguments, when passing a floating it will round it and in your case 0.1 that will be rounded to 0 so there is no pause, so the signal will be continuously issued but the painting takes a while so the data it will be stored in a queue for it increases the memory. On the other hand if a QTimer
is going to call a task continuously it is better to live in the thread of the object that handles the task so that it is enough that the QTimer
is the son of FakeCamera. Another improvement is the use of the decorator @QtCore.pyqtSlot()
since the connection is given in C++ making it more efficient. And finally I have improved the design since FakeCamera
should not interact directly with the GUI because if you want to use it with another GUI you will have to modify a lot of code, instead it is better to create slots.
from PyQt5 import QtCore, QtWidgets
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.toggled.connect(self.startcambutton.setCheckable)
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
fake_camera_thread = QtCore.QThread(self)
self.fake_camera_worker = FakeCamera()
self.fake_camera_worker.moveToThread(fake_camera_thread)
self.startcambutton.toggled.connect(self.fake_camera_worker.setState)
self.fake_camera_worker.image.connect(self.image_acquired)
fake_camera_thread.started.connect(self.fake_camera_worker.start)
fake_camera_thread.finished.connect(self.fake_camera_worker.deleteLater)
fake_camera_thread.finished.connect(fake_camera_thread.deleteLater)
fake_camera_thread.start()
@QtCore.pyqtSlot()
def acquire(self):
if self.startcambutton.isCheckable():
text = "Stop" if self.startcambutton.isChecked() else "Start"
self.startcambutton.setText(text)
self.contincheck.setEnabled(not self.startcambutton.isChecked())
@QtCore.pyqtSlot(object)
def image_acquired(self, image):
self.im.set_data(image)
self.canvas.draw()
class FakeCamera(QtCore.QObject):
image = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(FakeCamera, self).__init__(parent)
self.active = False
self.fake_camera_timer = QtCore.QTimer(self, interval=0)
self.fake_camera_timer.timeout.connect(self.acquire)
@QtCore.pyqtSlot()
def start(self):
self.fake_camera_timer.start()
@QtCore.pyqtSlot(bool)
def setState(self, state):
self.active = state
@QtCore.pyqtSlot()
def toggle(self):
self.active = not self.active
@QtCore.pyqtSlot()
def acquire(self):
""" this is the method running indefinitly in the associated thread """
if self.active:
self.new_acquisition()
QtCore.QThread.msleep(100)
def new_acquisition(self):
noise = np.random.normal(0, 1, (1000, 1000))
self.image.emit(noise)
if __name__ == '__main__':
import sys
app = QtCore.QCoreApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
mainGui = MyWindow()
mainGui.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())
Thanks for all the suggestions. Vary instructive, as always!
– Tommaso
Nov 14 '18 at 14:12
add a comment |
QThread.sleep()
only accepts whole arguments, when passing a floating it will round it and in your case 0.1 that will be rounded to 0 so there is no pause, so the signal will be continuously issued but the painting takes a while so the data it will be stored in a queue for it increases the memory. On the other hand if a QTimer
is going to call a task continuously it is better to live in the thread of the object that handles the task so that it is enough that the QTimer
is the son of FakeCamera. Another improvement is the use of the decorator @QtCore.pyqtSlot()
since the connection is given in C++ making it more efficient. And finally I have improved the design since FakeCamera
should not interact directly with the GUI because if you want to use it with another GUI you will have to modify a lot of code, instead it is better to create slots.
from PyQt5 import QtCore, QtWidgets
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.toggled.connect(self.startcambutton.setCheckable)
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
fake_camera_thread = QtCore.QThread(self)
self.fake_camera_worker = FakeCamera()
self.fake_camera_worker.moveToThread(fake_camera_thread)
self.startcambutton.toggled.connect(self.fake_camera_worker.setState)
self.fake_camera_worker.image.connect(self.image_acquired)
fake_camera_thread.started.connect(self.fake_camera_worker.start)
fake_camera_thread.finished.connect(self.fake_camera_worker.deleteLater)
fake_camera_thread.finished.connect(fake_camera_thread.deleteLater)
fake_camera_thread.start()
@QtCore.pyqtSlot()
def acquire(self):
if self.startcambutton.isCheckable():
text = "Stop" if self.startcambutton.isChecked() else "Start"
self.startcambutton.setText(text)
self.contincheck.setEnabled(not self.startcambutton.isChecked())
@QtCore.pyqtSlot(object)
def image_acquired(self, image):
self.im.set_data(image)
self.canvas.draw()
class FakeCamera(QtCore.QObject):
image = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(FakeCamera, self).__init__(parent)
self.active = False
self.fake_camera_timer = QtCore.QTimer(self, interval=0)
self.fake_camera_timer.timeout.connect(self.acquire)
@QtCore.pyqtSlot()
def start(self):
self.fake_camera_timer.start()
@QtCore.pyqtSlot(bool)
def setState(self, state):
self.active = state
@QtCore.pyqtSlot()
def toggle(self):
self.active = not self.active
@QtCore.pyqtSlot()
def acquire(self):
""" this is the method running indefinitly in the associated thread """
if self.active:
self.new_acquisition()
QtCore.QThread.msleep(100)
def new_acquisition(self):
noise = np.random.normal(0, 1, (1000, 1000))
self.image.emit(noise)
if __name__ == '__main__':
import sys
app = QtCore.QCoreApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
mainGui = MyWindow()
mainGui.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())
Thanks for all the suggestions. Vary instructive, as always!
– Tommaso
Nov 14 '18 at 14:12
add a comment |
QThread.sleep()
only accepts whole arguments, when passing a floating it will round it and in your case 0.1 that will be rounded to 0 so there is no pause, so the signal will be continuously issued but the painting takes a while so the data it will be stored in a queue for it increases the memory. On the other hand if a QTimer
is going to call a task continuously it is better to live in the thread of the object that handles the task so that it is enough that the QTimer
is the son of FakeCamera. Another improvement is the use of the decorator @QtCore.pyqtSlot()
since the connection is given in C++ making it more efficient. And finally I have improved the design since FakeCamera
should not interact directly with the GUI because if you want to use it with another GUI you will have to modify a lot of code, instead it is better to create slots.
from PyQt5 import QtCore, QtWidgets
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.toggled.connect(self.startcambutton.setCheckable)
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
fake_camera_thread = QtCore.QThread(self)
self.fake_camera_worker = FakeCamera()
self.fake_camera_worker.moveToThread(fake_camera_thread)
self.startcambutton.toggled.connect(self.fake_camera_worker.setState)
self.fake_camera_worker.image.connect(self.image_acquired)
fake_camera_thread.started.connect(self.fake_camera_worker.start)
fake_camera_thread.finished.connect(self.fake_camera_worker.deleteLater)
fake_camera_thread.finished.connect(fake_camera_thread.deleteLater)
fake_camera_thread.start()
@QtCore.pyqtSlot()
def acquire(self):
if self.startcambutton.isCheckable():
text = "Stop" if self.startcambutton.isChecked() else "Start"
self.startcambutton.setText(text)
self.contincheck.setEnabled(not self.startcambutton.isChecked())
@QtCore.pyqtSlot(object)
def image_acquired(self, image):
self.im.set_data(image)
self.canvas.draw()
class FakeCamera(QtCore.QObject):
image = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(FakeCamera, self).__init__(parent)
self.active = False
self.fake_camera_timer = QtCore.QTimer(self, interval=0)
self.fake_camera_timer.timeout.connect(self.acquire)
@QtCore.pyqtSlot()
def start(self):
self.fake_camera_timer.start()
@QtCore.pyqtSlot(bool)
def setState(self, state):
self.active = state
@QtCore.pyqtSlot()
def toggle(self):
self.active = not self.active
@QtCore.pyqtSlot()
def acquire(self):
""" this is the method running indefinitly in the associated thread """
if self.active:
self.new_acquisition()
QtCore.QThread.msleep(100)
def new_acquisition(self):
noise = np.random.normal(0, 1, (1000, 1000))
self.image.emit(noise)
if __name__ == '__main__':
import sys
app = QtCore.QCoreApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
mainGui = MyWindow()
mainGui.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())
QThread.sleep()
only accepts whole arguments, when passing a floating it will round it and in your case 0.1 that will be rounded to 0 so there is no pause, so the signal will be continuously issued but the painting takes a while so the data it will be stored in a queue for it increases the memory. On the other hand if a QTimer
is going to call a task continuously it is better to live in the thread of the object that handles the task so that it is enough that the QTimer
is the son of FakeCamera. Another improvement is the use of the decorator @QtCore.pyqtSlot()
since the connection is given in C++ making it more efficient. And finally I have improved the design since FakeCamera
should not interact directly with the GUI because if you want to use it with another GUI you will have to modify a lot of code, instead it is better to create slots.
from PyQt5 import QtCore, QtWidgets
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.toggled.connect(self.startcambutton.setCheckable)
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
fake_camera_thread = QtCore.QThread(self)
self.fake_camera_worker = FakeCamera()
self.fake_camera_worker.moveToThread(fake_camera_thread)
self.startcambutton.toggled.connect(self.fake_camera_worker.setState)
self.fake_camera_worker.image.connect(self.image_acquired)
fake_camera_thread.started.connect(self.fake_camera_worker.start)
fake_camera_thread.finished.connect(self.fake_camera_worker.deleteLater)
fake_camera_thread.finished.connect(fake_camera_thread.deleteLater)
fake_camera_thread.start()
@QtCore.pyqtSlot()
def acquire(self):
if self.startcambutton.isCheckable():
text = "Stop" if self.startcambutton.isChecked() else "Start"
self.startcambutton.setText(text)
self.contincheck.setEnabled(not self.startcambutton.isChecked())
@QtCore.pyqtSlot(object)
def image_acquired(self, image):
self.im.set_data(image)
self.canvas.draw()
class FakeCamera(QtCore.QObject):
image = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(FakeCamera, self).__init__(parent)
self.active = False
self.fake_camera_timer = QtCore.QTimer(self, interval=0)
self.fake_camera_timer.timeout.connect(self.acquire)
@QtCore.pyqtSlot()
def start(self):
self.fake_camera_timer.start()
@QtCore.pyqtSlot(bool)
def setState(self, state):
self.active = state
@QtCore.pyqtSlot()
def toggle(self):
self.active = not self.active
@QtCore.pyqtSlot()
def acquire(self):
""" this is the method running indefinitly in the associated thread """
if self.active:
self.new_acquisition()
QtCore.QThread.msleep(100)
def new_acquisition(self):
noise = np.random.normal(0, 1, (1000, 1000))
self.image.emit(noise)
if __name__ == '__main__':
import sys
app = QtCore.QCoreApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
mainGui = MyWindow()
mainGui.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())
answered Nov 14 '18 at 7:08
eyllanesceyllanesc
77.3k103156
77.3k103156
Thanks for all the suggestions. Vary instructive, as always!
– Tommaso
Nov 14 '18 at 14:12
add a comment |
Thanks for all the suggestions. Vary instructive, as always!
– Tommaso
Nov 14 '18 at 14:12
Thanks for all the suggestions. Vary instructive, as always!
– Tommaso
Nov 14 '18 at 14:12
Thanks for all the suggestions. Vary instructive, as always!
– Tommaso
Nov 14 '18 at 14:12
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53288308%2fpython-pyqt-memory-issue-with-qthread%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown