Package reflectometry :: Package model1d :: Package profileview :: Module layerInteractor

Source Code for Module reflectometry.model1d.profileview.layerInteractor

  1  """ 
  2  Layer interactor. 
  3  """ 
  4   
  5  import numpy 
  6  from reflectometry.model1d.model.layer import FlatLayer, SlopeLayer, \ 
  7                      SplineLayer, TetheredPolymerLayer, JoinLayer, NoLayer 
  8  from baseInteractor import BaseInteractor 
  9  from reflutils      import flatlayer_pickradius, slopelayer_pickradius, \ 
 10                             splinelayer_pickradius, interface_pickradius 
 11   
 12   
 13  # ===================== Layer interactors ======================== 
 14  # GUI starts here 
 15  # ================================================================ 
16 -class LayerInteractor(BaseInteractor):
17 """ 18 Abstract base class for the layer interactors. 19 20 Layers should define: 21 set_layer 22 move 23 save 24 restore 25 update 26 """
27 - def __init__(self, 28 base, 29 axes, 30 layer, 31 par, 32 color='black' 33 ):
34 BaseInteractor.__init__(self, base, axes, color=color) 35 self.layer = layer 36 self.markers = [] 37 self.par = par # The name of the par of this profile 38 self.model = base.model 39 self.layernum = None
40
41 - def set_n(self, n):
42 self.layernum = n
43
44 - def BestDepthLayerNum(self, x):
45 ret = -1 46 n = self.model.find( x ) 47 48 # Right 49 if abs(self.model.offset[n+1]-x) < interface_pickradius: 50 ret = n 51 # Left 52 if abs(self.model.offset[n] -x) < interface_pickradius: 53 ret = n-1 54 55 if ret < 0 : return 0 56 else: return ret
57 58
59 - def Artist2Name( self, label):
60 # Obtain Artist name 61 ValidParNames = ["mu", "rho", "theta", "phi"] 62 name = label.split("[")[0].strip() 63 64 if name in ValidParNames: 65 return label 66 else: 67 return ""
68 69
70 - def _lookupIndex( self, event ):
71 72 try: idx = self.layerMarker.index(event.artist) 73 except: idx = None 74 return idx
75 76
77 - def updateRhoValue(self, name, event):
78 """ Update the rho layer """ 79 n = self._curr_n 80 self.infopanel.updateNLayer( n ) 81 82 idx = self._lookupIndex( event ) 83 self.infopanel.updateRhoValue( event.ydata, idx=idx )
84 85
86 - def updateMuValue(self, name, event):
87 """ Update the mu layer """ 88 n = self._curr_n 89 self.infopanel.updateNLayer( n ) 90 91 idx = self._lookupIndex( event ) 92 self.infopanel.updateMuValue( event.ydata, idx=idx )
93 94
95 - def updatePhiValue(self, name, event):
96 """ Update the phi layer """ 97 n = self._curr_n 98 self.infopanel.updateNLayer( n ) 99 100 idx = self._lookupIndex( event ) 101 self.infopanel.updatePhiValue( event.ydata, idx=idx )
102 103
104 - def updateThetaValue( self, name, event):
105 106 """ Update the theta layer """ 107 n = self._curr_n 108 self.infopanel.updateNLayer( n ) 109 110 idx = self._lookupIndex( event ) 111 self.infopanel.updateThetaValue( event.ydata, idx=idx )
112 113
114 - def IsFlatLayer( self, label):
115 # Obtain Artist name 116 name = label.split("[") 117 118 if len(name) <= 1: 119 return True 120 else: 121 return False
122 123
124 - def _GetBestCurrLayerNum(self, event):
125 idx = self._lookupIndex( event ) 126 if idx==0: return self._save_depth_n +1 127 elif idx == self.getMarkerSize()-1: return self._save_depth_n 128 else: return self._save_n
129 130
131 - def getBestCurrLayerNum(self, event):
132 if self.IsFlatLayer( event.artist.get_label() ): 133 return self._save_n 134 else: 135 return self._GetBestCurrLayerNum(event)
136 137
138 - def setValue(self, event):
139 """ Update the layer value """ 140 _pn = self.Artist2Name( event.artist.get_label() ) 141 142 self._curr_n = self.getBestCurrLayerNum(event) 143 144 if len(_pn) > 0: 145 146 if _pn[:3] == "rho": self.updateRhoValue( _pn, event) 147 elif _pn[:2] == "mu": self.updateMuValue( _pn, event) 148 elif _pn[:3] == "phi": self.updatePhiValue( _pn, event) 149 elif _pn[:5] == "theta": self.updateThetaValue( _pn, event) 150 else: 151 raise ValueError("Invalid parmeter")
152 153
154 - def showRhoValue(self, event):
155 """ Show the rho layer """ 156 n = self._curr_n 157 self.infopanel.updateNLayer( n ) 158 159 idx = self._lookupIndex( event ) 160 self.infopanel.showRhoValue( idx=idx )
161 162
163 - def showMuValue(self, event):
164 """ show the mu layer """ 165 n = self._curr_n 166 self.infopanel.updateNLayer( n ) 167 168 idx = self._lookupIndex( event ) 169 self.infopanel.showMuValue( idx=idx )
170 171
172 - def showPhiValue(self, event):
173 """ show the phi layer """ 174 n = self._curr_n 175 self.infopanel.updateNLayer( n ) 176 177 idx = self._lookupIndex( event ) 178 self.infopanel.showPhiValue( idx=idx )
179 180
181 - def showThetaValue( self, event):
182 """ show the theta layer """ 183 n = self._curr_n 184 self.infopanel.updateNLayer( n ) 185 186 idx = self._lookupIndex( event ) 187 self.infopanel.showThetaValue( idx=idx )
188 189
190 - def showValue(self, event):
191 """ Update the layer value """ 192 _pn = self.Artist2Name( event.artist.get_label() ) 193 194 self._curr_n = self.getBestCurrLayerNum(event) 195 196 if len(_pn) > 0: 197 198 if _pn[:3] == "rho": self.showRhoValue( event) 199 elif _pn[:2] == "mu": self.showMuValue( event) 200 elif _pn[:3] == "phi": self.showPhiValue( event) 201 elif _pn[:5] == "theta": self.showThetaValue(event) 202 else: 203 raise ValueError("Invalid parmeter name")
204 205 206 # ----------------------------------------------------------
207 - def get_Marker(self, i ):
208 filled_markers =[ 'o', # '_draw_circle', 209 's', # '_draw_square', 210 'p', # '_draw_pentagon', 211 'd', # '_draw_thin_diamond', 212 'h', # '_draw_hexagon1', 213 '+', # '_draw_plus', 214 'x', # '_draw_x', 215 'D', # '_draw_diamond', 216 'H', # '_draw_hexagon2', 217 'v', #'_draw_triangle_down', 218 '^', # '_draw_triangle_up', 219 '<', # '_draw_triangle_left', 220 '>', # '_draw_triangle_right', 221 '1', # '_draw_tri_down', 222 '2', # '_draw_tri_up', 223 '3', # '_draw_tri_left', 224 '4', # '_draw_tri_right', 225 ] 226 return filled_markers[ i%len(filled_markers) ]
227 228 229 230 231 # ---------------------------------------------------------------------
232 -class FlatLayerInteractor(LayerInteractor):
233 """ 234 Interactor for FlatLayer to handle flat slabs. 235 """
236 - def set_layer(self, n, show=True):
237 """ 238 Setup the widgets required to edit layer n. 239 """ 240 self.layernum = n 241 v = self.layer._val 242 ax = self.axes 243 244 self.layerMarker = ax.plot( [], [], 245 '--', 246 label = self.par, 247 linewidth = 2, 248 color = self.color, 249 pickradius = flatlayer_pickradius, 250 zorder = 5 251 )[0] 252 self.markers = [ self.layerMarker ] 253 254 self.connect_markers(self.markers) 255 256 self.update(show)
257 258
259 - def update(self, show=True):
260 """ 261 Draw the widgets in their new positions. 262 """ 263 model = self.base.model 264 n = self.layernum 265 x = [ model.offset[n], model.offset[n+self.layer.span] ] 266 y = [self.layer._val]*2 267 268 h = self.markers[0] 269 h.set_data(x,y) 270 h.set_visible(show)
271 272
273 - def move(self, x, y, evt):
274 """ 275 Update the model with the new widget position. 276 """ 277 self.layer._val = y
278 279
280 - def save(self, evt):
281 """ 282 Save the current state of the model represented by the widget. 283 """ 284 self._saved_v = self.layer._val
285 286
287 - def restore(self):
288 """ 289 Restore the widget and model to the saved state. 290 """ 291 self.layer._val = self._saved_v
292 293 294 295 296 # ------------------------------------------------------------------
297 -class SlopeLayerInteractor(LayerInteractor):
298 """ 299 Interactor for SlopeLayer to handle the line control points. 300 301 For slope layer, we use 'p' marker 302 """
303 - def getMarkerSize(self):
304 return len(self.layerMarker)
305 306
307 - def set_layer(self, n):
308 """ 309 Setup the widgets required to edit layer n. 310 """ 311 self.layernum = n 312 313 left, right = self.layer._val 314 ax = self.axes 315 316 self.layerMarker = [ ax.plot( [], [], 317 linestyle = '', 318 markersize = 10, 319 label = "%s[%d]"%(self.par,i), 320 linewidth = 2, 321 color = self.color, 322 pickradius = slopelayer_pickradius, 323 zorder = 3, 324 alpha = 0.6, 325 marker = 'p', 326 visible = False 327 )[0] for i in xrange( 2 ) ] 328 329 slopeLine = ax.plot( [], [], 330 '--', 331 label = 'slope::line::'+self.par, 332 linewidth = 2, 333 color = self.color, 334 pickradius = 0, 335 zorder = 5, 336 visible = False 337 )[0] 338 339 340 self.markers = [self.layerMarker[0], self.layerMarker[1]] 341 342 self.connect_markers(self.markers) 343 self.markers.append( slopeLine ) 344 345 self.update()
346 347 348
349 - def update(self):
350 """ 351 Draw the widgets in their new positions. 352 """ 353 model = self.base.model 354 n = self.layernum 355 356 # We shift 5 point to avoid overlapping with depth marker 357 left_x = [ model.offset[n]] 358 right_x = [ model.offset[n+self.layer.span] ] 359 360 left_y = [self.layer._val[0] ] 361 right_y = [self.layer._val[1] ] 362 363 leftMarker = self.markers[0] 364 rightMarker = self.markers[1] 365 lineMarker = self.markers[2] 366 367 leftMarker.set( visible=(n>0)) 368 lineMarker.set( visible=(n>0)) 369 rightMarker.set(visible=(n>0)) 370 371 m_x = [ model.offset[n], model.offset[n+self.layer.span] ] 372 m_y = [self.layer._val[0], self.layer._val[1] ] 373 374 leftMarker.set_data( left_x, left_y ) 375 rightMarker.set_data(right_x, right_y) 376 lineMarker.set_data( m_x, m_y )
377 378 379
380 - def move(self, x, y, evt):
381 """ 382 Update the model with the new widget position. 383 """ 384 idx = self._lookupIndex( evt ) 385 386 if idx != None : 387 self.layer._val[ idx ] = y
388 389 # Otherwise: Do Nothing. 390 391 392
393 - def save(self, evt):
394 """ 395 Save the current state of the model represented by the widget. 396 """ 397 self._saved_v = self.layer._val
398 399 400
401 - def restore(self):
402 """ 403 Restore the widget and model to the saved state. 404 """ 405 self.layer._val = self._saved_v
406 407 408 409 410 #-----------------------------------------------------------------------
411 -class TetheredPolymerInteractor(LayerInteractor):
412 """ 413 Interactor for TetheredPolymer to handle bspline control points. 414 415 For TetheredPolymer layer, we use "circle" marker 416 """
417 - def Artist2Name( self, label):
418 # Obtain Artist name 419 ValidParNames = ["mu", "rho", "theta", "phi"] 420 name = label.split("_")[0].strip() 421 422 if name in ValidParNames: 423 return label 424 else: 425 return ""
426 427
428 - def setValue(self, event):
429 """ Update the layer value """ 430 _pn = self.Artist2Name( event.artist.get_label() ) 431 432 self._curr_n = self.getBestCurrLayerNum(event) 433 434 if len(_pn) > 0: 435 436 if _pn[:3] == "rho": self.updateRhoValue( _pn, event) 437 elif _pn[:2] == "mu": self.updateMuValue( _pn, event) 438 elif _pn[:3] == "phi": self.updatePhiValue( _pn, event) 439 elif _pn[:5] == "theta": self.updateThetaValue( _pn, event) 440 else: 441 raise ValueError("Invalid parmeter")
442 443
444 - def updateRhoValue(self, name, event):
445 """ Update the rho layer """ 446 n = self._curr_n 447 self.infopanel.updateNLayer( n ) 448 449 idx = self._lookupIndex( event ) 450 #print idx, name 451 if idx == 2: 452 self.infopanel.updateRhoValue( event.xdata, 453 idx=idx, 454 name=name ) 455 return 456 self.infopanel.updateRhoValue( event.ydata, idx=idx, name=name )
457 458
459 - def updateMuValue(self, name, event):
460 """ Update the mu layer """ 461 n = self._curr_n 462 self.infopanel.updateNLayer( n ) 463 464 idx = self._lookupIndex( event ) 465 self.infopanel.updateMuValue( event.ydata, idx=idx, name=name )
466 467
468 - def updatePhiValue(self, name, event):
469 """ Update the phi layer """ 470 n = self._curr_n 471 self.infopanel.updateNLayer( n ) 472 473 idx = self._lookupIndex( event ) 474 self.infopanel.updatePhiValue( event.ydata, idx=idx, name=name )
475 476
477 - def updateThetaValue( self, name, event):
478 479 """ Update the theta layer """ 480 n = self._curr_n 481 self.infopanel.updateNLayer( n ) 482 483 idx = self._lookupIndex( event ) 484 self.infopanel.updateThetaValue( event.ydata, idx=idx, name=name )
485 486
487 - def getMarkerSize(self):
488 return len(self.layerMarker)
489 490
491 - def set_layer(self, n):
492 """ 493 Setup the widgets required to edit layer n. 494 """ 495 self.layernum = n 496 497 ax = self.axes 498 499 polymerLine = ax.plot( [], [], 500 '--', 501 label = "%s_polymerSLD"%self.par, 502 linewidth = 2, 503 color = self.color, 504 pickradius = flatlayer_pickradius, 505 zorder = 5, 506 visible = False 507 )[0] 508 509 solventLine = ax.plot( [], [], 510 '--', 511 label = "%s_solventSLD"%self.par, 512 linewidth = 2, 513 color = self.color, 514 pickradius = flatlayer_pickradius, 515 zorder = 5, 516 visible = False 517 )[0] 518 519 L0Line = ax.axvline(x=0, 520 linewidth=2, 521 linestyle='--', 522 label="%s_L0"%self.par, 523 color=self.color, 524 alpha=0.5, 525 pickradius=interface_pickradius 526 ) 527 528 self.layerMarker = [ polymerLine, solventLine, L0Line] 529 530 self.markers = [] 531 for i in xrange( self.getMarkerSize() ): 532 self.markers.append(self.layerMarker[i]) 533 534 self.connect_markers(self.markers) 535 self.update()
536 537
538 - def update(self):
539 """ 540 Draw the widgets in their new positions. 541 """ 542 model = self.base.model 543 n = self.layernum 544 545 left_x = model.offset[n] 546 right_x = model.offset[n+self.layer.span] 547 span = right_x - left_x 548 549 L0 = self.layer._val[2] 550 nv = 2 551 552 if span*0.1 > L0: Lshift = L0 553 else: Lshift = span*0.1 554 555 control = [ 556 [left_x, left_x+Lshift], 557 [right_x-span*0.1, right_x] 558 ] 559 for i in xrange(nv): 560 self.markers[i].set(visible=(n>0)) 561 562 # Spline line 563 for i in xrange(nv): 564 m_x = [ control[i][0], control[i][1] ] 565 m_y = [ self.layer._val[i], self.layer._val[i] ] 566 self.markers[i].set_data(m_x, m_y) 567 568 self.markers[2].set_xdata( [L0, L0])
569 570
571 - def move(self, x, y, evt):
572 """ 573 Update the model with the new widget position. 574 """ 575 idx = self._lookupIndex( evt ) 576 577 if idx != None : 578 self.layer._val[idx] = y
579 580
581 - def save(self, evt):
582 """ 583 Save the current state of the model represented by the widget. 584 """ 585 self._saved_v = self.layer._val
586 587
588 - def restore(self):
589 """ 590 Restore the widget and model to the saved state. 591 """ 592 self.layer._val = self._saved_v
593 594 595 596 #-----------------------------------------------------------------------
597 -class SplineLayerInteractor(LayerInteractor):
598 """ 599 Interactor for SplineLayer to handle bspline control points. 600 601 For spline layer, we use "circle" marker 602 """
603 - def getMarkerSize(self):
604 return len(self.layerMarker)
605 606
607 - def set_layer(self, n):
608 """ 609 Setup the widgets required to edit layer n. 610 """ 611 self.layernum = n 612 613 ax = self.axes 614 615 splineLines = [ ax.plot( [], [], 616 '--', 617 label = 'slope::line::'+self.par, 618 linewidth = 2, 619 color = self.color, 620 pickradius = 0, 621 zorder = 5, 622 visible = False 623 )[0] for i in xrange(len(self.layer._val)-1) ] 624 625 self.layerMarker = [ax.plot( [], [], 626 linestyle='', 627 markersize = 10, 628 label = "%s[%d]"%(self.par,i), 629 linewidth = 2, 630 color = self.color, 631 pickradius = splinelayer_pickradius, 632 zorder = 3, 633 alpha = 0.6, 634 marker = 'o', 635 visible = False 636 )[0] for i in xrange( len(self.layer._val) ) ] 637 638 639 # FIXME: use fast way to combine two lists into a single list 640 self.markers = [] 641 for i in xrange( len(self.layerMarker) ): 642 self.markers.append(self.layerMarker[i]) 643 644 self.connect_markers(self.markers) 645 646 for i in xrange( len(splineLines) ): 647 self.markers.append( splineLines[i] ) 648 649 self.update()
650 651
652 - def update(self):
653 """ 654 Draw the widgets in their new positions. 655 """ 656 model = self.base.model 657 n = self.layernum 658 659 left_x = model.offset[n] 660 right_x = model.offset[n+self.layer.span] 661 span = right_x - left_x 662 663 nv = len( self.layer._val ) 664 control_z = numpy.arange(0.0, nv)/(nv-1.0)*span + left_x 665 666 for i in xrange(nv*2-1): 667 self.markers[i].set(visible=(n>0)) 668 669 # spline Markers 670 for i in xrange(nv): 671 self.markers[i].set_data(control_z[i], self.layer._val[i]) 672 673 # spline line 674 for i in xrange(nv-1): 675 m_x = [ control_z[i], control_z[i+1] ] 676 m_y = [ self.layer._val[i], self.layer._val[i+1] ] 677 self.markers[i+nv].set_data(m_x, m_y)
678 679
680 - def move(self, x, y, evt):
681 """ 682 Update the model with the new widget position. 683 """ 684 idx = self._lookupIndex( evt ) 685 if idx != None : 686 self.layer._val[idx] = y
687 688
689 - def save(self, evt):
690 """ 691 Save the current state of the model represented by the widget. 692 """ 693 self._saved_v = self.layer._val
694 695
696 - def restore(self):
697 """ 698 Restore the widget and model to the saved state. 699 """ 700 self.layer._val = self._saved_v
701 702 703 704 705 # -------------------------------------------------------------------
706 -class NoLayerInteractor(LayerInteractor):
707 """ 708 Null Interactor for undefined layers. 709 """
710 - def set_layer(self, n):
711 pass
712
713 - def update(self):
714 pass
715
716 - def move(self, x, y):
717 pass
718
719 - def save(self):
720 pass
721
722 - def restore(self):
723 pass
724 725 726 727 # ======================== LayerInteractor factory ==================== 728 # Associate layers with layer interactors through function 729 # interactor(layer) 730 # =====================================================================
731 -class _LayerInteractorFactory:
732 """ 733 Given a layer, find the associated interactor. 734 """
735 - def __init__(self):
743 - def __call__(self, 744 base, 745 axes, 746 layer, 747 par, 748 **kw 749 ):
750 if layer.__class__ in self.template: 751 return self.template[layer.__class__](base, axes, layer, par,**kw) 752 else: 753 return NoLayerInteractor(base, axes, layer, par,**kw)
754 755 Interactor = _LayerInteractorFactory() 756