myAxes.py 7.13 KB
Newer Older
Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
1
2
3
4
5
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# ================= FILE HEADER ========================================
#
Philipp Gast's avatar
Philipp Gast committed
6
#   myplotlib v3.0.1,
Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#
#   @file      myAxes.py
#   @author    Yori 'AGy' Fournier
#   @licence   CC-BY-SA
#
#   MyAxes class: Overlay of matplotlib Axes class
#   It is done such that the user can concentrate on
#   the important aspect of the figure and not the
#   technical part. It consists of a constructor,
#   that requires a Figure, the ratio of the xaxis over
#   the yaxis, and the frame in which the figure
#   should be plotted.
#
#   @Class MyAxes
#
#   @Constructor(self, fig, ratio, frame, +user defined kw):
#
#   @section Functions
#
#            - plotting: this is the overwritten function
#                        from Axes. It is called by it's
#                        parent-figure's function .plot()
#
#            - formatRawData: it computes out of the rawData
#                        given as argument some quantities that
#                        are returned as a dictionary and
#                        become accessable for plotting.
#
#   @section History
#
#   v 0.0.0 - MyAxes class for the myplotlib module, consists
#             of a constructor, a pltting function and
#             formatRawData.
#
41
42
#   v 2.2.3 - Add testRawData function
#
Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
43
44
45
46
# ======================================================================
#
#
# IMPORT ---------------------------------------------------------------
47
48
49
from . import SEVR, DBUG
from . import Axes
from . import rcParams
Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
50

51
_D_FRAME = [0.1, 0.1, 0.8, 0.8]
Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
52

53

54
# COMPUTE SMART FRAME -----------------------------------------------
55
56
def compute_fit_rect(fig, ratio, frame_rect):

57
    # get the frame allowed
58
    frame_pos_x, frame_pos_y, frame_width, frame_height = frame_rect
59
    # get the size of the figure
60
61
62
63
    fig_width = fig.get_figwidth()
    fig_height = fig.get_figheight()
    fig_dpi = fig.get_dpi()

64
    # get the font size in inches
65
66
    fontsize = rcParams['font.size'] / fig_dpi

67
    # the largest width and height allowed in inches
68
69
70
    max_width = fig_width * frame_width - 8. * (fig_dpi / 75.) * fontsize
    max_height = fig_height * frame_height - 4. * (fig_dpi / 75.) * fontsize

71
72
    # the hypothetical width and height in inches
    # for a given aspect ratio
73
74
75
    hypo_width = max_height / ratio
    hypo_height = max_width * ratio

76
77
78
    # if the hypothetical width is larger that the
    # maximum allowed width (in inches) then chose
    # the hypothetical Height
79
80
81
82
83
84
85
    if (hypo_width > max_width):
        height = hypo_height / fig_height  # height in percent
        width = max_width / fig_width     # width in percent
        pos_x = frame_pos_x + 4. * (fig_dpi / 75.) \
            * fontsize / fig_width      # posX
        pos_y = frame_pos_y + 3. * (fig_dpi / 75.) \
            * fontsize / fig_height     # posY
86
    else:
87
88
89
90
91
92
93
        height = max_height / fig_height
        width = hypo_width / fig_width
        pos_x = frame_pos_x + (frame_width - width) / 2.
        pos_y = frame_pos_y + 3. * (fig_dpi / 75.) * fontsize / fig_height

    rect = [pos_x, pos_y, width, height]

94
95
96
    return(rect)


Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
97
98
# Class MyAxes Overwriting Matplotlib.figure.Axes
class MyAxes(Axes):
99

Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
100
    # CONSTRUCTOR --------------------------------------------------------
101
    def __init__(self, fig, frame=_D_FRAME, *args, **kwargs):
102

103
        self.fig = fig
104
        self.declareKeywords()
105

106
        rect = self.computeRect(frame, *args, **kwargs)
107

108
        # clean kwargs
109
        kwargs.pop('ratio', None)
Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
110
111
        kwargs.pop('forceRatioOnWidth', None)
        kwargs.pop('forceRatioOnHeight', None)
