casa
$Rev:20696$
|
00001 import sys 00002 import os 00003 import pylab as pl 00004 import Tkinter as Tk 00005 from matplotlib.backend_bases import cursors 00006 import matplotlib 00007 rcParams = matplotlib.rcParams 00008 from matplotlib._pylab_helpers import Gcf 00009 00010 cursord = { 00011 cursors.MOVE: "fleur", 00012 cursors.HAND: "hand2", 00013 cursors.POINTER: "arrow", 00014 cursors.SELECT_REGION: "tcross", 00015 } 00016 00017 class PlotFlag: 00018 """ 00019 (1) Start the internal python interpreter... 00020 and make the 'pylab' module from the main python/casapy namespace 00021 visible inside it. ( done inside TPPlotter ) Note that 'pylab' is the 00022 only module of casapy that is visible from this internal interpreter. 00023 00024 (2) figmanager = pl.get_current_fig_manager() 00025 -> This gets a handle to the current window, canvas, toolbar. 00026 00027 (3) Create the python-C++ call-back module -> PyBind. 00028 ( description in tables/implement/TablePlot/PlotterGlobals.cc ) 00029 00030 (3) TablePlotTkAgg.py implements a python class called 'PlotFlag' 00031 which takes an instance of 'PyBind' and 'figmanager' and makes 00032 the connection between the two. 00033 00034 - Additional buttons are placed in the toolbar, and their callbacks 00035 defined to call methods of PyBind. 00036 - The toolbar event-loop is captured - by explicitly disconnecting 00037 previous bindings, and re-defining them for 'pan','zoom','mark-region' 00038 modes. (need to do all three, to get them to interact properly with each other) 00039 - Some Canvas events are also redefined to allow mark-region boxes to 00040 automatically resize and move around, when the window is resized or 00041 when in pan or zoom modes. ( This is needed to allow flagging with 00042 zooming ). 00043 00044 (4) Back to the internal python interpreter. The following steps are carried out. 00045 -> figmanager = pl.get_current_fig_manager() 00046 -> import PyBind 00047 -> from TablePlotTkagg import PlotFlag 00048 -> pf = PlotFlag( PyBind ) 00049 -> pf.setup_custom_features( figmanager ) 00050 00051 ----> All binding is complete at this point. 00052 ----> All other logic is to ensure things like... make sure new buttons are 00053 added only when needed... make sure they *are* added when needed... and 00054 this has to keep up with the native TkAgg matplotlib backend's 00055 whimsical decisions of when to create a new figure and when not to. 00056 00057 """ 00058 def __init__(self,PyBind): 00059 #print "Init PlotFlag" 00060 self.PyBind = PyBind; 00061 self.newtoolbar = True; 00062 self.quitted = False; 00063 00064 def sub(self): 00065 #pass 00066 self.quitted = True; 00067 self.PyBind.quit(True); 00068 00069 def setup_custom_features(self,cfigman): 00070 if (rcParams['backend'].lower() == 'agg'): 00071 return 00072 self.toolbar = cfigman.toolbar; 00073 self.canvas = self.toolbar.canvas; 00074 self.window = cfigman.window; 00075 self.figmanager = cfigman; 00076 self.figmanager.window.wm_title("CASA Plotter"); 00077 self.figmanager.window.protocol("WM_DELETE_WINDOW", self.sub); 00078 00079 if self.newtoolbar is True: 00080 # Add new buttons 00081 self.add_buttons(); 00082 00083 # Reconfigure buttons. 00084 self.configure_buttons(); 00085 00086 self.newtoolbar = False; 00087 00088 # Toolbar parameters 00089 self.panel=0; 00090 self.rows=0; 00091 self.cols=0; 00092 00093 # Canvas parameters 00094 self.regionlist=[]; 00095 self.panelregionlist=[]; 00096 self.axeslist=[]; 00097 self.erase_rects(); 00098 00099 # Re-Make event bindings 00100 self.canvas.keyvald.update({65307 : 'escape'}); 00101 self.canvas.mpl_disconnect(self.toolbar._idDrag) 00102 self.toolbar._idDrag=self.canvas.mpl_connect('motion_notify_event', self.mouse_move) 00103 self.canvas._tkcanvas.bind("<KeyRelease>", self.key_release); 00104 self.canvas._tkcanvas.bind("<Configure>", self.resize); 00105 self.canvas._tkcanvas.bind("<Destroy>", self.destroy); 00106 #self.window.bind("<Destroy>", self.destroy); 00107 00108 00109 def plotflag_cleanup(self): 00110 self.canvas._tkcanvas.bind("<Destroy>", None); 00111 00112 def set_cursor(self, cursor): 00113 self.toolbar.set_cursor(cursor); 00114 #self.toolbar.window.configure(cursor=cursord[cursor]); 00115 00116 def _NewButton(self, frame, text, file, command, side=Tk.LEFT): 00117 #file = os.path.join(rcParams['datapath'], 'images', file) 00118 #file = '/opt/casa/stable/darwin/python/2.5' + file; 00119 #im = Tk.PhotoImage(master=frame, file=file) 00120 if(os.uname()[0] == 'Darwin'): 00121 b = Tk.Button(master=frame, text=text, command=command) 00122 else: 00123 b = Tk.Button(master=frame, text=text, padx=2, pady=2, command=command) 00124 #master=frame, text=text, padx=2, pady=2, image=im, command=command) 00125 #b._ntimage = im 00126 b.pack(side=side) 00127 return b 00128 00129 def add_buttons(self): 00130 #self.newframe = Tk.Frame() 00131 self.newframe = Tk.Frame(master=self.window) 00132 bside = Tk.LEFT; 00133 self.toolbar.bMarkRegion = self._NewButton( frame=self.newframe, 00134 text="Mark Region", 00135 file="markregion.ppm", 00136 #file="markregion2.ppm", 00137 command=self.markregion, 00138 side=bside) 00139 self.toolbar.bFlag = self._NewButton(frame=self.newframe, 00140 text="Flag", 00141 file="flag4.ppm", 00142 command=None, 00143 side=bside) 00144 00145 self.toolbar.bUnflag = self._NewButton(frame=self.newframe, 00146 text="Unflag", 00147 file="unflag4.ppm", 00148 command=None, 00149 side=bside) 00150 00151 self.toolbar.bLocate = self._NewButton(frame=self.newframe, 00152 text="Locate", 00153 file="locate4.ppm", 00154 command=None, 00155 side=bside) 00156 00157 self.toolbar.bIterNext = self._NewButton(frame=self.newframe, 00158 text=" Next ", 00159 file="locate4.ppm", 00160 command=None, 00161 side=bside) 00162 00163 self.toolbar.bClear =None; 00164 #self.toolbar.bClear = self._NewButton(frame=self.newframe, 00165 # text=" Clear ", 00166 # file="locate4.ppm", 00167 # command=None, 00168 # side=bside) 00169 00170 self.toolbar.bQuit = self._NewButton(frame=self.newframe, 00171 text=" Quit ", 00172 file="locate4.ppm", 00173 command=None, 00174 side=bside) 00175 self.toolbar.bMarkRegion.config(background='lightblue'); 00176 self.toolbar.bFlag.config(background='lightblue'); 00177 self.toolbar.bUnflag.config(background='lightblue'); 00178 self.toolbar.bLocate.config(background='lightblue'); 00179 self.toolbar.bIterNext.config(background='lightblue',state='disabled'); 00180 #self.toolbar.bClear.config(background='lightblue'); 00181 self.toolbar.bQuit.config(background='lightblue'); 00182 00183 self.newframe.pack(side=Tk.BOTTOM,fill=Tk.BOTH); 00184 #self.newframe.pack_propagate(); 00185 00186 def configure_buttons(self): 00187 self.toolbar.bHome.config(command=self.home); 00188 self.toolbar.bForward.config(command=self.forward); 00189 self.toolbar.bBack.config(command=self.back); 00190 self.toolbar.bsubplot.config(command=self.configure_subplots); 00191 self.toolbar.bPan.config(command=self.pan); 00192 self.toolbar.bZoom.config(command=self.zoom); 00193 self.toolbar.bMarkRegion.config(command=self.markregion); 00194 self.toolbar.bFlag.config(command=self.flag); 00195 self.toolbar.bUnflag.config(command=self.unflag); 00196 self.toolbar.bLocate.config(command=self.locate); 00197 self.toolbar.bIterNext.config(command=self.iterplotnext); 00198 #self.toolbar.bClear.config(command=self.clearplot); 00199 self.toolbar.bQuit.config(command=self.quit); 00200 #self.toolbar.bIterstop.config(command=self.iterplotstop); 00201 ### comment the next line when Wes updates matplotlib. 00202 #self.toolbar.bsave.config(command=self.savefig); 00203 00204 def flag(self, *args): 00205 self.operate(1); 00206 00207 def unflag(self, *args): 00208 self.operate(0); 00209 00210 def locate(self, *args): 00211 self.operate(2); 00212 00213 def operate(self, flag=1): 00214 #print '** Record the following regions' 00215 #for pr in self.canvas.panelregionlist: 00216 #print 'Region on panel [%(r)d,%(c)d,%(p)d] : [%(t1).3f, %(t2).3f, %(t3).3f, %(t4).3f] '%{'r':pr[5],'c':pr[6], 'p':pr[4],'t1':pr[0],'t2':pr[2], 't3':pr[1], 't4':pr[3]}; 00217 self.PyBind.markregion(self.panelregionlist); 00218 self.erase_rects(); 00219 if( flag is 1 ): 00220 #print "**Flag !!"; 00221 self.PyBind.flagdata(); 00222 if( flag is 0 ): 00223 #print "**UnFlag !!"; 00224 self.PyBind.unflagdata(); 00225 if( flag is 2 ): 00226 #print "**Locate !!"; 00227 self.PyBind.locatedata(); 00228 00229 def iterplotnext(self, *args): 00230 self.PyBind.iterplotnext(); 00231 00232 def iterplotstop(self, *args): 00233 self.PyBind.iterplotstop(); 00234 00235 def clearplot(self, *args): 00236 #print 'Gui::calling clearplot' 00237 self.PyBind.clearplot(); 00238 #print 'Gui::finished clearplot' 00239 00240 def savefig(self, *args): 00241 import time; 00242 fname = 'plot-casapy-'+time.strftime('%Y-%m-%dT%H:%M:%S')+'.png'; 00243 print 'Saving figure as ', fname, ' in current working directory.' 00244 self.canvas.figure.savefig(fname); 00245 00246 def enable_iter_button(self): 00247 if (rcParams['backend'].lower() == 'agg'): 00248 return 00249 if( self.toolbar.bIterNext is not None ): 00250 self.toolbar.bIterNext.config(state='normal'); 00251 00252 def disable_iter_button(self): 00253 if (rcParams['backend'].lower() == 'agg'): 00254 return 00255 if( self.toolbar.bIterNext is not None ): 00256 self.toolbar.bIterNext.config(state='disabled'); 00257 00258 def enable_markregion_button(self): 00259 if (rcParams['backend'].lower() == 'agg'): 00260 return 00261 if( self.toolbar.bMarkRegion is not None ): 00262 self.toolbar.bMarkRegion.config(state='normal'); 00263 00264 def disable_markregion_button(self): 00265 if (rcParams['backend'].lower() == 'agg'): 00266 return 00267 if( self.toolbar.bMarkRegion is not None ): 00268 self.toolbar.bMarkRegion.config(state='disabled'); 00269 00270 def enable_flag_button(self): 00271 if (rcParams['backend'].lower() == 'agg'): 00272 return 00273 if( self.toolbar.bFlag is not None ): 00274 self.toolbar.bFlag.config(state='normal'); 00275 00276 def disable_flag_button(self): 00277 if (rcParams['backend'].lower() == 'agg'): 00278 return 00279 if( self.toolbar.bFlag is not None ): 00280 self.toolbar.bFlag.config(state='disabled'); 00281 00282 def enable_unflag_button(self): 00283 if (rcParams['backend'].lower() == 'agg'): 00284 return 00285 if( self.toolbar.bUnflag is not None ): 00286 self.toolbar.bUnflag.config(state='normal'); 00287 00288 def disable_unflag_button(self): 00289 if (rcParams['backend'].lower() == 'agg'): 00290 return 00291 if( self.toolbar.bUnflag is not None ): 00292 self.toolbar.bUnflag.config(state='disabled'); 00293 00294 def enable_locate_button(self): 00295 if (rcParams['backend'].lower() == 'agg'): 00296 return 00297 if( self.toolbar.bLocate is not None ): 00298 self.toolbar.bLocate.config(state='normal'); 00299 00300 def disable_locate_button(self): 00301 if (rcParams['backend'].lower() == 'agg'): 00302 return 00303 if( self.toolbar.bLocate is not None ): 00304 self.toolbar.bLocate.config(state='disabled'); 00305 00306 def enable_clear_button(self): 00307 if (rcParams['backend'].lower() == 'agg'): 00308 return 00309 if( self.toolbar.bClear is not None ): 00310 self.toolbar.bClear.config(state='normal'); 00311 00312 def disable_clear_button(self): 00313 if (rcParams['backend'].lower() == 'agg'): 00314 return 00315 if( self.toolbar.bClear is not None ): 00316 self.toolbar.bClear.config(state='disabled'); 00317 00318 def enable_quit_button(self): 00319 if (rcParams['backend'].lower() == 'agg'): 00320 return 00321 if( self.toolbar.bQuit is not None ): 00322 self.toolbar.bQuit.config(state='normal'); 00323 00324 def disable_quit_button(self): 00325 if (rcParams['backend'].lower() == 'agg'): 00326 return 00327 if( self.toolbar.bQuit is not None ): 00328 self.toolbar.bQuit.config(state='disabled'); 00329 00330 def draw_rubberband(self, event, x0, y0, x1, y1): 00331 if (rcParams['backend'].lower() == 'agg'): 00332 return 00333 ### workaround for matplotlib API changes 00334 #height = self.canvas.figure.bbox.height() #0.91.4 00335 #height = self.canvas.figure.bbox.height #>=0.98 00336 height = self.get_bbox_size(self.canvas.figure.bbox,"height") #workaround 00337 y0 = height-y0 00338 y1 = height-y1 00339 try: self.toolbar.lastrect 00340 except AttributeError: pass 00341 else: self.canvas._tkcanvas.delete(self.toolbar.lastrect) 00342 self.toolbar.lastrect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1, width=2,outline='black') 00343 00344 00345 def draw_rect(self, x0, y0, x1, y1, x0data, y0data, x1data, y1data,a,panel,rows,cols): 00346 self.panelregionlist.append([x0data,y0data,x1data,y1data,panel+1,rows,cols]); 00347 self.axeslist.append(a); 00348 ### workaround for matplotlib API changes 00349 #height = self.canvas.figure.bbox.height() #0.91.4 00350 #height = self.canvas.figure.bbox.height #>=0.98 00351 height = self.get_bbox_size(self.canvas.figure.bbox,"height") #workaround 00352 y0 = height-y0 00353 y1 = height-y1 00354 if(os.uname()[0] == 'Darwin'): 00355 rect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1, width=2,outline='black') 00356 else: 00357 rect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1, width=2,fill='black',stipple='gray50',outline='black') 00358 self.regionlist.append(rect); 00359 00360 def erase_rects(self): 00361 #print "erase rects" 00362 if (rcParams['backend'].lower() == 'agg'): 00363 return 00364 for q in self.regionlist: 00365 self.canvas._tkcanvas.delete(q); 00366 self.regionlist = []; 00367 self.panelregionlist = []; 00368 self.axeslist = []; 00369 00370 00371 def redraw_rects(self): 00372 for q in self.regionlist: 00373 self.canvas._tkcanvas.delete(q); 00374 self.regionlist = []; 00375 00376 for z in range(0,len(self.panelregionlist)): 00377 q = self.panelregionlist[z]; 00378 a = self.axeslist[z]; 00379 x0=q[0]; y0=q[1]; x1=q[2]; y1=q[3]; 00380 # map to new zoom limits (current fig co-ords) 00381 ### workaround for matplotlib API changes 00382 #px0,py0 = a.transData.xy_tup( (x0, y0) ) #0.91 00383 #px0,py0 = a.transData.transform( (x0, y0) ) #>=0.98 00384 px0,py0 = self.get_xy(a.transData, (x0, y0) ) #workaround 00385 ### workaround for matplotlib API changes 00386 #px1,py1 = a.transData.xy_tup( (x1, y1) ) #0.91 00387 #px1,py1 = a.transData.transform( (x1, y1) ) #>=0.98 00388 px1,py1 = self.get_xy(a.transData, (x1, y1) ) 00389 00390 ### workaround for matplotlib API changes 00391 #height = self.canvas.figure.bbox.height() #0.91 00392 #height = self.canvas.figure.bbox.height #>=0.98 00393 height = self.get_bbox_size(self.canvas.figure.bbox,"height") #workaround 00394 py0 = height-py0 00395 py1 = height-py1 00396 if(os.uname()[0] == 'Darwin'): 00397 rect = self.canvas._tkcanvas.create_rectangle(px0, py0, px1, py1, width=2,outline='black') 00398 else: 00399 rect = self.canvas._tkcanvas.create_rectangle(px0, py0, px1, py1, width=2,fill='black',stipple='gray50',outline='black') 00400 self.regionlist.append(rect); 00401 00402 def resize(self, event): 00403 #print 'canvas resize' 00404 self.canvas.resize(event); 00405 self.redraw_rects(); 00406 00407 def destroy(self,*args): 00408 #print 'Gui::destroy.' 00409 self.erase_rects(); 00410 self.newtoolbar = True; 00411 if self.quitted is False: 00412 self.quit(closewin=True); 00413 print " "; 00414 #print "................................................................"; 00415 #print "............. Please IGNORE Tkinter error message. ............."; 00416 #print "................................................................"; 00417 00418 def quit(self, closewin=True): 00419 #print 'quit with close-window : ', closewin; 00420 self.quitted = True; 00421 self.PyBind.quit(closewin); 00422 00423 def key_release(self, event): 00424 #print 'key release' 00425 self.canvas.key_release(event); 00426 key = self.canvas._get_key(event); 00427 if(key=='escape'): 00428 numreg = len(self.regionlist); 00429 if(numreg>0): 00430 self.canvas._tkcanvas.delete(self.regionlist[numreg-1]); 00431 self.regionlist.pop(); 00432 self.panelregionlist.pop(); 00433 self.axeslist.pop(); 00434 00435 00436 def home(self, *args): 00437 'restore the original view' 00438 if (rcParams['backend'].lower() == 'agg'): 00439 return 00440 self.toolbar.home(); 00441 self.redraw_rects(); 00442 00443 def back(self, *args): 00444 'move back up the view lim stack' 00445 if (rcParams['backend'].lower() == 'agg'): 00446 return 00447 self.toolbar.back(); 00448 self.redraw_rects(); 00449 00450 def forward(self, *args): 00451 'move forward in the view lim stack' 00452 if (rcParams['backend'].lower() == 'agg'): 00453 return 00454 self.toolbar.forward(); 00455 self.redraw_rects(); 00456 00457 def configure_subplots(self): 00458 'configure subplots' 00459 if (rcParams['backend'].lower() == 'agg'): 00460 return 00461 self.toolbar.configure_subplots(); 00462 self.redraw_rects(); 00463 00464 00465 def markregion(self, *args): 00466 'activate mark-region mode' 00467 if (rcParams['backend'].lower() == 'agg'): 00468 return 00469 if self.toolbar._active == 'MARKREGION': 00470 #self.toolbar._active = None 00471 self.erase_rects(); 00472 self.update_relief(newmode=None); 00473 else: 00474 #self.toolbar._active = 'MARKREGION' 00475 self.update_relief(newmode='MARKREGION'); 00476 00477 00478 if self.toolbar._idPress is not None: 00479 self.toolbar._idPress=self.canvas.mpl_disconnect(self.toolbar._idPress) 00480 self.toolbar.mode = '' 00481 00482 if self.toolbar._idRelease is not None: 00483 self.toolbar._idRelease=self.canvas.mpl_disconnect(self.toolbar._idRelease) 00484 self.toolbar.mode = '' 00485 00486 if self.toolbar._active: 00487 self.toolbar._idPress = self.canvas.mpl_connect('button_press_event', self.press_markregion) 00488 self.toolbar._idRelease = self.canvas.mpl_connect('button_release_event', self.release_markregion) 00489 self.toolbar.mode = 'Mark Region mode' 00490 self.canvas.widgetlock(self.toolbar) 00491 else: 00492 self.canvas.widgetlock.release(self.toolbar) 00493 00494 for a in self.canvas.figure.get_axes(): 00495 a.set_navigate_mode(self.toolbar._active) 00496 00497 self.toolbar.set_message(self.toolbar.mode) 00498 00499 def press_markregion(self, event): 00500 'the press mouse button in mark region mode callback' 00501 if (rcParams['backend'].lower() == 'agg'): 00502 return 00503 if event.button == 1: 00504 self.toolbar._button_pressed=1 00505 elif event.button == 3: 00506 self.toolbar._button_pressed=3 00507 else: 00508 self.toolbar._button_pressed=None 00509 return 00510 00511 # Check that the click is inside the canvas. 00512 00513 x, y = event.x, event.y 00514 00515 # push the current view to define home if stack is empty 00516 if self.toolbar._views.empty(): self.toolbar.push_current() 00517 00518 self.toolbar._xypress=[] 00519 for i, a in enumerate(self.canvas.figure.get_axes()): 00520 #if x is not None and y is not None and a.in_axes(x, y) and a.get_navigate(): 00521 if x is not None and y is not None and event.inaxes==a and a.get_navigate(): 00522 xmin, xmax = a.get_xlim() 00523 ymin, ymax = a.get_ylim() 00524 lim = xmin, xmax, ymin, ymax 00525 ### workaround for matplotlib API changes 00526 #self.toolbar._xypress.append(( x, y, a, i, lim, a.transData.deepcopy() )) #0.91.4 00527 #self.toolbar._xypress.append(( x, y, a, i, lim, a.transData.frozen() )) #>=0.98 00528 self.toolbar._xypress.append(( x, y, a, i, lim, self.copy_trans(a.transData))) #workaround 00529 one, two, three = event.inaxes.get_geometry() 00530 self.panel = three-1 00531 self.rows = one 00532 self.cols = two 00533 00534 self.toolbar.press(event) 00535 00536 def release_markregion(self, event): 00537 'the release mouse button callback in mark region mode' 00538 if (rcParams['backend'].lower() == 'agg'): 00539 return 00540 if not self.toolbar._xypress: return 00541 00542 for cur_xypress in self.toolbar._xypress: 00543 x, y = event.x, event.y 00544 lastx, lasty, a, ind, lim, trans = cur_xypress 00545 00546 xmin, ymin, xmax, ymax = lim 00547 00548 # mark rect 00549 ### workaround for matplotlib API changes 00550 #lastx, lasty = a.transData.inverse_xy_tup( (lastx, lasty) ) #0.91.4 00551 #lastx, lasty = a.transData.inverted().transform( (lastx, lasty) ) #>=0.98 00552 lastx, lasty = self.get_inverse_xy(a.transData, (lastx, lasty) ) #workaround 00553 ### workaround for matplotlib API changes 00554 #x, y = a.transData.inverse_xy_tup( (x, y) ) #0.91.4 00555 #x, y = a.transData.inverted().transform( (x, y) ) #>=0.98 00556 x, y = self.get_inverse_xy(a.transData, (x, y) ) #workaround 00557 Xmin,Xmax=a.get_xlim() 00558 Ymin,Ymax=a.get_ylim() 00559 00560 if Xmin < Xmax: 00561 if x<lastx: xmin, xmax = x, lastx 00562 else: xmin, xmax = lastx, x 00563 if xmin < Xmin: xmin=Xmin 00564 if xmax > Xmax: xmax=Xmax 00565 else: 00566 if x>lastx: xmin, xmax = x, lastx 00567 else: xmin, xmax = lastx, x 00568 if xmin > Xmin: xmin=Xmin 00569 if xmax < Xmax: xmax=Xmax 00570 00571 if Ymin < Ymax: 00572 if y<lasty: ymin, ymax = y, lasty 00573 else: ymin, ymax = lasty, y 00574 if ymin < Ymin: ymin=Ymin 00575 if ymax > Ymax: ymax=Ymax 00576 else: 00577 if y>lasty: ymin, ymax = y, lasty 00578 else: ymin, ymax = lasty, y 00579 if ymin > Ymin: ymin=Ymin 00580 if ymax < Ymax: ymax=Ymax 00581 00582 ### workaround for matplotlib API changes 00583 #px1,py1 = a.transData.xy_tup( (xmin, ymin) ) #0.91.4 00584 #px1,py1 = a.transData.transform( (xmin, ymin) ) #>=0.98 00585 px1,py1 = self.get_xy(a.transData, (xmin, ymin) ) #workaround 00586 #px2,py2 = a.transData.xy_tup( (xmax, ymax) ) #0.91.4 00587 #px2,py2 = a.transData.transform( (xmax, ymax) ) #>=0.98 00588 px2,py2 = self.get_xy(a.transData, (xmax, ymax) ) #workaround 00589 00590 self.draw_rect(px1, py1, px2, py2, xmin, ymin, xmax, ymax, a, self.panel, self.rows, self.cols) 00591 #print 'Region on panel [%(r)d,%(c)d,%(p)d] : [%(t1).3f, %(t2).3f, %(t3).3f, %(t4).3f] '%{'r':self.rows,'c':self.cols, 'p':self.panel+1,'t1':xmin,'t2':xmax, 't3':ymin, 't4':ymax}; 00592 00593 00594 #self.toolbar.draw() 00595 self.toolbar._xypress = None 00596 self.toolbar._button_pressed = None 00597 00598 self.toolbar.push_current() 00599 self.toolbar.release(event) 00600 00601 00602 def zoom(self, *args): 00603 'activate zoom to rect mode' 00604 if (rcParams['backend'].lower() == 'agg'): 00605 return 00606 if self.toolbar._active == 'ZOOM': 00607 #self.toolbar._active = None 00608 self.update_relief(newmode=None); 00609 else: 00610 #self.toolbar._active = 'ZOOM' 00611 self.update_relief(newmode='ZOOM'); 00612 00613 if self.toolbar._idPress is not None: 00614 self.toolbar._idPress=self.canvas.mpl_disconnect(self.toolbar._idPress) 00615 self.toolbar.mode = '' 00616 00617 if self.toolbar._idRelease is not None: 00618 self.toolbar._idRelease=self.canvas.mpl_disconnect(self.toolbar._idRelease) 00619 self.toolbar.mode = '' 00620 00621 if self.toolbar._active: 00622 self.toolbar._idPress = self.canvas.mpl_connect('button_press_event', self.press_zoom) 00623 self.toolbar._idRelease = self.canvas.mpl_connect('button_release_event', self.release_zoom) 00624 self.toolbar.mode = 'Zoom to rect mode' 00625 self.canvas.widgetlock(self.toolbar) 00626 else: 00627 self.canvas.widgetlock.release(self.toolbar) 00628 00629 for a in self.canvas.figure.get_axes(): 00630 a.set_navigate_mode(self.toolbar._active) 00631 00632 self.toolbar.set_message(self.toolbar.mode) 00633 00634 00635 def press_zoom(self, event): 00636 'the press mouse button in zoom to rect mode callback' 00637 if (rcParams['backend'].lower() == 'agg'): 00638 return 00639 if event.button == 1: 00640 self.toolbar._button_pressed=1 00641 elif event.button == 3: 00642 self.toolbar._button_pressed=3 00643 else: 00644 self.toolbar._button_pressed=None 00645 return 00646 00647 x, y = event.x, event.y 00648 00649 # push the current view to define home if stack is empty 00650 if self.toolbar._views.empty(): self.toolbar.push_current() 00651 00652 self.toolbar._xypress=[] 00653 for i, a in enumerate(self.canvas.figure.get_axes()): 00654 #if x is not None and y is not None and a.in_axes(x, y) and a.get_navigate(): 00655 if x is not None and y is not None and event.inaxes==a and a.get_navigate(): 00656 xmin, xmax = a.get_xlim() 00657 ymin, ymax = a.get_ylim() 00658 lim = xmin, xmax, ymin, ymax 00659 ### workaround for matplotlib API changes 00660 #self.toolbar._xypress.append(( x, y, a, i, lim, a.transData.deepcopy() )) #0.91.4 00661 #self.toolbar._xypress.append(( x, y, a, i, lim, a.transData.frozen() )) #>=0.98 00662 self.toolbar._xypress.append(( x, y, a, i, lim, self.copy_trans(a.transData))) #workaround 00663 00664 self.toolbar.press(event) 00665 00666 00667 def release_zoom(self, event): 00668 'the release mouse button callback in zoom to rect mode' 00669 if (rcParams['backend'].lower() == 'agg'): 00670 return 00671 if not self.toolbar._xypress: return 00672 00673 for cur_xypress in self.toolbar._xypress: 00674 x, y = event.x, event.y 00675 lastx, lasty, a, ind, lim, trans = cur_xypress 00676 # ignore singular clicks - 5 pixels is a threshold 00677 if abs(x-lastx)<5 or abs(y-lasty)<5: 00678 self.toolbar._xypress = None 00679 self.toolbar.release(event) 00680 self.toolbar.draw() 00681 return 00682 00683 xmin, ymin, xmax, ymax = lim 00684 00685 # zoom to rect 00686 ### workaround for matplotlib API changes 00687 #lastx, lasty = a.transData.inverse_xy_tup( (lastx, lasty) ) #0.91.4 00688 #lastx, lasty = a.transData.inverted().transform( (lastx, lasty) ) #>=0.98 00689 lastx, lasty = self.get_inverse_xy(a.transData, (lastx, lasty) ) #workaround 00690 ### workaround for matplotlib API changes 00691 #x, y = a.transData.inverse_xy_tup( (x, y) ) #0.91.4 00692 #x, y = a.transData.inverted().transform( (x, y) ) #>=0.98 00693 x, y = self.get_inverse_xy(a.transData, (x, y) ) #workaround 00694 Xmin,Xmax=a.get_xlim() 00695 Ymin,Ymax=a.get_ylim() 00696 00697 if Xmin < Xmax: 00698 if x<lastx: xmin, xmax = x, lastx 00699 else: xmin, xmax = lastx, x 00700 if xmin < Xmin: xmin=Xmin 00701 if xmax > Xmax: xmax=Xmax 00702 else: 00703 if x>lastx: xmin, xmax = x, lastx 00704 else: xmin, xmax = lastx, x 00705 if xmin > Xmin: xmin=Xmin 00706 if xmax < Xmax: xmax=Xmax 00707 00708 if Ymin < Ymax: 00709 if y<lasty: ymin, ymax = y, lasty 00710 else: ymin, ymax = lasty, y 00711 if ymin < Ymin: ymin=Ymin 00712 if ymax > Ymax: ymax=Ymax 00713 else: 00714 if y>lasty: ymin, ymax = y, lasty 00715 else: ymin, ymax = lasty, y 00716 if ymin > Ymin: ymin=Ymin 00717 if ymax < Ymax: ymax=Ymax 00718 00719 if self.toolbar._button_pressed == 1: 00720 a.set_xlim((xmin, xmax)) 00721 a.set_ylim((ymin, ymax)) 00722 elif self.toolbar._button_pressed == 3: 00723 if a.get_xscale()=='log': 00724 alpha=log(Xmax/Xmin)/log(xmax/xmin) 00725 x1=pow(Xmin/xmin,alpha)*Xmin 00726 x2=pow(Xmax/xmin,alpha)*Xmin 00727 else: 00728 alpha=(Xmax-Xmin)/(xmax-xmin) 00729 x1=alpha*(Xmin-xmin)+Xmin 00730 x2=alpha*(Xmax-xmin)+Xmin 00731 if a.get_yscale()=='log': 00732 alpha=log(Ymax/Ymin)/log(ymax/ymin) 00733 y1=pow(Ymin/ymin,alpha)*Ymin 00734 y2=pow(Ymax/ymin,alpha)*Ymin 00735 else: 00736 alpha=(Ymax-Ymin)/(ymax-ymin) 00737 y1=alpha*(Ymin-ymin)+Ymin 00738 y2=alpha*(Ymax-ymin)+Ymin 00739 a.set_xlim((x1, x2)) 00740 a.set_ylim((y1, y2)) 00741 00742 self.toolbar.draw() 00743 self.redraw_rects(); 00744 self.toolbar._xypress = None 00745 self.toolbar._button_pressed = None 00746 00747 self.toolbar.push_current() 00748 self.toolbar.release(event) 00749 00750 00751 def pan(self,*args): 00752 'Activate the pan/zoom tool. pan with left button, zoom with right' 00753 # set the pointer icon and button press funcs to the 00754 # appropriate callbacks 00755 if (rcParams['backend'].lower() == 'agg'): 00756 return 00757 00758 if self.toolbar._active == 'PAN': 00759 #self.toolbar._active = None 00760 self.update_relief(newmode=None); 00761 else: 00762 #self.toolbar._active = 'PAN' 00763 self.update_relief(newmode='PAN'); 00764 00765 if self.toolbar._idPress is not None: 00766 self.toolbar._idPress = self.canvas.mpl_disconnect(self.toolbar._idPress) 00767 self.toolbar.mode = '' 00768 00769 if self.toolbar._idRelease is not None: 00770 self.toolbar._idRelease = self.canvas.mpl_disconnect(self.toolbar._idRelease) 00771 self.toolbar.mode = '' 00772 00773 if self.toolbar._active: 00774 self.toolbar._idPress = self.canvas.mpl_connect( 00775 'button_press_event', self.press_pan) 00776 self.toolbar._idRelease = self.canvas.mpl_connect( 00777 'button_release_event', self.release_pan) 00778 self.toolbar.mode = 'pan/zoom mode' 00779 self.canvas.widgetlock(self.toolbar) 00780 else: 00781 self.canvas.widgetlock.release(self.toolbar) 00782 00783 for a in self.canvas.figure.get_axes(): 00784 a.set_navigate_mode(self.toolbar._active) 00785 00786 self.toolbar.set_message(self.toolbar.mode) 00787 00788 00789 def press_pan(self, event): 00790 'the press mouse button in pan/zoom mode callback' 00791 if (rcParams['backend'].lower() == 'agg'): 00792 return 00793 00794 if event.button == 1: 00795 self.toolbar._button_pressed=1 00796 elif event.button == 3: 00797 self.toolbar._button_pressed=3 00798 else: 00799 self.toolbar._button_pressed=None 00800 return 00801 00802 x, y = event.x, event.y 00803 00804 # push the current view to define home if stack is empty 00805 if self.toolbar._views.empty(): self.toolbar.push_current() 00806 00807 self.toolbar._xypress=[] 00808 for i, a in enumerate(self.canvas.figure.get_axes()): 00809 #if x is not None and y is not None and a.in_axes(x, y) and a.get_navigate(): 00810 if x is not None and y is not None and event.inaxes==a and a.get_navigate(): 00811 xmin, xmax = a.get_xlim() 00812 ymin, ymax = a.get_ylim() 00813 lim = xmin, xmax, ymin, ymax 00814 ### workaround for matplotlib API changes 00815 #self.toolbar._xypress.append((x, y, a, i, lim,a.transData.deepcopy())) #0.91.4 00816 #self.toolbar._xypress.append((x, y, a, i, lim,a.transData.frozen())) #>=0.98 00817 self.toolbar._xypress.append((x, y, a, i, lim,self.copy_trans(a.transData))) #workaround 00818 self.canvas.mpl_disconnect(self.toolbar._idDrag) 00819 self.toolbar._idDrag=self.canvas.mpl_connect('motion_notify_event', self.drag_pan) 00820 00821 self.toolbar.press(event) 00822 00823 00824 def release_pan(self, event): 00825 'the release mouse button callback in pan/zoom mode' 00826 if (rcParams['backend'].lower() == 'agg'): 00827 return 00828 self.canvas.mpl_disconnect(self.toolbar._idDrag) 00829 self.toolbar._idDrag=self.canvas.mpl_connect('motion_notify_event', self.mouse_move) 00830 if not self.toolbar._xypress: return 00831 self.toolbar._xypress = None 00832 self.toolbar._button_pressed=None 00833 self.toolbar.push_current() 00834 self.toolbar.release(event) 00835 self.toolbar.draw() 00836 self.redraw_rects(); 00837 00838 00839 def drag_pan(self, event): 00840 'the drag callback in pan/zoom mode' 00841 if (rcParams['backend'].lower() == 'agg'): 00842 return 00843 00844 def format_deltas(event,dx,dy): 00845 if event.key=='control': 00846 if(abs(dx)>abs(dy)): 00847 dy = dx 00848 else: 00849 dx = dy 00850 elif event.key=='x': 00851 dy = 0 00852 elif event.key=='y': 00853 dx = 0 00854 elif event.key=='shift': 00855 if 2*abs(dx) < abs(dy): 00856 dx=0 00857 elif 2*abs(dy) < abs(dx): 00858 dy=0 00859 elif(abs(dx)>abs(dy)): 00860 dy=dy/abs(dy)*abs(dx) 00861 else: 00862 dx=dx/abs(dx)*abs(dy) 00863 return (dx,dy) 00864 00865 for cur_xypress in self.toolbar._xypress: 00866 lastx, lasty, a, ind, lim, trans = cur_xypress 00867 xmin, xmax, ymin, ymax = lim 00868 #safer to use the recorded button at the press than current button: 00869 #multiple button can get pressed during motion... 00870 if self.toolbar._button_pressed==1: 00871 ### workaround for matplotlib API changes 00872 #lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) ) #0.91.4 00873 #lastx, lasty = trans.inverted().transform( (lastx, lasty) ) #>=0.98 00874 lastx, lasty = self.get_inverse_xy(trans, (lastx, lasty) ) #workaround 00875 ### workaround for matplotlib API changes 00876 #x, y = trans.inverse_xy_tup( (event.x, event.y) ) #0.91.4 00877 #x, y = trans.inverted().transform( (event.x, event.y) ) #>=0.98 00878 x, y = self.get_inverse_xy(trans, (event.x, event.y) ) #workaround 00879 if a.get_xscale()=='log': 00880 dx=1-lastx/x 00881 else: 00882 dx=x-lastx 00883 if a.get_yscale()=='log': 00884 dy=1-lasty/y 00885 else: 00886 dy=y-lasty 00887 00888 dx,dy=format_deltas(event,dx,dy) 00889 00890 if a.get_xscale()=='log': 00891 xmin *= 1-dx 00892 xmax *= 1-dx 00893 else: 00894 xmin -= dx 00895 xmax -= dx 00896 if a.get_yscale()=='log': 00897 ymin *= 1-dy 00898 ymax *= 1-dy 00899 else: 00900 ymin -= dy 00901 ymax -= dy 00902 elif self.toolbar._button_pressed==3: 00903 try: 00904 ### workaround for matplotlib API changes 00905 #dx=(lastx-event.x)/float(a.bbox.width()) #0.91.4 00906 #dx=(lastx-event.x)/float(a.bbox.width) #>=0.98 00907 dx=(lastx-event.x)/float(self.get_bbox_size(a.bbox,"width")) #workaround 00908 ### workaround for matplotlib API changes 00909 #dy=(lasty-event.y)/float(a.bbox.height()) #0.91.4 00910 #dy=(lasty-event.y)/float(a.bbox.height) #>=0.98 00911 dy=(lasty-event.y)/float(self.get_bbox_size(a.bbox,"height")) #workaround 00912 dx,dy=format_deltas(event,dx,dy) 00913 if a.get_aspect() != 'auto': 00914 dx = 0.5*(dx + dy) 00915 dy = dx 00916 alphax = pow(10.0,dx) 00917 alphay = pow(10.0,dy)#use logscaling, avoid singularities and smother scaling... 00918 ### workaround for matplotlib API changes 00919 #lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) ) #0.91.4 00920 #lastx, lasty = trans.inverted().transform( (lastx, lasty) ) #>=0.98 00921 lastx, lasty = self.get_inverse_xy(trans, (lastx, lasty) ) #workaround 00922 if a.get_xscale()=='log': 00923 xmin = lastx*(xmin/lastx)**alphax 00924 xmax = lastx*(xmax/lastx)**alphax 00925 else: 00926 xmin = lastx+alphax*(xmin-lastx) 00927 xmax = lastx+alphax*(xmax-lastx) 00928 if a.get_yscale()=='log': 00929 ymin = lasty*(ymin/lasty)**alphay 00930 ymax = lasty*(ymax/lasty)**alphay 00931 else: 00932 ymin = lasty+alphay*(ymin-lasty) 00933 ymax = lasty+alphay*(ymax-lasty) 00934 except OverflowError: 00935 warnings.warn('Overflow while panning') 00936 return 00937 a.set_xlim(xmin, xmax) 00938 a.set_ylim(ymin, ymax) 00939 self.redraw_rects(); 00940 00941 self.toolbar.dynamic_update() 00942 00943 00944 def mouse_move(self, event): 00945 #print 'mouse_move', event.button 00946 if (rcParams['backend'].lower() == 'agg'): 00947 return 00948 00949 if not event.inaxes or not self.toolbar._active: 00950 if self.toolbar._lastCursor != cursors.POINTER: 00951 self.set_cursor(cursors.POINTER) 00952 self.toolbar._lastCursor = cursors.POINTER 00953 else: 00954 if self.toolbar._active=='ZOOM': 00955 if self.toolbar._lastCursor != cursors.SELECT_REGION: 00956 self.set_cursor(cursors.SELECT_REGION) 00957 self.toolbar._lastCursor = cursors.SELECT_REGION 00958 if self.toolbar._xypress: 00959 x, y = event.x, event.y 00960 lastx, lasty, a, ind, lim, trans= self.toolbar._xypress[0] 00961 self.draw_rubberband(event, x, y, lastx, lasty) 00962 elif self.toolbar._active=='MARKREGION': 00963 if self.toolbar._lastCursor != cursors.SELECT_REGION: 00964 self.set_cursor(cursors.SELECT_REGION) 00965 self.toolbar._lastCursor = cursors.SELECT_REGION 00966 if self.toolbar._xypress: 00967 x, y = event.x, event.y 00968 lastx, lasty, a, ind, lim, trans= self.toolbar._xypress[0] 00969 self.draw_rubberband(event, x, y, lastx, lasty) 00970 elif (self.toolbar._active=='PAN' and 00971 self.toolbar._lastCursor != cursors.MOVE): 00972 self.set_cursor(cursors.MOVE) 00973 00974 self.toolbar._lastCursor = cursors.MOVE 00975 00976 if event.inaxes and event.inaxes.get_navigate(): 00977 00978 00979 try: s = event.inaxes.format_coord(event.xdata, event.ydata) 00980 except ValueError: pass 00981 except OverflowError: pass 00982 else: 00983 if len(self.toolbar.mode): 00984 self.toolbar.set_message('%s : %s' % (self.toolbar.mode, s)) 00985 else: 00986 self.toolbar.set_message(s) 00987 else: self.toolbar.set_message(self.toolbar.mode) 00988 00989 00990 def update_relief(self,newmode): 00991 'activate new mode' 00992 if (rcParams['backend'].lower() == 'agg'): 00993 return 00994 if self.toolbar._active == 'ZOOM': 00995 self.toolbar.bZoom.config(relief='raised'); 00996 if self.toolbar._active == 'PAN': 00997 self.toolbar.bPan.config(relief='raised'); 00998 if self.toolbar._active == 'MARKREGION': 00999 self.toolbar.bMarkRegion.config(relief='raised'); 01000 01001 self.toolbar._active = newmode; 01002 01003 if self.toolbar._active == 'ZOOM': 01004 self.toolbar.bZoom.config(relief='sunken'); 01005 if self.toolbar._active == 'PAN': 01006 self.toolbar.bPan.config(relief='sunken'); 01007 if self.toolbar._active == 'MARKREGION': 01008 self.toolbar.bMarkRegion.config(relief='sunken'); 01009 01010 01011 #### Workarounds for Matplotlib version handling (ugly) #### 01012 def ismatlab_new(self): 01013 verstr=matplotlib.__version__.split(".") 01014 maj=int(verstr[0]) 01015 sub=int(verstr[1]) 01016 return (maj>0 or sub>=98) 01017 01018 def get_inverse_xy(self,trans,(x,y)): 01019 if hasattr(trans,"inverse_xy_tup"): return trans.inverse_xy_tup((x, y)) 01020 elif hasattr(trans,"inverted"): return trans.inverted().transform((x, y)) 01021 else: return None 01022 01023 def get_xy(self,trans,(x,y)): 01024 return self.switch_func(trans,["xy_tup","transform"],(x,y)) 01025 01026 def copy_trans(self,trans): 01027 return self.switch_func(trans,["deepcopy","frozen"]) 01028 01029 def get_bbox_size(self,obj,func=""): 01030 return self.get_called_or_attr(obj,func) 01031 01032 def switch_func(self,obj,funcs=[],*args,**kwargs): 01033 """ 01034 Tries a list of functions and return a result from callable one. 01035 Deals with function name changes but parameters have to be unchanged. 01036 """ 01037 for func in funcs: 01038 called_func=self.get_called_or_attr(obj,func,*args,**kwargs) 01039 if called_func != None: break 01040 return called_func 01041 01042 def get_called_or_attr(self,obj,func="",*args,**kwargs): 01043 """ 01044 Returns a result from function call if it's callable. 01045 If not callable, returns the attribute or False (non-existent). 01046 """ 01047 #if not hasattr(obj,func): return False 01048 #else: called=getattr(obj,func) 01049 try: called=getattr(obj,func) 01050 #except: return False 01051 except: return None 01052 if callable(called): return called(*args,**kwargs) 01053 else: return called 01054 01055