source: sasview/src/sans/plottools/canvas.py @ a9d5684

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since a9d5684 was a9d5684, checked in by Mathieu Doucet <doucetm@…>, 11 years ago
  • Property mode set to 100644
File size: 8.6 KB
Line 
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
7from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
8from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap
9from matplotlib.backends.backend_agg import FigureCanvasAgg
10from matplotlib.backend_bases import MouseEvent, RendererBase
11from matplotlib.backends.backend_wx import GraphicsContextWx, PrintoutWx
12from matplotlib.backends.backend_wx import RendererWx
13
14
15def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
16    """
17    Draw the image instance into the current axes;
18
19    :param x: is the distance in pixels from the left hand side of the canvas.
20    :param y: the distance from the origin.  That is, if origin is
21        upper, y is the distance from top.  If origin is lower, y
22        is the distance from bottom
23    :param im: the class`matplotlib._image.Image` instance
24    :param bbox: a class `matplotlib.transforms.Bbox` instance for clipping, or
25        None
26
27    """
28    pass
29
30
31def select(self):
32    """
33    """
34    pass
35
36
37def unselect(self):
38    """
39    """
40    pass
41
42
43def OnPrintPage(self, page):
44    """
45    override printPage of matplotlib
46    """
47    self.canvas.draw()
48    dc = self.GetDC()
49    try:
50        (ppw, pph) = self.GetPPIPrinter()      # printer's pixels per in
51    except:
52        ppw = 1
53        pph = 1
54    (pgw, pgh) = self.GetPageSizePixels()  # page size in pixels
55    (dcw, dch) = dc.GetSize()
56    (grw, grh) = self.canvas.GetSizeTuple()
57
58    # save current figure dpi resolution and bg color,
59    # so that we can temporarily set them to the dpi of
60    # the printer, and the bg color to white
61    bgcolor = self.canvas.figure.get_facecolor()
62    fig_dpi = self.canvas.figure.dpi
63
64    # draw the bitmap, scaled appropriately
65    vscale = float(ppw) / fig_dpi
66
67    # set figure resolution,bg color for printer
68    self.canvas.figure.dpi = ppw
69    self.canvas.figure.set_facecolor('#FFFFFF')
70
71    renderer = RendererWx(self.canvas.bitmap, self.canvas.figure.dpi)
72    self.canvas.figure.draw(renderer)
73    self.canvas.bitmap.SetWidth(int(self.canvas.bitmap.GetWidth() * vscale))
74    self.canvas.bitmap.SetHeight(int(self.canvas.bitmap.GetHeight() * vscale))
75    self.canvas.draw()
76
77    # page may need additional scaling on preview
78    page_scale = 1.0
79    if self.IsPreview():
80        page_scale = float(dcw)/pgw
81
82    # get margin in pixels = (margin in in) * (pixels/in)
83    top_margin = int(self.margin * pph * page_scale)
84    left_margin = int(self.margin * ppw * page_scale)
85
86    # set scale so that width of output is self.width inches
87    # (assuming grw is size of graph in inches....)
88    user_scale = (self.width * fig_dpi * page_scale) / float(grw)
89    dc.SetDeviceOrigin(left_margin, top_margin)
90    dc.SetUserScale(user_scale, user_scale)
91
92    # this cute little number avoid API inconsistencies in wx
93    try:
94        dc.DrawBitmap(self.canvas.bitmap, 0, 0)
95    except:
96        try:
97            dc.DrawBitmap(self.canvas.bitmap, (0, 0))
98        except:
99            pass
100
101    # restore original figure  resolution
102    self.canvas.figure.set_facecolor(bgcolor)
103    ## used to be self.canvas.figure.dpi.set( fig_dpi)
104    self.canvas.figure.dpi = fig_dpi
105    self.canvas.draw()
106    return True
107
108GraphicsContextWx.select = select
109GraphicsContextWx.unselect = unselect
110PrintoutWx.OnPrintPage = OnPrintPage
111RendererBase.draw_image = draw_image
112
113
114class FigureCanvas(FigureCanvasWxAgg):
115    """
116    Add features to the wx agg canvas for better support of AUI and
117    faster plotting.
118    """
119
120    def __init__(self, *args, **kw):
121        super(FigureCanvas, self).__init__(*args, **kw)
122        self._isRendered = False
123       
124        # Create an timer for handling draw_idle requests
125        # If there are events pending when the timer is
126        # complete, reset the timer and continue.  The
127        # alternative approach, binding to wx.EVT_IDLE,
128        # doesn't behave as nicely.
129        self.idletimer = wx.CallLater(1, self._onDrawIdle)
130        # panel information
131        self.panel = None
132        self.resizing = False
133        self.xaxis = None
134        self.yaxis = None
135        self.ndraw = 0
136        # Support for mouse wheel
137        self.Bind(wx.EVT_MOUSEWHEEL, self._onMouseWheel)
138       
139    def set_panel(self, panel):
140        """
141        Set axes
142        """
143        # set panel
144        self.panel = panel
145        # set axes
146        self.xaxis = panel.subplot.xaxis
147        self.yaxis = panel.subplot.yaxis
148       
149    def draw_idle(self, *args, **kwargs):
150        """
151        Render after a delay if no other render requests have been made.
152        """
153        self.panel.subplot.grid(self.panel.grid_on)
154        if self.panel.legend != None and self.panel.legend_pos_loc:
155            self.panel.legend._loc = self.panel.legend_pos_loc
156        self.idletimer.Restart(5, *args, **kwargs)  # Delay by 5 ms
157
158    def _onDrawIdle(self, *args, **kwargs):
159        """
160        """
161        if False and wx.GetApp().Pending():
162            self.idletimer.Restart(5, *args, **kwargs)
163        else:
164            # Draw plot, changes resizing too
165            self.draw(*args, **kwargs)
166            self.resizing = False
167           
168    def _get_axes_switch(self):
169        """
170        """
171        # Check resize whether or not True
172        if self.panel.dimension == 3:
173            return
174
175        # This is for fast response when plot is being resized
176        if not self.resizing:
177            self.xaxis.set_visible(True)
178            self.yaxis.set_visible(True)
179            self.panel.schedule_full_draw('del')
180        else:
181            self.xaxis.set_visible(False)
182            self.yaxis.set_visible(False)
183            self.panel.schedule_full_draw('append')
184        # set the resizing back to default= False
185        self.set_resizing(False)
186       
187    def set_resizing(self, resizing=False):
188        """
189        Setting the resizing
190        """
191        self.resizing = resizing
192        self.panel.set_resizing(False)
193             
194    def draw(self, drawDC=None):
195        """
196        Render the figure using agg.
197        """
198        # Only draw if window is shown, otherwise graph will bleed through
199        # on the notebook style AUI widgets.
200        #    raise
201        fig = FigureCanvasWxAgg
202        if self.IsShownOnScreen() and self.ndraw != 1:
203            self._isRendered = True
204            self._get_axes_switch()
205            #import time
206            #st = time.time()
207            try:
208                fig.draw(self)
209            except ValueError:
210                pass
211            #self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
212            #self.gui_repaint(drawDC=drawDC)
213            #print "time", time.time() - st
214        else:
215            self._isRendered = False
216        if self.ndraw <= 1:
217            self.ndraw += 1
218       
219    def _onMouseWheel(self, evt):
220        """Translate mouse wheel events into matplotlib events"""
221        # Determine mouse location
222        w, h = self.figure.canvas.get_width_height()
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()
230        #print "delta,rotation,rate",delta,rotation,rate
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
251               
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.
256       
257        :TODO: Investigate what the root cause of the problem is.
258       
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       
269        return
Note: See TracBrowser for help on using the repository browser.