diff --git a/qcodes/config/qcodesrc.json b/qcodes/config/qcodesrc.json index ca4a05aa88d0..d16168699cfb 100644 --- a/qcodes/config/qcodesrc.json +++ b/qcodes/config/qcodesrc.json @@ -5,7 +5,8 @@ }, "gui" :{ "notebook": true, - "plotlib": "matplotlib" + "plotlib": "matplotlib", + "pyqtmaxplots": 100 }, "user": {} } diff --git a/qcodes/config/qcodesrc_schema.json b/qcodes/config/qcodesrc_schema.json index 44bb32778b87..8673699d6277 100644 --- a/qcodes/config/qcodesrc_schema.json +++ b/qcodes/config/qcodesrc_schema.json @@ -41,9 +41,14 @@ "type": ["string", "null"], "enum": ["QT", "matplotlib", null], "default": "matplotlib" + }, + "pyqtmaxplots": { + "description": "Maximum number of PyQtPlots to automatically keep in memory", + "type": "integer", + "default": 100 } }, - "required":[ "notebook", "plotlib" ] + "required":[ "notebook", "plotlib", "pyqtmaxplots" ] }, "user":{ "type" : "object", diff --git a/qcodes/plots/pyqtgraph.py b/qcodes/plots/pyqtgraph.py index bfce42b9d89e..44423c55adff 100644 --- a/qcodes/plots/pyqtgraph.py +++ b/qcodes/plots/pyqtgraph.py @@ -4,12 +4,13 @@ import numpy as np import pyqtgraph as pg import pyqtgraph.multiprocess as pgmp +from pyqtgraph.multiprocess.remoteproxy import ClosedError import warnings -from collections import namedtuple +from collections import namedtuple, deque from .base import BasePlot from .colors import color_cycle, colorscales - +import qcodes.config TransformState = namedtuple('TransformState', 'translate scale revisit') @@ -37,6 +38,15 @@ class QtPlot(BasePlot): """ proc = None rpg = None + # we store references to plots to keep the garbage collections from + # destroying the windows. To keep memory consumption within bounds we + # limit this to an arbitrary number of plots here using a deque + # The issue is that even when closing a window it's difficult to + # remove it from the list. This could potentially be done with a + # close event on win but this is difficult with remote proxy process + # as the list of plots lives in the main process and the plot locally + # in a remote process + plots = deque(maxlen=qcodes.config['gui']['pyqtmaxplots']) def __init__(self, *args, figsize=(1000, 600), interval=0.25, window_title='', theme=((60, 60, 60), 'w'), show_window=True, remote=True, **kwargs): @@ -55,7 +65,16 @@ def __init__(self, *args, figsize=(1000, 600), interval=0.25, else: # overrule the remote pyqtgraph class self.rpg = pg - self.win = self.rpg.GraphicsWindow(title=window_title) + try: + self.win = self.rpg.GraphicsWindow(title=window_title) + except ClosedError as err: + # the remote process may have crashed. In that case try restarting + # it + if remote: + self._init_qt() + self.win = self.rpg.GraphicsWindow(title=window_title) + else: + raise err self.win.setBackground(theme[1]) self.win.resize(*figsize) self.subplots = [self.add_subplot()] @@ -66,13 +85,16 @@ def __init__(self, *args, figsize=(1000, 600), interval=0.25, if not show_window: self.win.hide() - def _init_qt(self): + self.plots.append(self) + + @classmethod + def _init_qt(cls): # starting the process for the pyqtgraph plotting # You do not want a new process to be created every time you start a # run, so this only starts once and stores the process in the class pg.mkQApp() - self.__class__.proc = pgmp.QtProcess() # pyqtgraph multiprocessing - self.__class__.rpg = self.proc._import('pyqtgraph') + cls.proc = pgmp.QtProcess() # pyqtgraph multiprocessing + cls.rpg = cls.proc._import('pyqtgraph') def clear(self): """