source: sasview/src/sas/sasgui/plottools/canvas.py @ 34f23c8

ticket-1249
Last change on this file since 34f23c8 was 34f23c8, checked in by Paul Kienzle <pkienzle@…>, 5 years ago

py3/wx4 compatibility changes for gui. Refs #1249

  • Property mode set to 100644
File size: 8.6 KB
RevLine 
[a9d5684]1"""
2This module implements a faster canvas for plotting.
3it ovewrites some matplolib methods to allow printing on sys.platform=='win32'
4"""
5import wx
6import sys
[2df0b74]7import logging
[a9d5684]8from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
9from matplotlib.backend_bases import MouseEvent, RendererBase
10from matplotlib.backends.backend_wx import GraphicsContextWx, PrintoutWx
11from matplotlib.backends.backend_wx import RendererWx
12
[463e7ffc]13logger = logging.getLogger(__name__)
[c155a16]14
[a9d5684]15
16def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
17    """
18    Draw the image instance into the current axes;
19
20    :param x: is the distance in pixels from the left hand side of the canvas.
21    :param y: the distance from the origin.  That is, if origin is
22        upper, y is the distance from top.  If origin is lower, y
23        is the distance from bottom
24    :param im: the class`matplotlib._image.Image` instance
25    :param bbox: a class `matplotlib.transforms.Bbox` instance for clipping, or
26        None
27
28    """
29    pass
30
31
32def select(self):
33    """
34    """
35    pass
36
37
38def unselect(self):
39    """
40    """
41    pass
42
43
44def OnPrintPage(self, page):
45    """
46    override printPage of matplotlib
47    """
48    self.canvas.draw()
49    dc = self.GetDC()
50    try:
[2df0b74]51        (ppw, pph) = self.GetPPIPrinter()  # printer's pixels per in
[a9d5684]52    except:
53        ppw = 1
54        pph = 1
[34f23c8]55    pgw, _ = self.GetPageSizePixels()  # page size in pixels
56    dcw, _ = dc.GetSize()
57    grw, _ = self.canvas.GetSize()
[a9d5684]58
59    # save current figure dpi resolution and bg color,
60    # so that we can temporarily set them to the dpi of
61    # the printer, and the bg color to white
62    bgcolor = self.canvas.figure.get_facecolor()
63    fig_dpi = self.canvas.figure.dpi
64
65    # draw the bitmap, scaled appropriately
66    vscale = float(ppw) / fig_dpi
67
68    # set figure resolution,bg color for printer
69    self.canvas.figure.dpi = ppw
70    self.canvas.figure.set_facecolor('#FFFFFF')
71
72    renderer = RendererWx(self.canvas.bitmap, self.canvas.figure.dpi)
73    self.canvas.figure.draw(renderer)
74    self.canvas.bitmap.SetWidth(int(self.canvas.bitmap.GetWidth() * vscale))
75    self.canvas.bitmap.SetHeight(int(self.canvas.bitmap.GetHeight() * vscale))
76    self.canvas.draw()
77
78    # page may need additional scaling on preview
79    page_scale = 1.0
80    if self.IsPreview():
[2df0b74]81        page_scale = float(dcw) / pgw
[a9d5684]82
83    # get margin in pixels = (margin in in) * (pixels/in)
84    top_margin = int(self.margin * pph * page_scale)
85    left_margin = int(self.margin * ppw * page_scale)
86
87    # set scale so that width of output is self.width inches
88    # (assuming grw is size of graph in inches....)
89    user_scale = (self.width * fig_dpi * page_scale) / float(grw)
90    dc.SetDeviceOrigin(left_margin, top_margin)
91    dc.SetUserScale(user_scale, user_scale)
92
93    # this cute little number avoid API inconsistencies in wx
94    try:
95        dc.DrawBitmap(self.canvas.bitmap, 0, 0)
96    except:
97        try:
98            dc.DrawBitmap(self.canvas.bitmap, (0, 0))
[5251ec6]99        except Exception as exc:
100            logger.error(exc)
[a9d5684]101
102    # restore original figure  resolution
103    self.canvas.figure.set_facecolor(bgcolor)
[2df0b74]104    # # used to be self.canvas.figure.dpi.set( fig_dpi)
[a9d5684]105    self.canvas.figure.dpi = fig_dpi
106    self.canvas.draw()
107    return True
108
109GraphicsContextWx.select = select
110GraphicsContextWx.unselect = unselect
111PrintoutWx.OnPrintPage = OnPrintPage
112RendererBase.draw_image = draw_image
113
114
115class FigureCanvas(FigureCanvasWxAgg):
116    """
117    Add features to the wx agg canvas for better support of AUI and
118    faster plotting.
119    """
120
121    def __init__(self, *args, **kw):
122        super(FigureCanvas, self).__init__(*args, **kw)
123        self._isRendered = False
[2df0b74]124
[a9d5684]125        # Create an timer for handling draw_idle requests
126        # If there are events pending when the timer is
127        # complete, reset the timer and continue.  The
128        # alternative approach, binding to wx.EVT_IDLE,
129        # doesn't behave as nicely.
130        self.idletimer = wx.CallLater(1, self._onDrawIdle)
131        # panel information
132        self.panel = None
133        self.resizing = False
134        self.xaxis = None
135        self.yaxis = None
136        self.ndraw = 0
137        # Support for mouse wheel
138        self.Bind(wx.EVT_MOUSEWHEEL, self._onMouseWheel)
[2df0b74]139
[a9d5684]140    def set_panel(self, panel):
141        """
142        Set axes
143        """
144        # set panel
145        self.panel = panel
146        # set axes
147        self.xaxis = panel.subplot.xaxis
148        self.yaxis = panel.subplot.yaxis
[2df0b74]149
[a9d5684]150    def draw_idle(self, *args, **kwargs):
151        """
152        Render after a delay if no other render requests have been made.
153        """
[34f23c8]154        if self.panel is not None:
155            # TODO: grid/panel manip doesn't belong here
156            self.panel.subplot.grid(self.panel.grid_on)
157            if self.panel.legend is not None and self.panel.legend_pos_loc:
158                self.panel.legend._loc = self.panel.legend_pos_loc
[a9d5684]159        self.idletimer.Restart(5, *args, **kwargs)  # Delay by 5 ms
160
161    def _onDrawIdle(self, *args, **kwargs):
162        """
163        """
164        if False and wx.GetApp().Pending():
165            self.idletimer.Restart(5, *args, **kwargs)
166        else:
167            # Draw plot, changes resizing too
168            self.draw(*args, **kwargs)
169            self.resizing = False
[2df0b74]170
[a9d5684]171    def _get_axes_switch(self):
172        """
173        """
174        # Check resize whether or not True
175        if self.panel.dimension == 3:
176            return
177
178        # This is for fast response when plot is being resized
179        if not self.resizing:
180            self.xaxis.set_visible(True)
181            self.yaxis.set_visible(True)
182            self.panel.schedule_full_draw('del')
183        else:
184            self.xaxis.set_visible(False)
185            self.yaxis.set_visible(False)
186            self.panel.schedule_full_draw('append')
187        # set the resizing back to default= False
188        self.set_resizing(False)
[2df0b74]189
[a9d5684]190    def set_resizing(self, resizing=False):
191        """
192        Setting the resizing
193        """
194        self.resizing = resizing
195        self.panel.set_resizing(False)
[2df0b74]196
[a9d5684]197    def draw(self, drawDC=None):
198        """
199        Render the figure using agg.
200        """
201        # Only draw if window is shown, otherwise graph will bleed through
202        # on the notebook style AUI widgets.
203        #    raise
204        fig = FigureCanvasWxAgg
205        if self.IsShownOnScreen() and self.ndraw != 1:
206            self._isRendered = True
207            self._get_axes_switch()
[2df0b74]208            # import time
209            # st = time.time()
[a9d5684]210            try:
211                fig.draw(self)
[5251ec6]212            except ValueError as exc:
213                logger.error(exc)
[a9d5684]214        else:
215            self._isRendered = False
216        if self.ndraw <= 1:
217            self.ndraw += 1
[2df0b74]218
[a9d5684]219    def _onMouseWheel(self, evt):
220        """Translate mouse wheel events into matplotlib events"""
221        # Determine mouse location
[2df0b74]222        _, h = self.figure.canvas.get_width_height()
[a9d5684]223        x = evt.GetX()
224        y = h - evt.GetY()
225
226        # Convert delta/rotation/rate into a floating point step size
227        delta = evt.GetWheelDelta()
228        rotation = evt.GetWheelRotation()
229        rate = evt.GetLinesPerAction()
[2df0b74]230        # print "delta,rotation,rate",delta,rotation,rate
[a9d5684]231        step = rate * float(rotation) / delta
232
233        # Convert to mpl event
234        evt.Skip()
235        self.scroll_event(x, y, step, guiEvent=evt)
236
237    def scroll_event(self, x, y, step=1, guiEvent=None):
238        """
239        Backend derived classes should call this function on any
240        scroll wheel event.  x,y are the canvas coords: 0,0 is lower,
241        left.  button and key are as defined in MouseEvent
242        """
243        button = 'up' if step >= 0 else 'down'
244        self._button = button
245        s = 'scroll_event'
246        event = MouseEvent(s, self, x, y, button, self._key, guiEvent=guiEvent)
247        setattr(event, 'step', step)
248        self.callbacks.process(s, event)
249        if step != 0:
250            self.panel.is_zoomed = True
[2df0b74]251
[a9d5684]252    def _onRightButtonDown(self, evt):
253        """
254        Overload the right button down call back to avoid a problem
255        with the context menu over matplotlib plots on linux.
[2df0b74]256
[a9d5684]257        :TODO: Investigate what the root cause of the problem is.
[2df0b74]258
[a9d5684]259        """
260        if sys.platform == 'linux2' or self.panel.dimension == 3:
261            evt.Skip()
262        else:
263            FigureCanvasWxAgg._onRightButtonDown(self, evt)
264            # This solves the focusing on rightclick.
265            # Todo: better design
266            self.panel.parent.set_plot_unfocus()
267            self.panel.on_set_focus(None)
268        return
[b9f6d83]269
270    # CRUFT: wx 3.0.0.0 on OS X doesn't release the mouse on leaving window
271    def _onLeave(self, evt):
272        if self.HasCapture(): self.ReleaseMouse()
[2df0b74]273        super(FigureCanvas, self)._onLeave(evt)
Note: See TracBrowser for help on using the repository browser.