1from __future__ import (absolute_import, division, print_function,
2                        unicode_literals)
3
4import os
5
6from matplotlib._pylab_helpers import Gcf
7from matplotlib.backend_bases import (
8    _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2,
9    TimerBase)
10
11from matplotlib.figure import Figure
12from matplotlib import rcParams
13
14from matplotlib.widgets import SubplotTool
15
16import matplotlib
17from matplotlib.backends import _macosx
18
19from .backend_agg import FigureCanvasAgg
20
21
22########################################################################
23#
24# The following functions and classes are for pylab and implement
25# window/figure managers, etc...
26#
27########################################################################
28
29
30class TimerMac(_macosx.Timer, TimerBase):
31    '''
32    Subclass of :class:`backend_bases.TimerBase` that uses CoreFoundation
33    run loops for timer events.
34
35    Attributes
36    ----------
37    interval : int
38        The time between timer events in milliseconds. Default is 1000 ms.
39    single_shot : bool
40        Boolean flag indicating whether this timer should operate as single
41        shot (run once and then stop). Defaults to False.
42    callbacks : list
43        Stores list of (func, args) tuples that will be called upon timer
44        events. This list can be manipulated directly, or the functions
45        `add_callback` and `remove_callback` can be used.
46
47    '''
48    # completely implemented at the C-level (in _macosx.Timer)
49
50
51class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasAgg):
52    """
53    The canvas the figure renders into.  Calls the draw and print fig
54    methods, creates the renderers, etc...
55
56    Events such as button presses, mouse movements, and key presses
57    are handled in the C code and the base class methods
58    button_press_event, button_release_event, motion_notify_event,
59    key_press_event, and key_release_event are called from there.
60
61    Attributes
62    ----------
63    figure : `matplotlib.figure.Figure`
64        A high-level Figure instance
65
66    """
67
68    def __init__(self, figure):
69        FigureCanvasBase.__init__(self, figure)
70        width, height = self.get_width_height()
71        _macosx.FigureCanvas.__init__(self, width, height)
72        self._device_scale = 1.0
73
74    def _set_device_scale(self, value):
75        if self._device_scale != value:
76            self.figure.dpi = self.figure.dpi / self._device_scale * value
77            self._device_scale = value
78
79    def _draw(self):
80        renderer = self.get_renderer(cleared=self.figure.stale)
81
82        if self.figure.stale:
83            self.figure.draw(renderer)
84
85        return renderer
86
87    def draw(self):
88        self.invalidate()
89        self.flush_events()
90
91    def draw_idle(self, *args, **kwargs):
92        self.invalidate()
93
94    def blit(self, bbox):
95        self.invalidate()
96
97    def resize(self, width, height):
98        dpi = self.figure.dpi
99        width /= dpi
100        height /= dpi
101        self.figure.set_size_inches(width * self._device_scale,
102                                    height * self._device_scale,
103                                    forward=False)
104        FigureCanvasBase.resize_event(self)
105        self.draw_idle()
106
107    def new_timer(self, *args, **kwargs):
108        """
109        Creates a new backend-specific subclass of `backend_bases.Timer`.
110        This is useful for getting periodic events through the backend's native
111        event loop. Implemented only for backends with GUIs.
112
113        Other Parameters
114        ----------------
115        interval : scalar
116            Timer interval in milliseconds
117        callbacks : list
118            Sequence of (func, args, kwargs) where ``func(*args, **kwargs)``
119            will be executed by the timer every *interval*.
120        """
121        return TimerMac(*args, **kwargs)
122
123
124class FigureManagerMac(_macosx.FigureManager, FigureManagerBase):
125    """
126    Wrap everything up into a window for the pylab interface
127    """
128    def __init__(self, canvas, num):
129        FigureManagerBase.__init__(self, canvas, num)
130        title = "Figure %d" % num
131        _macosx.FigureManager.__init__(self, canvas, title)
132        if rcParams['toolbar']=='toolbar2':
133            self.toolbar = NavigationToolbar2Mac(canvas)
134        else:
135            self.toolbar = None
136        if self.toolbar is not None:
137            self.toolbar.update()
138
139        def notify_axes_change(fig):
140            'this will be called whenever the current axes is changed'
141            if self.toolbar != None: self.toolbar.update()
142        self.canvas.figure.add_axobserver(notify_axes_change)
143
144        if matplotlib.is_interactive():
145            self.show()
146            self.canvas.draw_idle()
147
148    def close(self):
149        Gcf.destroy(self.num)
150
151
152class NavigationToolbar2Mac(_macosx.NavigationToolbar2, NavigationToolbar2):
153
154    def __init__(self, canvas):
155        NavigationToolbar2.__init__(self, canvas)
156
157    def _init_toolbar(self):
158        basedir = os.path.join(rcParams['datapath'], "images")
159        _macosx.NavigationToolbar2.__init__(self, basedir)
160
161    def draw_rubberband(self, event, x0, y0, x1, y1):
162        self.canvas.set_rubberband(int(x0), int(y0), int(x1), int(y1))
163
164    def release(self, event):
165        self.canvas.remove_rubberband()
166
167    def set_cursor(self, cursor):
168        _macosx.set_cursor(cursor)
169
170    def save_figure(self, *args):
171        filename = _macosx.choose_save_file('Save the figure',
172                                            self.canvas.get_default_filename())
173        if filename is None: # Cancel
174            return
175        self.canvas.figure.savefig(filename)
176
177    def prepare_configure_subplots(self):
178        toolfig = Figure(figsize=(6,3))
179        canvas = FigureCanvasMac(toolfig)
180        toolfig.subplots_adjust(top=0.9)
181        tool = SubplotTool(self.canvas.figure, toolfig)
182        return canvas
183
184    def set_message(self, message):
185        _macosx.NavigationToolbar2.set_message(self, message.encode('utf-8'))
186
187
188########################################################################
189#
190# Now just provide the standard names that backend.__init__ is expecting
191#
192########################################################################
193
194@_Backend.export
195class _BackendMac(_Backend):
196    FigureCanvas = FigureCanvasMac
197    FigureManager = FigureManagerMac
198
199    @staticmethod
200    def trigger_manager_draw(manager):
201        # For performance reasons, we don't want to redraw the figure after
202        # each draw command. Instead, we mark the figure as invalid, so that it
203        # will be redrawn as soon as the event loop resumes via PyOS_InputHook.
204        # This function should be called after each draw event, even if
205        # matplotlib is not running interactively.
206        manager.canvas.invalidate()
207
208    @staticmethod
209    def mainloop():
210        _macosx.show()
211