Python - PyQt: Memory issue with QThread












1















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 a QThread. 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:
enter image description here
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_()









share|improve this question



























    1















    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 a QThread. 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:
    enter image description here
    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_()









    share|improve this question

























      1












      1








      1








      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 a QThread. 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:
      enter image description here
      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_()









      share|improve this question














      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 a QThread. 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:
      enter image description here
      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 13 '18 at 19:35









      TommasoTommaso

      225




      225
























          1 Answer
          1






          active

          oldest

          votes


















          2














          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_())





          share|improve this answer
























          • Thanks for all the suggestions. Vary instructive, as always!

            – Tommaso
            Nov 14 '18 at 14:12











          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
          });


          }
          });














          draft saved

          draft discarded


















          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









          2














          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_())





          share|improve this answer
























          • Thanks for all the suggestions. Vary instructive, as always!

            – Tommaso
            Nov 14 '18 at 14:12
















          2














          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_())





          share|improve this answer
























          • Thanks for all the suggestions. Vary instructive, as always!

            – Tommaso
            Nov 14 '18 at 14:12














          2












          2








          2







          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_())





          share|improve this answer













          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_())






          share|improve this answer












          share|improve this answer



          share|improve this answer










          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



















          • 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


















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Florida Star v. B. J. F.

          Danny Elfman

          Retrieve a Users Dashboard in Tumblr with R and TumblR. Oauth Issues