source: sasview/src/sas/sasgui/plottools/canvas.py @ 1b061a31

Last change on this file since 1b061a31 was d7bb526, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 9 years ago

Refactored plottools into sasgui

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