112
        kwargs.pop('fitInFrame', None)
113

114
115
        # parent constructor
        Axes.__init__(self, fig, rect, **kwargs)
116

117
        # set a default name. Should be individualized by instance
118
        self.name = 'default'
119

120
    # COMPUTE RECT -------------------------------------------------------
121
122
    def compute_rect(self, frame_rect, *args, **kwargs):

123
        # Get the optional keyword
124
        ratio = kwargs.pop('ratio', None)
125
126
127
128
        force_ratio_on_width = kwargs.pop('force_ratio_on_width', None)
        force_ratio_on_height = kwargs.pop('force_ratio_on_height', None)
        fit_in_frame = kwargs.pop('fit_in_frame', None)

Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
129
        # get the frame allowed
130
131
        frame_pos_x, frame_pos_y, frame_width, frame_height = frame_rect

132
        # get the size of the figure
133
134
135
        fig_width = self.fig.get_figwidth()
        fig_height = self.fig.get_figheight()

136
        if ratio is not None:
137
138
139
140
141

            if(force_ratio_on_width):
                frame_width = ratio * fig_width / (frame_height * fig_height)
            elif(force_ratio_on_height):
                frame_height = ratio * (frame_width * fig_width) / fig_height
142
            else:
143
144
145
146
147
148
                frame_height = ratio * (frame_width * fig_width) / fig_height

        rect = [frame_pos_x, frame_pos_y, frame_width, frame_height]

        if (fit_in_frame):

149
            if ratio is None:
150
151
152
153
                ratio = fig_height / fig_width

            rect = compute_fit_rect(self.fig, ratio, rect)

154
        return(rect)
155

156
    # DECLARE KEYWORDS -------------------------------------------------
157
    def declare_keywords(self):
Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
158
        self.keywords = {}
159

160
    # PLOTTING ---------------------------------------------------------
Yori 'AGy' Fournier's avatar
Yori 'AGy' Fournier committed
161
    def plotting(self):
162
163
        raise(NotImplementedError, 'You should have overwrite the'
              ' plotting function.')
164
        return(False)
165
166

    # UNPACKING --------------------------------------------------------
167
168
169
170
    def unpack_formatted_data(self, packed_data):
        '''Get a string return it evaluation.
        can be overwritten by user.'''
        return(eval(packed_data))
171
172

    # PACKING ----------------------------------------------------------
173
174
175
176
    def pack_formatted_data(self, formatted_data):
        '''Get an object return its representation.
        can be overwritten by user.'''
        return(str(formatted_data))
177
178

    # FORMATTING -------------------------------------------------------
179
180
181
    def format_raw_data(self, rawdata):
        raise(NotImplementedError, 'You should have overwrite the '
              'formatRawData function.')
182
        return(False)
183

184
    # TESTING THE RAWDATA ----------------------------------------------
185
    def test_raw_data(self, rawdata):
186
        return(True)
187

188
    # UPDATE -----------------------------------------------------------
189
    def update(self, *args, **kwargs):
190

191
192
        # Because matplotlib.axes.update expect kwargs and not **kwargs
        # ... (stupid!! no, this is just WRONG!!)
193
        if args:  # catch matplotlib kwargs
194
            kwargs.update(args[0])
195
196
            # kw_for_axes = {key: value for (key, value) in args[0].items() if
            # key not in self.keywords}  # Not compatible with python2.6
197

198
199
200
201
        kw_for_axes = {}
        for (key, value) in kwargs.items():
            if key not in self.keywords:
                kw_for_axes.update({key: value})
202

203
        Axes.update(self, kw_for_axes)  # update matplotlib.Axes
204

205
        # myplotlib update
206
207
        # self.keywords.update({key: value for (key, value) in kwargs.items()
        # if key in self.keywords})  # Not compatible with python2.6
208

209
210
211
        for (key, value) in kwargs.items():
            if key in self.keywords:
                self.keywords.update({key: value})