Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/cc_modules/cc_plot.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

12 CamCOPS is free software: you can redistribute it and/or modify 

13 it under the terms of the GNU General Public License as published by 

14 the Free Software Foundation, either version 3 of the License, or 

15 (at your option) any later version. 

16 

17 CamCOPS is distributed in the hope that it will be useful, 

18 but WITHOUT ANY WARRANTY; without even the implied warranty of 

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

22 You should have received a copy of the GNU General Public License 

23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

24 

25=============================================================================== 

26 

27**Plotting functions.** 

28 

29PROPER WAY TO USE MATPLOTLIB: 

30 

31- https://jbarillari.blogspot.co.uk/2009/09/threadsafety-and-matplotlibpylab.html?m=1 

32- https://sjohannes.wordpress.com/2010/06/11/using-matplotlib-in-a-web-application/amp/ 

33- https://matplotlib.org/faq/howto_faq.html#howto-webapp 

34- https://matplotlib.org/examples/api/agg_oo.html#api-agg-oo 

35 

36In summary: matplotlib is easy to use in a way that has global state, but that 

37will break in a threading application. Using the Figure() API is safe. Thus: 

38 

39.. code-block:: python 

40 

41 from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas 

42 from matplotlib.figure import Figure 

43 

44 fig = Figure() 

45 canvas = FigureCanvas(fig) 

46 ax = fig.add_subplot(111) 

47 ax.plot([1, 2, 3]) 

48 ax.set_title('hi mom') 

49 ax.grid(True) 

50 ax.set_xlabel('time') 

51 ax.set_ylabel('volts') 

52 canvas.print_figure('test') 

53 

54""" # noqa 

55 

56# ============================================================================= 

57# Basic imports 

58# ============================================================================= 

59 

60import atexit 

61import logging 

62import os 

63import shutil 

64import tempfile 

65 

66from cardinal_pythonlib.logs import BraceStyleAdapter 

67 

68log = BraceStyleAdapter(logging.getLogger(__name__)) 

69 

70 

71# ============================================================================= 

72# Constants 

73# ============================================================================= 

74 

75ENVVAR_HOME = "HOME" 

76ENVVAR_MPLCONFIGDIR = "MPLCONFIGDIR" 

77 

78# ============================================================================= 

79# Import matplotlib 

80# ============================================================================= 

81 

82# We need to use os.environ, since per-request stuff won't be initialized yet. 

83# That goes for anything that affects imports (to avoid the complexity of 

84# delayed imports). 

85if ENVVAR_MPLCONFIGDIR in os.environ: 

86 # 1+2. Use a writable static directory (speeds pyplot loads hugely). 

87 _mpl_config_dir = os.environ[ENVVAR_MPLCONFIGDIR] 

88else: 

89 # 1. Make a temporary directory (must be a directory per process, I'm sure) 

90 _mpl_config_dir = tempfile.mkdtemp() 

91 # 2. Ensure temporary directory is removed when this process exits. 

92 atexit.register(lambda: shutil.rmtree(_mpl_config_dir, ignore_errors=True)) 

93 

94# 3. Tell matplotlib about this directory prior to importing it 

95# http://matplotlib.org/faq/environment_variables_faq.html 

96os.environ[ENVVAR_MPLCONFIGDIR] = _mpl_config_dir 

97 

98# 4. Another nasty matplotlib hack 

99# matplotlib.font_manager reads os.environ.get('HOME') directly, and 

100# searches ~/.fonts for fonts. That's fine unless a user is calling with 

101# sudo -u USER, leaving $HOME as it was but removing the permissions - then 

102# matplotlib crashes out with e.g. 

103# PermissionError: [Errno 13] Permission denied: '/home/rudolf/.fonts/SABOI___.TTF' # noqa 

104# Note that an empty string won't help either, since the check is 

105# "is not None". 

106# You can't assign None to an os.environ member; see 

107# http://stackoverflow.com/questions/3575165; do this: 

108if ENVVAR_HOME in os.environ: 

109 _old_home = os.environ[ENVVAR_HOME] 

110 del os.environ[ENVVAR_HOME] 

111else: 

112 _old_home = None 

113 

114# 5. Import matplotlib 

115log.debug("Importing matplotlib (can be slow) (MPLCONFIGDIR={})...", 

116 _mpl_config_dir) 

117# noinspection PyUnresolvedReferences 

118import matplotlib # noqa: E402,F401 

119 

120# 6. Restore $HOME 

121if _old_home is not None: 

122 os.environ[ENVVAR_HOME] = _old_home 

123 

124# 7. Set the backend 

125# REPLACED BY OO METHOD # matplotlib.use("Agg") # also the default backend 

126# ... http://matplotlib.org/faq/usage_faq.html#what-is-a-backend 

127# ... http://matplotlib.org/faq/howto_faq.html 

128# matplotlib.use("cairo") # cairo backend corrupts some SVG figures 

129 

130# Load this once so we can tell the user we're importing it and it's slow 

131# REPLACED BY OO METHOD # import matplotlib.pyplot # noqa 

132 

133log.debug("... finished importing matplotlib") 

134 

135# REPLACED BY OO METHOD # # THEN DO e.g. # import matplotlib.pyplot as plt